CVE-2022-26937 Windows NFS 栈溢出漏洞分析

news2025/4/6 14:03:13

简介

NFS全称Network File System,即网络文件系统,用于服务器和客户机之间文件访问和共享的通信,从而使客户机远程访问保存在存储设备上的数据。

CVE-2022-26937是微软5月份修复的Windows
NFS中一个对NLM响应处理不当的栈溢出漏洞,攻击者可通过精心构造RPC数据包来利用此漏洞,从而劫持程序流程令服务器失陷。本文将从介绍NFS通信流程开始,对CVE-2022-26937漏洞进行分析,希望能够帮助读者了解NFS协议以及其通信流程中可能存在的不安全问题。

**01 **NFS通信流程

NFS是基于RPC远程过程调用协议来实现通信,RPC工作机制如下:

1661851977_630dd949c2b3d3e59d739.jpg!small?1661851978606

1. 客户端程序通过执行RPC系统调用的方式发送调用号与参数到服务端。

2. 服务端在收到客户端的系统调用请求后,本地执行对应系统调用,然后将结果返回给服务进程。

3. 服务进程将收到的结果封装后发送给客户端。

4. 客户端接受执行结果,继续执行。

这其中PRC结构如下表所示

Offset      Size(bytes) Description  
0x00        4           XID  
0x04        4           Message Type (Call: 0)  
0x08        4           RPC Version  
0x0C        4           Program (e.g., 100000: portmap)  
0x10        4           Program Version  
0x14        4           Procedure  
0x18        4           Credentials Flavour (e.g., AUTH_UNIX: 1)  
0x1c        4           Credentials Length (q)  
0x20        q           Credentials  
0x20+q      4           Verifier Flavour (e.g., AUTH_NULL: 0)  
0x24+q      4           Verifier Length: w  
0x28+q      w           Verifier  
0x28+q+w    N           Program-specific data

有了如上结构,我们就可以伪造客户端跟服务器进行通信,比如构造如下请求。

1661851990_630dd956a958e9a5dbd20.jpg!small?1661851991644

由上,我们伪造客户端发送GETPORT CALL NLM的系统调用,来获取服务器NLM运行端口,服务器执行对应系统调用后将端口号发送给客户端。

NLM结构如下表

Offset           Size(bytes) Description  
0x00             4           cookie length : w  
0x04             w           cookie contents  
0x04+w           4           exclusive  
0x08+w           4           lock caller_name length : q  
0x0c+w           q           lock caller_name contents  
0x0c+w+q         4           fh length : e  
0x10+w+q         e           fh Filehandle  
0x10+w+q+e       4           owner length : r  
0x14+w+q+e       r           owner contents  
0x14+w+q+e+r     4           svid  
0x18+w+q+e+r     4           l_offset  
0x1c+w+q+e+r     4           l_len  
0x20+w+q+e+r     244         data

有了如上结构,我们就可以伪造NLM协议向服务器发送请求。

1661852007_630dd96713f9662cc0cc1.jpg!small?1661852008305

如上图,我们伪造NLM协议包向服务器发送TEST_MSG请求,服务器会发送一个GETADDR来检索客户端ip地址等信息。

**02 **漏洞分析

当Windows NFS服务响应NLM调用时,会根据客户端发送系统调用程序号判断是否为异步过程,这其中NLM支持的过程列表如下[1]:

/*  
* NLM procedures  
*/  
  
