3 minute read.

Spot the Vuln Shirt (Solutions)


Unfortunately, we will not be directly selling these shirts, but I have another post tutorial on how to reproduce the shirt if you want to put in the bit of extra work to get one.

Regex (bug-bounty style vulnerability)

We've covered this vulnerability multiple times on the podcast and it was our Spot the Vuln on Episode 152 (written in Golang).

The  regex in allow. It looks normal, and if you test it in the obvious ways it seems to work. api.safe.com passes, api.notsafe.com fails. Because it matches the start of the string with ^ and ends with a / you can't just append data to the end like https://api.safe.com.notsafe.com. So it defends against some common issues.

The problem is that in regex . is a wildcard character. It'll match with any character at all, not just a . character. An attacker could register a domain like api-safe.com and the . will match the - character without complaint.

Its a somewhat common issue as far as regex problems go simply because it looks correct for a domain, so at a glance its easy to think it is correct. The context of this code is not known, but its easy to imagine a scenario where this would lead to a SSRF.

Just be Positive (memory corruption vulnerability)

This is an old spot the vuln, based on one that can be found in the 2006 book Art of Software Security Assessment. We have used it a couple times on the podcast, most recently on Episode 196.

TL;DR. length = length * -1 doesn't work with INT_MIN (INT_MIN * -1 == INT_MIN) becuse of a side-effect of how signed integers work. This means the attempt to correct the data is effectively a no-op, and the bounds check passes because INT_MIN is certainly smaller than MAX_PACKET. When read is eventually called, the length is converted to an unsigned int resulting in a far too large write into buf and a stack-based overflow.

Going a bit deeper for those unfamiliar, lets lay out what the code is doing. The idea is you have this read_data function which reads data from a socket file descriptor and processes that data and eventually returns a char * containing it after some other processing. After allocating a stack buffer to contain the raw packet data. It reads the first int from the packet into length which is going to represent the length of the data to read. It does some checks on the length value. First ensuring the value is always positive by multiplying any negative length by -1 to turn it into a positive value. Then checking that the length is not longer than the allocated buffer. Finally it reads length bytes into the buffer.

The problem stems from trying to correct the user input rather than rejecting it. This bit of code: length = length * -1 is the problem.

Mathematically this will work, for every value when multiplied by -1 it will flip the sign of the value. This doesn't hold true in a digital sense for most CPUs because of something called Two's Compliment which is how the CPU stores values that can be negative or positive. I won't go into all the details about how this work, but it means that signed integer types don't have an equal range of positive and negative values. For exmaple, a one-byte value in two's complient has a range of -128 to 127, this is a consequence of how two's compliment works. With a one-byte value, if you try to multiple -128 by -1, what you should get is 128 but the upper most bit of that gets truncated because it can't fit inside of a single byte. Resulting, rather unintuitively in -128 * -1 being -128. This is true at all sizes of signed integers, the minimum value multiplied by -1 will result in itself.

As such an attacker can provide a length value in the packet of INT_MIN the first if condition will in effect be a no-op. The second if condition doing the bounds check will see -4billion something which is of course smaller than MAX_PACKET and let it through. Then finally when length is passed into the read function it will implicitly be converted into an unsigned int which will be a large value, 4 billion something. Resulting in read copying in way more than MAX_PACKET bytes into buf giving you a stack-based linear overflow.