HackThisSite - Programming Mission 1
Challenge
Level 1 — Unscramble the words
找出被打乱顺序的原始单词,单词是从官方 wordlist 里随机挑的,30 秒内把原始单词按列表顺序用逗号分隔提交。
Find the original (unscrambled) words, which were randomly taken from a wordlist. Send a comma separated list of the original words, in the same order as in the list below. You have 30 seconds time to send the solution.
每次访问实例页都会重新生成一组乱序单词,30
秒的限时决定了只能脚本化。状态:verified(服务端返回
Good Job, ***, You have successfully completed this mission)。
Solution
- 实例页
https://www.hackthissite.org/missions/prog/1/里,乱序词逐个用<br />分隔,前面是List of scrambled words:,后面是Answer:,解析时按这两句切段最省事。 - 词表是公开的固定文件:
https://www.hackthissite.org/missions/prog/1/wordlist.zip(不需要登录态,匿名 curl 也能下)。解开后是wordlist.txt,1274 行、CRLF 行尾。 - 词表里不只有普通英文单词,还混着数字串和带符号的词(
121212、654321、8675309、666666、html:)等)。所以题面里出现888888这种条目是完全正常的,它本身就是一个合法条目。
1 | $ curl -s -o wordlist.zip https://www.hackthissite.org/missions/prog/1/wordlist.zip && unzip -o wordlist.zip >/dev/null |
打乱只改变字符顺序、不改变字符频次,所以每个乱序词 w
与原文的排序后字符串(把字符排序后拼接)完全相等。建索引时用排序后的串当
key,一次查表即可;重复字母(aremedr、rirmemto)也不会出错。
词表可能存在同一多重集对应多个词的极端情况,这里额外保底:命中不到时按长度过滤再比对排序串。
1 | def load_wordlist(path="wordlist.txt"): |
限时 30 秒,但真正的时间开销在解析上(字符串处理),网络只占两次往返:GET 实例页 + POST 答案。
1 | $ cd <hts-workspace> && export HTS_COOKIE='<mission-cookie>' |
一次真实的解析/匹配输出:
1 | scrambled: amtnar plradnot chyeok ubeadtht aremedr getayaw udsettn n1j6o3h cuatain rirmemto |
提交表单字段是 solution(submitbutton
只是按钮文案),成功响应里含
successfully completed this mission。
- 顺序不能变:题面要求按列表顺序提交,所以解析出来的顺序必须原样保留(不要排序、不要去重)。
- 数字条目照抄:像
888888这种条目在词表里就是它自己,匹配后回填原值即可;如果按必须是英文单词过滤,反而会把它丢掉导致整行答案错位。 - 行尾:词表是 CRLF,读行时统一
strip(),否则 key 会多一个\r,全部匹配失败。 - 不应使用
set建索引:重复字母的乱序词会被压缩(例如aabb与abab只有排序串相等),必须是排序后字符串而不是字符集合。
Vulnerabilities
关卡把答案空间完全暴露给客户端:词表是公开可下载的静态文件,乱序只是字符置换,服务端只比对最终字符串没有任何时间/次数之外的防护。结论很直接:任何在客户端做推导的校验都等价于把答案交给攻击者,30 秒限时也只是把人工操作挡在门外,对脚本毫无难度(解析 + 查表在毫秒级完成)。修复方向:把词表放在服务端、客户端只提交结果哈希并加随机 nonce;若必须本地校验,至少不应提供完整词表。
mantra,portland,hockey,butthead,dreamer,gateway,student,john316,nautica,mortimer