As a preface, when I originally found this bug I was unfamiliar the class of “null byte buffer overflow” even existed. I was simply fuzzing a standard web application’s input field and ran into a very interesting behavior that turned out to be a cool bug.

Until recently I hadn’t ever seen a report that mentioned anything close to this issue, but a friend messaged me this report by the HackerOne user “maxarr” which seemed to be nearly identical to the issue found:

Hah this tweet received a lot of likes. Can share what happened: lua with binding to insecure C function leads to memory disclosure. https://t.co/7J0kFSd2ci — Sergey Belov (@sergeybelove) October 30, 2019

I don’t know specifics about maxaar’s report, but the conclusion by Sergey Belov (who leads the AppSec team at mail.ru) seems to be a very similar conclusion reached trying to piece together what exactly was going on with our report which was on another program.

It will be interesting to see what sort of issues similar to this are found in the future as the bug class seems to be sort of unique.

Background

I was in San Francisco the few days leading up to me finding this bug with some friends. We’d spent the day hacking before a few of us were traveling to Vegas for DEF CON the next day.

Some of them were going on a road trip and I decided to join them at the last minute, trading my seat for a travel voucher and leaving my checked baggage with all of my clothes behind. After buying some new clothes, we all met up the next morning at a nearby Starbucks to hack before we left.



Discovering the Issue

When I initially pulled up the application I had the idea of re-registering a victim’s account to the platform by using some string manipulation tricks. The main idea was having the account username be “victim” plus a character that would be removed (e.g. null byte, CRLF characters, spaces, weird Unicode, etc).

This was based on the idea that the application would pass the string to multiple functions during registration and would possibly remove certain characters along the way.

What I was attempting, if successful, would look something like this…

What I was attempting to do while testing the registration functionality – if this worked, then the attacker would be logged in with the username “victim”

Although this did not work during testing, I thought it would be useful to share for understanding how I had approached the application.

I had attempted to register an account with the following characters with the idea I could possibly overwrite the registration of the victim’s email address if the null byte was ever removed at some point with the flow of the application.

victim%00@domain.com

The request went through, but the strange thing was when it reflected my registered email address it showed up as follows…

victimL@domain.com

The server had replaced the “%00” with “L” for some unknown reason. I thought this was strange, so I went back and attempted to register the following email to see if I could reproduce it and what would happen if I had more than one null byte…

victim%00%00%00@domain.com

… and received the following response …

victimIdL@domain.com

It seemed to be random data that was being swapped in for null bytes, so I attempted to send as many as I could. It turned out there was absolutely no limit outside of the maximum number of characters in a POST body. I sent a huge request and waited about 10 seconds for the response.

I eyed the response body and saw what looked to be human readable data and lots of memory. I sent it again and similar but different information popped up.

At this point I turned my laptop to everyone and showed them what I was working on. Everyone freaked out after each replay of the request as new things kept popping up from the server memory. We saw RSA private keys, internal HTTP requests, the DOMs of users, plaintext usernames and passwords, and tons and tons of secrets. We all left to go back to the hotel so we could properly exploit the issue and write up the report

What was going on?

Originally we had no idea how to even guess what was going on since the issue was so strange. We all agreed it was some sort of memory corruption/buffer overflow, but it seemed so weird since we were using multiple null bytes.

What it turned out to be was exactly as described in the Tweet from Sergey Belov. There was a front end application that was passing data to an insecure function on an underlying C application.

The C application expected two things from the front end application (registration form)…

String

String length

If you sent the string “abc%00” the front end application would consider the information as follows…

String: abc%00

String length: 4

This was totally fine if the only evaluation was done by the front end application, but during the pass off between the front end application and the underlying C application the null byte was removed. The new data that was sent was as follows…

String: abc

String length: 4

If you’re familiar with C this is probably all you need to know, but essentially each new null byte increased the string length but didn’t actually occupy the string value. This allowed an attacker to create huge empty string tanks that were filled with server memory as the server simply read the number of bytes into the string value before it was returned to the front end application.

The overview looks something like this…

This allowed an attacker to simply re-submit this request over-and-over and receive megabytes of information at a time.

An example of what data was sent and what data was received. There was a substantial amount of memory, so the process followed to prove impact was pulling a lot of data and grepping through it to find secrets. Please note, we had permission to do this, and would not suggest doing this without permission.

We wrote a script that could automatically gather data and were able to pull a good amount of information showing an incredible click-to-showcase proof of concept and hastily submitted the report

Timeline

2019-07-30 01:30 – Reported

2019-07-30 02:47 – Initial response

2019-08-11 00:24 – Bounty paid

2019-08-19 19:05 – Resolved

Addendum

Everyone who helped with this finding is credited here:

Nathanial Lattimer (@d0nutptr)

André Baptista (@0xacb)

Miguel Regala (@Regala_)

José Sousa (@JLLiS)

Yassine Aboukir (@Yassineaboukir)

This was a really cool bug and a crazy experience. I can’t disclose any more information about this particular issue, but was thrilled to see the positive reaction from both the bug bounty program and product team who treated this issue with great care.

Huge thanks to everyone involved for making things like this possible.

Feel free to follow me on Twitter if you enjoyed the read!