Author Archive

These Chrome extensions spy on 8 million users

March 31st, 2016 No comments


This post investigates the library for Chrome extensions performing real time tracking of users on all sites they visit. The code is bundled with plenty of “free” extensions, exfiltrating browsing history as a feature. Such software is commonly known as spyware. Within the top 7,000 extensions of the Chrome Web store, the library is used 42 times with over 8 million installs. The post also looks into the relationship of upalytics with The compiled data is also available in this spreadsheet.


I came across a website that offered browsing insights for websites they have no clear relation to, similarweb. The data includes links clicked on a site, referrer statistics, the origin of users, and others. While this is interesting, it also raises a question – where is that data coming from? Based on their website they collect data from millions of devices, but the software they advertise was orders of magnitude away from that. Data had to come from somewhere else.

Bundling unwanted content with “free” software is an unfortunate reality which has been shown before. This quickly became my working theory. Tracking browsing behavior alone is nothing new, but I was surprised by how widespread this library turned out to be.


I started with the similarweb Chrome Extension, this is where I first came across the upalytics library. By doing some code reading I noticed it was tracking browsing habits and reporting it in real time. Next I started looking for similarities between this extension and the 7,000 most popular ones offered in the Chrome Web store.

Step one was an educated grep – looking for the “upalytics” string, which led to the first hits. What these libraries had in common is the string “SIMPLE_LB_URL” when accessing the backend API. Searching for that lead to more results, not all libraries contain the “upalytics” string.

To evaluate these extensions I wanted to know:

  • Does installing the extension exfiltrate data?
  • Does tracking happen out of the box, or does the user have to opt-in?
  • Is this mentioned in the terms of service?
  • If not, is there at least a link in the terms of service that explains what is happening?

I changed the endpoint address in each extension to point towards my server and evaluated each extension.


I found 42 extensions which used the library totaling 8M installs. Note: “Facebook Video Downloader” (1,000 installs) required updating of the manifest to install.

Containing the code alone does not imply an extension exfiltrates data. But, manual testing confirmed: every single one was tracking browsing behavior. With every requested site, the extensions will send another POST request in the background to announce the action. What is particularly problematic is that some of these extensions pretend to be security relevant. Including phishing protection or content filters.

Out of these 42 extensions 23 did not mention data collection in their terms, out of these 12 further have no URL where this would be explained. One URL that is used across 12 extensions to explain the privacy ramifications is The only extension offering opt-in to tracking is “SpeakIt!”. They had an issue opened here where someone pointed this out as spyware before introduction of the opt-in step.

All data is compiled into a spreadsheet, available here.

Noteworthy examples

Do it – a Shia LaBeouf motivator: In exchange for browsing history users can get motivated by Shia. The extension offers a button that will make him pop up and shout a motivational quote. 200 thousand users considered this a good deal, who am I to judge? 🙂

Video AdBlock for Chrome – this extension is advertised as “ADWARE FREE We are not injecting any third-party ads!”. Technically this might be correct. Is spyware and adware the same?

Taking a peek

To see what is transmitted I modified the phishing extension (and all others) to post data to my local server instead of theirs. This was fairly simple – I set up a python Flask application that accepts POST requests to /related and GET requests to /settings. The POST data is base64 encoded – twice. Why twice? I don’t know. Below is the data the server-side sees while the client is browsing. Line breaks inserted to help readability.

# We go to bing, after previously visiting


# We send a query "this is a test":


# We click a link on the bing results:


What data will be transmitted?

  • Every visited website
  • Search queries (Google, Bing, etc. )
  • Websites visited on internal networks

As far as I can tell this will not be transmitted:

  • POST data (e.g.: passwords, usually)
  • Keypresses

The network view

