In this session we cover two more advanced use cases in the context of the HTTP protocol. The first part focuses on building a sidejacking detector and the second on creating tractable Bro data structures for web services.
Sidejacking (or session hijacking) refers to an HTTP issue where an attacker obtains a valid session cookie of a victim and uses it for impersonation. To illustrate, consider the example where the victim Brody checks his latest tweets at the local Starbucks hotspot while the attacker Brooke eavesdrop on the wireless network from two seats behind. Brooke extracts the value of the Cookie header of all HTTP requests to twitter.com and uses it then for her own requests to Twitter. Thus, she has hijacked Brody’s Twitter session and can now impersonate him.
The Firefox extension Firesheep made sidejacking available to the masses, without requiring the technical know-how to conduct the above sketched steps manually. Network operators and incident responders clearly would like to find out when their users’ security and privacy is compromised. So how can we detect this attack?
At a very basic level, we have to monitor each session cookie and observe whether it appears in more than one user context. That is, if one IP address uses a cookie previously used by another IP address, we have likely witnessed a sidejacking incident.
A slightly weaker notion of sidejacking is what we call session cookie reuse, i.e., reusing a cookie across different user agents, say Chrome and Firefox, while the IP address remains the same. For simplicity reasons, we will only focus on this notion in this exercise, but there is no conceptual difference in the detection logic to the more general notion of sidejacking.
In practice, the detection is more complicated and needs to handle a few more corner cases. The full version of the detector addresses all of these issues and can be downloaded from the Bro scripts repository. There is another caveat. Recently Firesheep started using TLS for some services (Google, Twitter and Facebook) for the hijacked attacker connections, which prevents the detection for these services entirely.
Exercise
Since the detection of sidejacking involves tracking session cookies, we need a way to access the cookie in the first place. Bro does not keep track of cookie values by default, so you need to extend it to do so. To this end, extend the HTTP::Info record with a field named cookie and fill in the value by writing a handler for the http_header event.
You might find it helpful to use the provided script scaffold reuse1.bro. Adapt the script, run it on the trace twitter.pcap, and verify that the session cookies are logged successfully.
Exercise
It turns out that Firesheep only uses a subset of the full list of cookie key-value pairs, namely those that identify the user session. For the remainder of this exercise, we mean this "sessionized" cookie when referring to a cookie. You may reuse our provided code that sessionizes the cookie.
Your objective is now to check whether the cookie is seen with different user agents. Write code to track the user agent per cookie and raise a Notice if you see a different user agent than in the past. The current version of your script is reuse2.bro, which you can use as a starting point for this exercise. Test your script with the same trace twitter.pcap; your output should not generate more than a single Notice.
The webchat of Facebook is a feature that allows you to chat with your friends while having a Facebook window open in the browser. In this exercise, we learn how to use high-level data structures in Bro to represent such custom protocols inside of HTTP.
Behind the scenes, the webchat utilizes a long-lived AJAX connection to transfer the incoming and outgoing messages. A user that logs in to Facebook automatically opens such a connection, destined to ^([0-9]+\.)+channel\.facebook.com$, to receive asynchronous status updates (e.g., notifications that your friends are currently typing). Whenever Facebook wants to notify you, it encodes a message as JSON object and ships it back to you as JavaScript code, which may look like this:
for (;;);{"t":"msg","c":"p_100002331422524","s":33,"ms":[{ "msg":{"text":"You used the same IV as before????", "time":1303218869728,"clientTime":1303218869270,"msgID":"2665942259"}, "from":100002331422524,"to":100002297942500,"from_name":"Mondo Cheeze", "from_first_name":"Mondo","from_gender":2,"to_name":"Udder Kaos", "to_first_name":"Udder","to_gender":2,"type":"msg"}]}
Your goal is to extract the messages from a Facebook webchat conversation and put them into high-level Bro data structures where they are easy to manipulate, print, and work with. After all, who wants to write boilerplate code whose only purpose is to fight the representation of the data rather than analyzing the data itself?
Exercise
First off, download the script bodies.bro which reassembles the HTTP body of connections involving a given host. It provides a new event http_event that has the full HTTP body as argument. (Without that script, you would have to reassemble that body from the individual http_entity_* events.)
Next, download the scaffold of the Facebook analyzer facebook.bro, which you will work with in this exercise. The script contains a function:
function parse_fb_message(data: string) : ChatMessage
which takes a big string and converts into a record ChatMessage. Your job is finish writing this function. To this end, you need to extract the values time, from_name, to_name, and text from the JSON object inside the HTTP body.
After having finished the implementation, run your script on the trace url.pcap and look at the output. Extra credit: can you reconstruct the encrypted URL?
Note
You may find the following string procssing functions useful:
sub(str: string, re: pattern, repl: string): string
Substitute once the pattern re with the string repl in str and return the new string. The function gsub has the same signature and replace all occurences of re in str with repl.
sub_bytes(s: string, start: count, n: int): string
Obtain a substring of s that starts at position start and has length n.
strstr(big: string, little: string): count
Locates the first occurrence of the string little in big. Returns 0 if little is not found in big.
find_last(str: string, re: pattern) : string
Returns the last occurrence of the given pattern in the given string.
© 2014 The Bro Project.