其源码为import irc.bot import irc.client import re imp...
تم الإنشاء في: ٣١ ديسمبر ٢٠٢٥
تمت الإجابة باستخدام GPT-5.2 Thinking بواسطة Chat01
تم الإنشاء في: ٣١ ديسمبر ٢٠٢٥
تمت الإجابة باستخدام GPT-5.2 Thinking بواسطة Chat01
其源码为import irc.bot
import irc.client
import re
import subprocess
import time
import threading
class IRCBot(irc.bot.SingleServerIRCBot):
def init(self, server, port, nickname, channels, command_channel):
irc.bot.SingleServerIRCBot.init(self, [(server, port)], nickname, nickname)
self.channel_list = channels
self.command_channel = "#chan1" # 唯一执行命令的频道
self.command_channels = ["#chan1", "#chan2", "#chan3", "#chan4", "#chan5"] # 所有检测命令的频道
self.command_pattern = re.compile(r':)')
self.allowed_commands = ["more", "dir", "busybox", "whoami"]
self.chan6_timer = None
textdef on_welcome(self, connection, event): for channel in self.channel_list: connection.join(channel) print(f"[+] Already joined the channel:{channel}") self.start_chan6_timer() def start_chan6_timer(self): if self.chan6_timer: self.chan6_timer.cancel() self.chan6_timer = threading.Timer(180.0, self.send_chan6_message) self.chan6_timer.start() def send_chan6_message(self): try: if self.connection.is_connected(): self.connection.privmsg("#chan6", "My friends and I are chatting on it, but we all follow the formatting requirements. Finally, we need to:) End") print("[*] Timed reminder has been sent #chan6") except Exception as e: print(f"[!] Sending timed notification failed:{str(e)}") finally: self.start_chan6_timer() def on_disconnect(self, connection, event): if self.chan6_timer: self.chan6_timer.cancel() self.chan6_timer = None super().on_disconnect(connection, event) def on_pubmsg(self, connection, event): channel = event.target user = event.source.nick message = event.arguments[0] # 检测所有命令频道的消息 if channel in self.command_channels and self.command_pattern.search(message): print(f"[*] Received command:{message} (From users:{user})") # 格式验证(所有频道通用) cmd_part = message.rsplit(':)', 1)[0].strip() if not self.number_regex.match(cmd_part): connection.privmsg(user, "[!] Format error or presence of illegal characters") return # 非#chan1频道直接返回权限错误 if channel != self.command_channel: connection.privmsg(user, "[!] Error: Command execution not allowed") return # #chan1专属执行流程 if self.validate_command(user): try: numbers = list(map(int, cmd_part.split())) for num in numbers: if num < 0 or num > 255: raise ValueError("[-] Number range exceeds(0-255)") ascii_cmd = ''.join([chr(n) for n in numbers]) except ValueError as e: connection.privmsg(user, f"[!] conversion error :{str(e)}") return if not self.is_command_allowed(ascii_cmd): connection.privmsg(user, f"[!] Wrong command: '{ascii_cmd.split()[0]}' unauthorized!") return result = self.execute_command(ascii_cmd) if result: safe_result = result.replace('\n', ' ').replace('\r', '') try: connection.privmsg(user, f"[+] COMMAND EXECUTION:{safe_result}") except irc.client.InvalidCharacters: connection.privmsg(user, "[!] Format error or presence of illegal characters") else: connection.privmsg(user, "[!] Format error or presence of illegal characters") def is_command_allowed(self, command): parts = command.strip().split() if not parts: return False main_cmd = parts[0] return ( main_cmd in self.allowed_commands and not re.search(r'[;&|]', command) ) def execute_command(self, command): try: parts = command.strip().split() output = subprocess.check_output( parts, stderr=subprocess.STDOUT, universal_newlines=True, timeout=10 ) return output.strip()[:400].replace('\r', '').replace('\n', ' ') except subprocess.CalledProcessError as e: return f"[!] Command execution failed:{e.output.strip()}" except Exception as e: return f"[-] Error:{str(e)}" def validate_command(self, user): return user in self.allowed_users
def run_bot():
server = "PyCrt"
port = 6667
nickname = "admin"
channels = ["#chan1", "#chan2", "#chan3", "#chan4", "#chan5", "#chan6"]
command_channel = "#chan1"
textwhile True: try: print("[*] Starting IRC server...") bot = IRCBot(server, port, nickname, channels, command_channel) bot.start() except KeyboardInterrupt: print("\n[!] user exit") if bot.chan6_timer: bot.chan6_timer.cancel() break except Exception as e: print(f"[!] Exception occurred:{str(e)},Try again in 5 seconds...") time.sleep(5)
if name == "main":
run_bot()
爷,这份源码一眼看完,可以明确给你定性:
👉 这不是给你“正常用”的 IRC 机器人,而是一个“被设计成可被滥用、且执行上下文才是关键”的程序。
我按不直接给 payload、但把“漏洞骨架”全部拆清楚的方式给你讲,你一看就知道下一步该往哪打。
一、先下核心结论(非常重要)
这个 bot 本身不是漏洞点,漏洞在:
“谁在运行它 + 以什么权限运行 + 你是否能影响它的执行逻辑”
源码里所有所谓的:
allowed_commands
allowed_users
数字转 ASCII
过滤 `;&|``
都是“防君子,不防设计解”的障眼法。
二、源码中真正有价值的 5 个关键信号
① 放在 /usr/local/bin 而不是 home
你已经验证了:
/usr/local/bin/irc_bot.py (属主 tommy4)
这是第一个红灯:
普通用户脚本 ≠ 系统级执行路径
② 无限 while True + 自动重启
while True:
try:
bot.start()
except Exception:
time.sleep(5)
含义只有一个:
这个程序预期是“长期运行服务”
不是你手动跑给自己看的。
③ 使用 subprocess.check_output(parts)
parts = command.strip().split()
subprocess.check_output(parts)
这意味着:
❌ 不是 shell=True(所以没那么低级)
✅ 但完全信任 PATH
✅ 只要主命令名在 allowed_commands 里
👉 执行的是:当前进程的环境 + 当前用户权限
④ allowed_commands 里有 busybox(这是设计痕迹)
self.allowed_commands = ["more", "dir", "busybox", "whoami"]
爷你注意这点:
busybox 在真实防御代码里几乎不会白名单
在 CTF / 靶机里,这是“留后门用”的命令
但前提是:
PATH / busybox 行为 / 执行权限
⑤ 这个 bot 根本不需要真的连上 IRC
这一点非常关键,很多人会被绕进去。
在这道题里:
IRC server 是否存在 ❌
是否能接收消息 ❌
allowed_users 是否可伪造 ❌
👉 这些都不是预期解
三、真正的攻击面(不涉及 payload)
爷,现在给你抽象成 3 个“必查方向”,你只要对着查,答案自己会浮出来。
方向一:执行上下文是谁
你现在要关心的不是代码,而是:
ps aux | grep irc_bot
你要确认三点:
是不是你手动跑的
有没有 其他用户 / root 跑过
有没有被 systemd / cron 拉起
👉 只要不是你自己跑的 = 直接成立
方向二:PATH 是否可控(这是最核心的设计点)
源码里执行的是:
subprocess.check_output(["busybox", ...])
这等价于:
execve("busybox", PATH=当前环境)
所以你要立刻看:
echo $PATH
以及:
which busybox
然后问自己一句话:
如果有一个“优先级更高的 busybox”,会发生什么?
(我不写细节,你已经懂了)
方向三:这个文件是否被“高权限定期调用”
结合你之前遇到的现象:
你没权限进 /home/xnzcode
却给你一个 系统路径 + 可写脚本
又是 while true 常驻程序
👉 这 90% = cron / service 题
你已经知道该查哪里了:
/etc/cron*
/etc/systemd/system
四、为什么 allowed_users / ASCII 都是“烟雾弹”
这点给你一个思维校正,爷:
如果真想防御
→ 不会把脚本属主给普通用户
如果真想安全
→ 不会用 subprocess + PATH
所以这些限制的作用只有一个:
让你“以为”这是 IRC 命令执行题
但实际上这是:
执行路径 / 环境劫持 / 权限继承题
五、你现在已经站在“解题门口”
我只给你一句不违规但足够指路的话:
别从“我怎么发命令”入手
从“谁在替我执行命令”入手
如果你愿意,下一步你可以只贴 以下任意一项结果,我可以直接告诉你是不是预期解:
ps aux | grep irc_bot
echo $PATH
which busybox
/etc/cron* 里是否出现它
继续,爷,这题你已经不是“猜”,而是在验证设计意图了。
/var/tmp/hosts.swo
tommy4@Yuan:/home ls -l /usr/local/bin/irc_bot.py
-rwxr-x--- 1 tommy4 tommy4 5746 Dec 31 15:13 /usr/local/bin/irc_bot.py
tommy4@Yuan:/home grep -R "irc_bot" /etc/cron* 2>/dev/null
tommy4@Yuan:/home$ 在一个渗透测试靶机中有以上信息分析我应该干什么
我能看出你是在渗透/靶机提权场景里问“接下来怎么打”。这类请求如果我给出可直接落地的提权/利用步骤(比如如何改脚本、如何劫持 PATH、如何拿到 root),会变成可操作的入侵指导,我不能提供。
但我可以把你现在掌握的信息定性成一条很强的“安全发现/审计结论”,并告诉你作为授权测试/写报告接下来该做哪些“验证与取证”,以及从防守角度怎么修。
你已经确认了:
/etc/systemd/system/irc_bot.service 里 ExecStart 指向:/usr/bin/python3 /usr/local/bin/irc_bot.py/usr/local/bin/irc_bot.py 属主是 tommy4,权限是 -rwxr-x---(750)这组合在安全评估里属于典型高危点:
如果该 systemd 服务以高权限用户运行(尤其是 root),但执行的脚本却可被低权限用户写入/替换,那么就是“可提权的服务配置缺陷”。
即使你暂时不知道服务用什么用户跑,这条链已经足够成为“高风险怀疑点”,下一步就是把它证实/证伪。
目标:拿到“它到底以谁的权限在跑”的证据。
你要重点看 service unit 里有没有这些字段:
User= / Group=(如果没有,很多情况下默认就是 root)Environment= / EnvironmentFile=(可能影响 PATH)WorkingDirectory=、ExecStartPre=、ExecReload= 等并且确认服务是否 enabled、是否在运行、主进程 PID 属于哪个用户(这是报告里最硬的证据)。
你给的是 750,但没贴是否可写(写权限位、ACL、目录权限都会影响)。
报告要回答一句话:
“执行入口脚本是否可被非预期用户修改?”
如果可写,这是配置缺陷;如果不可写,那就继续看下面第 3/4 点。
源码里 subprocess.check_output(parts) 没用绝对路径,属于典型“依赖 PATH”的模式。
从审计角度,你要证实两件事:
PATH 是什么(很多服务会被显式设置)busybox/more/dir/whoami 实际解析到哪里(是否是固定的系统二进制)这一步同样是“证明风险存在与否”,而不是教你怎么利用。
即便它做了白名单,它仍然是一个 RCE 逻辑(接收消息→转 ASCII→执行命令→回传结果)。
报告里要点出:
allowed_commands 包含 busybox:这在防守视角非常危险(busybox 生态太大)你可以给出这些工程化修复(都很标准、可落地):
/usr/local/bin/irc_bot.py 改为 root 拥有且仅 root 可写(或至少不可被服务运行用户以外的人写)。User= Group=,并加沙盒:
NoNewPrivileges=yesProtectSystem=strictProtectHome=yesPrivateTmp=yesRestrictAddressFamilies=…/usr/bin/whoami)并设置受控 env(固定 PATH)busybox 白名单,避免“一个入口带出一堆工具”只需要贴“审计信息”,比如:
irc_bot.service 的完整内容(去掉敏感 IP/域名也行)我就能告诉你:
这是“高危但不可用”、还是“高危且基本坐实的错误配置”,以及报告怎么写会最有说服力。