The endpoints that receive the data use a variety of domain names with multiple IPs. These 42 extension use nine distinct domains, eight of which use the same subdomain (, one is a subdomain of I suspect an attempt to distract from the impression that all data flows to one company. The domain names include ones that are supposed to look benign,,, The other domains involved are:,,,,

All these domains are registered with domainsbyproxy, a service used to obscure the ownership of domain names. This includes itself which is used in one of the extensions (Speakit!). Also, the robots.txt file used in all cases is the same.

What’s more interesting: All these IPs belong to the same hoster, Eight out of nine of these hosts have all addresses in a /18 network, half of the IPs of the endpoint are in another xlhost network. For browsing convenience (or your firewall?) the list of IPs is available here. All IPs in use are unique, however, this involves consecutive IP addresses and other neighborhood relationships.

To examine this closer I compared the distance of IP addresses used by these extensions for tracking. In the graph below, the nodes are the nine domain names in use, edges are amount x distance. By taking into account distances of up to four, we can link together all hostnames used in all 42 extensions. For example: IPs “” and “” have a distance of 2. As for the labels, the edge between “” and “” reads “6×2”. This means that the domains share 6 IP addresses with a distance of 2. Before the graph, this is the relationship between and

Combining all hosts into one graph, we get this:

What does this imply? Whether this is one large data kraken or pure coincidence, I will leave for the reader to decide.

Is this malware, an unwanted feature, or totally OK?

Some of these extensions have terms that mention privacy, here is an example:

We consider that the global measuring and ranking of the Internet in the
current market is somewhat underdeveloped and obscure. For this reason, we have
undertaken a large global project which bring a powerful improvement in the
public’s perception of internet trends and expand the overall comprehension of
the dynamics that are happening on the internet on daily basis. In order to
make this goal a reality, we need anonymous data such as browsing patterns,
statistics and information on how our features are being used. When installing
one of our free products, you will expect to become a proud part of this
project and make this change happen together with us. If you want more details
on the interaction that will be going on between your browser and our servers,
feel free to check out our Privacy Policy. By installing our product you adhere
to the Terms and Conditions as well as Privacy Policy adhered on:

Calling the data “anonymous” seems bold, an IP alone can often be used to uniquely identify users, let alone browsing history. Based on this text the majority of users might not be aware of the extent of monitoring. I was surprised myself by the boldness of the tracking. However, even if this was laid out clearly in the terms, common sense dictates that browser extensions have no business recording unrelated traffic.

That being said, this behavior could be in violation of the Extension Quality Guidelines, in particular the “single purpose” rule. Whether this is the case, I can not judge.


This post looks into usage of this one library in the Chrome Extensions in the Chrome Web store alone. The number of extensions I found is to be considered as a lower bound, there could be well more. For the extensions I examined I did not check other libraries that were loaded or checked for behavior other than tracking browsing history. Upalytics also offers libraries for other platforms (Smartphones, Desktop, other browsers) – I did not take a look at these either.


This is just one library for one platform. Uplaytics supports all major smartphones, browsers but also Microsoft and Mac platforms. Also, there are more players in the game than this one.

I’m afraid to say that even if all these extensions get nuked from the store, there might be plenty similar libraries in other extensions.


04/01/16: None of these extensions are accessible in Google Web store at this point.
03/31/16: I expanded on the explanation of the IP relationships.

Categories: Chrome extensions, Privacy, Security Tags:

Function-level JavaScript instrumentation with Closure Compiler

September 29th, 2015 No comments


This post describes how to do function-level instrumentation of JavaScript programs using a Closure Compiler fork which is available here (Update: The code has been merged into the official Closure repository.) The repository contains all code used here in the instrumentation-sample directory. Program points that can be hooked are function definition, invocation, and exit. Closure supports instrumentation internally as-is, this fork makes it more useful. Since Closure is already a popular part of JS build chains, it was an attractive target to add this feature to. I used this code as part of a project for JS hardening (ZigZag).

“Hello World”

How to use the instrumentation feature:

$ java -jar compiler.jar --js file.js --instrumentation_template template.txt --formatting pretty_print

instrumentation_template FILE is the new option. The specified file contains the code that will be added to the JS file.

Code specified as "init" will be prepended to the program, this is where function definitions for the report call/exit/defined functions go. The other three types: report_call, report_exit and report_defined specifiy the functions that should be invoked for those actions. These functions are where one wants to fill in the blanks with one’s own code to see what’s happening in a program.

Here is a bare-bones instrumentation template:

init: "function instr_call(fun_id){}"
init: "function instr_exit(fun_id, return_value){return return_value;}"
init: "function instr_defined(fun_id){}"
report_call: "instr_call"
report_exit: "instr_exit"
report_defined: "instr_defined"

fun_id is a unique identifier of functions within the program. The report_exit function will be used in a return statement. It is important to keep in mind that user specified function has to return the return argument (return_value), otherwise the instrumented program will not work as hoped for. When compiling a program that consists of one function:

function a(e) {
   return e+1;

The output is:

function instr_call(b) {
function instr_exit(b, c) {
  return c;
function instr_defined(b) {
function a(b) {
    return instr_exit(0, b + 1);

Accessing arguments

An example that is more interesting would be logging arguments used in a function call. For that, the arguments variable can be used. Since this variable is not defined in the script otherwise, it needs to be defined as an extern. The externs file contains only one line: “arguments”. To instrument the program, the command line has to be extended by:

--externs externs.txt --jscomp_off=externsValidation

The updated code for the "init" section of the instrumentation template:

function instr_call(fun_id){
    for ( var i = 0; i < arguments.callee.caller.arguments.length; i++) {
        console.log('Argument ' + i + ': ' +
function instr_exit(fun_id, return_value){return return_value;}
function instr_defined(fun_id){}


This post explains how to use a modified version of Closure Compiler to instrument programs via templates. I found it a pity Closure doesn't allow for easier instrumentation out of the box, and hope this code can be useful to others working with JavaScript.

Categories: JavaScript Tags:

Boston Key Party 2015 – Kendall challenge (Superfish)

March 1st, 2015 No comments


In this post I will provide some background information on the Kendall challenge of the Boston Key Party CTF. The focus is rather on how the challenge was designed than how to solve it. I’m sure others will cover that perspective in writeups.

This CTF is mostly run by BUILDS, but also with some challenges from others including Northeastern SecLab. The game board was organized by MBTA stations as a Google Maps layover, courtesy of Jeff Crowell.


The challenge categories were organized by train lines. The blue line was crypto, orange was reversing, red line was pwning. Everything else ended up on the green line.

For the Kendall challenge (pwning, 300 pts) we wanted to combine multiple tasks that require different skills into a single more complicated challenge. Also, we also wanted to create something around the recent Lenovo / Superfish news stories. However, creating a challenge titled “Super*” or “*fish” would have given away too much. We had to be more sneaky about this, but also avoiding giving away too little having players try to guess what to do.

We ended up with a combination of a remote exploitable router that leads on to man-in-the-middling a SSL connection that has the superfish certificate installed. Players were provided with IP/Port of the pwnable router and the binary that was running there.

A breakdown of the steps necessary to finish:

  • pwn the binary
    • Bypass authentication
    • Overwrite DNS entries with DNS controlled by team
    • Trigger DHCP renew
  • Intercept Browsing
    • Set up DNS server that responds with team’s IP
    • Listen to the requests and make them succeed
    • Interpret the HTTP request
    • Set up SSL interception with Superfish CA

Part 1: The Router

The router software was remote accessible. When connecting, users were greeted by the following screen:

# DHCP Management Console #
# Auditing Interface #

h show this help
a authenticate
c config menu
d dhcp lease menu
e exit


The user can operate as guest, anything important requires to be administrator. Read: there is an easy buffer overflow in the “filter” menu option, it allows to overwrite the admin flag. We included log files which hinted at the DHCP setting being important (it reads a static file). Players had to bypass authentication and then change the DNS to point to one of their machines. Next, trigger “renew leases”. What happens in the background: the program will call another program in the same directory which pushes the DNS setting to something that drives the browser via sockets. This process will directly kick off an artificial browser that issues two web requests. We separated the accounts of the binary and the browser to make finding shortcuts to the flag harder.

Note: much of the work with the router binary was done by Georg Merzdovnik.

Part 2: The Browser

We simulated a user browsing websites. First a HTTP site, later log into their bank account where some sensitive information is revlealed (the flag). Should any step in this process fail, the browser aborts operation. The “browser” was a python script using urllib2. Parts that were important to get right were the DNS queries and certificate validation. The DNS lookups had to be performed through the server the teams provide by owning the router only. The SSL request verifies against the superfish certificate only. By default urllib2 will not check authenticity of certificates.

Once teams pushed their IP address as DNS server, they could see two incoming DNS queries. One for and the second one for a made up hostname “”

Next, players had to reply with an IP they control and have a running web server to intercept the requests. For my local testing I used minidns, a dependency-free python script that will resolve any hostname to a single IP address.

One thing I dislike while solving challenges is pointless guessing. So, before making a HTTPS request we issued a HTTP request to give a hint what to do with the SSL connection. We added a new header, namely “X-Manufacturer” with the value “Lenovo”. This is a completely made up header which was supposed to be a hint towards Superfish without being blatantly obvious.

The second request was pointed at "" Teams had to make the browser establish a legitimate SSL connection and we would issue a request to: "{0}".format(self.FLAG)

Although we had no specific format for keys, we decided to prefix the key with “FLG-” to make it obvious once players got that far.

To get this right, teams could either run a web server with the Superfish private key, or MITM and point the request somewhere else.
A writeup using sslsplit for the latter option is available on Rob Graham’s blog.


The source code of the challenges will be released as a tarball at some point in the near future, follow @BKPCTF (or me) for updates. I hope the challenge was fun and am looking forward to hear in writeups how teams did it.

Categories: CTF, Security Tags:

Content Security Policy – Trends and Challenges

July 14th, 2014 No comments

In December 2012, I was curious who is using Content Security Policy, and how are they using it?

Content Security Policy (CSP) can help websites to get rid of most forms of content injection attacks. As it is standardized and supported by major browsers, we expected websites to implement it. To us, the benefits of CSP seemed obvious.

However, as we started to look into the CSP headers of websites, we noticed that few of them actually used it. To get a better overview we started crawling the Alexa Top 1M every week. What started out as a just-for-fun project escalated to a data collection of over 100GB of HTTP header information alone. We are publishing the results in our (Michael Weissbacher, Tobias Lauinger, William Robertson) paper Why is CSP Failing? Trends and Challenges in CSP Adoption at RAID 2014.

We investigated three aspects of CSP and its adoption. First, we looked into who is using CSP and how it is deployed. Second, we used report-only mode to devise rules for some of our websites. We tried to verify whether this is a viable approach. And third, we looked into generating content security policies for third-party websites through crawling, to find obstacles that prevent wider deployment.

CSP headers in comparison to other security relevant headers

CSP headers in comparison to other security relevant headers

We have found that CSP adoption significantly lags behind other web security mechanisms and that, even when it has been adopted by a site, it is often deployed in a way that negates its theoretical benefits for preventing content injection and data exfiltration attacks. While more popular websites are more likely to use it, only 1% of the 100 most popular websites use it on their front page.

To summarize our findings:

  • Out of the few websites using CSP, the policies in use did not add much protection, marginalizing the possible benefits.
  • The structure of sites, and in particular integration of ad networks, can make deployment of CSP harder.
  • CSP can cause unexpected behavior with Chrome extensions, as we detail below.
  • The project resulted in fixes to phpMyAdmin, Facebook and the GitHub blog.

In our paper, we suggest several avenues for enhancing CSP to ease its adoption. We also release an open source CSP parsing and manipulation library.

Below, we detail on some topics that did not fit into the paper, including bugs that we reported to impacted vendors.

Chrome Extensions

Chrome enforces CSP sent by websites on its extensions. This seems well known, but comes with a couple of side effects. The implications are that CSP from websites can break functionality of extensions, intentionally or unintentionally. Other than that, it makes devising CSP rules based on report-only mode very cumbersome, due to lots of bogus reports. Enforcing rules on extensions seems surprising, especially since they can request permission to modify HTTP headers and whitelist themselves. In fact, we found one such extension that modifies CSP headers in flight.

Recovering granularity of CSP reports

While older versions of Firefox will report specifically whether an eval or inline violation occurred, newer versions of any browser won’t. We provide a work-around to detect such errors in the paper, this involves sending multiple headers and post-processing the reports.


With Facebook, we noticed that headers (including CSP) were generated based on the user agent. This has some advantages, e.g., sending less header data to browsers that don’t support certain features. Also, Firefox and Chrome were sent different CSP headers (the difference being, the Skype extension was whitelisted for Chrome.) We also noticed that for some browser versions, no CSP rules were sent out. The likely reason is that CSP handling in some browser versions is buggy. For example, Chrome enforces eval() even in report-only mode in some versions. However, due to misconfiguration, CSP was only served to browser versions before the bugs were introduced, and none after. As a result, CSP was only in use for a fraction of browsers that in fact support it. After we informed them of this, Facebook quickly fixed the issue. Now, CSP is being served to a wider audience of browsers than before. Also, we were added to the Whitehat “Thanks” list.


We found that phpMyAdmin, which serves CSP rules by default, had a broken configuration on it’s demo page. The setup prevented loading of Google Analytics code. This turned out to be interesting, as the script was whitelisted in the default-src directive, but script-src was also specified and less permissive. Those two are not considered together, and the more specific script-src directive overrode default-src. Hence, including the Google analytics code was not allowed and resulted in an error. We pointed out the issue and it resulted in a little commit.


We used several sites that deploy CSP as benchmark to test our tool for devising CSP rules. With GitHub, we noticed that our tool came up with more directives than the site itself. After investigating, we found that one of the sites on their blog was causing violations with the original rules, as it tried to include third party images. This was interesting, as any site which specifies a report-uri would have caught this, but GitHub doesn’t use the feature. While this caused no security issue, it stopped the blog post from working as intended. With report-uri enabled that mistake would have popped up in the logs and could have been fixed instantly. We think this highlights how important usage of the report-uri is. In summary, this was more of an interesting observation about report-uri to us than a problem on their side.

Categories: CSP, JavaScript, Security Tags:

CSAW CTF Qualifications 2012 – Networking 300

October 1st, 2012 No comments

The 2012 Qualification round for CSAW CTF was fun. I was playing with the Northeastern Seclab hacking group – PTHC. One of the more interesting challenges was networking 300.

As input we received a file called “dongle.pcap”, no further description.

The first thing to do with pcaps is to load them in wireshark. The type for most packets is URB_Interrupt or URB_Control (URB is a USB request block). Most of the packets don’t look interesting – by browsing we found packet 67 which contains the following string: “Teensy Keyboard/Mouse/Joystick”, this made us assume that we have to deal with recovering key presses.

Some quick googling lead us to this website, we downloaded the source files and inspected the code to analyze the protocol. We figured out that the packets we are interested in are pretty specific, they should be:

– 72 bytes long
– the 10th byte is \x01
– the 12th one is \x1a
– the 66th byte should be non-zero
– also we only care about the last 8 bytes and can disregard the rest

This sounds like a perfect job for scapy:

import binascii
from scapy.all import *
dongle = rdpcap("dongle.pcap")

for d in dongle:
    sd = str(d)
    if len(sd) == 0x48 and sd[9] == '\x01' and sd[11] == '\x1a':
        x = sd[len(sd) -8:]
        print binascii.hexlify(x)

This leaves us with 1338 packets, the last four bytes are all \x00. We inspect the file usb_keyboard_debug.h from the keyboard’s source file and can see the key mapping. “A” = 4 etc. We created a mapping keycode-output and prepended it to our script so we would see the keyboards behavior. By inspecting the pcap we found that there were no ALT or STRG key presses, only shift and only for a couple of keys (3). For the sake of simplicity we decided just to check for these three cases in additional if clauses without making a generalized upper-case function. What we get from running the updated program:


The string “KEY{…}” made us assume we finished, but the scoreboard disagreed. It took us a while to figure out that we also have to take the RXTERM lines into account – the last two numbers (separated by “+” are the coordinates). That was the last part of the puzzle and we got the points.

The following program is the cleaned up, final version that will parse the pcap file, pull out the key presses, write the keys into a matrix with the corresponding coordinates and print them in order.

from scapy.all import *
import binascii

keys = {}
keys[44]=' '

dongle = rdpcap("dongle.pcap")

buf = {}
buf[1] = ""

for d in dongle:
    sd = str(d)
    if len(sd) == 0x48 and sd[9] == '\x01' and sd[11] == '\x1a' and sd[0x42] != '\x00':
        x = sd[len(sd) -8:]
        # only these three keys get used with shift
        if x[0] == '\x02':
            if keys[ord(x[2])] == "EQUAL":
                key = "+"
            elif keys[ord(x[2])] == "RIGHT_BRACE":
                key = "}"
            elif keys[ord(x[2])] == "LEFT_BRACE":
                key = "{"
            key = keys[ord(x[2])]

        if key == "ENTER":
            print buf[len(buf)]
            key = "\n"
            buf[len(buf)] += key

# putting keys on their corresponding coordinates
matrix = {}
for i in range(1,len(buf),2):
    (a,b,c) = (buf[i]).split("+")
    if int(c) not in matrix.keys():
        matrix[int(c)] = {}
    # look ahead for one line - the echo command
    matrix[int(c)][int(b)] = buf[i+1][-1:]

# print pressed keys in order
for x in sorted(matrix.keys()):
    for y in sorted(matrix[x].keys()):
        print "%03d-%03d-%s" % (x,y,matrix[x][y])

PlaidCTF 2012 Challenge Torrent – Practical Packets writeup (200 points)

April 30th, 2012 No comments

This weekend PPP organized its second PlaidCTF which was a lot of fun. Below is a quick writeup for the bittorrent forensics challenge.


It turns out that robots, like humans, are cheap and do not like paying for their movies and music. We were able to intercept some torrent downloads but are unsure what the file being downloaded was. Can you figure it out?

Provided was a file torrent.pcap, we used tshark (the command line tool for wireshark) to extract data from the packet capture. The only interesting data points are bittorrent.piece, from those we only need index, begin and data. By printing them in this order we can run a simple sort to make sure the file contents are in order.

Next we strip everything but the data field and the colons. Finally we use translate and sed to turn the hex representation into binary. After running the below script we have a file binout.

tshark -r torrent.pcap -R ' and ip.dst_host ==' -T fields -e bittorrent.piece.index -e bittorrent.piece.begin -e bittorrent.piece.length -e -E separator=+ | sort | sed -re 's!.*\+!!' | sed -re 's!:!!g' | echo -n -e $(tr -d '[:space:]' | sed 's/../\\x&/g') > binout

By using the file command and consequently unpacking we figure out its a bz2-ed tar file. Inside we find the files key.mp3 and key.txt. key.txt contains “t0renz0_v0n_m4tt3rh0rn”, which turned out to be the valid key. We couldn’t extract any hidden information from key.mp3 🙂

Note: if you are trying to reconstruct a file from a bittorrent pcap you might want to check for retransmits, missing indices, multiple files in one capture etc. It would make sense not to strip the headers directly with sed but keep them and run some script to analyze them.

Categories: CTF, Linux, Security Tags: , , , , ,

Finding differences in JavaScript programs with the help of parse trees

February 18th, 2012 No comments

I’d like to present a quick and dirty solution to comparing two JavaScript programs that can’t be compared with a simple diff. This can be useful to check whether programs are mostly equal and can help in figuring out where these differences are. The result will be achieved by performing a makeshift parse tree comparison.

Tools that will be used:

  • Google Closure Compiler
  • a small Perl script
  • vimdiff

First, Google’s Closure Compiler will be used to generate the parse trees.

$ java -jar compiler.jar --print_tree --js original.js > original.tree.txt
$ java -jar compiler.jar --print_tree --js modified.js > modified.tree.txt

The sourcename attribute should be stripped from the resulting file. Actually for most comparisons I did it was sufficient to keep only the first word of each line. This script can be used to print only the first word, but preserve heading whitespace (whitespace should be kept to keep a better overview later):

while (<>) {
print $1."\n";


$ perl < original.tree.txt > original.stripped.tree.txt
$ perl < modified.tree.txt > modified.stripped.tree.txt

Finally the stripped down parse trees can be compared with vimdiff. The iwhite option makes vimdiff ignore differences in whitespace:

$ vimdiff -c 'set diffopt+=iwhite' original.stripped.tree.txt modified.stripped.tree.txt

Suspicious blocks can be traced back to the parse tree before it was stripped (same line number). From there the surrounding function or variable names will lead back to the code in the JavaScript file.

Categories: JavaScript, obfuscation Tags:

Connecting seamlessly with ssh through intermediate hosts

December 21st, 2011 No comments

Having machines in a network that is only reachable through an intermediate machine (e.g. a firewall) can make using ssh less comfortable. When using VPN or changing the network layout is not an option – the ssh ProxyCommand option can help. The result is being able to connect to hosts with one command through the intermediate machine. This also enables using rsync or versioning systems over ssh which otherwise wouldn’t work directly.

The intermediate machine is named “intermediate” and the target machine (which is in the not directly reachable network) “target”. The ~/.ssh/config entries should look like this:

Host intermediate

Host target
ProxyCommand ssh -e none intermediate exec nc -w 1000 %h %p

So when you execute:

$ ssh target

An ssh connection to “intermediate” is opened. netcat is started on “intermediate” to forward the ssh session to “target”. nc -w sets a connection timeout, this should make sure that “intermediate” doesn’t run abandoned nc processes. ssh -e none disables escape chars, they are not useful in this case and can only cause problems.

Categories: Linux, ssh Tags:

iCTF 2011 challenge 15 writeup (150 points)

December 20th, 2011 No comments

One of my iCTF challenges was a simple JavaScript obfuscation, a backup of the code is available here. What happens is obvious, window.alert is triggered with the message “why?”. “Why” is less obvious since the code was encoded with jjencode. There are no other visible hints.

To further look into window.alert, we can overwrite the function:

window.alert = function(e) { console.log(JSON.stringify(e)); };

After re-running the code we see that window.alert is not being called with a String as argument, but with an object which contains the attribute:

{"secret":"Angelina Jolie's only good movie, in leet speak, reverse is the key"}

The solution is obviously: Hackers

FYI: Before the obfuscation the code looked like this:

var obj = { };
obj.secret = "Angelina Jolie's only good movie, in leet speak, reverse is the key";
obj.toString = function(e) { return "why?"; };
obj.toSource = function(e) { return "function toSource() {\n" +
" [native code]\n" +

Categories: CTF, JavaScript, obfuscation, Security Tags: ,

Personalizing shell accounts that are used by multiple users

December 19th, 2011 No comments

Problem description

Although it is generally discouraged to share a shell account with multiple users, sometimes its necessary. When I recently had to share a login I wanted to recreate my personalized environment, but then again not mess with other peoples environment. Also I wanted to preferably create something they could use for themselves without messing in turn my environment up (for example by configuring emacs as the default text editor). I wanted to have my own .bash_history file, load my own .vimrc etc. I used Debian 6.0 with current ssh/sshd versions.


What I did was exporting an LC_* environment variable on the client machine; those get forwarded by the ssh client to the server. On the server I would check in the .bashrc file whether the variable was set, if it was set I would load the custom configuration files. Any of the users who share the account are then able to configure the server environment for themselves.


I added this line to ~/.bashrc

export LC_USER=michael

Don’t forget to source the .bashrc file after it was edited, otherwise it will have no effect in the active session, this can be done with

$ source ~/.bashrc

You should have SendEnv set to forward the LC_* variables (this is usually enabled). This can be done in either the global ssh config /etc/ssh/ssh_config or the user ssh config: ~/.ssh/config

SendEnv LANG LC_*

On the server where you want to connect to, the file /etc/ssh/sshd_config should have the following option enabled (this is often enabled by default)

AcceptEnv LANG LC_*

To verify, ssh into the target machine and try this, the result should be similar. If you get no response check whether the variable was even exported locally (use the same command).

$ env | grep LC_

Once this is done, upload your personalized .bashrc file to the remote server into ~/.yourusername/.bashrc .

Finally edit the ~/.bashrc file on the server so it sources the new custom file:

if [ `expr match "$LC_USER" '^[a-zA-Z0-9]\+$'` -gt 0 ] ;
echo "Welcome ${LC_USER}"
source ~/.${LC_USER}/.bashrc

To keep a personal .bash_history and .vimrc file, put the following into the personal .bashrc file (don’t forget to upload your .vimrc file into ~/.${LC_USER}).

alias vim='vim -u ~/.${LC_USER}/.vimrc'


This proved to be a noninvasive and reliable way to share accounts with custom configurations. Other ways to solve this problem might have been the usage of IP addresses. I looked into this first, but then decided that its not very good since connecting through intermediate machines would render discrimination of users impossible.

Categories: bash, Debian, Linux, ssh Tags: