网易首页 > 网易号 > 正文 申请入驻

FreeBSD Wi-Fi协议栈中的堆溢出漏洞分析

0
分享至

今年4月,FreeBSD修复了Wi-Fi协议栈中潜藏了13年的堆溢出漏洞,该漏洞允许网络附件的攻击者在受影响的FreeBSD Kernel安装中执行任意代码。这个漏洞最初是由一位名为m00nbsd的研究人员报告给ZDI的,并在2022年4月作为FreeBSD-SA-22:07.wifi_meshid进行了修复。该研究人员慷慨地提供了这份关于该漏洞的详细文章,以及演示该漏洞的PoC代码。

我们的目标是利用FreeBSD内核的Wi-Fi协议栈中的堆溢出漏洞,从而在目标FreeBSD系统上实现内核远程代码执行。这个漏洞已分配编号CVE-2022-23088,并影响到2009年以来的所有FreeBSD版本,以及许多FreeBSD的衍生产品,如pfSense和OPNsense。目前,该漏洞已经于2022年4月被FreeBSD项目组所修复。

当系统在扫描到可用的Wi-Fi网络时,它会监听其附近的Wi-Fi接入点所发出的管理帧。实际上,存在多种类型的管理帧,但我们只对一种类型感兴趣:信标管理子类型,其格式如下所示:

