Now Reading
Reversing and Tooling a Signed Request Hash in Obfuscated JavaScript

Reversing and Tooling a Signed Request Hash in Obfuscated JavaScript

2024-01-20 07:29:00

I used to be hacking on a bug bounty program just lately and found that the web site is signing each request, stopping you from modifying the URL, together with GET parameter values. I needed to find how they have been doing this and discover a manner round it. If it requires a little bit of effort, it’s doubtless that not many individuals have examined round it. Not desirous to diminish the corporate’s safety, I’ll redact data to guard their id.

Initially whereas testing the goal, I obtained generic error messages within the response when modifying the URL and GET parameter values. Ultimately, I spotted I used to be solely seeing these errors when modifying GET params and never the POST params. There are two headers despatched to the server and the server validates it to make sure that they match.

Headers:

  • Time: 1703010077113
  • Signal: 16428:088d7f8c3eaa175c94d1ab016be9a0c1132e329f:7a5:6581a7f6

Making an attempt to switch the URL with out updating these headers outcomes on this error:

{"error":{"code":401,"message":"Please refresh the web page"}}

From requests, we don’t see these header values wherever being despatched from the server. We all know the consumer has to generate them, in order that they doubtless exist in JavaScript. The very first thing we do is bust open browser dev instruments and seek for the headers.

Utilizing Ctrl+Shift+F in Firefox underneath Search, we are able to search each JavaScript useful resource loaded within the DOM at the moment. The phrases Signal and Time are pretty generic, so there are lots of outcomes. Sadly, after going by way of all the outcomes, I nonetheless couldn’t discover it. That means that these values are obfuscated.

After looking out by way of the entire JavaScript libraries, I finally found a closely obfuscated file:
https://[cdn]/[path]/33415.js?rev=5d210e7-2023-11-29

There are fairly a couple of JavaScript deobfuscation instruments and libraries on-line, every having their very own methods and having completely different outcomes relying on how the code was obfuscated.

Examples:

Sadly, even with operating the code by way of deobfuscation instruments, it ended up nonetheless being extremely obfuscated. Possibly there’s a particular instrument that may get cleaner output, however I made a decision to maneuver on and attempt to deal with it myself. It may be vital to learn the way to take action in case you are caught in conditions the place the instruments can’t assist.

One of many strategies I’ve discovered to work greatest when making an attempt to navigate obfuscated code is to first attempt to perceive the pseudo code as a lot as attainable and begin inserting breakpoints. We all know that it’s signing these requests and we’re on the lookout for two issues initially:

  • There aren’t any core JavaScript perform strings within the code, in order that they obfuscated all of the string values. Discovering the place they’re saved within the obfuscated code and the way they’re calling them will probably be a serious first step to determining what’s going on within the code.
  • We all know that the string values Signal and Time are additionally obfuscated, so presumably in the identical location.
  • It wants data from the request with a purpose to signal it, we all know that it must also be utilizing the URL string someplace within the code too.

So how will we place a breakpoint within the browser and what does it do? There are good movies explaining this in-depth on YouTube, however to place it merely:

  1. Press F12 (or equal keybind) to open your browsers’ Developer Instruments
  2. In Firefox, head over to “Debugger”. In Chrome, it’s the “Sources” tab.
  3. From right here, issues will probably be browser particular, however they principally function the identical
  4. For Firefox, go to the Sources tab and choose one of many JavaScript useful resource information.
  5. Click on the “{} button to beautify the supply whether it is minified.
  6. Hovering over the numbers on the left aspect of every line of code, you will notice that you may click on on them.
  7. Clicking on a type of numbers will set a breakpoint.
  8. Each time the browser executes this code, it’ll pause all execution.

That is useful for engineers to know points occurring with their code in real-time. It’s useful for hackers when reverse engineering code to higher perceive the way it works.

After beautifying the obfuscated JavaScript, inserting a couple of breakpoints, and triggering requests, we finally see that these variables on the finish of the code are associated to the request signer:

The breakpoint will set off when it hits the code execution and the dev instruments will show the variable values saved within the DOM on the time of the breakpoint. So now we all know this a part of the code is expounded to what we’re on the lookout for.

