如何绕过Golang木马的HTTPS证书验证
字数 1299 2025-08-24 07:48:34

Golang木马HTTPS证书验证绕过技术详解

一、背景与问题描述

在分析Golang编写的木马程序时,发现其使用HTTPS协议进行通信。为了在本地测试环境中完整复现攻击场景,需要构建一个HTTPS WEB服务器。但使用自签名证书时,Golang木马程序无法成功访问服务器,原因是Golang默认会对服务器证书进行严格验证。

二、Golang证书验证机制

1. InsecureSkipVerify标志

Golang的crypto/tls库中,通过InsecureSkipVerify标志控制客户端是否验证服务器的证书链和主机名:

TLSClientConfig: &tls.Config{
    InsecureSkipVerify: true, // 设置为true可跳过证书验证
}

2. 验证流程

证书验证主要发生在crypto/tls/handshake_client.go文件中的verifyServerCertificate函数,该函数会根据InsecureSkipVerify标志决定是否执行验证。

三、解决方案

1. 源代码修改方案

如果有源代码,直接设置InsecureSkipVerify: true即可:

transport := &http.Transport{
    TLSClientConfig: &tls.Config{
        InsecureSkipVerify: true,
    },
}
client := &http.Client{
    Transport: transport,
}

2. 二进制修改方案(无源码情况)

当没有源代码时,可以通过修改二进制文件来绕过证书验证。

关键二进制特征

不同Golang版本的关键特征:

  1. Go 1.21.0至Go 1.23.0 (windows-amd64):

    原始: 41 80 BA A0 00 00 00 00 0F 85
    修改: 41 80 BA A0 00 00 00 00 0F 84
    
  2. Go 1.21.0至Go 1.22.6 (windows-386):

    原始: 0F B6 7E 50 97 84 C0 97 0F 85
    修改: 0F B6 7E 50 97 84 C0 97 0F 84
    
  3. Go 1.23.0 (windows-386):

    原始: 0F B6 4E 50 84 C9 0F 85
    修改: 0F B6 4E 50 84 C9 0F 84
    

修改原理

将条件跳转指令jnz(0F 85)修改为jz(0F 84),即反转验证逻辑。

3. 自动化修改脚本

使用Go编写的批量修改脚本:

package main

import (
    "bytes"
    "fmt"
    "io/ioutil"
    "os"
    "path/filepath"
    "strings"
)

func WalkDir(dirPth, suffix string) (files []string, err error) {
    files = make([]string, 0, 30)
    suffix = strings.ToUpper(suffix)
    err = filepath.Walk(dirPth, func(filename string, fi os.FileInfo, err error) error {
        if fi.IsDir() {
            return nil
        }
        if strings.HasSuffix(strings.ToUpper(fi.Name()), suffix) {
            files = append(files, filename)
        }
        return nil
    })
    return files, err
}

func replaceBinaryData(filePath string, oldData, newData []byte) error {
    fileContent, err := ioutil.ReadFile(filePath)
    if err != nil {
        return fmt.Errorf("failed to read file: %w", err)
    }
    modifiedContent := bytes.Replace(fileContent, oldData, newData, -1)
    err = ioutil.WriteFile(filePath, modifiedContent, 0644)
    if err != nil {
        return fmt.Errorf("failed to write file: %w", err)
    }
    return nil
}

func main() {
    files, err := WalkDir("F:\\GolandProjects\\awesomeProject9", "exe")
    if err != nil {
        fmt.Println("Error:", err.Error())
    }

    for _, onefile := range files {
        oldData1 := []byte{0x41, 0x80, 0xBA, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x85}
        newData1 := []byte{0x41, 0x80, 0xBA, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x84}
        
        oldData2 := []byte{0x0F, 0xB6, 0x7E, 0x50, 0x97, 0x84, 0xC0, 0x97, 0x0F, 0x85}
        newData2 := []byte{0x0F, 0xB6, 0x7E, 0x50, 0x97, 0x84, 0xC0, 0x97, 0x0F, 0x84}
        
        oldData3 := []byte{0x0F, 0xB6, 0x4E, 0x50, 0x84, 0xC9, 0x0F, 0x85}
        newData3 := []byte{0x0F, 0xB6, 0x4E, 0x50, 0x84, 0xC9, 0x0F, 0x84}

        datas, err := ioutil.ReadFile(onefile)
        if err != nil {
            fmt.Println(err.Error())
        }

        if bytes.Contains(datas, oldData1) {
            err = replaceBinaryData(onefile, oldData1, newData1)
        } else if bytes.Contains(datas, oldData2) {
            err = replaceBinaryData(onefile, oldData2, newData2)
        } else if bytes.Contains(datas, oldData3) {
            err = replaceBinaryData(onefile, oldData3, newData3)
        }

        if err != nil {
            fmt.Printf("Error: %v\n", err)
        } else {
            fmt.Println("Binary data replaced successfully.")
        }
    }
}

