本次2023 XCTFfinals 我们 SU 取得了第7名的成绩,感谢队里师傅们的辛苦付出!同时我们也在持续招人,只要你拥有一颗热爱 CTF 的心,都可以加入我们!欢迎发送个人简介至:suers_xctf@126.com或直接联系书鱼(QQ:381382770)
以下是我们 SU 本次 2023XCTFfinals的 writeup
web
web-dbtrick
0x01 预期解
admin.php 中读代码可以发现是从ctf.admin中读取username、password,如果能查询出数据着执行readfile(‘/flag’)
1 | #admin.php |
查询数据库版本
1 | SHOW VARIABLES LIKE 'version' |
现在的思路就是获取出admin中的数据,但在测试过程中发现过滤了很多的函数
1 | black list |
常见的思路几乎全被完全过滤,alter可用,ctf.admin表不存在,考虑需要建表,但在后续的测试中发现rename被过滤导致失败。
考虑写文件查询secure_file_priv参数结果为空,但load关键字也被堵死
1 | show global variables like "secure%"; |
最后考虑到mariadb 数据复制
mariadb 数据复制配置
在/etc/mysql/my.conf [mysqld]
块下添加如下三条配置
1 | [mysqld] |
- server_id 主从服务器的id不能为同一个,默认情况下都为1
- secure_file_priv mysql文件写权限
- log-bin 二进制日志
MySQL - binlog日志简介及设置 - hongdada - 博客园 (cnblogs.com)
主服务器需执行sql:
1 | CREATE USER 'm4x'@'%' IDENTIFIED BY '123456'; |
从服务需执行sql:
1 | CHANGE MASTER TO |
主从同步完成后即可在服务器中成功创建出admin表,并且其中的用户名密码都为可控
0x02 清华非预期解法
XCTF 决赛中清华对一道web题的非预期,在该题中过滤了很多的关键字,包括SELECT
等,预期解为mariadb主从复制,但清华使用EXECUTE IMMEDIATE
绕过了黑名单导致非预期,这里详细来分析一下该种绕过方法。
基础用法
EXECUTE IMMEDIATE Statement (oracle.com)
在 MariaDB 10.0.3 之后,新增了一个名为 EXECUTE IMMEDIATE
的 SQL 语句,它可以将字符串作为动态 SQL 查询语句执行。这个语句的语法如下:
1 | EXECUTE IMMEDIATE stmt_string [INTO var_name [, ...]] |
其中,stmt_string
是要执行的 SQL 查询字符串,可以包含占位符。var_name
是可选的参数列表,用于从查询结果中接收值。
例如,下面的代码展示了如何使用 EXECUTE IMMEDIATE
执行一个简单的 SELECT 查询:
1 | SET @id = 123; |
在这个例子中,我们将 @id
变量的值拼接到 SQL 查询字符串中,然后使用 EXECUTE IMMEDIATE
关键字执行该查询,并输出结果。
绕过分析
具体解题思路参考 The 7th XCTF Finals WEB WP 这篇文章,该题中手工测试出的黑名单如下:
1 | black list |
其中过滤了常用的几大关键字,通过EXECUTE IMMEDIATE
可以进行绕过
原题中使用的环境为mariadb 10.3.38
1 | EXECUTE IMMEDIATE 'SELECT * FROM ctf.admin'; |
可以直接执行字符串中的sql语句,在基于这一点的情况下就很容易进行绕过
该题中并没有过滤各种字符串编码,所以我们可以使用如下方法进行绕过
1 | EXECUTE IMMEDIATE UNHEX('53454c454354202a2046524f4d206374662e61646d696e'); |
将SELECT * FROM ctf.admin
转为hex再使用UNHEX方法转换为字符串进行执行
同理,这里也可以使用BASE64之类的进行绕过
web-signin
React state 前端储存数据 + 后台HTTP3.0
1 | [1] Username and password store in React Redux state |
https://www.freebuf.com/vuls/304954.html
在引入了Fiber的React(16.8+),会多出 reactFiber$xxxx 属性,该属性对应的就是这个DOM在React内部对应的FiberNode,可以直接使用child属性获得子节点。节点层级可以从React Dev Tool内查看。通过读取每个FiberNode的 memoizedProps 和 memoizedState ,即可直接获取需要的Prop和State。在高版本使用React Hooks的项目中,FiberNode的 memorizedState 是一个链表,该链表内的节点次序可以参考该组件源码内 useState 的调用顺序。旧版React,引入的属性是 reactInternalInstance 。State也是一个Object而非链表,可以方便地看到每个state的名字。
在浏览器安装React Dev Tool,前端获取到state中的账号密码
登入后端使用HTTP3.0 发送请求即可获取到flag
1 | docker run -it --rm ymuski/curl-http3 curl -Lv https://172.35.3.40/user/home --http3 |
rev
rev2 我不是病毒
直接分析exe的话,可以发现关键的逻辑集中在main函数最后return的那个函数中,在sub_140005C80函数中会创建一个进程(要是找不到这个函数的话,先关闭地址随机化,或者自行分析),附加调试之后可以发现这个进程就是python310.dll,所以初步判断这个程序是python打包的exe。
用pyinstxtractor.py直接解包,可以发现有.pyz这样的文件和PYZ-00.pyz_extracted这个文件夹,而且后者中的所有文件都是加密过的
参考记python逆向 - TLSN - 博客园 (cnblogs.com)
[原创]Python逆向——Pyinstaller逆向-软件逆向-看雪论坛-安全社区|安全招聘|bbs.pediy.com (kanxue.com)
可以看到“archive.pyc就是加密的过程,crypto_key是加密的密钥,而我们需要解密.pyz文件”
所以分别反编译两个文件,可以找到CRYPT_BLOCK_SIZE = 16,加密方式是AES,加密密钥是
1 | HelloHiHowAreYou |
编写解密脚本
1 | import tinyaes |
由于PYZ-00.pyz_extracted文件中有太多的文件,再加上我的本地反编译失败,只能用在线网站反编译,次数被限制到了10min一次,所以在做的时候一直没找到真正的加密在哪里。
赛后复现的时候想到了题目描述,目的是找到产品密钥
所以找到了sign.pyc.encrypted文件,解密后
1 | import hashlib as 沈阳 |
变量名应该是被特地修改,降低可读性的,汉字也都经过了URL编码,不过整体逻辑很好理解
先用密钥初始化RC4的S盒,然后RC4解密shellcode,加载shellcode对输入进行处理
解密shellcode
1 | from Crypto.Cipher import ARC4 |
然后用函数指针加载shellcode
1 |
|
用IDA分析得到的exe
第一段shellcode可以分为两个部分,在0x167之前都是对后面部分的SMC,先动态调试,得到解密后的shellcode之后再复制写入ida
1 | ida_bytes.patch_bytes(要写入的地址,bytes.fromhex(“解密后的二进制shellcode”)) |
可以得到真正的加密函数
加密主要是将输入中的两个字符拼接在一起然后加密,直接爆破
1 |
|
Misc
maze
1 | from pwn import * |
shop
1 | import numpy as np |
Crypto
TSA
只需要向server发送$t^e\cdot c\;mod\;n$即可绕过限制,然后解得$t\cdot flag$,除以$t$就是flag了,exp中$t$取的2:
1 | from Crypto.Util.number import * |
Three
一个简单的三方安全协议,理论上知道两方的交互信息就能得到所有秘密,但是题目的形式偏向misc,很多表述不清楚,让人做的难受… 根据Save_Data()函数:
1 | def Save_Data(self): |
猜测压缩包密码大约就是在出题的时间附近,根据压缩包时间掩码爆破一下得到C的密码:2022-08-27 20:16:17.930813
. 利用A C的信息能够还原flag,思路见exp:
1 | from Crypto.Util.number import * |
pwn
haslang
1 | from pwn import * |