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.
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.
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.