So now that we found the overall space of our code through breakpoints, we’re left with determining how this half works:


        t = n[o( - 570, 'nY58')](u(), W, n[o( - 555, 'U[zo')], '');
        perform o(W, n) {
          return d(W - - 774, n)
        }
        const c = n[o( - 467, 'lMAW')](u(), window, n[o( - 557, 'EJC^')], null),
        i = {};
        i[o( - 444, 'BF4)')] = + new Date;
        const f = n[o( - 493, 'jUU[')](u(), e.default, n[o( - 565, '2tt4')], null),
        okay = n[o( - 579, 'FRHE')](
          r(),
          [
            n[o( - 501, 'We4x')],
            i[o( - 444, 'BF4)')],
            t,
            f ||
            0
          ][o( - 519, 'r83A')]('n')
        );

It is a helpful trick when beginning to convert your obfuscated code into one thing simpler to learn. Given this piece of code, we are able to set a breakpoint on the primary line (variable okay):


        okay = n[o( - 579, 'FRHE')](
          r(),
          [
            n[o( - 501, 'We4x')],
            i[o( - 444, 'BF4)')],
            t,
            f ||
            0
          ][o( - 519, 'r83A')]('n')
        );

When the browser pauses on that line, we are able to copy values and ship them to the console:

This may be helpful when the obfuscation is making an attempt to cover string values or perform names.

Setting a breakpoint, we are able to begin to determine what these obfuscated values are. We are able to see that the w variable is an object with details about the request. That is then used to assign the present URL path to the const t.

Shifting alongside, we are able to see that the const c is storing our requests’ Person-Agent:

We are able to see that the i variable is an object that’s storing “time”, a unix timestamp, doubtless used for the Time header within the request.

We are able to see f variable is storing the worth 379578839:

The okay variable is a hash worth, however we don’t understand how it’s generated. The code that generates the hash:


        okay = n[o( - 579, 'FRHE')](
          r(),
          [
            n[o( - 501, 'We4x')],
            i[o( - 444, 'BF4)')],
            t,
            f ||
            0
          ][o( - 519, 'r83A')]('n')
        );

Setting a breakpoint on okay, we are able to then begin to use “Step In” (F11 in Firefox). It will take us by way of the code execution one step at a time. This helps us to know what the obfuscated code is doing, however finally we’ll see what they’re hashing. After stepping by way of about 25 occasions, we finally see within the following picture that it’s calling a perform named createOutputMethod with a string containing a few of our suspected strings.

The worth n is:

"NQ4UQIjeSeFbaORiNgZEt0AVXvwYYGQPn1703012009162n/api2/v2/customers/notifications/countn379578839"

The variable W is a perform named“createOutputMethod” from one other library:

https://[cdn]/[path]/chunk-vendors-b49fab05.js

Going by way of that JavaScript file, we are able to see that perform is a part of an exterior library named js-sha1:


 /*
 * [js-sha1]{@hyperlink https://github.com/emn178/js-sha1}
 *
 * @model 0.6.0
 * @creator Chen, Yi-Cyuan [emn178@gmail.com]
 * @copyright Chen, Yi-Cyuan 2014-2017
 * @license MIT
 */


So now we all know that the hash is the next:

js-sha1([string]ntimestampnpathn[number])

We are able to examine these values in opposition to the request to get a greater thought of what they is perhaps:

We are able to see that the quantity on the finish of the hash (379578839) is the User_Id of the request.

Given what data we have now now, we are able to rewrite the obfuscated code to one thing simpler to know:


	const c = W["url"];

	// const d = window.navigator.userAgent;
	const d = userAgent;

  	  f["time"] = +new Date;
	
	const i = W["headers"]["user-id"];

	const okay = sha1(
		[
			n["frWIg"], // pE5CRmAhC8fvaWy6u58tKDTEKCZyTKLA
			f["time"], // time
			c, // url
			i || // user-id
			0
		]["join"]('n')
	);


