This Week in Safety: Use After Release for Dummies, Wi-Fi Hacking, and PHP-FPM


In a brilliant text, [Stephen Tong] brings us his “Use-After-Free for Dummies“. It’s a startling story of a vulnerability that really shouldn’t exist, and a walkthrough for completing a capture-the-flag challenge. The vulnerable binary runs on a Raspberry Pi, which is very important. It is a multithreaded application that uses data sharing without locking, via a pair of integers readable by several threads. These integers are declared using the volatile keyword, which is a useful way to tell a compiler not to optimize too heavily, because that value may be changed by another thread.

On an x86 machine, this approach works just fine, because all out-of-order execution features are guaranteed to be globally transparent. In other words, even though thread 1 can speed up execution by modifying the shared memory in advance, the processor will keep the shared memory modifications in the correct order. When this shared memory controls concurrency, it’s really important that the order happens as you expect it to. What surprised me was that the ARM platform does not provide this global memory order. While out-of-order execution will be transparent to the modifying thread, other threads and processes may observe these out-of-order actions. An example may help:

volatile int value;
volatile int ready;

// Thread 1
value = 123; // (1)
ready = 1; // (2)

// Thread 2
while (!ready); // (3)
print(value); // (4)

It is one of [Stephen]’s examples. If this was set to run in two threads, on an x86 machine you would be guaranteed that (4) would always print 123. On an ARM, no such guarantee. You may very well have an uninitialized value. It’s a race condition. Now you may look at this and ask yourself like me, how can anyone program anything for ARM chips? First thing, although reorganizing memory is one thing, ARM guarantees consistency within a single thread. This quirk only affects multithreaded programming. And second, libraries for multithreaded programming provide semantics for marking memory accesses that must be properly ordered across threads.

The actual exploitable binary in question uses a circular queue for the inter-process buffer and tracks a head and tail location to determine how full the buffer is. One process inserts data, the second reads it. The vulnerability is that when the buffer is completely full, rearranging the memory manipulation may cause a race condition. This circular buffer is filled with pointers, and when the race is won by an attacker, the same pointer is used twice. Essentially, the program now has two references to the same object. Without any other tricks, this results in a free double error when the second reference is freed.

What are some tricks we could use to make this an achievement? First, know that we have two references to an object. This object contains a pointer to another string, the length of which is entirely controlled by the data supplied by the user. We can trigger a release of one of those references, which leads to the object being freed, but we still have another reference, which now points to uninitialized memory. To turn this into an arbitrary reading, a very clever trick is used. Before freeing our object, we allocate another object and store a long string. Next, we free the object we have a double reference to, and finally free the object with the long string. Finally, we allocate an additional object, but the string we store is designed to look like a valid object. Memory is reallocated in a last-in, first-out order, so the string is stored in reclaimed memory to which we always have a reference. The program expects the object to contain a pointer to a string, so our fake object can point to arbitrary memory, which we can then read.

The last trick is arbitrary writing, which is even more difficult to achieve. The trick here is actually to do the double free, but to manipulate the system so that it doesn’t result in a segfault. We can use the above trick to write arbitrary data to a freed memory location. Since the location was added to the free list twice, the system still considered it free even though it is also in use. The Linux memory manager uses a nifty trick to manage reclaimed blocks of memory, by storing a pointer to the next reclaimed location in each block. Write the slot you want to replace in this free chunk, then allocate another chunk. The system now thinks your arbitrary location is the next free memory location to use. The next allocation is your arbitrary write. The write-up contains more details, as well as the rest of the exploit chain, so be sure to read the whole thing.

How secure is this WiFi?

[Ido Hoorvitch] from CyberArk had some pandemic-induced free time and chose to collect packet captures from 5,000 password-protected WiFi networks around Tel Aviv. Previously, you had to capture a 4-way handshake to have a chance of breaking WPA encryption. In 2018, a new technique was discovered where a single authentication response was all that was needed to attempt to crack the key – no active user required. The magic string here is the PMKID, which is a SHA-1 hash of the WPA password and other network details, first run through a key derivation function.

The popular tool, Hashcat, can take advantage of a GPU to speed up the cracking of a PMKID. SHA-1 hashes are one of the things GPUs are particularly good at, after all. The 8 Quadros handled almost 7 million hash calculations per second. The problem with trying to crack a WPA key is that although it must be at least 8 characters long, it can be much longer, creating huge search space. The first thing [Ido] used was to take advantage of one of the common sources of passwords, a mobile phone number. In Tel Aviv, this means the password is 05 followed by 8 more digits. It’s a searchable keyspace, and of the 5,000 networks sniffed, nearly half were hacked using this approach. Then pointed Hashcat to a dictionary file, to automatically try known passwords. Between the dictionary attack and constraint-based approaches like cell number format, 70% of targeted networks were hacked. Take-out? Use a long password that is not easy to guess and will not easily fit into a constrained search.

Using Google after Free PoC

Reported in June this year by the Security For Everyone team, CVE-2021-30573 now has a published PoC. This vulnerability has been fixed in Chrome/Chromium 92. The trigger code is a bit simple but very malformed. When trying to parse this code just by looking at it, I immediately called it “cursed” HTML, so it’s no wonder Chrome had trouble with it too.

Root PHP Worker

A bug in PHP-FPM discovered by Ambionics Security allows control of a PHP worker to pass directly to the root of the system. Although this is a serious issue, it is not a remote code execution vulnerability. Some other techniques must first be used to support a PHP worker thread. This means that an attacker should be able to run PHP code and then find a way to escape the PHP “sandbox”. Although this is not trivial, there are techniques and bugs to make this possible.

The problem is that the inter-process communication mechanism is shared mapped memory and too much of the data structure is made available to individual workers. A worker can modify the main data structure, causing the higher-level process to write to arbitrary memory locations. Although the location may be arbitrary, actual data writes are extremely limited in this vulnerability. In fact, it boils down to two write primitives: Set-0-to-1 and clear-1168-bytes. This might not seem like a lot, but there are a lot of flags that can be toggled by setting a value to 1, and the rest of the exploit makes heavy use of this technique. The real trick is to generate arbitrary error messages and then use the 0 to 1 primitive to corrupt the data structure of those messages.

The vulnerability has existed for a very long time, since PHP 5.3.7. It is fixed in versions 8.0.12, 7.4.25 and 7.3.32. A final issue here is that PHP 7.3 still has security support, but this was considered an invasive change, and PHP maintainers initially chose not to push the fix to this older version. After some back and forth on the bug discussion, the right call was made and version 7.3.32 was released with the fix.

Gitlab in the wild

HN Security asked a customer to report something suspicious, and it turns out CVE-2021-22205 is being used in the wild. This bug is a problem in ExifTool, where a DjVu file can execute arbitrary perl code. This RCE was abused to make new users administrators on the attacked system. If you’re using Gitlab, make sure you’re up to date. Versions 13.10.3, 13.9.6 and 13.8.8 were released with the patch on April 14 this year. It seems that 14.* versions were never vulnerable, as 14.0 was released after this fix. Hackerone and the entire Bug Bounty community have their share of problems, but this one’s disclosure thread is an example of a program running correctly.


Comments are closed.