Monday, August 4, 2008

Hijacking Sessions to support legacy applications.

So here's the scenario. Server A is a legacy ASP application that is currently being phased out. Server B is a fancy new ASP.NET application that takes HTML data and converts it to a PDF.

The Client wants Server A to print some data in a report. Server A refers the Client over to Server B. Server B wants to get the HTML data from Server A and then return the PDF output to the client.

The only problem with the whole setup is that Server A holds the client authentication in Session and if Server B tries to access the file directly, all the client get's is a nice PDF document of the Server A's login screen.

So the solution to the problem is to have the client give permission to Server B to masquerade as the Client and hijack the session.

First we have to understand how Session is maintained. In ASP the server drops a cookie on the client with a special id number that it uses for all future interactions with the server to identify itself.

So we can use javascript to get the Session ID and oddly enough the cookies key. In the form aspsessionidXXXX=XXXXXXXXXXXXX where X's are arbitrary id's that the server sets.

<script type="text/javascript" language="javascript">
function GetSessionCookie() {
var reSessionID = new RegExp('aspsessionid[^=]*=[^;,\b]*','gi')
var arrCookies = document.cookie.toString().split(';');
var strWork;

for(var i=0;i < arrCookies.length;i++)
if(reSessionID.test(arrCookies[i].toString())) {
return arrCookies[i].toString();
}
}
</script>

So now we're ready to make the magic happen.

Get the Target URL and Session Key and Session ID that have been passed to Server B:

string Target = "http://www.ServerA.com/Some/Page.asp";
string SessionID = string.Empty;
string SessionKey = string.Empty;
foreach (string item in Request.QueryString.Keys)
{
if (item.Trim().StartsWith("ASPSESSIONID"))
{
SessionKey = item.Trim();
SessionID = Request[item].ToString().Trim();
break;
}
}

string pageContent = GetWebPage(Target, SessionKey, SessionID);

With GetWebPage written as such:

private string GetWebPage(string url, string SessionKey, string SessionID)
{
try
{
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(url);
webRequest.Timeout = 6000;
CookieContainer myContainer = new CookieContainer();
myContainer.Add(new Cookie(SessionKey, SessionID, "/", GetDomainFromUrl(url)));
webRequest.CookieContainer = myContainer;
HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse();
Stream responseStream = webResponse.GetResponseStream();
string responseEncoding = webResponse.ContentEncoding.Trim();
if (responseEncoding.Length == 0)
responseEncoding="us-ascii";
StreamReader responseReader = new StreamReader(responseStream, System.Text.Encoding.GetEncoding(responseEncoding));
return(responseReader.ReadToEnd());
}
catch
{
return(string.Empty);
}
}

While it's not the most elegant solution and I'm sure there are a plethora of flaws security wise. It does get the job done.

Contributors