program NLM_PROG {  
version NLM_VERSX {  
/*  
*  synchronous procedures  
*/  
void         NLM_NULL(void) = 0;  
nlm_testres  NLM_TEST(struct nlm_testargs) = 1;  
nlm_res      NLM_LOCK(struct nlm_lockargs) = 2;  
nlm_res      NLM_CANCEL(struct nlm_cancargs) = 3;  
nlm_res      NLM_UNLOCK(struct nlm_unlockargs) = 4;  
  
/*  
*  server   NLM call-back procedure to grant lock  
*/  
nlm_res      NLM_GRANTED(struct nlm_testargs) = 5;  
  
/*  
*  asynchronous requests and responses  
*/  
void         NLM_TEST_MSG(struct nlm_testargs) = 6;  
void         NLM_LOCK_MSG(struct nlm_lockargs) = 7;  
void         NLM_CANCEL_MSG(struct nlm_cancargs) =8;  
void         NLM_UNLOCK_MSG(struct nlm_unlockargs) = 9;  
void         NLM_GRANTED_MSG(struct nlm_testargs) = 10;  
void         NLM_TEST_RES(nlm_testres) = 11;  
void         NLM_LOCK_RES(nlm_res) = 12;  
void         NLM_CANCEL_RES(nlm_res) = 13;  
void         NLM_UNLOCK_RES(nlm_res) = 14;  
void         NLM_GRANTED_RES(nlm_res) = 15;  
  
/*  
*  synchronous non-monitored lock and DOS file-sharing  
*  procedures (not defined for version 1 and 2)  
*/  
nlm_shareres NLM_SHARE(nlm_shareargs) = 20;  
nlm_shareres NLM_UNSHARE(nlm_shareargs) = 21;  
nlm_res      NLM_NM_LOCK(nlm_lockargs) = 22;  
void         NLM_FREE_ALL(nlm_notify) = 23;  
} = 3;  
} = 100021;

以对NLM_TEST_MSG调用的处理为例,在NlmDispatch函数中可以看出不论是同步的NLM_TEST还是异步处理的NLM_TEST_MSG最终都将走向NlmTestLock()函数处理。

