- Home
- About us
- Products & Services
- Blog
- News
- Contact
- Client Portal
On a recent engagement we gained unrestricted administrative access to a certain proprietary web application by exploiting a Session Fixation flaw. According to the WASC Threat Classification v2, Session Fixation is an attack technique that forces a user's session ID to an explicit value. In other words, by feeding the victim a session token under the attacker's control, the attacker can bypass the authentication phase and gain unauthorized account access as the victim. There are a number of techniques that can be used to feed the token to the victim. The most common is by exploiting a XSS or header injection vulnerability.
Due to the fact that this was a proprietary application, the client was unable to easily resolve the issue. Today, we will discuss two methods to virtually patch this flaw using ModSecurity. The first approach removes the session token submitted within the authentication request. On some frameworks, the application will automatically assign a new session token if none is submitted when succesfully logging in to the application. This approach uses inter-module communication between ModSecurity and ModHeaders, as shown below.
# Set the enviroment variable REMOVE_COOKIE if the request is a
# login request (e.g. contains the passwd POST parameter) to force
# the back-end app to issue a new session token
SecRule REQUEST_URI "/login.php" "phase:2,chain, \
log,msg:'Removing Cookie'"
SecRule REQUEST_METHOD "^post$" t:none,t:lowercase,chain
SecRule &ARGS:passwd "@eq 1" setenv:REMOVE_COOKIE
# Have ModHeaders remove the Cookie header if the env variable
# REMOVE_COOKIE is set
RequestHeader unset Cookie env=REMOVE_COOKIE
While the above approach is pretty straightforward, it does not work in all situations. The next approach can be used if the application does not set a new session token when an authentication request is submitted without a session token. This method uses the session collection to tie a new token to the existing application-generated session token. There are two parts to this solution, the ModSecurity rules and the Lua script. First, let's look at the ModSec rules.
# Create the session collection
SecRule REQUEST_COOKIES:/^ASP.NET/ ^(.+)$ \
"phase:2,capture,log,pass,\
setsid:%{TX.0},\
setvar:SESSION.TIMEOUT=172800,msg:'captured sessid %{TX.0}'"
# If this is a login request, create an additional session token
# tie it to the SESSION and add it to the TOKEN enviroment variable
SecRule &ARGS:passwd "@eq 1" "phase:2,chain,log,allow, \
exec:/opt/modsecurity/etc/customrules/CreateSessionToken.lua, \
setenv:TOKEN=%{TX.token},setenv:AUTHENTICATED \
setvar:SESSION.token=%{TX.token}, \
msg:'Setting token to %{TX.token}'"
SecRule REQUEST_METHOD POST
# Have ModHeaders set our new session token if the AUTHENTICATED enviroment
# variable exists (set in the previous rule)
Header add Set-Cookie "SessionID=%{TOKEN}e; path=/; HttpOnly; Secure" \
env=AUTHENTICATED
# Ensure that the request only has one SessionID cookie to prevent cookie
# stuffing attacks
SecRule &REQUEST_COOKIES:SessionID "!@eq 1" "phase:4,chain,deny,log,auditlog, \
msg:'Missing or incorrect number of SessionID cookies'"
SecRule RESPONSE_BODY Logout
# Enforce all authenticated requests (e.g. has the string "Logout" in the
# response body) to have the proper session token
SecRule RESPONSE_BODY Logout "phase:4,chain,deny,log,auditlog, \
msg:'SessionID does not match the real Session token'"
SecRule REQUEST_COOKIES:SessionID "!@streq %{SESSION.token}"
It is difficult to create a "random" token within the ModSecurity rules language, thus the contents of CreateSessionToken.lua is shown below. Although this approach is slightly more complication, it allows us to patch Session Fixation regardless of the functionality in the backend application.
#!/usr/bin/lua
function main()
-- Create random token value
local ip = m.getvar("REMOTE_ADDR")
local md5 = require "md5"
math.randomseed( os.time() )
randomtoken = md5.sumhexa(ip .. math.random())
m.log(3, "RandomToken: " .. randomtoken);
m.setvar("TX.token", randomtoken);
return 1
end
Post new comment