四、版本兼容性分析

1. 操作系统影响

测试发现二进制特征与操作系统类型无关,相同Golang版本在不同操作系统下的特征一致。

2. 编译方式影响

不同编译方式(普通编译、使用-ldflags、使用garble)生成的二进制特征相同。

3. Golang版本影响

不同Golang版本的关键特征有所不同,主要分为三类:

  1. Go 1.21.0至Go 1.23.0 (windows-amd64)
  2. Go 1.21.0至Go 1.22.6 (windows-386)
  3. Go 1.23.0 (windows-386)

五、实践步骤

  1. 确定Golang版本:使用工具分析目标二进制文件的Golang版本
  2. 查找特征码:根据版本选择对应的特征码进行搜索
  3. 修改二进制:将jnz(0F 85)修改为jz(0F 84)
  4. 测试验证:运行修改后的程序验证是否绕过证书验证

六、注意事项

  1. 此方法仅用于安全研究和分析目的
  2. 修改二进制可能违反软件许可协议
  3. 不同Golang版本的特征可能不同,需要针对性处理
  4. 未来Golang版本可能会改变验证机制,需要持续关注

七、总结

通过分析Golang的TLS验证机制,我们可以在没有源代码的情况下,通过二进制修改绕过HTTPS证书验证。关键在于识别不同版本的特征码,并正确修改条件跳转指令。这种方法对于分析Golang编写的恶意软件特别有用,可以方便地在测试环境中复现攻击场景。

Golang木马HTTPS证书验证绕过技术详解 一、背景与问题描述 在分析Golang编写的木马程序时,发现其使用HTTPS协议进行通信。为了在本地测试环境中完整复现攻击场景,需要构建一个HTTPS WEB服务器。但使用自签名证书时,Golang木马程序无法成功访问服务器,原因是Golang默认会对服务器证书进行严格验证。 二、Golang证书验证机制 1. InsecureSkipVerify标志 Golang的 crypto/tls 库中,通过 InsecureSkipVerify 标志控制客户端是否验证服务器的证书链和主机名: 2. 验证流程 证书验证主要发生在 crypto/tls/handshake_client.go 文件中的 verifyServerCertificate 函数,该函数会根据 InsecureSkipVerify 标志决定是否执行验证。 三、解决方案 1. 源代码修改方案 如果有源代码,直接设置 InsecureSkipVerify: true 即可: 2. 二进制修改方案(无源码情况) 当没有源代码时,可以通过修改二进制文件来绕过证书验证。 关键二进制特征 不同Golang版本的关键特征: Go 1.21.0至Go 1.23.0 (windows-amd64) : Go 1.21.0至Go 1.22.6 (windows-386) : Go 1.23.0 (windows-386) : 修改原理 将条件跳转指令 jnz (0F 85)修改为 jz (0F 84),即反转验证逻辑。 3. 自动化修改脚本 使用Go编写的批量修改脚本: 四、版本兼容性分析 1. 操作系统影响 测试发现二进制特征与操作系统类型无关,相同Golang版本在不同操作系统下的特征一致。 2. 编译方式影响 不同编译方式(普通编译、使用 -ldflags 、使用 garble )生成的二进制特征相同。 3. Golang版本影响 不同Golang版本的关键特征有所不同,主要分为三类: Go 1.21.0至Go 1.23.0 (windows-amd64) Go 1.21.0至Go 1.22.6 (windows-386) Go 1.23.0 (windows-386) 五、实践步骤 确定Golang版本 :使用工具分析目标二进制文件的Golang版本 查找特征码 :根据版本选择对应的特征码进行搜索 修改二进制 :将 jnz (0F 85)修改为 jz (0F 84) 测试验证 :运行修改后的程序验证是否绕过证书验证 六、注意事项 此方法仅用于安全研究和分析目的 修改二进制可能违反软件许可协议 不同Golang版本的特征可能不同,需要针对性处理 未来Golang版本可能会改变验证机制,需要持续关注 七、总结 通过分析Golang的TLS验证机制,我们可以在没有源代码的情况下,通过二进制修改绕过HTTPS证书验证。关键在于识别不同版本的特征码,并正确修改条件跳转指令。这种方法对于分析Golang编写的恶意软件特别有用,可以方便地在测试环境中复现攻击场景。