This post describes reverse engineering a Javascript popunder script. These are widely used on questionable websites to open webpages behind the user's browser window. If such a window is left open, it can be used for any number of things; typically advertisements and coin mining scripts. Most modern browsers have put in place many protections to prevent this sort of thing, but this script bypasses any current protections in Chrome. At the time of writing:
The script I reverse engineered for this article was found in-use in a number of places. I focused on one anonymous photo sharing site that was using the script on almost every user action. I checked the source to try to find the script. I noticed a host loading a script with the folder name "pu" (popunder perhaps?) and the file name appeared to be an affiliate link to one of the sites that the popunders point to. This script contained non-obfuscated configuration for an object stored on the Window object:
There are two obvious dynamically injected scripts, both of which were of interest; however they were heavily obfuscated with a technique that I failed to identify. I.e. I couldn't find a de-obfuscator/unpacker. I examined the window object created in the picture above:
I did a google search for the author's name. I discovered a few Github links to outdated versions of his popunder script that no longer work in modern browsers. In addition to that, I found an empty Github repository that points to a website where he is selling this exact popunder script for an exorbitant price. If you track down this site, be prepared to be bombarded with product demonstrations. The authors also kindly left "debugger;" tags all through the scripts on an interval, so if one is to open developer tools he or she will hit endless breakpoints.
Whenever I see "unlockable" I like to take it as a personal challenge. The script offers other features that I did not attempt to reverse engineer because they aren't uncommon.
This script had extreme obfuscation. Someone with more experience in JS analysis might be able to identify the technique used, but I was unable. Here is a sample (after pretty-printing with Chrome):
This script is obviously inflated, and if the author's publicly available obsolete version is any indication, there are plenty of features/functionality to distract from the core function of the script and the main question I wanted to answer:
How is this script bypassing Chrome's protections against popunders?
I focused on Chrome but (again) looking at the author's old versions, it was clear they targeted different browsers in different ways.
The main obstacles for a popunder script in modern Chrome are a set of protections it uses:
Blocking any windows that are not a direct result of a user action. One user action = one allowed window, all others are blocked by the popup blocker.
Disallowing Javascript from controlling the window and tab focus. Popups draw focus and no amount of blur() and window.opener.focus() will affect it.
Adblock Plus
The script in question bypasses each of these individually and with some intresting tricks. The first step was dealing with the obfuscation. I used two techniques here. First, it is obvious that the script was using event listeners. Using Chrome's developer tools, it easy to set break points on pretty much every type of event:
This made it possible to automatically break on events the script was obviously using; for example, mouse events (on click/mouseup/mousedown etc); I used this technique to manually debug the script, but I discovered a problem rather quickly. The script used Window.open to open a new tab, and then execute Javascript within that tab. This made debugging difficult, and I was not making much progress. This sent me on a tangent and I ended up developing a Chrome plugin that could inject monitoring code into a given object (including window/document etc.) and automatically log every function call including the arguments and their values. This turned out to be extremely useful, and I am going to write it up in another article and make the source available in the near future. For now, the items of interest that were logged are as follows:
From this sequence of events and a lot of debugging obfuscated code (and adding debug statements to my injected script) I was able to reverse engineer and re-create it's functionality. I am going to use my version of the script to describe it's behavior because it's not full of obfuscations.
The first protection this script bypasses is the window-per-user action rule in Chrome. Typically, if a single link is used to open multiple windows, or a non-user initiated action is used to open a window, only the first is allowed, as seen when running this snippet:
The script avoids this limitation by abusing the way Chrome identifies a "user action". The initial base page that launches the sequence of events does so with a mousedown listener, distinct form a click listener in that it is only half of the action of clicking the mouse. This initial mousedown event triggers the first window.open call, creating a new tab. Crucial to this scripts functionality is that the new tab receives focus, and the user's mouse is positioned over top of it BEFORE they have a chance to release the mouse button. By the time the user releases the initial click, a second mouseup event is captured, allowing the new tab to open the popunder window. In the video below, I intentionally held the mouse button to elongate the process:
The next issue is the matter of restoring focus back to the main window and away from the pop-up. There are numerous obsolete methods of doing this, but the one this script uses is something I had not seen before and, at the time of writing, works flawlessly. After opening the popunder, the tab dynamically loads a DIV element (Step 6 above) that is populated with a Base64 encoded PDF file. When Chrome attempts to load the file, it alerts the user in a way that brings focus back to the tab. In real-time this alert is invisible, but can be seen if a delay is added between Step 6 and 7 from above. I did this with my JS injection extension and a "debug;" tag:
This alert is cleared immediately after it is displayed by deleting the DIV that contains the PDF. That small detail, that the alert can be cleared programmatically, is what sets this technique apart from others like using a normal "alert()" or "confirm()" box. Additionally, the target pages that are popped-under are loaded as if the user had navigated to them and are not blocked by normal ad-blockers. Here is a real-time demonstration of the whole sequence:
I decided NOT to publish my version of the script here, because I don't want it to be used. I will probably come back and publish it when it no longer works. Below is a redacted pseudo-code version that gives a general idea of how it functions without the implementation details:
This was an interesting RE, and a technique that is widely used on modern browsers. Popups, popunders and redirects seem relatively harmless, but I think it's important to tackle them. They can be used for real abuse like coinhive mining or drive-by attacks etc. I'm also pretty happy with the injection/monitoring extension I developed to help investigate this script, and I will publish it soon. This technique could be mitigated in a variety of ways. Primarily, removing the focus-grabbing property of the PDF notification and disallowing mousedown and mouseup events to be treated independently on different pages. Another option, one I'm using, is to use Adblock to block the script by name. This is a very permeable method but with most site admins just copy-pasting in the embed code without renaming files, it should be sufficient.