Show Notes

76 - Fake Vulns, More Valve, and an AWS Cognito issue

At the time of recording I understood this to be a bit of a tongue-in-cheek report about a vulnerability in turing machines in general. After the episode I had the change to look at bit more and realized its disclosing a specific vulnerability that effectively would allow executing data input and code input. So it is a legitimate vulnerability enough though the impact is limited.

Race conditions on the web are one of my favorite vulnerability classes. Easy and often fairly impactful. In this case the race is against the rate-limiting of password reset token checks on AWS Cognito. It normally allowed 5-20 attempts per hour, but by making many at once you could get several attempts through the check before the count caught up.

The device administration web-app fails to properly validate the session cookie allowing for an unauthorized attacker to gain access. The issue depends on the internal ifttt_token not being set (default). In this case an attempt to read the ifttt_token out of nvram will return a null string. When validating the session token (asus_token cookie) it will be checked against the ifttt_token value using strcmp. So by sending the cookie with a value starting with a NULL byte the strcmp will pass thinking you are authenticated as ifttt.

Two vulnerabilities. Firstly the SCM_RUN_FROM_PACKAGE environment var within the Azure Function container contained a “Shared Access Signature” (SAS) that was scoped for r/w. This means an attacker who compromised the function could replace/backdoor the package that would be run.

The second issue was the CONTAINER_START_CONTEXT_SAS_URI which pointed to a MS owned blob, and was an encrypted configuration file for the function. Decrypted using the CONTAINER_ENCRYPTION_KEY. Being able to read your own config isn’t much of an issue, but the SAS was scoped to be able to read any of the configs (encrypted) within the same bucket.

This, the author thought could be chained with another bug in the Init Mesh HTTP service, which would decrypt the a “restricted token” that is passed in. It had verbose error messages, including a warning if the padding was incorrect. This meant that the author could potentially run a padding oracle attack by passing in the encrypted config as the token against a victim init mesh service. Unfortunately the service didn’t do padding validation correctly so this wasn’t an option, but really only luck saved the Azure team here unless that incorrect calculation was for security purposes.

Very long post, covering an old issue (2013) tons of background about Java bytecode, App Engine and ASM (library). Some context for the issue is that App Engine would perform in-process sandboxing. It would allow user’s to write normal Java code, including some dangerous classes like Reflection and custom classloaders. These are dangerous because they can lead to fully arbitrary code execution. The App Engine team used ASM to parse and rewrite the Java bytecode from the user to inject various security checks.

The vulnerability focuses on getting around those checks to call into dangerous functionality. First part the document covers a kinda cool attack on bytecode parsing that ultimately wasn’t relevant for App Engine. The actual issue used though was in how ASM would process strings. In Java strings are stored with two bytes of length and then that many bytes of data (not null terminated). ASM however did no checking on string length before writing it out. So if it thinks a string is 65536 (0x10000) it’ll write out 0x0000. The JVM will see that and then start processing the actual string data and new bytecode and not the string.

The problem was actually providing a workable classfile that would have such a large string. For this the author took advantage of a disconnect between the strings the JVM expected and what ASM would accept. Notably MUTF-8 is used by the JVM which encodes null-bytes as a two (non-null) bytes. ASM would always write out strings with MUTF-8 but it would accept as input strings containing regular null bytes, and rewrite them. This created the ability to craft a string that was appropriately sized in theory but when ASM processed it would overflow.

Workplace by Facebook would allow workplace administrators to enable a “self-invite” option. Anyone with an email on an approved domain could invite themselves into the workplace. It however didn’t actually validate the domain on the request that created the invite. The author doesn’t indicate that it validates it at all, however it appears that there are probably multiple requests that happen before this one, so it is probably validated earlier in the chain. However by making the request directly, the invite did work.

Interesting post that covers a bit about the meta of bug-hunting in Source Engine games and some how-to information. There are two OOB read vulnerabilities used in the chain.

First vulnerability is an OOB read which allows you to return a pointer for an IClientNetworkable object. This is just due to an unchecked (checked by asserts that are compiled out in prod builds) user-provided index. A function is called based on this pointer for eip control.

