20145326蔡馨熠《网络对抗》shellcode注入&Return-to-libc攻击深入
20145326蔡馨熠《网络对抗》shellcode注入&Return-to-libc攻击深入
准备一段shellcode
-
首先我们应该知道,到底什么是shellcode。经过上网查询资料,得知Shellcode实际是一段代码(也可以是填充数据),是用来发送到服务器利用特定漏洞的代码,一般可以获取权限。另外,Shellcode一般是作为数据发送给受攻击服务器的。 Shellcode是溢出程序和蠕虫病毒的核心,提到它自然就会和漏洞联想在一起,毕竟Shellcode只对没有打补丁的主机有用武之地。网络上数以万计带着漏洞顽强运行着的服务器给hacker和Vxer丰盛的晚餐。漏洞利用中最关键的是Shellcode的编写。由于漏洞发现者在漏洞发现之初并不会给出完整Shellcode,因此掌握Shellcode编写技术就显得尤为重要。
-
经过刘宇栋老师和许心远同学的前期铺垫,我们这次实践显得相对容易了。
-
参考资料
-
这次用的shellcode跟老师一样。
设置环境
- 需要提前安装execstack这个指令。先是用“execstack -s ”来设置堆栈可执行;然后用“execstack -q ”来查询文件的堆栈是否可执行,“X”表示文件可执行;“more /proc/sys/kernel/randomizevaspace”用来查询地址随机化是开启状态还是关闭状态,参数“2”表示开启,参数“0”表示关闭。
构造要注入的payload
- 刘老师上课就讲了,Linux下有两种基本构造攻击buf的方法:retaddr+nop+shellcode和nop+shellcode+retaddr。因为retaddr在缓冲区的位置是固定的,所以shellcode要不在它前面,要不就在它后面。
-
简单一点,意思就是如果缓冲区小,就把shellcode放后边,缓冲区大就把shellcode放前边。
-
至于我们这个buf呢,它够放这个shellcode了,所以结构为:nops+shellcode+retaddr。
-
最开始我也很好奇nop到底有什么用,后来得知在无源码调试程序中,可以用NOP将一段功能取消掉,还可以将NOP的代码段改成自己的代码(类似shellcode),用处非常多。在这里,一为是了填充,二是作为“着陆区/滑行区”。返回地址只要落在任何一个nop上,自然会滑到我们的shellcode。
-
在终端中输入如下指令:
-
上面最后的\x4\x3\x2\x1将覆盖到堆栈上的返回地址的位置。我们得把它改为这段shellcode的地址。 特别提醒:最后一个字符千万不能是\x0a。不然下面的操作就做不了了,\x0a代表回车,我们继续往下看。接下来要做的,就是确定\x4\x3\x2\x1到底该填什么。
此时,我们再打开一个终端,并输入如下指令。注意输入完指令后,先不要急着按回车键!!!切记!!!不然后面调试进程的时候会出错误!!
-
再开另外一个终端,用gdb来调试20145326cxy_shellcode这个进程。
-
如图的标记,我找到了20145326cxy_shellcode的进程号是2989。“ps -ef | grep 20145326cxy_shellcode”命令意为查找与20145326cxy_shellcode有关的进程,并用-ef格式显示出来。然后启动gdb调试这个进程,通过设置断点,来查看注入buf的内存地址; GDB可以对正在执行的程序进行调度,它允许开发人员中断程序并查看其状态,之后还能让这个程序正常地继续执行,我们通常使用“attach”命令来实现这个功能;使用“ disassemble foo”可以反汇编一段内存地址,使我们更直观的观察程序运行。指令“c”代表继续执行被调试的程序,直至下一个断点或程序结束,是Continue的简写;如图,我们应该断在ret指令那儿。ret完,就跳到我们覆盖的retaddr那个地方了。
于是在0x080484ae那儿设置断点。 使用“break *0x080484ae”,此时再在之前“cat...”那个终端按下回车。
如果之前“cat...”那会儿就敲了回车键,那么在找进程号的时候会出错,attach的时候也会报错,不信可以自己试试。
-
关于分析,老师给的实验指导里写得很清楚,我就简单说一下。就是结合地址来具体分析,然后找到shellcode代码,shellcode代码起始地址为0xffffd2fc,所以我们将返回地址改为0xffffd300。
-
后来发现这是个坑啊!!!可以,很愉快。于是又经过一阵子的gdb调试找问题。最后发现可能是代码也在堆栈上,当前栈顶也在这,一push就把指令自己给覆盖了。原来如此!不过没关系,离成功不远了!
Try again
- 结构为:anything+retaddr+nops+shellcode。
在最开始的gdb调试中,明明就已经看到了0x01020304,这就是返回地址的位置,shellcode就挨着的呀,所以地址不就是0xffffd31c加上4个字节等于0xffffd320么!!!于是做如下修改!
Finally,we succeed~!悄悄告诉你们,我因为一个小错误,结果一直出不来,一个晚上都没解决!觉都没睡好!还好我 never give up,第二天重新来过,终于成功了。这个小错误是什么呢?就是我在替换\x4\x3\x2\x1的时候,写成了\00\d3\ff\ff。把十六进制的标识“x”忘了!!!所以呀,细节决定成败!!!希望大家不要犯和我一样的错误!
Return-to-libc攻击深入
基础知识
-
缓冲区溢出的常用攻击方法是用 shellcode 的地址来覆盖漏洞程序的返回地址,使得漏洞程序去执行存放在栈中的 shellcode。为了阻止这种类型的攻击,一些操作系统使得系统管理员具有使栈不可执行的能力。这样的话,一旦程序执行存放在栈中的 shellcode 就会崩溃,从而阻止了攻击。
-
现在存在一种缓冲区溢出的变体攻击,叫做 return-to-libc 攻击。这种攻击不需要一个栈可以执行,甚至不需要一个 shellcode。取而代之的是我们让漏洞程序调转到现存的代码来实现我们的攻击。攻击者在实施攻击时仍然可以用恶意代码的地址(比如 libc 库中的 system()函数等)来覆盖程序函数调用的返回地址,并传递重新设定好的参数使其能够按攻击者的期望运行。这就是为什么攻击者会采用return-into-libc的方式,并使用程序提供的库函数。这种攻击方式在实现攻击的同时,也避开了数据执行保护策略中对攻击代码的注入和执行进行的防护。
实践过程
- 输入如下命令,安装一些用于编译32位C程序的东西。(因为这一步忘了截图,所以用的是老师的图~)
-
输入命令“linux32”进入 32 位 linux 环境。输入“/bin/bash”使用 bash。
-
Ubuntu 和其他一些 Linux 系统中,使用地址空间随机化来随机堆和栈的初始地址,这使得猜测准确的内存地址变得十分困难,而猜测内存地址是缓冲区溢出攻击的关键。因此本次实验中,我们要关闭这一功能。
-
linux 系统中,/bin/sh 实际是指向/bin/bash 或/bin/dash 的一个符号链接。为了重现这一防护措施被实现之前的情形,我们使用另一个 shell 程序(zsh)代替/bin/bash。
- 把以下代码(漏洞程序)保存为“retlib.c”文件,保存到 /tmp 目录下。代码如下:
- 编译该程序,并设置 SET-UID,命令如下。“gcc -z execstack -o test test.c” 表示栈可执行;"gcc -z noexecstack -o test test.c"表示栈不可执行。GCC 编译器有一种栈保护机制来阻止缓冲区溢出,所以我们在编译代码时需要用 “–fno-stack-protector” 关闭这种机制。
- 我们还需要用到一个读取环境变量的程序,再编译一下。
- 把以下代码(攻击程序)保存为“exploit.c”文件,保存到 /tmp 目录下。代码如下。
- 代码中“0x11111111”、“0x22222222”、“0x33333333”分别是 BIN_SH、system、exit 的地址。
- 需要我们接下来获取。用刚才的 getenvaddr 程序获得 BIN_SH 地址。
- gdb 获得 system 和 exit 地址。
- 修改 exploit.c 文件。
- 删除刚才调试编译的 exploit 程序和 badfile 文件,重新编译修改后的 exploit.c,然后先运行攻击程序 exploit,再运行漏洞程序 retlib,可见攻击成功,获得了 root 权限!!!!!成功了!!!
举一反三
将/bin/sh 重新指向/bin/bash(或/bin/dash),观察能否攻击成功,能否获得 root 权限。
- 之前的实践做出来后,因为太兴奋,直接关掉了虚拟机,没发现还有后面的练习。 很尴尬,于是我重新打开了虚拟机,又从头开始做了一遍,边做边深入理解。
- 我突然发现之前在Return-to-libc中实践的时候,没有注明自己的学号,太大意了! 不过没关系,人生豪迈,不过从头再来。这一遍我得注明学号了!!!以免盗图!!!我还是很有版权意识~~~
- 好了,言归正传。
- 首先,我们将/bin/sh重新指向/bin/bash。结果发现无法获取root权限,因为bash内置了权限降低的机制;如果我们要想在/bin/sh指向/bin/bash时获取root权限,那我们就要想办法在调用/bin/bash之前提升正在运行的进程的Set-UID至root权限,Linux下有一个setuid()函数,它很有用。
- 对于setuid(),它可以用来重新设置执行目前进程的用户识别码,不过,要让此函数有作用,其有效的用户识别码必须为0(root)。在调用系统函数system(“/bin/sh”)之前,我们可以先调用系统函数setuid(0)来提升权限。在bof的返回地址处(&buf[24])写入setuid()的地址,setuid的地址同样可以通过gdb来获得。
- 修改攻击程序。
- 修改完成后,编译运行,发现攻击成功!
posted on 2017-03-07 17:15 20145326蔡馨熠 阅读(332) 评论(3) 编辑 收藏 举报