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


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: 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.