Linux内核TCP MSS机制详细分析
前言
上周Linux内核修复了4个CVE漏洞[1],其中的CVE-2019-11477感觉是一个很厉害的Dos漏洞,不过因为有其他事打断,所以进展的速度比较慢,这期间网上已经有相关的分析文章了。[2][3]
而我在尝试复现CVE-2019-11477漏洞的过程中,在第一步设置MSS的问题上就遇到问题了,无法达到预期效果,但是目前公开的分析文章却没对该部分内容进行详细分析。所以本文将通过Linux内核源码对TCP的MSS机制进行详细分析。
测试环境
1. 存在漏洞的靶机
操作系统版本:Ubuntu 18.04
内核版本:4.15.0-20-generic
地址:192.168.11.112
内核源码:
$ sudo apt install linux-source-4.15.0
$ ls /usr/src/linux-source-4.15.0.tar.bz2
带符号的内核:
$ cat /etc/apt/sources.list.d/ddebs.list
deb http://ddebs.ubuntu.com/ bionic main
deb http://ddebs.ubuntu.com/ bionic-updates main
$ sudo apt install linux-image-4.15.0-20-generic-dbgsym
$ ls /usr/lib/debug/boot/vmlinux-4.15.0-20-generic
关闭内核地址随机化(KALSR):
# 内核是通过grup启动的,所以在grup配置文件中,内核启动参数里加上nokaslr
$ cat /etc/default/grub |grep -v "#" | grep CMDLI
GRUB_CMDLINE_LINUX_DEFAULT="nokaslr"
GRUB_CMDLINE_LINUX=""
$ sudo update-grub
装一个nginx,供测试:
$ sudo apt install nginx
2. 宿主机
操作系统:MacOS
Wireshark:抓流量
虚拟机:VMware Fusion 11
调试Linux虚拟机:
$ cat ubuntu_18.04_server_test.vmx|grep debug
debugStub.listen.guest64 = "1"
编译gdb:
$ ./configure --build=x86_64-apple-darwin --target=x86_64-linux --with-python=/usr/local/bin/python3
$ make
$ sudo make install
$ cat .zshrc|grep gdb
alias gdb="~/Documents/gdb_8.3/gdb/gdb"
gdb进行远程调试:
$ gdb vmlinux-4.15.0-20-generic
$ cat ~/.gdbinit
define gef
source ~/.gdbinit-gef.py
end
define kernel
target remote :8864
end
3. 攻击机器
自己日常使用的Linux设备就好了
地址:192.168.11.111
日常习惯使用Python的,需要装个scapy构造自定义TCP包
自定义SYN的MSS选项
有三种方法可以设置TCP SYN包的MSS值
1. iptable
# 添加规则
$ sudo iptables -I OUTPUT -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 48
# 删除
$ sudo iptables -D OUTPUT -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 48
2. route
# 查看路由信息
$ route -ne
$ ip route show
192.168.11.0/24 dev ens33 proto kernel scope link src 192.168.11.111 metric 100
# 修改路由表
$ sudo ip route change 192.168.11.0/24 dev ens33 proto kernel scope link src 192.168.11.111 metric 100 advmss 48
# 修改路由表信息就是在上面show的结果后面加上 advmss 8
3. 直接发包设置
PS:使用scapy发送自定义TCP包需要ROOT权限
from scapy.all import *
ip = IP(dst="192.168.11.112")
tcp = TCP(dport=80, flags="S",options=[('MSS',48),('SAckOK', '')])
flags选项S表示SYN
,A表示ACK
,SA表示SYN, ACK
scapy中TCP可设置选项表:
TCPOptions = (
{
0 : ("EOL",None),
1 : ("NOP",None),
2 : ("MSS","!H"),
3 : ("WScale","!B"),
4 : ("SAckOK",None),
5 : ("SAck","!"),
8 : ("Timestamp","!II"),
14 : ("AltChkSum","!BH"),
15 : ("AltChkSumOpt",None),
25 : ("Mood","!p"),
254 : ("Experiment","!HHHH")
},
{
"EOL":0,
"NOP":1,
"MSS":2,
"WScale":3,
"SAckOK":4,
"SAck":5,
"Timestamp":8,
"AltChkSum":14,
"AltChkSumOpt":15,
"Mood":25,
"Experiment":254
})
但是这个会有一个问题,在使用Python发送了一个SYN包以后,内核会自动带上一个RST包,查过资料后,发现在新版系统中,对于用户发送的未完成的TCP握手包,内核会发送RST包终止该连接,应该是为了防止进行SYN Floor攻击。解决办法是使用iptable过滤RST包:
$ sudo iptables -A OUTPUT -p tcp --tcp-flags RST RST -s 192.168.11.111 -j DROP
对于MSS的深入研究
关于该漏洞的细节,别的文章中已经分析过了,这里简单的提一下,该漏洞为uint16溢出:
tcp_gso_segs 类型为uint16
tcp_set_skb_tso_segs:
tcp_skb_pcount_set(skb, DIV_ROUND_UP(skb->len, mss_now));
skb->len的最大值为17 * 32 * 1024
mss_now的最小值为8
>>> hex(17*32*1024//8)
'0x11000'
>>> hex(17*32*1024//9)
'0xf1c7'
所以在mss_now小于等于8时,才能发生整型溢出。
深入研究的原因是因为进行了如下的测试:
攻击机器通过iptables/iproute
命令将MSS值为48后,使用curl请求靶机的http服务,然后使用wireshark抓流量,发现服务器返回的http数据包的确被分割成小块,但是只小到36,离预想的8有很大的差距
这个时候我选择通过审计源码和调试来深入研究为啥MSS无法达到我的预期值,SYN包中设置的MSS值到代码中的mss_now的过程中发生了啥?
随机进行源码审计,对发生溢出的函数tcp_set_skb_tso_segs
进行回溯:
tcp_set_skb_tso_segs <- tcp_fragment <- tso_fragment <- tcp_write_xmit
最后发现,传入tcp_write_xmit函数的mss_now都是通过tcp_current_mss函数进行计算的
随后对tcp_current_mss
函数进行分析,关键代码如下:
# tcp_output.c
tcp_current_mss -> tcp_sync_mss:
mss_now = tcp_mtu_to_mss(sk, pmtu);
tcp_mtu_to_mss:
/* Subtract TCP options size, not including SACKs */
return __tcp_mtu_to_mss(sk, pmtu) -
(tcp_sk(sk)->tcp_header_len - sizeof(struct tcphdr));
__tcp_mtu_to_mss:
if (mss_now < 48)
mss_now = 48;
return mss_now;
看完这部分源码后,我们对MSS的含义就有一个深刻的理解,首先说一说TCP协议:
TCP协议包括了协议头和数据,协议头包括了固定长度的20字节和40字节的可选参数,也就是说TCP头部的最大长度为60字节,最小长度为20字节。
在__tcp_mtu_to_mss
函数中的mss_now
为我们SYN包中设置的MSS,从这里我们能看出MSS最小值是48,通过对TCP协议的理解和对代码的理解,可以知道SYN包中MSS的最小值48字节表示的是:TCP头可选参数最大长度40字节 + 数据最小长度8字节。
但是在代码中的mss_now表示的是数据的长度,接下来我们再看该值的计算公式。
tcphdr结构:
struct tcphdr {
__be16 source;
__be16 dest;
__be32 seq;
__be32 ack_seq;
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u16 res1:4,
doff:4,
fin:1,
syn:1,
rst:1,
psh:1,
ack:1,
urg:1,
ece:1,
cwr:1;
#elif defined(__BIG_ENDIAN_BITFIELD)
__u16 doff:4,
res1:4,
cwr:1,
ece:1,
urg:1,
ack:1,
psh:1,
rst:1,
syn:1,
fin:1;
#else
#error "Adjust your <asm/byteorder.h> defines"
#endif
__be16 window;
__sum16 check;
__be16 urg_ptr;
};
该结构体为TCP头固定结构的结构体,大小为20bytes
变量tcp_sk(sk)->tcp_header_len
表示的是本机发出的TCP包头部的长度。
因此我们得到的计算mss_now的公式为:SYN包设置的MSS值 – (本机发出的TCP包头部长度 – TCP头部固定的20字节长度)
所以,如果tcp_header_len
的值能达到最大值60,那么mss_now就能被设置为8。那么内核代码中,有办法让tcp_header_len
达到最大值长度吗?随后我们回溯该变量:
# tcp_output.c
tcp_connect_init:
tp->tcp_header_len = sizeof(struct tcphdr);
if (sock_net(sk)->ipv4.sysctl_tcp_timestamps)
tp->tcp_header_len += TCPOLEN_TSTAMP_ALIGNED;
#ifdef CONFIG_TCP_MD5SIG
if (tp->af_specific->md5_lookup(sk, sk))
tp->tcp_header_len += TCPOLEN_MD5SIG_ALIGNED;
#endif
所以在Linux 4.15内核中,在用户不干预的情况下,内核是不会发出头部大小为60字节的TCP包。这就导致了MSS无法被设置为最小值8,最终导致该漏洞无法利用。
总结
我们来总结一下整个流程:
攻击者构造SYN包,自定义TCP头部可选参数MSS的值为48
靶机(受到攻击的机器)接收到SYN请求后,把SYN包中的数据保存在内存中,返回SYN,ACK包。
攻击者返回ACK包
三次握手完成
随后根据不同的服务,靶机主动向攻击者发送数据或者接收到攻击者的请求后向攻击者发送数据,这里就假设是一个nginx http服务。
攻击者向靶机发送请求:
GET / HTTP/1.1
。靶机接收到请求后,首先计算出
tcp_header_len
,默认等于20字节,在内核配置sysctl_tcp_timestamps
开启的情况下,增加12字节,如果编译内核的时候选择了CONFIG_TCP_MD5SIG
,会再增加18字节,也就是说tcp_header_len
的最大长度为50字节。随后需要计算出mss_now = 48 – 50 + 20 = 18
这里假设一下该漏洞可能利用成功的场景:有一个TCP服务,自己设定了TCP可选参数,并且设置满了40字节,那么攻击者才有可能通过构造SYN包中的MSS值来对该服务进行Dos攻击。
随后我对Linux 2.6.29至今的内核进行审计,mss_now的计算公式都一样,tcp_header_len
长度也只会加上时间戳的12字节和md5值的18字节。
参考
https://github.com/Netflix/security-bulletins/blob/master/advisories/third-party/2019-001.md
Linux内核TCP MSS机制详细分析的更多相关文章
- armv8(aarch64)linux内核中flush_dcache_all函数详细分析【转】
转自:http://blog.csdn.net/qianlong4526888/article/details/12062809 版权声明:本文为博主原创文章,未经博主允许不得转载. /* * __ ...
- armv8(aarch64)linux内核中flush_dcache_all函数详细分析
/* * __flush_dcache_all() * Flush the wholeD-cache. * Corrupted registers: x0-x7, x9-x11 */ ENTRY( ...
- Linux内核态抢占机制分析(转)
Linux内核态抢占机制分析 http://blog.sina.com.cn/s/blog_502c8cc401012pxj.html 摘 要]本文首先介绍非抢占式内核(Non-Preemptive ...
- Linux内核抢占实现机制分析【转】
Linux内核抢占实现机制分析 转自:http://blog.chinaunix.net/uid-24227137-id-3050754.html [摘要]本文详解了Linux内核抢占实现机制.首先介 ...
- Linux内核poll/select机制简析
0 I/O多路复用机制 I/O多路复用 (I/O multiplexing),提供了同时监测若干个文件描述符是否可以执行IO操作的能力. select/poll/epoll 函数都提供了这样的机制,能 ...
- Linux内核 TCP/IP、Socket参数调优
Linux内核 TCP/IP.Socket参数调优 2014-06-06 Harrison.... 阅 9611 转 165 转藏到我的图书馆 微信分享: Doc1: /proc/sy ...
- Linux内核中锁机制之RCU、大内核锁
在上篇博文中笔者分析了关于完成量和互斥量的使用以及一些经典的问题,下面笔者将在本篇博文中重点分析有关RCU机制的相关内容以及介绍目前已被淘汰出内核的大内核锁(BKL).文章的最后对<大话Linu ...
- Linux内核中锁机制之完成量、互斥量
在上一篇博文中笔者分析了关于信号量.读写信号量的使用及源码实现,接下来本篇博文将讨论有关完成量和互斥量的使用和一些经典问题. 八.完成量 下面讨论完成量的内容,首先需明确完成量表示为一个执行单元需要等 ...
- Linux内核中锁机制之内存屏障、读写自旋锁及顺序锁
在上一篇博文中笔者讨论了关于原子操作和自旋锁的相关内容,本篇博文将继续锁机制的讨论,包括内存屏障.读写自旋锁以及顺序锁的相关内容.下面首先讨论内存屏障的相关内容. 三.内存屏障 不知读者是是否记得在笔 ...
随机推荐
- 如何在SAP UI5应用里添加使用摄像头拍照的功能
昨天Jerry的文章 纯JavaScript实现的调用设备摄像头并拍照的功能 介绍了纯JavaScript借助WebRTC API来开发支持调用设备的摄像头拍照的web应用.而我同事遇到的实际情况是, ...
- Linux网卡上添加多个IP
1.查看现有网卡ip addr show看到ens_xxx 2.进入网卡配置项cd /etc/sysconfig/network-scripts/vim ifcfg-ens192末尾添加2行:IPAD ...
- Nacos Docker集群部署
参考文档:https://nacos.io/zh-cn/docs/quick-start-docker.html 1.从git上下载nacos-docker项目,本地目录为/docksoft/naco ...
- 三步操作gitHub汉化插件安装--谷歌浏览器
如果本文对你有用,请爱心点个赞,提高排名,帮助更多的人.谢谢大家!❤ 如果解决不了,可以在文末进群交流. 一个好用基于chrome的插件,用来汉化gitHub,大致效果图如下: 步骤一: 首先下载谷歌 ...
- layui 单选框、复选框、下拉菜单 不显示问题 记录
1. 如果是 ajax嵌套了 页面, 请确保 只有最外层的页面引入了 layui.css 和 layui.js 内层页面 切记不要再次引入 2. layui.use(['form', 'upload ...
- go中三个点(...)用法
go命令中三个点含义 An import path is a pattern if it includes one or more "..." wildcards, each of ...
- POST请求接口实列
通过响应状态来判断是否读取数据与抛出异常,然后通过判断获取的字节数去读取数据或抛出异常 /** * 发送HttpPost请求 * @param strURL * 服务地址 * @param param ...
- China International Industry Fair 2019
Today i visit the CIIF 2019, as a "professional visitor" since i have made an appointment ...
- linux系统编程之信号(四)
今天继续探讨信号相关的东东,话不多说,正入正题: 信号在内核中的表示: 下面用图来进一步描述这种信号从产生到递达之间的状态(信号阻塞与未诀): 那是怎么来决定的呢?下面慢慢来举例分解: 所以,通过 ...
- qingqing的项目
1 https://www.cnblogs.com/zhangqing979797/p/10147679.html 2 https://www.cnblogs.com/zhangqing979797/ ...