We aren’t completed but although, we perceive a bit about how the code works now, however the Signal header nonetheless has extra values in it that we have now not decided but. On the finish of the category, there’s this large return with nested perform calls. To maintain it brief, I’ve eliminated the nested capabilities.


  return i[o( - 442, 'WQdV')] = [
          o( - 560, 'r83A'),
          k,
          function (W) {
            function t(W, n) {
              return o(W - 583, n)
            }
            return Math[t(89, 'BF4)')](
…
}(okay),
          n[o( - 483, 'Trv&')]
        ][o( - 458, '$LL1')](':'),
        i
      }
    }
  }

We are able to see in one of many capabilities it’s passing in ‘:’, on condition that the Signal header has values separated by :, we are able to assume that is becoming a member of values. We are able to examine this with our breakpoint and console trick to see that’s certainly the be a part of perform.

Checking the values that get joined:

See Also

Keep in mind that the Signal header worth appears to be like like this:

Signal: 16428:b866803f2316ba4682c03cf401039bc1abc068c9:770:6581a7f6

The massive nest of perform calls are doubtless math operations manipulating the hash worth to give you that ultimate quantity (e.g. 770).

At this level we have now a couple of choices to contemplate:

  • Will we end reversing this completely, do we have to? We most likely must if we wish to convert it to a different language.
  • Have we recognized sufficient about how the code works with a purpose to manipulate the values we wish?
  • We don’t wish to run code manually to signal requests, it’ll decelerate our testing. How can we make this work mechanically?

One choice we have now is to make use of a browser extension like Useful resource Override (Firefox, Chrome) or the browser built-in script overrides, which may be accessed by right-clicking sources within the Debugger.

This isn’t tremendous environment friendly although, if we wish to manipulate the requests in Burp Suite then we both have to rewrite the code for Python or Java. It could take much more effort to proceed reversing the obfuscated code and rewrite it in one other language. A faster choice is to repeat the code, make the modifications we wish, set it up as a NodeJS server, and make the most of that service throughout requests in Burp as a plugin.

Here’s a diagram of the idea:

server.js

Now that we verified that we are able to manipulate the URL and generate the proper hashes, we have to discover a method to go this information to Burp mechanically. I’ve by no means written a Burp plugin earlier than, so I used to be unfamiliar with the extension API. Fortunately within the yr 2023, we have now ChatGPT to hurry issues up.

To my shock, it generated pretty correct code that was about 60% practical and wanted small changes resulting from API adjustments made to the Burp extender.

The ultimate redacted plugin code may be discovered right here, reqsigner.py:

To make use of the plugin, we have now to make sure we have now a Jython jar and our module folder for our put in Python modules:

With the extension loaded, we are able to then go forward and begin manipulating requests in Burp Suite:

I used to be in a position to modify the GET “restrict” parameter worth and I’m not receiving the 401 error code.

This was a enjoyable impediment to beat that did finally result in discovering one vulnerability in a GET parameter of an API name. The difficulty was low-hanging, however you first needed to put in some effort to check for it. A key takeaway from that is that it’s best to all the time be prepared to place within the effort to check one thing that nobody else needs to. Think about the next thought:

  • If there’s an unauthenticated vulnerability, somebody most likely discovered it whereas scanning.
  • If the vuln. requires an account however is low-hanging, somebody most likely discovered it and not using a scanner.
  • If it requires extra obstacles to entry equivalent to organising funds, buying extra entry, or studying obfuscated code, there’s a good likelihood there are undiscovered vulnerabilities lingering that individuals simply by no means put within the effort to seek out.

Going the additional mile is the place you’ll strike gold whereas doing safety testing and analysis.

Though reversing obfuscated code can generally be daunting, the tooling that exists in the present day makes it simpler than ever. With just a little little bit of follow studying how one can use the debugger and skim the DOM, you possibly can navigate convoluted code and perceive it with ease.

Take a look at “DEFCON 29 CTF Qualifier: 3FACTOOORX Write-up” for extra JavaScript reversing.

Source Link

What's Your Reaction?
Excited
0
Happy
0
In Love
0
Not Sure
0
Silly
0
View Comments (0)

Leave a Reply

Your email address will not be published.

2022 Blinking Robots.
WordPress by Doejo

Scroll To Top