第一届华成杯WriteUP
这是中原工学院新生赛的比赛,作为特邀小白来体验题目了
主要打的是二进制方向
pwn的house of apple和web的还没消失的flag感觉还是有点困难,用了ai
Reverse
乱花渐欲迷人眼
进去之后先通过start函数定位到main函数:
这里就可以看到给出了密钥了
1 | int __cdecl sub_401080() |
这里面有的函数是有花指令的
像这种假跳转,nop掉即可

像这种的假call nop即可

还原出来的函数:
1 | int __usercall sub_401360@<eax>(int a1@<edx>, int a2, int a3, int i_1) |
猜测是RC4但是魔改了
写出脚本还原即可:
1 | def rc4_ksa(key): |
你知道Android吗
进去是一个apk
1 | package com.example.program; |
给了密文,接下来需要去so文件进行分析:
明显这是凯撒移位3的加密。但是针对字母、数字、特殊符号,加密方式也不一样。
因此写出脚本:
1 | def decrypt_char(c): |
签到题
ida打开
1 | __int64 __fastcall main() |
可以看到逻辑还是比较清晰的,给出了密文,和异或密钥76
解密脚本:
1 | import struct |
Base64
看到main函数基本确定是Base64编码
1 | // The function seems has been flattened |
下面是编码表:
cyberchef一把梭出来了:
点击即送flag
打开是一个程序,需要点击一百万次才会给flag

打开CE搜索到目标量,然后直接改为1000000
点击确定 即可拿到flag:
请你喝茶
这是py打包的exe,首先进行解包

解包之后,反编译pyc得到主要逻辑:
1 | # Decompiled with PyLingual (https://pylingual.io) |
可以看到魔数什么都给了,简单写出解密脚本就ok了
写出脚本如下:
1 | import struct |
Pwn
Apple After Death
根据题目提示应该是采取House of Apple的思路
看main函数也是提供了一个菜单:
1 | int __fastcall main(int argc, const char **argv, const char **envp) |
可以看到程序delete功能处存在UAF漏洞:
而show 可以读取已释放 chunk 的内容,edit 可以修改已释放 chunk 的内容
我们先申请一个大chunk,释放后其会存在unsort bins之中,此时show即可打印出相关地址,实现地址泄露
对大 chunk 做:
deleteedit把前 0x10 字节清零- 再次
delete
绕过double free检查
再次 show 这个 chunk,会看到 safe-linking 编码后的 next 指针进而得到精确 chunk 地址
House of Apple链路是:
- 伪造一个
_IO_FILE - vtable 指向
_IO_wfile_jumps - 配好 fake
_wide_data - 在退出时通过
_IO_flush_all遍历_IO_list_all - 进入宽字符写路径
- 触发
_IO_wdoallocbuf - 最终调用我们伪造的函数指针
堆上伪造的 fake FILE 关键布局如下:
1 | fake = flat({ |
fake _wide_data:
1 | fake += flat({ |
fake wide vtable:
1 | fake += flat({ |
我们单独用一个 8 字节小 chunk 负责 tcache poisoning 只覆盖 _IO_list_all 这 8 个字节
最后:
- UAF 改写 tcache next:
1 | edit(idx, p64(_IO_list_all ^ heap_key)) |
- 连续两次
malloc(8) - 第二次返回的位置就是
_IO_list_all - 写入 fake FILE 的地址
这样退出程序时:
1 | _IO_list_all = fake_file |
就能进入我们构造的链。程序打印 Bye! 后,glibc 会执行 flush。此时 fake FILE 被遍历并触发 system("$0")拿到shell
exp:
1 | from pwn import * |
你想走后门吗
这个题目没有给附件,但是题目提示说:做人不要总想着走后门,先把这100道题做完。 当然, 如果后门真的在4199164,并且从这里过去只需要72步。 那是你的事。
也就是说后门地址已经给了,而且溢出长度估计也就是72,省的我们一个个找调试了

nc连接之后需要我们进行100次加法运算,接下来估计发送评价的机会就是进行栈溢出
这里需要用正则匹配算式,因此还是比较困难!!!
写出脚本
1 | from pwn import * |
Wake Up
这是一道沙箱,对 execve(59) 和 execveat(322) 做了 seccomp 规则处理

题目main函数给出了栈溢出:
1 | int __fastcall main(int argc, const char **argv, const char **envp) |
思路也很简单:
先栈溢出,触发一次 sigreturn,执行 read(0, .bss, 0x400),把第二阶段链读进 .bss
第二段继续 sigreturn,执行 open(“/flag”, 0, 0)
再执行 read(3, buf, 0x100) 读 flag
最后执行 write(1, buf, 0x100) 把 flag 打出来
写出脚本:
1 | from pwn import * |
别乱说话
看到main函数 明显存在格式化字符串漏洞

在看到plt段有明显的system和puts
所以思路就很清晰了,就是格式化字符串泄露got表地址然后覆盖puts got表地址为system。

程序也存在/bin/sh字符串,因此可以方便地实现劫持got表
写出脚本如下:
1 | from pwn import * |
nc-shell
连接之后直接给了shell
但是直接输入cat /flag会提示权限不足
因此输入ls /bin看有没有其他方式,发现藏了一个程序cat_real
输入即可拿到flag

Web
(还没)消失的flag
首页泄露源码:
1 | flag1 = open("/tmp/flag1.txt", "r") |
关键点有两个:
/shell存在命令执行,但只过滤了普通空格,没有过滤tab、重定向、$()、分号等。flag1是打开的文件对象,flag2在启动时已经被读入 Python 全局变量。
因此思路是:
- 第一次利用
/shell时,从 reloader 父进程的文件描述符里读flag1。 - 同时采集 Werkzeug Debugger PIN 计算所需的环境信息。
- 把
app.py改成一个合法的最小 Flask 回显器,但伪造回原来的mtime,尽量不触发 reloader 重启。 - 根据采集到的信息本地计算 PIN。
- 进入
/console,解锁后直接读全局变量flag2。
首发 payload 的目标:
- 读取父进程
fd 3中已删除的flag1 - 采集
username、boot_id、cgroup、网卡 MAC - 把这些内容写进
/tmp/out - 将
app.py改写成合法源码,把/tmp/out嵌进DATA返回 - 最后把
app.py的修改时间改回原值,避免 reloader 立刻重启
核心逻辑如下:
1 | t=$(stat -c %Y app.py) |
远端是 Werkzeug 2.2.3,PIN 计算使用的是:
usernamemodname = "flask.app"app name = "Flask"mod file = "/usr/local/lib/python3.10/site-packages/flask/app.py"uuid.getnode()对应的十进制 MACget_machine_id(),本题里实际退化到boot_id + cgroup
本地计算脚本:
1 | import hashlib |
算得:
- PIN:
138-199-619 - Cookie 名:
__wzdffb3ec770ca322f8d95b
先访问 /console 拿到 SECRET:
ltYHytQLpWQSwxc8MkYQ
然后做 PIN 认证:
1 | /console?__debugger__=yes&cmd=pinauth&pin=138-199-619&s=ltYHytQLpWQSwxc8MkYQ |
认证成功后,直接执行:
1 | __import__('app').flag2 |
返回:
7bc-9baf-5dd567c6cc5a}}
拼接即可
flag{b09d4a7a-1c50-47bc-9baf-5dd567c6cc5a}}

exp:
1 |
|
mediadrive
随便上传一个文件,就能发现preview.php。
后面f参数可以进行路径绕过,但是提示权限不足

看到cookie的user是一个反序列字符串:里面可以控制basePath
因此构造恶意cookie如下:
O%3A4%3A%22User%22%3A3%3A%7Bs%3A4%3A%22name%22%3Bs%3A5%3A%22guest%22%3Bs%3A8%3A%22encoding%22%3Bs%3A3%3A%22GBK%22%3Bs%3A8%3A%22basePath%22%3Bs%3A1%3A%22%2F%22%3B%7D
直接输入f=flag还是不行
根据提示:提示:文件访问过程中有编码转换环节。请读取服务器上的 /flag 文件获取 flag。 提示:直接访问flag行不通,有没有什么欺骗解析的方法?
我们输入脏字节即可,这里采取0xff这个字节即可
输入f参数为f%FFl%FFa%FFg
即可绕过检测拿到flag

刘易斯镇长的秘密
用户名输入万能密码即可进入主页面获取flag
我用的密码:1' or 1=1 --+

Crypto
Vigenère Challenge
这应该是维吉尼亚密码
密钥尝试弱密钥key 解密出来的base64能被解码出来:
然后推测这是键盘密码
比如第一个wazxde,包围起来的字母就是s,以此类推
解出来得是sTriFe
Misc
超级简单二维码
第一个字节出错了,修改即可

解压后扫码即可
情书
010发现后面藏得有压缩包文件头,但是每个字节的16进制都是倒叙排列
写一个脚本提取文件:
1 | from pathlib import Path |
有一个txt没加密:
1 | 这是一个数学家爱上公主的故事 |
查阅典故得知是笛卡尔写给克里斯蒂娜公主的第十三封信。试出来一个密码 Kristina 解出 1.txt

英语真的不简单吧
foremost文件提取后得到:EJ FM GH FH EJ CC GD FN FB CB CI FK DL DH FL GF FD EC DB FM FA EG CE FK DI DG CA FL DI EH C〇 CM
这里是把a-o看作十六进制,然后转换为字符再解密即可

Forensics
女装流量
首先追踪流寻找到flag.zip

这个进行了压缩包加密操作

这个命令我们解码之后是这样:cd "C:\Program Files\phpstudy_pro\WWW\LitCTF-pcapng" && zip -P "zhongyuangongxueyuandrops" f1ag.zip flag.php && echo 1a925 && cd && echo 6ffeb1
这里已经给出密码了zhongyuangongxueyuandrops
解压即可