The sociology of open-source security fixes, continued

Share on twitter
Share on linkedin
Share on reddit

Why a second anecdote about security bugs in open-source software

In a previous episode, having just reported a vulnerability in the open-source, connected program cpuminer, I uselessly worried about what to do. In fact, there was no urgency and no responsibility, as the vulnerability had already been advertised a couple of months earlier on the fulldisclosure mailing list.

The same thing happened again recently, with a different enough conclusion to warrant discussion.

tiny-AES128-C, a “small and portable implementation of … AES128”

So tiny-AES128-C is the name of this project on GitHub. There is no documentation in the form of a separate file from the implementation, but the header file says:

CBC enables AES128 encryption in CBC-mode of operation and handles 0-padding.

The words “handles 0-padding” mean that although AES works on 16-byte blocks, the library will handle a message length that is not a multiple of 16 by padding the last block with zeroes for you.

Or will it? Meanwhile the comment in the implementation file says:

NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0).
You should pad the end of the string with zeros if this is not the case.

When in doubt, use the source! No, not this source, the non-comment part of the source:

void AES128_CBC_encrypt_buffer(uint8_t* output, uint8_t* input, uint32_t length, …)
{
  intptr_t i;
  uint8_t remainders = length % KEYLEN;
              /* Remaining bytes in the last non-full block */

So despite some confusion in the documentation available in the form of comments, it looks like last non-full blocks are supported after all. But the attention of my colleague Vincent Benayoun was drawn to what the function AES128_CBC_encrypt_buffer does next:

  for(i = 0; i < length; i += KEYLEN)
  {
    XorWithIv(input);
    …
    input += KEYLEN;
    …
  }

A partial specification of XorWithIv, if the library contained one, might look like:

/*@ 
  requires \valid_read(Iv + (0 .. 15));
  requires \valid(buf + (0 .. 15));
  assigns buf[0..15] \from Iv[0..15], buf[0..15];
*/
void XorWithIv(uint8_t* buf);

In other words, XorWithIv assumes that the pointer passed to it points to a complete block, and exclusive-ors this block in place as part of CBC mode.

Concretely, say you are using tiny-AES128 to encrypt a 33-byte input buffer, like this:

char key[16] = …
char iv[16] = …
uint8_t input[33] = …
uint8_t output[48]; // rounded up to next multiple of block size
AES128_CBC_encrypt_buffer(output, input, 33 /*length*/, key, iv);

This would mean that AES128_CBC_encrypt_buffer will call the function XorWithIv successively with argument input, input + 16 and input + 32. In this last case, XorWithIv will replace bytes from input + 32 to input + 47 with the exclusive-or of their previous values with the last encrypted block.

To rephrase, an attacker that can cause the input length to be 33 can force 15 bytes past the end of input to be xored with values that they shouldn't have been xored with: the second block of the output message, which CBC mode specifies must be xored with the third input block before processing it.

If the attacker knows all of the initialization vector, key and the 33 bytes of contents of input and can influence at least one of them, then the attacker can choose what at least some of the bytes between input + 33 to input + 47 will be xored with. A single computer and a bit of patience will let the attacker brute-force a 32-byte message in the AES image of which 4 bytes chosen in the second block have the exact value convenient for each for the attacker's purposes. One can imagine that pre-computed tables and massive computational resources would let a prepared attacker choose the values of between 6 and 15 bytes in the encrypted second block, xored in memory past the input buffer.

No need to report

As in the case of cpuminer, the vulnerability had already been found a few weeks earlier. This time, there wasn't even a need to inform the developer specifically, as a fix had been submitted to them on GitHub as a pull request, but so far, ignored.

Vincent wasn't even trying to find vulnerabilities in tiny-AES128-C: the goal was to demonstrate that it didn't have any. So he naturally continued his work with Andreas Wehrmann's fixed version.

Things became interesting when Vincent found that a small problem remained in that version for some input lengths. What to do? The author of tiny-AES128 had not even reacted yet to the very serious buffer overflow reported to him weeks earlier, would they do something about a comparatively less serious out-of-bounds read that can only result in denial of service?

The sociological experiment begins with Vincent submitting a pull request to Andreas Wehrmann, to fix the problem in his fork. And what happened next was that although Andreas had apparently done little more with his GitHub account than submitting the tiny-AES128 bugfix he had found, and had not used GitHub since, the pull request was accepted within 24h.

Conclusion

In this second anecdote, the original author had abandoned their software, but a kind stranger on the Internet who had already provided a fix for the egregious bugs in it graciously accepted the responsibility for proof-reading and validating a fix for the issue that had been left over. Again, fixes happened only very quickly or not at all. Again, if you plan to depend on a piece of open-source software, the anecdote shows that you would do well to do your own Google search for known security vulnerabilities in it. You shouldn't assume that publicly known vulnerabilities have been fixed in the software component.

This anecdote also shows the other side of the GitHub-does-not-allow-private-reports coin. On the one hand, one would sometimes like to report security-sensitive issues privately. On the other hand, if the software has been abandoned by its author, it may good to know that security issues have been left without resolution, or that an issue you would give the author some time to fix was already reported months ago.

 

Vincent was working on the RAPID project AUROCHS, funded by the Direction Générale de l'Armement, while he found both the known bug and the unknown bug in tiny-AES128 described in this post.

You might also like these articles

Sign up for our monthly newsletter

Get notified of new articles !