v10 = *v27;  
v13 = (*v27 + 0xD8i64);  
v6 = *v13;  
if ( v6 == 1 )  
{  
LABEL_54:  
v19 = NlmTestLock(v10);  
LABEL_85:  
v5 = v19;  
if ( v19 >= 0 )  
goto LABEL_90;  
v9 = NfsDeviceExtension;  
goto LABEL_87;  
}  
if ( v6 != 2 )  
{  
if ( v6 != 3 )  
{  
if ( v6 != 4 )  
{  
if ( v6 != 5 )  
{  
if ( v6 == 6 )  
goto LABEL_54;  
...  
}

而在NlmTestLock()函数中则判断系统调用为NLM_TEST_MSG后调用NlmGetClientAddressAndConnection()函数。

__int64  NlmTestLock(__int64 a1){  
...  
if ( *(_DWORD *)(a1 + 0xD8) == 6 )  
{  
v5 = NlmGetClientAddressAndConnection(  
*(_QWORD *)(a1 + 48),  
(__int64)(v7 + 0x30),  
(unsigned __int16 *)v7 + 38,  
*((_DWORD *)v7 + 27),  
*((_DWORD *)v7 + 28),  
*((_DWORD *)v7 + 26),  
&v28,  
&v33,  
&v30,  
&v29);  
...  
}  
}

该函数实现接受客户端响应包以获得客户端ip地址等信息,而该函数在处理ipv6响应包时,由于没有对返回的地址字符串进行长度验证,从而导致栈溢出。

调用OncRpcSendCallWaitReply函数接受客户端响应包。

if ( v40 >= 0 )  
{  
v40 = OncRpcSendCallWaitReply(v65, v105, v63, 0x7530i64, &v106);  
if ( v40 >= 0 )  
v40 = NlmSendWait(&v106);  
if ( v105 )  
{  
OncRpcConnectionClose(v105);  
v105 = 0i64;  
}  
}

重点在于对响应包v106的处理,首先判断此次调用ip类型,v103在函数开始进行了赋值,如果a2指针的值为2,对应ipv4的响应包,反之对应ipv6。

_int64 __fastcall NlmGetClientAddressAndConnection(__int64 a1, __int64 a2, unsigned __int16 *a3,.,.,., _LIST_ENTRY **a10)  
v27 = *(_WORD *)a2 == 2;  
...  
v103 = v27;

如果v103=true,进入ipv4处理,将客户端端口赋值给v104,反之进入ipv6响应,将客户端返回包字符串长度赋值给v78后将返回包字符串赋值给v83,然后调用memmove()函数将包含有客户端地址信息的返回包复制到v118上,v118为栈上固定长度的数组。但是,这里没有对地址长度进行大小验证,导致如果响应的地址字符串大于一定长度,就会导致栈溢出。

if ( v103 )   // ipv4  
{  
if (   
...  
}  
v104 = v71;  
}  
else       // ipv6  
{  
v115[1] = 96;  
v116 = v118;  
if ( *(int *)(v106 + 0x108) >= 0  
&& (v72 = *(_QWORD *)(v106 + 0x48)) != 0  
&& ((v73 = *(_DWORD *)(v72 + 64) - *(_DWORD *)(v72 + 56), v74 = *(_DWORD *)(v72 + 76), v74 < v73) ? (v75 = 0) : (v75 = v74 - v73),  
v75 >= 4) )  
{  
v76 = _byteswap_ulong(**(_DWORD **)(v72 + 64)); //将ip size 更改端后赋值  
*(_QWORD *)(*(_QWORD *)(v106 + 72) + 64i64) += 4i64; //此时指向ip。  
}  
else  
{  
v76 = XdrDecodeIntSlow(v106);  
}  
v77 = v106;  
v115[0] = v76;  
v78 = v76; // 这里将返回包长度赋值后并没有对其长度进行判断  
if ( *(int *)(v106 + 0x108) < 0  
|| ((v79 = *(_QWORD *)(v106 + 0x48)) == 0  
|| (v80 = *(_DWORD *)(v79 + 0x40) - *(_DWORD *)(v79 + 56), v81 = *(_DWORD *)(v79 + 76), v81 < v80) ? (v82 = 0) : (v82 = v81 - v80),  
v82 < (unsigned int)v78) )  
{  
XdrDecodeOpaqueSlow(v106, (unsigned int)v78, v118);  
}  
else  
{  
v83 = 0i64;  
if ( v79 )  
v83 = *(const void **)(v79 + 0x40);  // v83 = 指向data的指针  
memmove(v118, v83, v78); //overflow  
...  
v66 = 0;  
v118[v78] = 0;  
}

我们这里构造0x800大小的响应字符串,可以看到rcx此时距离rbp只有0x30,但是r8长度却有0x800大小,最终导致栈溢出。

1: kd> r  
rax=00000000000008d4 rbx=01d8afece5a0aac9 rcx=ffffc401bcf1f400  
rdx=ffff8809f3ded01c rsi=0000000000000000 rdi=ffff8809f2648070  
rip=fffff809b007620f rsp=ffffc401bcf1f330 rbp=ffffc401bcf1f430  
r8=0000000000000800  r9=fffff809aff77140 r10=ffffc401baa9f780  
r11=ffffc401bcf1f300 r12=0000000000000000 r13=00000000b‘5860100  
r14=ffff8809f375a400 r15=0000000000000800  
iopl=0         nv up ei ng nz na po nc  
cs=0010  ss=0018  ds=002b  es=002b  fs=0053  gs=002b             efl=00000286  
nfssvr!NlmGetClientAddressAndConnection+0x88b:  
fffff809`b007620f e82c690200      call    nfssvr!memcpy (fffff809`b009cb40)  
1: kd> p  
nfssvr!NlmGetClientAddressAndConnection+0x890:  
fffff809`b0076214 488b4748        mov     rax,qword ptr [rdi+48h]  
1: kd> dd ffffc401bcf1f400  
ffffc401`bcf1f400  41414141 41414141 41414141 41414141  
ffffc401`bcf1f410  41414141 41414141 41414141 41414141  
ffffc401`bcf1f420  41414141 41414141 41414141 41414141  
ffffc401`bcf1f430  41414141 41414141 41414141 41414141  
ffffc401`bcf1f440  41414141 41414141 41414141 41414141  
ffffc401`bcf1f450  41414141 41414141 41414141 41414141  
ffffc401`bcf1f460  41414141 41414141 41414141 41414141  
ffffc401`bcf1f470  41414141 41414141 41414141 41414141  
1: kd> k  
# Child-SP          RetAddr               Call Site  
00 ffffc401`bcf1f330 41414141`41414141     nfssvr!NlmGetClientAddressAndConnection+0x890  
01 ffffc401`bcf1f4c0 41414141`41414141     0x41414141`41414141  
02 ffffc401`bcf1f4c8 41414141`41414141     0x41414141`41414141

**03 **修复

在更新后,加入了对返回通用地址字符串长度的判断,从而杜绝了栈溢出的发生。

1661852053_630dd995e0f85a97d4f3e.jpg!small?1661852054667

最后

对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

同时每个成长路线对应的板块都有配套的视频提供:


当然除了有配套的视频,同时也为大家整理了各种文档和书籍资料&工具,并且已经帮大家分好类了。

因篇幅有限,仅展示部分资料,有需要的小伙伴,可以【扫下方二维码】免费领取:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/190097.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

OAuth2(1)

目录 一、什么是OAuth2.0 二、OAuth2中的角色 三、认证流程 四、生活中的OAuth2思维 五、令牌的特点 六、OAuth2授权方式 1.授权码 2.隐藏方式 3.密码方式 4.凭证方式 一、什么是OAuth2.0 OAuth2.0是目前使用非常广泛的授权机制&#xff0c;用户授权第三方应用…

红宝书学习

第一章 认识js js的组成部分有哪些&#xff1f; ①ecma 核心语法 api ②dom 提供与网页内容交互的方法和接口 ③bom 浏览器对象模型&#xff0c;提供了和浏览器交互的接口 use strict 是什么&#xff1f; use strict 是一种 ECMAscript5 添加的&#xff08;严格模式&#xff…

玩了半年NFT,一心进军Web3的Prada到底要怎么玩?

图片来源&#xff1a;无界AI绘画工具生成2022年1月&#xff0c;奢侈品品牌Prada与阿迪达斯玩了一把“联合营销”&#xff0c;玩法是这样的&#xff1a;首先&#xff0c;两个品牌邀请粉丝上传个人照片&#xff0c;然后&#xff0c;品牌抽取3000名粉丝的作品&#xff0c;交由数字…

【Rust】12. 自动化测试

12.1 编写测试 12.1.1 测试函数 测试函数&#xff1a;在一个函数前加上一行 #[test] 注解将普通函数变成测试函数 12.1.2 assert! 宏 12.1.3 assert_eq! 与 assert_ne! assert_eq!(left, right) 与 assert_eq!(left, right) 在失败时会返回 left 与 right 两个值&#xff0c…

Python学习-----起步1

目录 Python的下载&#xff08;解释器&#xff09; IDLE进入Python解释器 交互模式 脚本模式 注释 单行注释&#xff1a; 多行注释 Python的下载&#xff08;解释器&#xff09; 百度网盘链接&#xff1a; https://pan.baidu.com/s/1WEmOAGGHtHc1fxZzNGKu6A?pwd5356 …

web3小白入门:区块链的了解

记录web3学习的过程&#xff0c;从小白开始所有的web3相关的学习内容都会更新在github&#xff0c;github地址这篇文章主要说明区块链的一些概念为什么要了解区块链&#xff1f;Web3 是以区块链技术为核心,构建新一代的去中心化互联网组件,再基于它们来构建我们想要提供的服务、…

HTTP状态码301和302区别

Http 状态码 301 和 302 定义&#xff1a; 1、什么时候使用301&#xff1f; 你将永久更改网页的 URL时。你将永久迁移到新域名时。当你从 HTTP 切换到 HTTPS 时。你希望修复非 www / www 重复内容问题时。永久合并两个或多个页面或网站时。你将永久更改网站的 URL 结构时。 …

万字详解递归与递推

秋名山码民的主页 &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f64f;作者水平有限&#xff0c;如发现错误&#xff0c;还请私信或者评论区留言&#xff01; &#x1f44d;目录前言递归斐波那契数列问题的递归爬楼梯问题力扣递归实现…

js 点击图片实现查看大图

js 点击图片实现查看大图 点击图片放大缩小&#xff08;遮罩&#xff09; 截图&#xff1a;点击放大&#xff0c;并显示ico放大镜 代码如下&#xff1a; <!DOCTYPE html> <html> <head> <meta charset"UTF-8"> <title>点击图片放大缩…

buu 浪里淘沙 1

题目描述&#xff1a; 题目分析&#xff1a; 看了这一大串字符串&#xff0c;发现里面都是由16个字母组成&#xff0c;即 “tonight success notice we example crypto should back space sublim found system morning user the enter ”&#xff0c;并且这16个字母在第一行中…

【Kotlin】DSL 领域特定语言 ① ( apply 标准库函数分析 | 普通匿名函数 | 扩展匿名函数 | 泛型扩展匿名函数 )

文章目录一、apply 标准库函数分析1、apply 函数展示2、apply 函数原型分析函数原型参数和返回值分析3、匿名函数类型分析4、扩展函数回顾5、泛型扩展函数函数类型6、泛型扩展匿名函数7、apply 标准库函数参数分析泛型扩展函数匿名函数 与 普通匿名函数 对比apply 函数参数不是…

登高望远,一文解答 2023 年你最关心的前端热点问题

动手点关注干货不迷路本文预计阅读 25 min&#xff0c;建议先收藏后观看~一、刀光剑影的 2022时光荏苒&#xff0c;这绝不平淡的 2022 年已经走上历史的黄页&#xff0c;新的一年也逐渐看不到故人回首的光影。感谢你对前端技术领域持续关注&#xff0c;我们一直在这里等你。① …

致敬2202年,这些优秀的裁缝们

文 | 鹰钩鼻涕虫2202年过去了&#xff0c;不知道小伙伴们是否和我一样&#xff0c;绝大多数时间处于迷茫之中&#xff0c;除去其他因素不谈&#xff0c;在最后一个月到来之前&#xff0c;NLP 学界的表现似乎不如前几年那样“精彩”&#xff0c;甚至可说是“寡淡”&#xff0c;翻…

spring事务执行流程分析_5(注解形式 advisor等前置对象的生成)

调用beanFactory处理器 依旧进入刷新refresh方法AbstractApplicationContext#refresh -> 在上一篇文章spring事务执行流程分析_4(注解形式 EnableTransactionManagement的作用) 解析EnableTransactionManagement注解就是在此方法进行的,也就是在会注册 名字&#xff1a;i…

简单模拟vue实现数据劫持-视图更新双向绑定-2

接上&#xff0c; new一个实例对象&#xff0c;vc&#xff0c;构造函数动态绑定一个空对象&#xff0c;并在构造函数上绑定静态方法 $on进行事件的注册&#xff0c;$emit抛出执行事件 function observe() {// 利用策略模式-可以快速根据特定的事件&#xff0c;执行多个函数th…

最全总结 | 聊聊 Selenium 隐藏浏览器指纹特征的几种方式!

大家好&#xff0c;我是安果&#xff01;我们使用 Selenium 对网页进行爬虫时&#xff0c;如果不做任何处理直接进行爬取&#xff0c;会导致很多特征是暴露的对一些做了反爬的网站&#xff0c;做了特征检测&#xff0c;用来阻止一些恶意爬虫本篇文章将介绍几种常用的隐藏浏览器…

第九层(9):STL之map/multimap

文章目录前情回顾map/multimap概念差别构造函数赋值操作大小操作函数交换函数插入函数删除函数查找函数统计函数排序规则下一座石碑&#x1f389;welcome&#x1f389; ✒️博主介绍&#xff1a;一名大一的智能制造专业学生&#xff0c;在学习C/C的路上会越走越远&#xff0c;后…

三线金叉选股公式,均线、成交量、MACD共振

均线、成交量、MACD三线金叉共振选股公式思路还是比较简单的&#xff0c;分别写出均线金叉、成交量的均量线金叉、MACD的快线和慢线金叉&#xff0c;最后用AND连接这三个条件。 一、编写选股公式所需通达信函数 1、EXIST函数 含义&#xff1a;是否存在 用法&#xff1a;EXIST…

OpenGL | 搭建OpenGL 画画框架

一、搭建OpenGL 画画框架3D场景初始化&#xff08;1&#xff09; 代码void Init() {glMatrixMode(GL_PROJECTION); //将当前矩阵指定为投影矩阵,对投影矩阵操作gluPerspective(50.0f, 800.0f / 600.0f, 0.1f, 1000.0f);//创建一个对称的透视投影矩阵&#xff0c;并且用这个…

世界上最大型的 Demo Drop DJ 比赛来到元宇宙!

简要介绍 WBDD 于 2023 年 1 月 26 日至 2 月 8 日进入元宇宙。 认识世界上最大型的 DJ 比赛获胜者&#xff0c;并在元宇宙中伴随着他们的音乐跳舞。 该体验将是线下活动的延伸&#xff0c;由 Mike Williams 担任活动大使。 体验将对所有人开放。 完成 80% 的任务&#xff…