struct ieee80211_beacon {

uint8_t i_fc[2];

uint8_t i_dur[2];

uint8_t i_addr1[IEEE80211_ADDR_LEN];

uint8_t i_addr2[IEEE80211_ADDR_LEN];

uint8_t i_addr3[IEEE80211_ADDR_LEN];

uint8_t i_seq[2];

// ... sequence of ieee80211_option structures of varying lengths ...

struct ieee80211_option {

uint8_t id;

uint8_t len;

// ... ‘len’ bytes of data ...

在FreeBSD中,信标帧是通过ieee80211_parse_beacon()函数进行解析的。该函数将遍历帧中的选项序列,并保留指向稍后将使用的选项的指针。其中,我们对选项IEEE80211_ELEMID_MESHID特别感兴趣:

wh = mtod(m, struct ieee80211_frame *);

frm = (uint8_t *)&wh[1];

efrm = mtod(m, uint8_t *) + m->m_len;

// Iterate over the sequence of options in the packet

while (efrm - frm > 1) {

switch (*frm) {

case IEEE80211_ELEMID_MESHID:

// Keep a pointer to the packet’s MeshId option

scan->meshid = frm;

break;

// Advance to the next option

frm += frm[1] + 2;

注意,这里并没有对选项长度(frm[1])执行健全性检查。稍后,在函数sta_add()中,memcpy调用会将该选项长度作为size参数,并将一个固定大小的缓冲区作为目标缓冲区:

if (sp->meshid != NULL && sp->meshid[1] != 0)

memcpy(ise->se_meshid, sp->meshid, 2+sp->meshid[1]);

由于缺乏对选项长度的必要检查,因此这就满足一个理想的缓冲区溢出条件:攻击者可以同时控制溢出的大小(sp->meshid[1]) 以及将要写入的内容(sp->meshid)。

因此,当FreeBSD系统正在扫描可用的网络时,攻击者可以通过发送一个具有超大的IEEE80211_ELEMID_MESHID选项的信标帧来触发内核堆溢出。

构建Write-What-Where原语

下面,让我们来看看ise->se_meshid,它就是在内存复制过程中被溢出的缓冲区——其定义位于ieee80211_scan_entry结构体中:

struct ieee80211_scan_entry {

uint8_t se_meshid[2+IEEE80211_MESHID_LEN]; // #define IEEE80211_MESHID_LEN 32

struct ieee80211_ies se_ies;

这里,se_meshid的溢出允许攻击者覆盖后面的se_ies字段。而se_ies字段的类型为struct ieee80211_ies,定义如下:

struct ieee80211_ies {

uint8_t *wpa_ie;

uint8_t *rsn_ie;

uint8_t *wme_ie;

uint8_t *ath_ie;

uint8_t *htcap_ie;

uint8_t *htinfo_ie;

uint8_t *tdma_ie;

uint8_t *meshid_ie;

uint8_t *vhtcap_ie;

uint8_t *vhtopmode_ie;

uint8_t *vhtpwrenv_ie;

uint8_t *apchanrep_ie;

uint8_t *bssload_ie;

uint8_t *spare[4];

uint8_t *data; // [1/2] Remember these two fields

int len; // [2/2]

在这里,我们覆盖的目标是最后两个字段,即data字段与len字段。

回到sta_add(),在调用memcpy函数之后不久,就有一个函数调用用到了se_ies字段:

if (sp->meshid != NULL && sp->meshid[1] != 0)

// This can overwrite ‘se_ies’

memcpy(ise->se_meshid, sp->meshid, 2+sp->meshid[1]);

// This uses ‘se_ies’ as first argument

(void) ieee80211_ies_init(&ise->se_ies, sp->ies, sp->ies_len);

这个ieee80211_ies_init()函数的定义为:

// ‘ies’ can be overflown into, so we can fully control its contents

int

ieee80211_ies_init(struct ieee80211_ies *ies, const uint8_t *data, int len)

memset(ies, 0, offsetof(struct ieee80211_ies, data));

$0 if (ies->data != NULL && ies->len != len) { // <-- Ayy, we control these two fields

IEEE80211_FREE(ies->data, M_80211_NODE_IE);

ies->data = NULL;

if (ies->data == NULL) {

ies->data = (uint8_t *) IEEE80211_MALLOC(len, M_80211_NODE_IE,

IEEE80211_M_NOWAIT | IEEE80211_M_ZERO);

if (ies->data == NULL) {

ies->len = 0;

return 0;

$1 memcpy(ies->data, data, len); // <-- I believe in miracles

ies->len = len;

return 1;

这里的data参数指向帧中信标选项的开头位置,len为所有信标选项的整体长度。换句话说,(data,len)对描述了选项缓冲区,它是攻击者所发送的帧的一部分。

图1 选项缓冲区

正如代码片段中所指出的,由于缓冲区的溢出问题,IES结构体将完全处于攻击者的控制之下。

因此,在$1处:

我们可以控制len字段,考虑到其中存放的是选项缓冲区的整体长度,我们可以通过在帧中添加或删除选项来决定其值。

我们可以控制data字段的值,因为它是选项缓冲区的内容。

我们可以通过溢出来控制ires->data指针。

这样,我们就得到了一个近乎完美的write-what-where原语,我们可以利用它在任意内核内存地址处写入任意数据,为此,我们只需发送一个具有超大MeshId选项的信标帧。

该原语所面临的各种约束

该原语具有下列约束:

FreeBSD内核希望帧中含有SSID和Rates选项。这意味着有2x2=4字节的选项缓冲区是我们无法控制的。此外,我们的超大MeshId选项有一个2字节的头部,因此共有6字节是我们无法控制的6。为了方便和简单起见,我们将把这些字节放在选项缓冲区的开头位置。

我们的超大MeshId选项必须大到可以覆盖IES->data和IES->len字段,但不能覆盖其他字段。具体来说,这个长度就是MeshId选项182字节的长度,加上其2字节头部。为了容纳MeshId选项和上面提到的两个选项,我们将使用一个188字节的选项缓冲区。

我们覆盖的IES->data和IES->len字段分别是选项缓冲区中的最后8个字节和4个字节,因此它们也受到约束。

ies->len字段必须等于len,这样$0处的分支才不会被执行。

给定前面提到的约束条件后,帧的总体布局如下所示:

图2 帧布局示意图

保持目标的稳定性

假设我们发送一个信标帧,以触发我们的原语覆盖内核内存的一个区域。当内核随后试图释放ies->data时,就会出现问题。这是因为ies->data应该指向一个malloc分配的缓冲区,而在我们的覆盖它之后,就未必还是这样了。

为了保持稳定性并避免目标崩溃,我们可以发送第二个纠正信标帧,将ies->data覆盖为NULL,将ies->len覆盖为0。之后,当内核试图释放ies->data时,它将发现指针为NULL,因此不会执行任何操作。这就能保持目标的稳定性,并确保我们的原语不会使其崩溃。

选择写什么,写到哪里

现在我们有了一个很好的write-what-where原语,只需一个信标帧和一个纠正帧就能触发它。那么,我们可以用它来做什么呢?

首先想到的,就是使用该原语覆盖内核在内存中执行的指令。幸运的是,在FreeBSD中,内核的text段是不可写的,所以我们无法使用该原语直接覆盖内核指令。此外,由于内核实现了w^x机制,因此没有可写页是可执行的。

但是,映射可执行页的页表是可写的。

注入implant

在这里,我们将向目标的内核注入一个implant,它用于处理通过Wi-Fi帧发送给它的“命令”——如果你愿意,这也可以是一个完整的内核后门。

注入这个implant的过程将需要四个信标帧。

这是一个有点颠簸的技术旅程,所以请系好安全带。

第1帧:注入payload

背景知识:直接映射是一个特殊的内核内存区域,它将系统的整个物理内存连续映射为可写页,但是只读text段的物理页除外。

我们使用该原语在物理地址0x1000处使用直接映射写入数据。

图3 第1帧

这里选择的物理地址为0x1000,因为它未被使用。我们写入的数据如下所示:

图4 第1帧

其中,该implant的shellcode区域存放的是我们的implant的指令。其余的字段将在下面解释。

第2帧:覆盖L3 PTE

背景知识:x64 CPU使用页表将虚拟地址映射到物理RAM页,具有从L4(根)到L0(叶)的4级层次结构,这方面的更多细节,请参考AMD和Intel的官方规范。

在这一步中,我们使用原语来覆盖L3页表项(PTE),并使其指向我们在物理地址0x1000处作为第1帧的一部分写入的L2 PTE。我们精心设计的L2 PTE精确地指向我们的三个L1 PTE,这些PTE本身指向两个不同的区域:(1)内核text段的两个物理页,和(2)我们implant的shellcode。

换句话说,通过覆盖一个L3 PTE,我们在页面表中创建了一个分支,将存放内核代码的两页内存映射为可写的,同时,将我们的shellcode所在内存映射为可执行的。

图5 第2帧

现在,我们的shellcode已经被映射到了目标的虚拟内存空间,并为执行做好了充分的准备。我们将在下面解释,为什么要对内核text段的两个页面进行映射。

细心的读者可能会关心这里的原语所面临的限制。实际上,并没什么好担心的:L3 PTE空间大部分是未填充的。我们只需要选择一个未填充的地址范围,只要在这个范围内覆盖188个字节,就不会出现任何问题。

第3帧:给text段打补丁

为了跳转到我们新映射的shellcode,我们将修补text段中sta_input()函数的开头部分。这个函数是Wi-Fi协议栈的一部分,每当内核收到Wi-Fi帧时都会调用它,所以,这里似乎是调用我们的implant的最佳位置。

但是,内核text段是不可写的,我们怎样才能修补它呢?方法是将包含该函数的text页映射为可写的。这就是我们发送第1帧和第2帧的缘故:有了前两个L1 PTE,我们就能得到sta_input()的指令字节的可写视图:

图6 sta_input()的可写视图

通过第1帧和第2帧创建的可写视图,我们就可以使用前面的原语来修补sta_input()函数开头部分的字节,并将其替换为:

48 b8 77 66 55 44 movabs $AddressOfOurImplantShellCode,%rax

33 22 11 00

ff d0 callq *%rax

这样就能调用我们的shellcode了。然而,我们的原语的约束条件也开始发挥作用了:

原语的第一个约束是,我们无法控制覆盖的前6个字节。覆盖sta_input的内存并非上上之策,因为它将在函数的开头部分放置6个字节的垃圾内容,导致目标崩溃。

然而,如果我们看一下sta_input的指令转储,就会发现其内存布局还是很不错的:

8f90

8f90: 5e pop %rsi

8f91: 41 5f pop %r15

8f93: 5d pop %rbp

8f94: c3 retq

8f95: 66 2e 0f 1f 84 00 nopw %cs:0x0(%rax,%rax,1)

8f9c: 00 00 00 00

8f9f: 90 nop

8fa0

8fa0: 55 push %rbp | GETS PATCHED

8fa1: 48 89 e5 mov %rsp,%rbp | GETS PATCHED

8fa4: 41 57 push %r15 | GETS PATCHED

8fa6: 41 56 push %r14 | GETS PATCHED

8fa8: 41 55 push %r13 | GETS PATCHED

8faa: 41 54 push %r12 | GETS PATCHED

8fac: 53 push %rbx

8fad: 48 83 ec 48 sub $0x48,%rsp

所以,我们可以覆盖sta_input开头部分前6个字节的内存,因为这6个字节的内容,只是覆盖之前sta_newstate()函数的不可达的NOP操作,对执行并没有影响。

通过这个技巧,我们就无需担心这前6个字节,因为它们被有效地废弃了。

原语的第二个约束是,我们被强制覆盖182个字节,因此,我们不能只覆盖前几个字节。这其实不是一个问题,因为可以用内存中已经存在的相同指令字节来填充其余的字节。

原语的第三个约束是,我们写入的最后12个字节是ires->data和ires->len字段,这些字段不能解析为有效的指令。这的确是一个问题,因为这些字节位于 sta_input()内。如果内核试图执行这些字节,很快就会崩溃。为了解决这个问题,我们必须在implant中设置相应的纠正代码。当第一次调用时,我们的implant必须纠正写入sta_input()的最后12个字节的内容。虽然稍微有点复杂,但还是可以搞定的。

解除这些约束之后,sta_input()函数每次被调用时,我们的implant就会被执行,也就是说,每次目标机器收到Wi-Fi帧时都会执行它。

第4帧:纠正帧

最后,为了保持目标的稳定性,我们还需发送最后一个信标帧:在这个帧中,我们将ies->data覆盖为NULL,将ies->len覆盖为0。

这样就能确保目标不会崩溃。

后续的通信信道

利用前面提到的四个帧,我们就能可靠地将implant注入到目标机器的内核中。并且,我们的implant将在与sta_input()完全相同的上下文中被调用:

static int

sta_input(struct ieee80211_node *ni, struct mbuf *m,

const struct ieee80211_rx_stats *rxs, int rssi, int nf)

值得注意的是,第二个参数m是包含内核当前正在处理的帧的缓冲区的内存区域。因此,implant可以查看该缓冲区(通过%rsi)的内容,并根据其内容执行相应的操作。

换句话说,我们为implant找到了一条有效的通信信道。因此,我们可以将implant编码为服务器后门,并通过该通道向其发送命令。

现在,让我们来回顾一下:

  • FreeBSD内核中存在一个堆缓冲区溢出漏洞;并且,攻击者可以通过发送一个带有过大MeshId选项的信标帧来触发这个漏洞。

  • 利用这个漏洞,攻击者可以实现一个write-what-where原语,对发送的每个信标帧执行一次内核内存覆盖。

  • 只需将这个原语执行三次,就能把implant注入到目标系统的内核中。

  • 第4个信标帧被用来清理es->data和es->len字段,以防止崩溃。

  • 最后,我们的implant就能在目标系统的内核中运行,作为一个完整的后门,可以处理发送给它的后续Wi-Fi帧。

实际上,完整的漏洞利用代码可以从https://github.com/thezdi/PoC/tree/master/CVE-2022-23088处找到。这份代码的运行机制是:先注入一个简单的implant程序,然后,该程序将使用攻击者随后发送的字符串来调用printf()函数。为了在Wi-Fi网卡名为wifi0的Linux 机器上使用该exploit,需要使用以下命令将网卡切换到监控模式:

$ sudo ifconfig wifi0 down$ sudo iwconfig wifi0 mode monitor$ sudo ifconfig wifi0 up

然后,为所需的目标系统构建并运行漏洞利用。举例来说,如果目标系统为pfSense 2.5.2,则可以使用下面的命令:

$ ./build.py kernels/pfSense-2.5.2-RELEASE$ sudo ./exploit.py wifi0 kernels/pfSense-2.5.2-RELEASE[+] Phase 1: writing page1[+] Phase 2: writing L3[+] Phase 3: patching kernel[+] Phase 4: repairing[+] Finished> Hello there, I’m in the kernel

请务必在安全的测试环境中运行上述命令,因为这会对您附近的所有易受攻击的系统造成影响。

完成上面的操作后,查看目标系统的tty0控制台。如果一切顺利的话,应该会看到“Hello there, I’m in the kernel”。

参考及来源:https://www.zerodayinitiative.com/blog/2022/6/15/cve-2022-23088-exploiting-a-heap-overflow-in-the-freebsd-wi-fi-stack

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
丝芭传媒再发文:要求鞠婧祎方立刻停止侵权抹黑,将就其和关联人涂某某等涉嫌严重经济犯罪行为,向相关监管机构实名举报

丝芭传媒再发文:要求鞠婧祎方立刻停止侵权抹黑,将就其和关联人涂某某等涉嫌严重经济犯罪行为,向相关监管机构实名举报

极目新闻
2025-12-18 11:17:51
卷走53亿!又一大佬带全家跑路,欠中国银行20亿,投资者血本无归

卷走53亿!又一大佬带全家跑路,欠中国银行20亿,投资者血本无归

以茶带书
2025-12-09 23:33:58
16GB+1TB!新机官宣:12月26日,全新上市

16GB+1TB!新机官宣:12月26日,全新上市

科技堡垒
2025-12-21 11:37:47
妇产科你听过最炸裂的八卦是啥?网友:这么年轻就能怀孕吗

妇产科你听过最炸裂的八卦是啥?网友:这么年轻就能怀孕吗

带你感受人间冷暖
2025-12-20 00:05:18
80 后 OG 天后太绝了!颜值气质封神,退圈仍让人念念不忘

80 后 OG 天后太绝了!颜值气质封神,退圈仍让人念念不忘

萝卜有点甜
2025-12-13 16:00:40
冬至前夜探访成都“羊肉汤一条街”:生意不如往年火爆 有商家表示“不涨价”

冬至前夜探访成都“羊肉汤一条街”:生意不如往年火爆 有商家表示“不涨价”

封面新闻
2025-12-21 10:33:14
深扒许亚军现任张澍朋友圈,才懂何晴输在哪,和廖京生竟是这关系

深扒许亚军现任张澍朋友圈,才懂何晴输在哪,和廖京生竟是这关系

阿纂看事
2025-12-20 13:42:56
重磅!全球首款,2nm手机芯片诞生,已开始量产

重磅!全球首款,2nm手机芯片诞生,已开始量产

每日经济新闻
2025-12-21 16:08:07
明星下场了,赞一个!

明星下场了,赞一个!

西楼饮月
2025-12-20 22:23:26
美军悍然扣押中方油轮!大陆拦截美对台岛军售的船只,时机已到了

美军悍然扣押中方油轮!大陆拦截美对台岛军售的船只,时机已到了

大国观察眼
2025-12-22 00:10:52
波尔解说德甲焦点大战:樊振东独得2分,萨尔布吕肯3-2险胜杜塞

波尔解说德甲焦点大战:樊振东独得2分,萨尔布吕肯3-2险胜杜塞

乒谈
2025-12-22 01:20:02
王诗龄打卡哈尔滨冰雪大世界,穿迪奥束腰棉服,捧着冰块好可爱

王诗龄打卡哈尔滨冰雪大世界,穿迪奥束腰棉服,捧着冰块好可爱

小椰的奶奶
2025-12-22 00:55:49
高盛:未来2年房价或再跌10-30%  警告可能出现房价负反馈循环

高盛:未来2年房价或再跌10-30% 警告可能出现房价负反馈循环

财富情报局
2025-12-19 23:06:25
中国男人,正在美国化

中国男人,正在美国化

茶狐看世界本尊
2025-12-19 23:45:04
1984年,叶剑英病情危重,中央已安排追悼会,危机时刻钟南山一举扭转局面

1984年,叶剑英病情危重,中央已安排追悼会,危机时刻钟南山一举扭转局面

文史明鉴
2025-12-19 18:18:20
中场在更衣室泼奶昔?伦纳德:当时有点生气,不过也就那么一说

中场在更衣室泼奶昔?伦纳德:当时有点生气,不过也就那么一说

懂球帝
2025-12-22 07:56:12
要解开南京博物院捐赠之谜,建议先查清这神秘的两个人

要解开南京博物院捐赠之谜,建议先查清这神秘的两个人

李老逵乱摆龙门阵
2025-12-20 10:54:00
携程被大量用户卸载!注销按钮点击超300万次,平台暂未发布道歉

携程被大量用户卸载!注销按钮点击超300万次,平台暂未发布道歉

火山詩话
2025-12-21 06:24:06
南京博物院风波后续:全网追的神秘“老同志”,他到底是谁?

南京博物院风波后续:全网追的神秘“老同志”,他到底是谁?

恪守原则和底线
2025-12-22 05:40:06
周末打虎!江西省政协副主席尹建业任上落马,长期在云南任职

周末打虎!江西省政协副主席尹建业任上落马,长期在云南任职

界面新闻
2025-12-21 16:53:19
2025-12-22 08:32:49
嘶吼RoarTalk incentive-icons
嘶吼RoarTalk
不一样的互联网安全新视界
8094文章数 10544关注度
往期回顾 全部

科技要闻

生态适配已超95% 鸿蒙下一关:十万个应用

头条要闻

鲁比奥急转弯猛夸中国 介文汲:他深谙官场之道法

头条要闻

鲁比奥急转弯猛夸中国 介文汲:他深谙官场之道法

体育要闻

勇士火箭赢球:王牌之外的答案?

娱乐要闻

星光大赏太尴尬!抢话挡镜头,场地还小

财经要闻

老房子“强制体检”,政府出手了

汽车要闻

-30℃,标致508L&凡尔赛C5 X冰雪"大考"

态度原创

本地
家居
手机
游戏
旅游

本地新闻

云游安徽|访黄山云海古村,读一城山水风骨

家居要闻

高端私宅 理想隐居圣地

手机要闻

小屏机遇冷,厂商计划新增大屏旗舰

PlayStation 5 Pro国行版体验报告:画质性能全都要"/> 主站 商城 论坛 自运营 登录 注册 PlayStation 5 Pro国行版体...

旅游要闻

【征集展示】黑龙江大学生:辽宁冰雪不“冻”人

无障碍浏览 进入关怀版