The next step is to create a fake object, which is done using a ConVar which just allows the server to set arbitrary variables on the client with whatever content they want. This is also in the globals section so it doesn’t move around.

In order to fully exploit this and build a ROP chain without depending on a dll that has no ASLR (xinput) so it will impact all source games an infoleakwas also necessary. There was a bug in the handling of pakfiles. First pakfiles (zip) can be provided by the server (as part of a map as I understand it) and act as a sort of overlay. Its zip. When a file is request, the game will check if it exists inside the pakfile and retrieve it from there if it does.

The vulnerability is that when the server requests the client upload a file, it will also respect the pakfile, which is noted as somewhat odd behavior since the pakfile comes from the server. The game will determine if the file it too large to send by reading the signed size out of the zip header, and doing a signed comparison. So a negative size will pass the check, but when actually reading the file will result in an over-read.

Another indexing issue, and entity index is received and used, but only the lower bound (>= 0) is checked, leading to an OOB access and a virtual function call.

First goes into some background details on QMI, what kinds of services it provides, and details on how they fuzzed the interface (used QEMU hexagon to emulate the modem in conjunction with AFL). They talk about one of the vulns the fuzzer dug up, which was a heap overflow in the voice service’s call_config_req handler. This handler takes two fields from an attacker-provided payload, being the number of calls to make and an array of call contexts which are 0x160 bytes per call. There’s no limit on the number of calls you can make, you can provide 0xFF. They don’t state what the limit should be, just the fact that this leads to an out-of-bounds write on the heap. Given the circumstances, this could probably be taken to code execution since you control how far OOB you write and a lot of data that gets written there.

Though that could also be a curse, might end up smashing too much data and just cause an unavoidable crash too.

Fairly simple to understand bug in the JS Engine (v8) used by Foxit Reader. The crash is just two lines of code.

this.pageNum = 1;
this.addAnnot({page: 1, type: "FileAttachment", point: [11,14,6,8]});

What happens here is the pageNum is changed, this queues the close handler of page0 to be executed. Then the FileAttachment annotation is created, this triggers a file picker dialog to be shown. This dialog blocks execution of the main thread until it returns.

While execution is blocked, page0 elsewhere is closing and attempts to get annotations objects and destroy them. This results in the annotations backing object getting freed from under the dialog. Once the dialog is dismissed the rest of the code executes on the freed object.

Cool bug, but hard to actually exploit despite getting PC control. The vuln uses GLSL, a c-like shader language that gets translated into C before being executed. The translation process also does things like zero-initing new arrays. The problem is that it doesn’t validate there is enough stack space for new arrays being allocated. So it is possible to declare an array that will overflow available stack space and end up in another threads stack.

This is somewhat mitigated by the zero-init process which will attempt to write 0s to the allocated array, crashing when it hits a guard page.

The zero-init only happens when the array is actually declared in code, however the stack frame is setup at the start of the function call. This means that that a variable can be declared that’ll be stored after the end of the large buffer despite the larger one not actually being initialized. Allowing for a targeted overwrite that doesn’t write over the guard page.

Good bit of background on this one, does a good job of explaining the root of the issue. There are two parts, first is a 2020 CVE. Within DirectComposition you can create Tracker objects, and TrackerBindingManager which will manage pairs of trackers. The trackers in-turn when a binding is added will store a pointer to its BindingManager. When you free one tracker, it’ll free the other, and of course it’ll free its entry in the BindingManager’s list. It does this by setting the list’s entry->entry_id to 0, then another function iterates and removes the zeroed entries. Vulnerability being that while tracker’s only maintain a reference to one manager, you can add it to multiple managers. When free’d only the one binding manager will be freed, leaving a dangling pointer in the older managers. The object has a function pointer that is called on Commit so rip control is possible with a UAF

The patch for this is that it would check if the tracker->binding_obj is zero before adding it. If it is already bound then it won’t add the binding. The bypass is to take advantage of the fact that an entry->id==0 is a special case internally. You can update the binding manager and provide a new entry id for a tracker pair, setting that to 0 will trigger a call to have the BindingManagerReferences removed, but not a call to free the trackers. Enabling you to zero the binding_obj and access the original vulnerability.