20145227鄢曼君《网络对抗》shellcode注入&Return-to-libc攻击深入
shellcode注入实践
shellcode基础知识
- Shellcode实际是一段代码,但却作为数据发送给受攻击服务器,将代码存储到对方的堆栈中,并将堆栈的返回地址利用缓冲区溢出,覆盖成为指向 shellcode的地址。
实践过程
- 获取shellcode的C语言代码
- 将环境设置为:堆栈可执行、地址随机化关闭
- 选择
anything+retaddr+nops+shellcode
的结构构造攻击buf。我们用\x4\x3\x2\x1
覆盖到堆栈上的返回地址的位置,把它改为这段shellcode的地址
- 在终端注入这段攻击buf。注意输入指令之后不要立即按回车,在后面的调试过程中需要继续运行的时候再回车
- 再打开另外一个终端,查找与20145227pwn1有关的进程,并用-ef格式显示出来,找到该进程的进程号为2066
- 打开gdb,用attach指令对该进程进行调试
- 对foo函数进行反汇编
- 在ret处设置断点,查看注入buf的内存地址,来分析我们之前猜测的返回地址位置是否正确以及shellcode的地址
图中
0x01020304
的位置0xffffd30c
是返回地址的位置,由于结构为anything+retaddr+nops+shellcode
,由此推断我们的shellcode的地址为0xffffd320
将返回地址修改为0xffffd320重新注入,成功!
Return-to-libc攻击深入
Return-to-libc攻击简介
- shellcode注入攻击在实施时通常首先要将恶意代码注入目标漏洞程序中。但是,程序的代码段通常设置为不可写,因此攻击者需要将此攻击代码置于堆栈中。于是为了阻止此种类型的攻击,缓冲区溢出防御机制采用了非执行堆栈技术,这种技术使得堆栈上的恶意代码不可执行。为了避开这种防御机制,缓冲区溢出又出现了新的变体 return-into-libc 攻击。
- Return-into-libc攻击方式不具有同时写和执行的行为模式,因为其不需要注入新的恶意代码,取而代之的是重用漏洞程序中已有的函数完成攻击,让漏洞程序跳转到已有的代码序列(比如库函数的代码序列)。攻击者在实施攻击时仍然可以用恶意代码的地址(比如 libc 库中的 system()函数等)来覆盖程序函数调用的返回地址,并传递重新设定好的参数使其能够按攻击者的期望运行。这就是为什么攻击者会采用return-into-libc的方式,并使用程序提供的库函数。这种攻击方式在实现攻击的同时,也避开了数据执行保护策略中对攻击代码的注入和执行进行的防护。
- 攻击者可以利用栈中的内容实施return-into-libc攻击。这是因为攻击者能够通过缓冲区溢出改写返回地址为一个库函数的地址,并且将此库函数执行时的参数也重新写入栈中。这样当函数调用时获取的是攻击者设定好的参数值,并且结束后返回时就会返回到库函数而不是 main()。而此库函数实际上就帮助攻击者执行了其恶意行为。更复杂的攻击还可以通过 return-into-libc的调用链(一系列库函数的连续调用)来完成。
攻击过程
- 本次实践在32位Linux下进行,先设置32位linux环境。
- 进入32位linux环境后,将地址随机化关闭,并且把/bin/sh指向zsh:/bin/bash能够通过使shell程序放弃自己的root权限来防范缓冲区溢出攻击及其他利用shell程序的攻击,在本实践中,我们用zsh程序替代/bin/bash程序,解除此防护措施。
- 将漏洞程序保存在/tmp目录下,并编写20145227retlib.c程序,代码如下:
- 编译该代码,使用
–fno-stack-protector
来关闭阻止缓冲区溢出的栈保护机制,并设置给该程序的所有者以suid权限,可以像root用户一样操作:
- 编写并编译20145227getenvaddr.c。这个程序是用来读取环境变量的,代码如下:
- 编写攻击程序,写好后先放到tmp目录下。
- 获取 BIN_SH 地址(如下图为0xffffde21)。
- 利用gdb获得system和exit地址:
- 将上述所找到的三个内存地址填写在20145227exploit.c中:
- 删除刚才调试编译的20145227exploit程序和badfile文件,重新编译修改后的20145227exploit.c:
- 先运行攻击程序20145227exploit,再运行漏洞程序20145227retlib,攻击成功,获得了root权限: