HackThisSite - Programming Mission 11

Challenge

Level 11 — Reverse Ascii Shift

页面给出一串随机生成的 % 分隔的 ASCII 码和一个 Shift 值。把每个码按 Shift 反向还原,得到的字符串就是答案(Decoded ASCII)。限时 3 秒

This string was randomly generated. It will not be recognizable text. You have 3 seconds to take the information from the website, and apply that to your algorithm.

状态:verified(服务端返回 Good Job, ***, You have successfully completed this mission)。

Solution

  • 实例页 https://www.hackthissite.org/missions/prog/11/ 的正文里有三行:Generated String: 89%71%64%55%42%89%84%37%57%Shift: 3Decoded ASCII
  • 表单只有一个 solution 字段,submit (remaining time: 3 seconds) 说明 3 秒是从页面生成那一刻开始算的:抓页面、解码、POST 必须在同一进程内连续完成,任何手动步骤都不可能。
1
2
3
4
5
6
Level 11
This string was randomly generated. It will not be recognizable text. You have 3 seconds to take the
information from the website, and apply that to your algorithm.
Generated String: 89%71%64%55%42%89%84%37%57%
Shift: 3
Decoded ASCII

% 只是分隔符,每个数字是一个被平移过的字符码。题面用词是 reverse(反向还原),所以先尝试 chr(code - shift);实测该方向被服务端直接接受:

1
2
3
Generated String: 52%31%42%54%33%52%48%33%37%   Shift: 2
try shift -2 -> '&2*H!TNF3%M'
verdict: True # 服务端接受 -> Good Job, ***, You have successfully completed this mission

也就是说编码端做的是 code = ord(plain) + shift,解码端就是逐码减 shift 再拼接(拼接结果为原始字符,其中可能出现 %! 这类符号,不可把这些符号当分隔符二次切分)。

1
2
def decode(codes, shift):
return "".join(chr(c - shift) for c in codes)
1
2
$ cd <hts-workspace> && export HTS_COOKIE='<mission-cookie>'
$ uv run python challenges/hts-prog/11/solve.py

solve.py 的结构:

  1. GET /missions/prog/11/ 并在内存里解析(不落盘、不做多余解析);
  2. re.findall(r"\d+", ...) 取出所有码、从 Shift: N 取平移量;
  3. 直接 POST /missions/prog/11/index.php,字段 solution

因为实例每次 GET 都会重新生成,脚本里还留了一个保险分支:若 -shift 被判错,就重新 GET 一个新实例再试 +shift(实测 -shift 一次即通过,保险分支未被触发)。

  • 提交前打印解码结果,提交后打印服务端响应。两项输出都保留在终端中,答案与接受它的服务端响应属于同一条证据链。
  • 3 秒不是主要难点:解析和拼接是微秒级,瓶颈是两次 HTTP 往返。真正要避免的是多开进程/多写文件这类额外开销。

Vulnerabilities

该题属于纯客户端可推导题型:算法(逐码减 shift)由题面给出,服务端只校验最终字符串。信息完整出现在响应里时,限时只能限制手工操作,对脚本无效。这类题型要做防护,应让每次提交的实例与服务端状态强绑定(nonce + 单次有效),并避免下发完整可推理的数据。

&2*H!TNF3%M