20145215《网络对抗》shellcode注入&Return-to-libc攻击深入
Shellcode注入
基础知识
- Shellcode实际是一段代码,但却作为数据发送给受攻击服务器,将代码存储到对方的堆栈中,并将堆栈的返回地址利用缓冲区溢出,覆盖成为指向 shellcode的地址。
实践过程
shellcode的生成方法指导书上已经写得很详细了,在做实验时我直接用的是老师上课用的shellcode:
将环境设置为:堆栈可执行、地址随机化关闭
选择
anything+retaddr+nops+shellcode
的结构构造攻击buf,先猜测返回地址所在位置,并且找到shellcode
所在地址在终端注入这段攻击buf:
先不输入“回车”,在后面的调试过程中需要继续运行的时候再回车,此时再打开另外一个终端,用gdb来调试
20145215pwn1
这个进程,先找到该进程的进程ID,再打开gdb,用attach
指令对该进程进行调试:对
foo
函数进行反汇编:在
ret
处设置断点,接着继续运行到断点处,显示当前esp的值并依照此位置显示接下来的内存地址内容,来分析我们之前猜测的返回地址位置是否正确以及shellcode
的地址:- 由上图可以看出,第一个红色方块中的内容是我们之前猜测返回地址而输入的值,第二个方块中的内容则是
shellcode
代码,由此我们可以推断出shellcode
地址为:0xffffd384
继续运行,如红色方块中所示,可以确认返回地址是被我们之前输入的
\x01\x02\x03\x04
所覆盖的:将返回地址修改为
0xffffd384
,重新注入,可以发现已经成功了!
Return-to-libc攻击深入
基础知识
-
Return-into-libc
攻击方式不具有同时写和执行的行为模式,因为其不需要注入新的恶意代码,取而代之的是重用漏洞程序中已有的函数完成攻击,让漏洞程序跳转到已有的代码序列(比如库函数的代码序列)。攻击者在实施攻击时仍然可以用恶意代码的地址(比如 libc 库中的 system()函数等)来覆盖程序函数调用的返回地址,并传递重新设定好的参数使其能够按攻击者的期望运行。这就是为什么攻击者会采用return-into-libc
的方式,并使用程序提供的库函数。这种攻击方式在实现攻击的同时,也避开了数据执行保护策略中对攻击代码的注入和执行进行的防护。 - 攻击者可以利用栈中的内容实施
return-into-libc
攻击。这是因为攻击者能够通过缓冲区溢出改写返回地址为一个库函数的地址,并且将此库函数执行时的参数也重新写入栈中。这样当函数调用时获取的是攻击者设定好的参数值,并且结束后返回时就会返回到库函数而不是 main()。而此库函数实际上就帮助攻击者执行了其恶意行为。更复杂的攻击还可以通过return-into-libc
的调用链(一系列库函数的连续调用)来完成。
实践过程
其实一开始想用实验楼的环境来做,但是不知道为什么在输入sudo的随机密码时一直提示错误,所以最后就直接在kali上做了。当然,这个攻击最终的目标是要获取root权限,因此在做之前,我先另外添加了一个用户:
进入32位linux环境,将地址随机化关闭,并且把
/bin/sh
指向zsh
:将漏洞程序保存在
/tmp
目录下:编译该代码,使用
–fno-stack-protector
来关闭阻止缓冲区溢出的栈保护机制,并设置给该程序的所有者以suid权限,可以像root用户一样操作:读取环境变量的程序:
将攻击程序保存在
/tmp
目录下:用刚才的
getenvaddr
程序获得BIN_SH
地址:利用gdb获得
system
和exit
地址:将上述所找到的三个内存地址填写在
20145215exploit.c
中:删除刚才调试编译的
20145215exploit
程序和badfile
文件,重新编译修改后的20145215exploit.c
:先运行攻击程序
20145215exploit
,再运行漏洞程序20145215retlib
,攻击成功,获得了root权限:
深入思考
将/bin/sh重新指向/bin/bash时的攻击
首先,我们将/bin/sh重新指向/bin/bash,运行漏洞程序:
- 发现无法获取root权限,因为bash内置了权限降低的机制,虽然我们可以使得bof返回时执行system(“/bin/sh”),但是我们也依旧获取不到root权限。
- 如果我们要想在/bin/sh指向/bin/bash时获取root权限,那我们就要想办法在调用/bin/bash之前提升正在运行的进程的Set-UID至root权限,那么我们就可以绕过对bash的权限限制。
之后在查资料时发现了Linux下一个setuid()函数,查看其帮助文档:
- 从帮助文档中可以得知setuid()用来重新设置执行目前进程的用户识别码,不过,要让此函数有作用,其有效的用户识别码必须为0(root)。在Linux 下,当root 使用setuid()来变换成其他用户识别码时,root 权限会被抛弃,完全转换成该用户身份,也就是说,该进程往后将不再具有可setuid()的权利。
- 因此我们可以利用函数setuid(0)来实现我们的目标,在调用系统函数system(“/bin/sh”)之前,先调用系统函数setuid(0)来提升权限。
我们先要对攻击程序进行修改,在bof的返回地址处(&buf[24])写入setuid()的地址,setuid的地址同样可以通过gdb来获得:
setuid的参数0写在与其入口地址相隔一个字节的位置(即buf[32])处(因为setuid()执行完毕之后,会转向存放setuid入口地址的下一个位置,所以这个位置应该放入system函数的入口地址),同理system的参数放入&buf[36]处。
修改完成后,编译运行,发现攻击成功!