HexBrain

U.S. Dept Of Defense · Report #1679969

Host Header Injection on https://███/████████/Account/ForgotPassword

Validstatus
mediumseverity
19upvotes
14 Oct 2022disclosed

Summary

Twitter: https://twitter.com/0x1int

Also for this type of Host header injection / cache poisoning bug I will give you advice for fixing this bug. Since I seee your team still didn't validate host header. Though I noticed that host header is not validated on multiple domains. And I'm not sure that you shouldn't keep password reset links and keep new method for password reset such as getting temporary password on your email. Like your team implemented that. Suggested Mitigation/Remediation Actions I'd like to recommend to use $_SERVER['SERVER_NAME'] rather than $_SERVER['HTTP_HOST']. Make sure to check if it matches a whitelist of domains that you serve:

 $domains = ['example.com', 'foo.bar.baz'];
 if ( ! in_array($_SERVER['SERVER_NAME'], $domains)) {
     // error
 }

Despite what its name implies, unless you followed the first solution, $_SERVER['SERVER_NAME'] will also be populated by the Host header value when PHP runs through a "catch all" configuration. Thus, they are both equal unless your HTTP server is properly configured, hence why both solutions above refer to $_SERVER['SERVER_NAME'] - it effectively doesn't matter if you use that or $_SERVER['HTTP_HOST']. $_SERVER['SERVER_NAME'] will also be populated by the Host header value when code runs through a "catch all" configuration. Though they are both equal unless your HTTP server is properly configured, hence why both solutions above refer to $_SERVER['SERVER_NAME'] - it effectively doesn't matter if you use that or $_SERVER['HTTP_HOST']. 1 This is the code to resolve the host header attack. URL have to be specified.

$allowed_domains = array('yourdomain1', 'yourdomain2', 'yourdomain3');
$default_domain  = 'yourprimedomain';

if (in_array($_SERVER['HTTP_HOST'], $allowed_domains, TRUE))
{
    $domain = $_SERVER['HTTP_HOST'];
}
else
{
    $domain = $default_domain;
}

if (! empty($_SERVER['HTTPS']))
{
    $config['base_url'] = 'https://'.$domain;
}
else
{
    $config['base_url'] = 'http://'.$domain;
}

And put this on your header:

<base href="<?php echo base_url(); ?>" />

One more fix:

com.ibm.rules.decisioncenter.check-host = true
com.ibm.rules.decisioncenter.host-whitelist-patterns = <url_patterns>
com.ibm.rules.decisioncenter.check-host. 

If it's set to true then host servers are validated but if it's set to false such as com.ibm.rules.decisioncenter.check-host = false then value will be false obviously. com.ibm.rules.decisioncenter.host-whitelist-patterns this specifies URLs that needs to be validated in a nutshell whitelisted. Make sure to use Regex as a property value so, the ".*" at the end is required to specify any following characters. You can specify several values, separated by a comma. Second thing: Configure a parameter for Rule Execution Server. Try to find In your XML file, ilog.rules.res.TRUSTED_HOSTS_CONFIGURATION parameter. Only if there's XML file:

<context-param>
   <description>
      Host HTTP header protection configuration.
      When enabled, please provide a list of trusted hosts (separated by semicolons).
      Each host must be a valid regular expression.
      Example: enableHostsWhitelist=true,hosts=host1.com;.*\.company\.com;myserver
   </description>
   <param-name>ilog.rules.res.TRUSTED_HOSTS_CONFIGURATION</param-name>
   <param-value>enableHostsWhitelist=false,hosts=</param-value>
</context-param>

When ilog.rules.res.TRUSTED_HOSTS_CONFIGURATION parameter enableHostsWhitelist set to true, the protection against the host header injection is enabled. In this case, enter a list of the host servers that are trusted. You can enter multiple hosts, separated by a semicolon (;). If the ilog.rules.res.TRUSTED_HOSTS_CONFIGURATION parameter is set to false then value will be false. And Repackage and redeploy the Decision Center and Rule Execution Server archives.

Thank you! Have a good day!

How it was found

Dear DoD Team,

I found one high bug on your another domain. This is from Hack US Program. Affected domain is https://█████/

An attacker can manipulate the Host header as seen by the web application and cause the application to behave in unexpected ways. Very often multiple websites are hosted on the same IP address. This is where the Host Header comes in. This header specifies which website should process the HTTP request. The web server uses the value of this header to dispatch the request to the specified website. Each website hosted on the same IP address is called a virtual host. And It's possible to send requests with arbitrary Host Headers to the first virtual host.

Here's the PoC btw:

███

Thank you DoD!

Impact

Tampering of Host header can lead to the following attacks:

  1. Web Cache Poisoning-Manipulating caching systems into storing a page generated with a malicious Host and serving it to others.
  2. Password Reset Poisoning-Exploiting password reset emails and tricking them to deliver poisoned content directly to the target.
  3. Cross Site Scripting - XSS can be performed, if the value of Host header is used for writing links without HTML-encoding. For example Joomla used to write Host header to every page without HTML Encoding like this: <link href=”http://_SERVER['HOST']”> which led to cross site scripting.
  4. Access to internal hosts-To access internal hosts. 5.) It can also lead to Phishing Attacks.

System Host(s)

███████

Affected Product(s) and Version(s)

Users are affected

CVE Numbers

Steps to Reproduce

  1. Go to this domain: https://███/████████/
  2. Go to vendor login.
  3. Create a test account.
  4. Go to Forgot Password Reset: https://████/██████/Account/ForgotPassword
  5. Before inserting your email.
  6. Turn your foxy proxy on.
  7. Open your burp suite and go to http history tab.
  8. Now insert your email.
  9. In http history in burp you will see this request:
POST /████████/Account/ForgotPassword HTTP/1.1
Host: ███
Cookie: .AspNetCore.Antiforgery.wZhPOrJ1UhI=; TS014b77bb=; ASP.NET_SessionId=; TS0144f203=; CSRF-TOKEN=
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:104.0) Gecko/20100101 Firefox/104.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: hr,hr-HR;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Content-Type: application/json
X-Csrf-Token: 
X-Requested-With: XMLHttpRequest
Content-Length: 35
Origin: https://███████
Referer: https://████/█████████/Account/ForgotPassword
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Te: trailers
Connection: close

{"Email":"███████"}
  1. Send this request to repeater tab.
  2. Change Host headet to attacker.com
  3. It shoud look like this:
POST /████████/Account/ForgotPassword HTTP/1.1
Host: attacker.com
Cookie: .AspNetCore.Antiforgery.wZhPOrJ1UhI=; TS014b77bb=; ASP.NET_SessionId=; TS0144f203=; CSRF-TOKEN=
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:104.0) Gecko/20100101 Firefox/104.0
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: hr,hr-HR;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Content-Type: application/json
X-Csrf-Token: 
X-Requested-With: XMLHttpRequest
Content-Length: 35
Origin: https://████
Referer: https://███████/████/Account/ForgotPassword
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
Te: trailers
Connection: close
  1. Now send request and you will get 200 OK response.
  2. Go to your mail/gmail.
  3. You will see some magic.
  4. PoC is down there.

Suggested Mitigation/Remediation Actions

This report was disclosed publicly on HackerOne and belongs to its author and the program. See the original