题目信息
- 挑战: IRC: Duckhunt (#136)
- 类型: Fun / Gaming
- 作者: gizmore
- 难度: 2
- 解决人数: 53
- 链接:
https://www.wechall.net/en/challenge/irc/duckhunt/index.php
题目描述
Hello! IRC is not dead. Prove this theorem by reaching level 5 in
#duckhunt.
连接 WeChall IRC 服务器,在 #duckhunt 频道玩 Duck Hunt 游戏,达到
level 5(140 XP)。Nickname 必须匹配 WeChall 用户名。
验证方式:挑战不再使用静态密码。提交时 WeChall 服务器通过 API
https://wc3.wechall.net/duck_hunt/level.php?username=<username>
查询 IRC 等级。Level >= 5 即判定 solved。
技术分析
IRC 服务器
| 项目 |
值 |
| Server |
irc.wechall.net:6697 (TLS 1.3) |
| IRCd |
InspIRCd-3 |
| Channel |
#duckhunt |
| Bot |
Alfred (Eggdrop, Duck Hunt v2.11) |
| Topic |
No botting/scripting allowed \| unarmed? try !shop 5 or wait until 00:01 |
连接流程
- OpenSSL TLS 连接到
irc.wechall.net:6697
- 发送
NICK <username> +
USER <username> 0 * :<username>(替换为 WeChall
用户名)
- 关键: 服务器在注册前发送 PING,必须回复
PONG :<token> 才能完成注册
- 等待
376 (End of MOTD) → 发送
JOIN #duckhunt
- 自动加入
#wechall(默认频道)+
#duckhunt
等级进度
| Level |
Required XP |
Accuracy |
Reliability |
Ammo/Clip |
Clips |
| 1 |
-4 |
55% |
85% |
6 |
2 |
| 2 |
20 |
56% |
86% |
6 |
2 |
| 3 |
50 |
57% |
87% |
6 |
2 |
| 4 |
90 |
58% |
88% |
6 |
2 |
| 5 |
140 |
59% |
89% |
6 |
2 |
每只鸭子 = 5 XP。弹药每日 00:01 UTC 重置。默认 12 发(2 clips ×
6)。
Duck 检测:quack 变体列表
实测收集到的所有鸭子叫声变体(按出现频率排序):
| 变体 |
正则匹配 |
备注 |
KAAAAK |
K[A]{2,} |
最常见 |
KAAAACK |
K[A]{2,} |
|
KAAACK |
K[A]{2,} |
|
KWAK |
KWA[KC]? |
|
KWAAK |
KWA[KC]? |
|
KWAAACK |
KWA[KC]? |
|
QUAC |
Q[UW]A{1,}C?K? |
法语版 |
QWAAACK |
Q[UW]A{1,}C?K? |
新变体 |
AAARK |
A{2,}RK |
罕见 |
Hello world |
— |
机械鸭子(玩家 prank, 不射) |
最终检测正则(覆盖所有已知变体,排除击杀结果/逃逸消息):
1 2 3 4 5 6 7 8
| is_duck = ( nick == 'Alfred' and re.search(r'K[A]{2,}|KWA[KC]?|Q[UW]A{1,}C?K?|A{2,}RK', clean, re.IGNORECASE) and 'BANG' not in clean and 'escapes' not in clean.lower() and 'shot down' not in clean.lower() and 'total of' not in clean.lower() )
|
游戏状态消息
| 类型 |
模式 |
处理 |
| 击杀 |
shot down the duck in X.XXs |
hits++, ducks++ |
| 未命中 |
Missed. [missed: -1 xp] |
xp-- |
| 卡壳 |
*CLACK* Your gun is jammed |
发送 !reload |
| 空弹夹 |
*CLICK* EMPTY MAGAZINE |
发送 !reload |
| 逃逸 |
The duck escapes. |
重置 duck_active |
| 事故 |
accident |
xp 惩罚 |
弹药管理
- 初始 2 chargers × 6 rounds = 12 发
- 每日 00:01 UTC 自动重置
- 枪卡壳/空弹夹后需要
!reload 命令
- 商店物品 #2 (Extra clip, 20 XP) 可补充弹药
- 商店物品 #21 (Bread, 2 XP) 提高鸭子频率
- 商店物品 #10 (Four-leaf clover, 13 XP) 每只鸭子随机 +1~10 XP
Bot 实现
文件位置
1 2 3 4 5 6
| <ctf-workspace>/challenges/wechall/duckhunt/ ├── bot_v8.py # 最终版 ├── bot_state.json # 进度持久化 (xp, level, shots, hits, ducks) ├── test_connect.py # IRC 连接测试 ├── monitor.py # 频道监听诊断 └── bot.py ~ bot_v7.py # 各迭代版本
|
架构
subprocess.Popen 调用 openssl s_client
处理 SSL(绕过 Python socket 的代理问题)
- 独立 reader 线程 +
queue.Queue 非阻塞 I/O
- 自动 PING/PONG 保活 + idle 超时重连
- Duck 检测 → 自动射击(随机延迟 0.3-1.5s)
- Jam/Empty → 自动
!reload
- State 持久化 JSON
- 断开自动重连(递增等待,最多 20 次)
运行方式
1 2
| cd <ctf-workspace> uv run python3 challenges/wechall/duckhunt/bot_v8.py
|
Daemon 化(VPS 上):
1
| setsid python3 -u bot.py > bot.log 2>&1 < /dev/null &
|
实战记录
Vultr VPS 部署 (2026-07-16)
- 实例:
vc2-1c-0.5gb @ ewr (New Jersey), Debian 12
- IP:
207.246.124.216
- 运行时长: ~2 小时,然后销毁
进度
| 指标 |
值 |
| 运行时间 |
~2h |
| 射击次数 |
4 |
| 命中 |
1 |
| 击杀鸭子 |
2 |
| XP |
10 |
| 等级 |
1 |
遇到的问题和修复
- 注册 PING 未回复 → 在 NICK/USER 后立即处理 PING
token
- Quack 变体不断发现 → 正则从
KAAACK
逐步扩展到覆盖 9 种变体
- 枪卡壳后停止射击 → 添加
!reload
自动处理
- SSH
nohup & disown 不稳定 → 改用
setsid daemon 化
- 网络偶尔断开 → 自动重连机制
时间估算
以实测速率(~2 鸭子/小时,55% 命中率):
- 140 XP = 28 只鸭子
- 预计 12-20 小时连续运行
- 建议 UTC 白天(06:00-18:00)运行,夜间鸭子休眠
剩余步骤
- 运行
bot_v8.py 直至 level 5
- 访问
https://www.wechall.net/en/challenge/irc/duckhunt/index.php(自动检测等级)
- 如需 VPS,使用 vultr-cli 创建实例并部署 bot
Vultr 快速部署
1 2 3 4
| export VULTR_API_KEY='...' vultr-cli instance create --region ewr --plan vc2-1c-0.5gb --os 2136 --label ctf-duckhunt
|
提交检查
1 2 3
| curl -sk 'https://wc3.wechall.net/duck_hunt/level.php?username=<username>'
|