|
| 1 | +--- |
| 2 | +layout: post |
| 3 | +title: "JS-Recon is no more!" |
| 4 | +date: "2019-04-23 17:47:21 +0530" |
| 5 | +tag: |
| 6 | + - security |
| 7 | + - attack |
| 8 | + - vulnerability |
| 9 | + - javascript |
| 10 | + - jsrecon |
| 11 | +--- |
| 12 | + |
| 13 | +In 2010, Andlabs discovered an attack to fingerprint open TCP ports at client |
| 14 | +workstation. Here is a blog post from them which is describing this |
| 15 | +vulnerability. JS-Recon was a tool developed by them proving the weakness of |
| 16 | +the browser. Unfortunately JS-Recon is not available on mentioned link for |
| 17 | +unknown reasons. I tried my best to find the source code of that tool, but I |
| 18 | +was ended with no results. Because the source code of the tool is not |
| 19 | +available, the only way to confirm the possibility of this attack was to |
| 20 | +reconstruct it from steps mentioned by the author.rom steps mentioned by the |
| 21 | +author. |
| 22 | + |
| 23 | +In this post I will share my experience of rebuilding this attack. Because this |
| 24 | +attack is from the front-end side, knowledge of basic Javascript API is |
| 25 | +expected from the reader. The original blog post does not include any code |
| 26 | +samples. For the easiness of the reader, I have prepared small code snippets |
| 27 | +and attached them with related sections. I expect you run code samples at the |
| 28 | +developer console of your browser. |
| 29 | + |
| 30 | +### Glossary |
| 31 | + |
| 32 | +* **Empty / Available port**: A port where no service is running. |
| 33 | +* **Non-empty / Occupied port**: A port where some service is running. |
| 34 | +* **XHR**: A short form of [XML Http Request][mdn_xhr]. |
| 35 | +* **Socket**: A TCP/IP raw socket. |
| 36 | + |
| 37 | +According to the author, If I write a Javascript code to open a XHR to |
| 38 | +http://localhost:8084 and host that code at xyz.com then when you visit the |
| 39 | +xyz.com the browser will open that XHR to port 8084 of your workstation, |
| 40 | +because the localhost for your browser is your workstation. This gap invites |
| 41 | +many vulnerabilities for users. One of them is the possibility to fingerprint |
| 42 | +open TCP ports at client workstation. |
| 43 | + |
| 44 | +The author claims that the browser takes recognizably more time to open a XHR |
| 45 | +targeting an occupied port. Comparatively, time took to open a XHR aimed at an |
| 46 | +empty port was short. |
| 47 | + |
| 48 | +```javascript |
| 49 | +//Sample code to measure the time browser took to open the socket. |
| 50 | + |
| 51 | +var requestPort = function(port) { |
| 52 | + var startTime = null; |
| 53 | + var xhr = new XMLHttpRequest(); |
| 54 | + xhr.onreadystatechange = function() { |
| 55 | + if (xhr.readyState === 1) { |
| 56 | + var timeTook = Date.now() - startTime; |
| 57 | + console.log("Browser took : " + timeTook + " microsecounds to open."); |
| 58 | + } |
| 59 | + }; |
| 60 | + startTime = Date.now(); |
| 61 | + url = "http://localhost:" + port; |
| 62 | + xhr.open("GET", url, true); |
| 63 | +}; |
| 64 | +``` |
| 65 | + |
| 66 | +You can paste this code at a developer console of your browser. Calling this |
| 67 | +function by writing `requestPort(8084)` at a console will open the XHR for port |
| 68 | +`8084`. The function will print the time browser took to open a socket on that |
| 69 | +port. I request you to call this function with a combination of empty and |
| 70 | +non-empty ports to find response timings. |
| 71 | + |
| 72 | +I tried opening a bunch of requests using `requestPort()` function at suspected |
| 73 | +ports. For me the method was giving unidentifiable pattern in the time browser |
| 74 | +took to open a socket. Below is a histogram of comparing time took to open a |
| 75 | +socket on empty port(8084) and non-empty port(27017). |
| 76 | + |
| 77 | + |
| 78 | + |
| 79 | + |
| 80 | +Above is a graph of 6000 requests done to measure the response time using a |
| 81 | +function I shared earlier. More than 5000 requests has ended in nearly no time. |
| 82 | +There were less than 1000 requests which ended in 1 microseconds. From the |
| 83 | +above results, We can conclude that mentioned method is not giving different |
| 84 | +results for occupied and empty port. We can conclude that mentioned method by |
| 85 | +AndLabs is failing to distinguish an occupied port from a non occupied port. |
| 86 | + |
| 87 | +I tried hard to find any possible cause for the failure of this attack. I |
| 88 | +didn't found any certain evidences. May be our hardware or browser code has |
| 89 | +improved for opening a TCP sockets quicker than what it used to. I will not |
| 90 | +lie, but that abstract blog post by the AndLabs took sometime to understand the |
| 91 | +anatomy of this attack. I wasn't happy with going back from this point. Just |
| 92 | +for my satisfaction, I tried every possible combinations of `xhr.readyState` |
| 93 | +values to find any pattern. From my observation, I recognized that timing for |
| 94 | +returning a header from an occupied port was delayed. Comparatively, this |
| 95 | +response was quick for ports where no service was running. I am comparing the |
| 96 | +time browser took to return a response headers whereas in the previous method |
| 97 | +it was dependent on the time browser took for opening a socket. |
| 98 | + |
| 99 | +```javascript |
| 100 | +var requestPort = function(port) { |
| 101 | + var startTime = null; |
| 102 | + var xhr = new XMLHttpRequest(); |
| 103 | + xhr.onreadystatechange = function() { |
| 104 | + if (xhr.readyState === 2) { |
| 105 | + var timeTook = Date.now() - startTime; |
| 106 | + console.log("Browser took : " + timeTook + " microsecounds to send response headers."); |
| 107 | + } |
| 108 | + }; |
| 109 | + startTime = Date.now(); |
| 110 | + url = "http://localhost:" + port; |
| 111 | + xhr.open("GET", url, true); |
| 112 | + xhr.send(null); |
| 113 | +}; |
| 114 | +``` |
| 115 | + |
| 116 | +Above code will measure the time browser took to receive headers from the |
| 117 | +destination. You should call this function requestPort() with a few |
| 118 | +combinations of empty and non-empty ports. |
| 119 | + |
| 120 | + |
| 121 | + |
| 122 | +This graph is representing header response time of 6000 requests fired at both |
| 123 | +active (27017) and inactive port (8084). The response time of inactive port not |
| 124 | +go beyond 200 microseconds. Comparing this with a header response time of |
| 125 | +occupied ports, we can see that response time of non-empty port is recognizably |
| 126 | +higher than empty port. |
| 127 | + |
| 128 | +Browser is the most common tool used by us. Asserting this venerability requires |
| 129 | +knowledge of Javascript and everyone is not a developer. As I mentioned earlier, |
| 130 | +I failed to find the source code of JS-Recon (the tool written by AndLabs |
| 131 | +proving possibility of this attack). For those reasons, I decided to write a |
| 132 | +tool pioneered on my improvements on an attempt of Andlabs. Today, I have |
| 133 | +successfully completed that tool. I have decided to name it |
| 134 | +["Chatur"][chatur_pronounciation]. Chatur means intelligent person in Hindi. |
| 135 | +Please find the source code of Chatur [here][chatur_github]. Chatur is a free |
| 136 | +software. Try this tool and share your thoughts with me. This is not a |
| 137 | +bulletproofed idea, but it works most of the time you will try. |
| 138 | + |
| 139 | +[andlabs_blogpost]: http://blog.andlabs.org/2010/12/port-scanning-with-html5-and-js-recon.html |
| 140 | +[mdn_xhr]: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest |
| 141 | +[chatur_pronounciation]: https://youtu.be/Tih_dP_Tv2w |
| 142 | +[chatur_github]: https://github.com/ultimatecoder/chatur |
0 commit comments