Challenge
Level 8 — Code an IRC bot
写一个 IRC bot 完成指定动作:bot 必须用一个已注册、开启
autoop 并已绑定 HTS 账号 的 nick 连到
irc.hackthissite.org;notice 机器人 moo 发送
!perm8,moo 会回一条
!md5 <随机串>,bot 必须回
!perm8-result <hash>;哈希正确后在限时内
moo 会向 bot 发 VERSION 请求(bot 必须应答,且不能是
mIRC);随后 moo 会发 !perm8-attack,bot
必须尽快加入 #takeoverz 并尝试 kick moo。
绑定方式:在 #perm8 里输入
!link <hts 账号>,然后在站点的 IRC Nickname Link Page
上确认。
状态:blocked 。关卡依赖的握手机器人 moo
当前不在 IRC 网络上 (证据见下),因此无法完成
md5/version/attack 流程。
Solution
题目要求的第一件事是用一个已注册并绑定 HTS 账号的
nick。这一步是真实操作过的:
用 NickServ 注册
nick:/msg NickServ REGISTER <password> <email>。服务端要求该
nick 已在线至少 30
秒才允许注册(第一次直接注册会被拒绝:You must have been using this nick for at least 30 seconds to register)。
注册后会收到确认邮件,必须
/msg NickServ CONFIRM <passcode>;未确认的账号会在 1
天后过期(Your account will expire, if not confirmed, in 1 day)。
登录后
!link <hts 账号>:/msg NickServ IDENTIFY <password>
→ /msg NickServ SET AUTOOP ON(返回
Services will from now on set status modes on <nick> in channels)。
在 #perm8 发
!link <hts 账号>,然后到
https://www.hackthissite.org/pages/irc/link.php
确认绑定。
1 2 3 4 5 6 7 >> NS REGISTER <password> <mailbox> << :NickServ!services@hackthissite.org NOTICE <botnick> :Nickname <botnick> registered. << :NickServ!services@hackthissite.org NOTICE <botnick> :Your email address is not confirmed. ... >> NS CONFIRM <passcode> << :NickServ!services@hackthissite.org NOTICE <botnick> :Your email address of ... has been confirmed. >> NS SET AUTOOP ON << :NickServ!services@hackthissite.org NOTICE <botnick> :Services will from now on set status modes on <botnick> in channels.
!link 没有生效(Link Page
上始终没有出现待确认记录),继续排查发现根本原因:moo
这个 nick 在网络上不存在 。
1 2 3 4 5 6 >> ISON moo # 空回复:不在线 >> WHOIS moo << :wolf.irc.hackthissite.org 401 <botnick> moo :No such nick/channel << :wolf.irc.hackthissite.org 318 <botnick> moo :End of /WHOIS list. >> PRIVMSG #perm8 :!link <hts 账号> (无任何回应)
同时:
#perm8 里只有一个闲置的
op(353 <botnick> = #perm8 :<botnick> @weekend),没有任何机器人;
#takeoverz
存在(322 <botnick> #takeoverz 1 :[+ntr]),加入时确实会像题面说的自动拿到
op;
Link Page 表格一直是空的(没有待确认的 nick 行),说明
!link 没有任何进程在处理。
服务端 ISON 对
moo、Moo、moo_、HTSBot
等候选一律返回空,只有 ChanServ
存在。结论:这关依赖的服务端机器人已下线 ,!link
→ !md5 → VERSION → !perm8-attack
的整条链路都无法触发;这是关卡自身的失效,不是本地实现问题。
为完成本关的注册步骤,按题面协议写了一个不依赖任何现成客户端脚本的最小实现(纯
socket + TLS,VERSION 应答完全可控,因此不会被判成 mIRC):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 import socketimport sslimport timeHOST, PORT = "irc.hackthissite.org" , 6697 VERSION_REPLY = "htsbot 1.0 (python socket IRC client)" class IRC : """Minimal IRC client: TLS connect, NickServ identify, PING/PONG.""" def __init__ (self, nick, timeout=2.0 ): self .nick = nick self .buf = "" raw = socket.create_connection((HOST, PORT), timeout=20 ) ctx = ssl.create_default_context() ctx.check_hostname = False ctx.verify_mode = ssl.CERT_NONE self .s = ctx.wrap_socket(raw, server_hostname=HOST) self .s.settimeout(timeout) def send (self, line ): self .s.sendall((line + "\r\n" ).encode()) def pump (self, seconds ): """Read for `seconds`, auto-PONG the server, return the new lines.""" end, got = time.time() + seconds, [] while time.time() < end: try : data = self .s.recv(8192 ) except socket.timeout: continue except OSError: break if not data: break self .buf += data.decode("utf-8" , "replace" ) while "\r\n" in self .buf: line, self .buf = self .buf.split("\r\n" , 1 ) got.append(line) if line.startswith("PING" ): self .send("PONG " + line.split(":" , 1 )[-1 ]) return got def login (self, password ): self .send("NICK " + self .nick) self .send(f"USER {self.nick} 0 * :{VERSION_REPLY} " ) self .pump(5 ) self .send(f"NS IDENTIFY {password} " ) self .pump(4 ) def notice (self, target, text ): self .send(f"NOTICE {target} :{text} " ) def version_reply (self, target ): self .send(f"NOTICE {target} :\x01VERSION {VERSION_REPLY} \x01" )
事件循环部分(等 moo 上线后可直接跑):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 def serve (bot, password ): import hashlib bot.login(password) bot.notice("moo" , "!perm8" ) while True : for line in bot.pump(2 ): if "!md5 " in line: challenge = line.split("!md5 " , 1 )[1 ].strip() bot.notice("moo" , "!perm8-result " + hashlib.md5(challenge.encode()).hexdigest()) if "\x01VERSION\x01" in line: bot.version_reply("moo" ) if "!perm8-attack" in line: bot.send("JOIN #takeoverz" ) bot.pump(2 ) bot.send("KICK #takeoverz moo :bye" )
(完整可运行版本在工作区
challenges/hts-prog/8/ircbot.py;register_nick.py
负责注册/确认/identify,link_nick.py 负责发
!link。所有脚本都从命令行/环境变量取账号与凭据,不把凭据写进文件。)
1 2 3 $ cd <hts-workspace> && export HTS_COOKIE='<mission-cookie>' $ uv run python challenges/hts-prog/8/link_nick.py confirm <passcode> # 邮箱确认 $ uv run python challenges/hts-prog/8/link_nick.py link <hts账号> # #perm8 里 !link
moo 不在线是可复现的:WHOIS moo 稳定返回
401 No such nick/channel,ISON 无回显。
关卡页面本身没有任何服务中断提示,Link Page
也不报错,只是永远不会有记录,容易误判成自己的绑定姿势有问题。
判定口径:服务端行为(机器人是否存在)优先于题面描述 ;这类依赖站方
bot 的关卡一旦 bot 下线,应当记录为 blocked 而不是继续堆代码。
Vulnerabilities
关卡设计上依赖一个长期在线的站方机器人来做状态机推进,这种耦合本身就是可用性风险:机器人一停,关卡对所有玩家同时失效,而且从关卡页面看不出任何异常(题面照常给出、表单照常存在)。更稳妥的做法是把握手逻辑放到站点自身的服务里(例如由站点后端持有状态、机器人只是展示层),或者至少在
bot 掉线时在关卡页显示维护状态。
未通关:服务端机器人 moo 已下线(WHOIS 返回
401,见正文证据);前置注册/autoop/link 与 bot 实现均已完成,moo
恢复后可继续