Skip to content

Adding blog post JS-Recon is no more #82

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
147 changes: 147 additions & 0 deletions _posts/2019-04-23-js-recon-is-no-more.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
---
layout: post
title: "JS-Recon is no more!"
date: "2019-04-23 17:47:21 +0530"
tag:
- security
- attack
- vulnerability
- javascript
- jsrecon
---

In 2010, Andlabs discovered an attack which fingerprints open TCP ports in
client workstations. You can read [this blog post][andlabs_blogpost] which
describes the details of this method. JS-Recon was a tool implementing this
attack on its clients to prove the danger of this vulnerability. Unfortunately,
JS-Recon is no longer available on the aforementioned link for unknown reasons.
I tried my best to find the source code of this tool, but I finished with no
results. Because the source code of the tool is not available, the only way to
confirm the possibility of this attack was to reconstruct it from the steps
mentioned in that blog post.

In this post I will share my experience of rebuilding this attack. Because this
attack is from the front-end side, knowledge of basic Javascript API is expected
from the reader. The original blog post does not include any code samples. For
the benefit of the reader, I have prepared small code snippets and attached them
with related sections. I expect you run code samples in the developer console of
your browser.

### Glossary

* **Empty / Available port**: A port where no service is running.
* **Non-empty / Occupied port**: A port where some service is running.
* **XHR**: A short form of [XML Http Request][mdn_xhr].
* **Socket**: A TCP/IP raw socket.

According to that blog post, If I write a Javascript code to open an XHR to

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The XHR API distinguishes between opening (initializing) and sending (triggering/delivering) a request, so be more precise here.

`http://localhost:8084` and host that code at example.com then when you visit

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"hosted at example.com, visiting example.com will open that XHR to"

the `example.com` the browser will open that XHR to port `8084` of your
workstation, because the `localhost` for your browser is your workstation. This
gap invites many vulnerabilities for users. One of them is the possibility to

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does a gap "invite" vulnerabilities? I'd expect it to lead to vulnerabilities...

fingerprint open TCP ports at the client workstation.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fingerprinting has a very specific meaning when doing scans, detecting the type of the service listening on an open port. Perhaps you meant to write "detect"?


That post further claims that the browser takes recognizably more time to open
an XHR targeting an occupied port. Comparatively, time took to open an XHR aimed
at an empty port was short.

```javascript
//Sample code to measure the time browser took to open the socket.

var requestPort = function(port) {
var startTime = null;
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState === 1) {
var timeTook = Date.now() - startTime;
console.log("Browser took : " + timeTook + " microseconds to open.");
}
};
startTime = Date.now();
url = "http://localhost:" + port;
xhr.open("GET", url, true);
};
```

You can paste this code at the developer console of your browser. Calling this

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Paste into.

function by writing `requestPort(8084)` at the console will open an XHR for port
`8084`. The function will print how long the browser took to open a socket on
that port. I invite you to call this function with a combination of empty and

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about "I recommend you to"?

non-empty ports to observe various response timings.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What we really want here is to observe the difference in response timings.


I tried opening a bunch of requests using `requestPort()` function at suspected

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does "suspected ports" mean here? Please clarify. Didn't you know what ports were open and guessed for ports that are most likely open or closed?

ports. For me the method was giving unidentifiable patterns in the time the
browser took to open a socket. Below is a histogram of comparing times taken to
open a socket on empty port(8084) and non-empty port(27017).

![Graph showing response time to open socket.]({{site.url}}/assets/images/js_recon_is_no_more/graph_response_time_open_socket.png)


Above is a graph of 6000 requests done to measure the response times using the
function I shared earlier. More than 5000 requests have ended in nearly no time.
There were less than 1000 requests which ended in 1 microseconds. From the above

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry to be nitpicky about your methodology, but how is a single microsecond different from "nearly no time"? Can you reliably measure network requests with that precision? Have you established an estimated error margin?

results, We can conclude that mentioned method is not giving different results

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lower-case the "We".

for occupied and empty port. We can conclude that mentioned method by AndLabs is
failing to distinguish occupied ports from nonoccupied ports.

I tried hard to find possible causes for the failure of this attack. I didn't

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"reasons why this attack failed, but didn't find any conclusive evidence"

find any conclusive evidence. Perhaps our hardware or browser code has improved

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See my comment about networking. Generally speaking networking requests are slow, yet you claim to find meaningful differences between a single microsecond and "no time at all", so I'd start there...

for opening a TCP sockets quicker than what it used to. I will be honest, but it

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd drop this sentence because it doesn't help understanding the nature of the attack.

took sometime to understand the anatomy of this attack from that abstract blog
post by Andlabs. However I was not about to give up and turn back from this
point. Just for my satisfaction, I tried every possible combinations of

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"To satisfy my curiosity, I tried all possible combinations of"

`xhr.readyState` values to find any pattern. From my observation, I recognized
that timings for returning a header from an occupied port was delayed.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"the timings for... were delayed"

Comparatively, this response was quick for ports where no service was running. I

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"the response"

am comparing the time in which the browser returned a response headers whereas
in the previous method it was dependent on the time browser took for opening a
socket.

```javascript
var requestPort = function(port) {
var startTime = null;
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState === 2) {
var timeTook = Date.now() - startTime;
console.log("Browser took : " + timeTook + " microseconds to send response headers.");
}
};
startTime = Date.now();
url = "http://localhost:" + port;
xhr.open("GET", url, true);
xhr.send(null);
};
```

The above code will measure times the browser has taken to receive headers from
the destination. You should call the function requestPort() with several
combinations of empty and non-empty ports.

![Graph showing header response time.]({{site.url}}/assets/images/js_recon_is_no_more/graph_header_response_time.png)

This graph is representing header response times of 6000 requests fired at both

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"the header response times"

active (27017) and inactive port (8084). The response times of inactive ports do

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"and inactive ports"

not go beyond 200 microseconds. Comparing this with a header response time of
occupied ports, we can see that response time of non-empty port is recognizably

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"the response time... is noticably higher"

higher than empty port.

The web browser is the most common tool we use. Asserting this vulnerability
requires knowledge of Javascript and everyone is not a developer. As I mentioned

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"JavaScript". I'd omit the developer thing here and instead mention that your tool is aimed at non-developers.

earlier, I failed to find the source code of JS-Recon (the tool written by
AndLabs proving possibility of this attack). For those reasons, I decided to
write a tool pioneered on my improvements on an attempt of Andlabs. Today, I
have successfully completed that tool. I have decided to name it
["Chatur"][chatur_pronounciation]. Chatur means intelligent person in
[Hindi][hindi]. Please find the source code of Chatur [on
Github][chatur_github]. Chatur is a free software. Please try out this tool and
share your thoughts on my tool with me. This is not a bulletproofed idea, but it
works most of the time you will try.

###### Proofreaders: Trent W. Buck, [quakerquickoats via #emacs at Freenode](mailto:quakerquickoats@gmail.com)

[andlabs_blogpost]: http://blog.andlabs.org/2010/12/port-scanning-with-html5-and-js-recon.html
[mdn_xhr]: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest
[chatur_pronounciation]: https://youtu.be/Tih_dP_Tv2w
[chatur_github]: https://github.com/ultimatecoder/chatur
[hindi]: https://en.wikipedia.org/wiki/Hindi
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.