Scanning and Hooking Dynamic, Client-Side Data in Modern Web Applications

Scanning the DOM for interesting data + hooking getters/setters. Demonstrating POC w/ a PowerApps example.


Table of Contents


Introduction

This is not very useful. Well, it could be, but it’s often not worth the effort. Nonetheless, it was a fun side project for a couple days that may be worth having in my back pocket.

Modern web applications are incredibly complex and the transpiled javascript code can be difficult to decipher. Modern frameworks like React.js, Angular.js, Vue.js, and the myriad of others have made client-side DOM inspection a bit of a cluster with a tangled web of non-sensible parameter keys and circular references. That said, it may be interesting to investigate the static/dynamic HTML/JavaScript that is generated by webpages; even recently in 2021, there have been reports where sensitive information such as Social Security Numbers have been leaked to the public for someone saavy enough to just inspect the HTML page source. There are several tools dedicated to scraping webpages for interesting embedded content: LinkFinder for endpoints, Burp Suite and it’s BApps extensions for everything from js scripts to secrets/keys/comments/etc., Selenium web automation for customized scanning, etc.

Inspecting the dynamic DOM may lead to interesting web application results or information disclosure. The reason I say this is not entirely useful is because we have more powerful tools such as proxies (BurpSuite/Zap/Fiddler/etc.) that can help us see data in flight, and key values are often stored in cookies or other accessable places which can be much easier to modify.

The main goal of this tool is to output the exact window object paths that store scanned values and help aid in the quick analysis of the DOM and app structure in an unfamiliar app. This knowledge can assist in the reversing of a web application and find interesting functions that can be hooked to alter application behavior or find DOM objects containing sensative data.

If scanning and updating values is the only use case, then a tool like the marvalous CheatEngine would be much better suited for the task.

All that said, I still found this to be fun and interesting, so here goes.

The Setup

As an example, I have created a Microsoft PowerApp to use as a test. PowerApps is a low code/no code solution and as such will create it’s own virtual DOM and do a bunch of magic to render the final application.

This app is very simple. There is a button that sets a variable called role to the value of user

powerapp example

And an output box to show the value of the role variable

powerapp text text box

Additionally, if the role value becomes admin, the text box will fill green.

admin role

Scanning

As for a basic scanner, the code can be found here: scanner.js.

To use it, just copy/paste it into the browser console and run one of the following commands to scan either object keys or values:

// scans for keys in DOM that include "searchterm"
s.new().scanKeys_includes("searchterm")     
// scans for keys in DOM that == "searchterm"
s.new().scanKeys_loose_equals("searchterm") 

// scans for values in DOM that .includes("searchterm")
s.new().scanValues_includes("searchterm")   
// scans for values in DOM that == "searchterm"
s.new().scanValues_loose_equals("searchterm") 
// scans for values in DOM that are between min and max values
s.new().scanValues_between(min, max)

Note that each command has a .new() scan. By default, each subsequent scan will be based off the last scan so DOM objects with changing values can be tracked as their value changes. .new() will re-define the base object as the top level window DOM object.

Scanning: PowerApps Example

Now, looking at this app, I can see I am a user.

powerapps user example

Running a scan using s.new().scanValues_loose_equals('user'), I see the results:

user scan results

The output shows all the DOM objects off of the window object that have these values. window['_r10']['_scopeVaribles']['1.role']['1'] seems interesting as it includes some key terms in the key/value names such as scopeVariables and 1.role. However, it’s buried in a strange object called _r10 which would have been difficult to find manually.

I mean, seriously… this is the output of the window object.

window output

  1. This is the scroll wheel on the output, there are a MASSIVE amount of top-level properties.
  2. Even after expanding the _r10 property, you can’t really tell the values that are embedded at a deeper level.

Good luck finding this manually. I’ll see you in a few years…

Running window['_r10']['_scopeVaribles']['1.role']['1'] shows me the same user value:

viewing DOM object

At this point, I try to change the value by running window['_r10']['_scopeVariables']['1.role']['1'] = 'meow', however, the app remains unaffected. It’s time to try something else.

Hooking

I have slapped together a quick hooking function based off the information in this github project: break-on-access.

The function I will use in this example can be found here: hook.js

In essence, this will allow me to hook the getter/setter of the property such that I will trigger the debugger when this value is read or written to.

Hooking: PowerApps Example

Now that I know the object I want to hook is: window['_r10']['_scopeVariables']['1.role']['1']

I will copy/paste the hooking code above to the console and set a breakpoint:

var bp = hook(window['_r10']['_scopeVariables']['1.role'], ['1'])
bp.enable()

Now, clicking the button to set the variable has resulted in a breakpoint being triggered: triggered breakpoint

I can now step out of my code to see where it was triggered. Conveniently, this function is nicely named, and the parameters make sense based on what I know as the developer.

breakpoint info

  1. Function name = setScopeVariableValue
  2. Variable name = role
  3. Variable value = user

I can now set another breakpoint here on line 12935 of this file pa.core.bundle1.js

set new breakpoint

And also I will run this command to clear my custom breakpont:

bp.disable()

After resuming normal app execution, I press the button again to set the variable and trigger the breakpoint again.

Abuse

Now that I have a breakpoint triggered and I see variable names:

breakpoint triggered: setScopeVariableValue

I can now run a simple console command to change the r value to admin

r = 'admin'

And resume execution.

Now the variable has been set to admin and the app state has changed accordingly.

result

At this point I have now hooked a shared function; this function that controls all scope variable assignments in the app. From here it’s quite easy to set conditional breakpoints and/or just watch the data that passes through this function - and, potentially, modify it at will to affect app behavior.

Conclusion

In summary, while this isn’t the most useful, it can be fun and interesting to play around with. Client side data should always be validated server side and obfuscation from modern frameworks can not be assumed to result in a more secure application.

This example in this post was to find a DOM object that holds a particular value, find the function that controls it, and change execution flow. Additionally, this technique can be used for purely information disclosure purposes. For example, this scanning code could be updated to support regex and scanning for interesting patterns such as SSN values XXX-XX-XXXX, credit carn numbers, keys, secrets, credentials, etc. is quite trivial.

Note that my code is extremely unpolished (and possibly broken) and meant to only prove a gerneral Proof of Concept. Feel free to take and modify/extend/rewrite it as needed. The implementation could also be wrapped into a browser extension to make it easier to use or paired with a tool like Selenium for automated scanning.

So there it is, sort of useless… but also perhaps not.

Downloads





Comments