SMB(Server Message Block)是一个协议名,它能被用于Web连接和客户端与服务器之间的信息沟通。其目的是将DOS操作系统中的本地文件接口“中断13”改造为网络文件系统。
SMB1.0协议由于在文件共享传输过程中存在的传输效率低以及传输空间小等缺陷被人们所摒弃。为了更好的实现网络中文件的共享过程,在SMB1.0的基础上开发了新的网络文件传输协议,并将其命名为SMB2.0。该协议在实现了文件共享传输的基本功能的基础上对文件传输的效率、文件缓存的空间以及文件并发传输等问题进行改进,使得在局域网或更高配置的网络环境下,文件传输过程的速度和效率等得到了很大的提升。
Windows Vista SP2; Windows Server 2008 SP2 and R2 SP1; Windows 7 SP1; Windows 8.1; Windows Server 2012 Gold and R2; Windows RT 8.1; and Windows 10 Gold, 1511, and 1607; and Windows Server 2016
以上系统打开445号端口都容易受到影响。
对于客户端操作系统:
对于服务器操作系统:
重新启动系统。
官方文档链接
实验中需要用到三个机器,分别是调试机,靶机(被调试机),攻击机
首先需要用调试机调试靶机,具体实现双机调试请看这里 。
另外需要在攻击机安装fuzzbunch实现永恒之蓝漏洞:
安装Python 2.6
安装PyWin32 需要以管理员权限进行安装
下载fuzzbunch
在shadowbroker-master的子目录windows中新建listeningposts文件夹,同时修改FuzzyBunch.xml文件内容,设置相应的ResourcesDir和LogDir参数值,修改结果如下图所示。(路径根据实际情况而定)
打开靶机445端口,具体打开方法点这里
关闭防火墙:控制面板-系统和安全-Windows防火墙-打开或关闭Windows防火墙-全部选择关闭防火墙。
在攻击机中启动命令行,进入fuzzbench的Windows文件夹,用python启动fuzzbench:
启动后设置靶机IP-设置击机IP-重定向no-log地址确认(无误直接回车)-0+回车(创建新的项目)-为项目命名-设置路径(Yes)-:
使用永恒之蓝建立后门:
之后一直回车到出现设置靶机系统版本,本次复现靶机为Win7,所以选择1:
下一项模式选择1,FB模式:
接下来一直回车即可,即可看到插件运行,并成功利用永恒之蓝漏洞在靶机系统中留下后门:
SrvOs2FeaListToNt()函数用于将FEA list转化为NTFEA list,需要分配在内核地址大非分页池分配缓冲区来存放转化后的NTFEA list,因此需要先计算转化后的NTFEA list的大小,计算大小是通过srv!SrvOs2FeaListSizeToNt()函数进行的,这个函数被SrvOs2FeaListToNt()调用。
0xffdff000处地址是系统预留的地址空间,用于存放时钟,版本,配置等信息,其分配的权限为可执行:
使用Ida打开srv.sys
首先按shift + F1,再按insert键导入两个结构:
查看srv!SrvOs2FeaListSizeToNt()函数源码,右键a1,使用_FEALIST结构覆盖:
LOWORD(v1->cbList) = (_WORD)v3 - (_WORD)v1; 这句代码中存在一处错误,cbList长度本为4字节,这里却被强制转换为2字节,导致赋值过程中无视高2字节的内容,只赋值了低2字节的内容。
原来的SrvOs2FeaListSizeToNt()函数中的C代码:
修补后:
(v1->cbList)数据类型由WORD变为了DWORD
将Windbg(需要配置好符号文件)连接靶机进行内核调试,连接成功Windbg会显示:
运行前先设置几个断点来采集运行过程中涉及的关键数据:
设置好断点后,在攻击机运行脚本,开始攻击
这时,Windbg会记录下攻击过程中的数据:
整理一下:
可以看到经过某处指令后feasize由00010000变为0001ff5d,这就是代码LOWORD(v1->cbList) = (_WORD)v3 - (_WORD)v1;产生的错误计算,其原因请看下面对应的汇编指令:
此时esi和eax寄存器的值:
FeaEnd: esi = a4217035h
FeaStart: eax = a42070d8h
FeaSize: [eax] = 00010000
经过sub指令后esi的值为ff5d,而在给[eax]赋值的时候使用WORD类型,导致了eax内容变成了0001ff5d。
继续查看内存池的数据:
分配的起始地址为85da4008,正常分配的结束地址为85da4008h + 00010fe8h = 85db4ff0h,到了Windbg中显示的最后一块池时,发生了错误,并返回STATUS_INVALID_PARAMETER(0xC000000D),查看85db4ff0h中的内容后发现这片地址属于srvnet.sys分配的池。而这个池中存在一个很关键的数据,这个数据是一个地址,当靶机系统再接受数据的时候会将数据复制到这个地址+0x80的地方。
这个关键的地址位于_MDL结构中的MappedSystemVa成员,这个结构在srvnet池分配开始的地址+3c,我们在Windbg中找到这个结构:
可以看到此时的这个结构的MappedSystemVa成员已经被覆盖为0xffdfef80,当靶机再次接受数据时,会向0xffdf80+0x80也就是在刚才介绍中提到的系统预留的一片可执行空间0xffdff000,此时攻击机获得一次写入shellcode的机会。那么程序是如何执行这部分shellcode的呢?
当靶机接受完这部分数据后,还会调用srvnet!SrvNetWskReceiveComplete这个函数,巧了,刚好这个函数会调用到写入的shellcode,就此,成功劫持了靶机的执行流。
查看0xffdff000地址空间内容如图所示:
0xffdff1f1处为shellcode的开始。
360 核心安全技术博客:NSA Eternalblue SMB 漏洞分析
看雪:MS17-010 SMB 远程命令执行漏洞利用分析
CSDN:【漏洞分析】MS17-010:深入分析“永恒之蓝”漏洞
use Eternalblue
kd> !pte
0xffdff000
VA ffdff000
PDE at C0603FF0 PTE at C07FEFF8
contains
000000000018A063
contains
00000000001E3163
pfn
18a
-
-
-
DA
-
-
KWEV pfn
1e3
-
G
-
DA
-
-
KWEV
kd> !pte
0xffdff000
VA ffdff000
PDE at C0603FF0 PTE at C07FEFF8
contains
000000000018A063
contains
00000000001E3163
pfn
18a
-
-
-
DA
-
-
KWEV pfn
1e3
-
G
-
DA
-
-
KWEV
struct _FEA{
BYTE fEA;
/
/
标记位
BYTE cbNAME;
/
/
记录名称长度
USHORT cbValue;
/
/
记录值的长度
}FEA;
struct _FEA{
BYTE fEA;
/
/
标记位
BYTE cbNAME;
/
/
记录名称长度
USHORT cbValue;
/
/
记录值的长度
}FEA;
struct _FEALIST{
ULONG cbList;
/
/
记录Fea
List
总长度
_FEA
list
[
1
];
}FEALIST,
*
PFEALIST;
struct _FEALIST{
ULONG cbList;
/
/
记录Fea
List
总长度
_FEA
list
[
1
];
}FEALIST,
*
PFEALIST;
int
__stdcall SrvOs2FeaListSizeToNt(_FEALIST
*
FeaList)
{
_FEALIST
*
v1;
/
/
eax
char
*
v2;
/
/
edi
_FEA
*
v3;
/
/
esi
int
v4;
/
/
ebx
int
v6;
/
/
[esp
+
Ch] [ebp
-
4h
]
v1
=
FeaList;
/
/
a1
v6
=
0
;
/
/
NtFeaList的大小
v2
=
(char
*
)FeaList
+
FeaList
-
>cbList;
/
/
获取指向表结尾的指针v2
v3
=
FeaList
-
>
list
;
/
/
指向表的开始
if
( FeaList
-
>
list
< (_FEA
*
)v2 )
/
/
表的开始指针应该在结尾指针之前
{
while
( &v3[
1
] < (_FEA
*
)v2 )
/
/
从FeaList表的第一个元素开始,遍历整个表
{
v4
=
v3
-
>cbValue
+
v3
-
>cbNAME;
/
/
获取当前FEA的长度
if
( &v3[
1
].cbNAME
+
v4 > (BYTE
*
)v2 )
/
/
检查下一个FEA是否有效
break
;
if
( RtlSizeTAdd(v6, (v4
+
12
) &
0xFFFFFFFC
, &v6) <
0
)
/
/
增加NtFeaList的大小,每次
12
字节
return
0
;
v3
=
(_FEA
*
)((char
*
)v3
+
v4
+
5
);
/
/
下一个FEA,加
5
意思是每个FEA后面有
5
字节的NULL
if
( v3 >
=
(_FEA
*
)v2 )
return
v6;
v1
=
FeaList;
/
/
重置v1
}
LOWORD(v1
-
>cbList)
=
(_WORD)v3
-
(_WORD)v1;
/
/
此处发生计算错误
}
return
v6;
}
int
__stdcall SrvOs2FeaListSizeToNt(_FEALIST
*
FeaList)
{
_FEALIST
*
v1;
/
/
eax
char
*
v2;
/
/
edi
_FEA
*
v3;
/
/
esi
int
v4;
/
/
ebx
int
v6;
/
/
[esp
+
Ch] [ebp
-
4h
]
v1
=
FeaList;
/
/
a1
v6
=
0
;
/
/
NtFeaList的大小
v2
=
(char
*
)FeaList
+
FeaList
-
>cbList;
/
/
获取指向表结尾的指针v2
v3
=
FeaList
-
>
list
;
/
/
指向表的开始
if
( FeaList
-
>
list
< (_FEA
*
)v2 )
/
/
表的开始指针应该在结尾指针之前
{
while
( &v3[
1
] < (_FEA
*
)v2 )
/
/
从FeaList表的第一个元素开始,遍历整个表
{
v4
=
v3
-
>cbValue
+
v3
-
>cbNAME;
/
/
获取当前FEA的长度
if
( &v3[
1
].cbNAME
+
v4 > (BYTE
*
)v2 )
/
/
检查下一个FEA是否有效
break
;
if
( RtlSizeTAdd(v6, (v4
+
12
) &
0xFFFFFFFC
, &v6) <
0
)
/
/
增加NtFeaList的大小,每次
12
字节
return
0
;
v3
=
(_FEA
*
)((char
*
)v3
+
v4
+
5
);
/
/
下一个FEA,加
5
意思是每个FEA后面有
5
字节的NULL
if
( v3 >
=
(_FEA
*
)v2 )
return
v6;
v1
=
FeaList;
/
/
重置v1
}
LOWORD(v1
-
>cbList)
=
(_WORD)v3
-
(_WORD)v1;
/
/
此处发生计算错误
}
return
v6;
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2021-9-6 07:54
被Sal_Tay编辑
,原因: