Fernando J. Pereda’s blag

May 26, 2008

Security trivia II

Filed under: blag — Tags: , — Fernando J. Pereda @ 6:25 pm

Suppose you have the following code:

char *start, *end, *ref;
/* Some code that process user input so that start and end
 * point to valid memory addresses and start + k < end.
 * Where k is some constant you can't control.
 * ref is made to point to some program-defined chunk,
 * but you can't control this one.
 */
int n = end - start;
if (memcmp(ref, start, n) != 0) {
    printf("Checksum mismatch. Access denied.\n");
    exit(EXIT_FAILURE);
}
printf("Checksum matches. Access granted.\n");

This one is probably easier than the first one, but anyway:

  • What’s wrong with this code?
  • What environment do you need to exploit it?
  • Given such an environment, how can you exploit it?
  • How would you fix the code?

If you’ve seen me ranting about this in #exherbo-dev, you are not allowed to answer :)

— ferdy

Advertisements

13 Comments »

  1. I would have added a check against the length of ref in the code before the memcmp(). It can often be exploited since you can have a third outcome of the memcmp(), namely a segmentation fault, and then you can very fast brute first the secret password/token/checksum.

    Comment by Stian Skjelstad — May 27, 2008 @ 12:11 am

  2. Making memcmp segfault is a possibility, in certain situations, it’d mean a DoS. There’s something subtlier and worse in the code though.

    — ferdy

    Comment by Fernando J. Pereda — May 27, 2008 @ 12:13 am

  3. We should ensure, that n is atleast as long as ref, (I assume we dont compare null terminated sequence, what is suggested by memcmp rather than strcmp). If we did not , user can give all possible sequences of length 1, namely 256, and gain access fast.

    Comment by prak — May 27, 2008 @ 1:17 am

  4. As required in the snippet, start and end are, at least, ‘k’ bytes away.

    — ferdy

    Comment by Fernando J. Pereda — May 27, 2008 @ 1:47 am

  5. Sorry, understood this wrong. There is also possibility of integer overflow, for sufficiently large diffrence of (end – start), n would be negative, and memcmp would return 0. But I am still not sure if I understand your snippet…

    Comment by prak — May 27, 2008 @ 2:31 am

  6. Nah, that is even more stupid, it will be casted back to size_t by compiler, since memcmp has signature memcmp(void*, void*, size_t). That wont work, as I tought it will.

    Comment by prak — May 27, 2008 @ 3:15 am

  7. It’s a matter of getting an integer overflow on end – start so n is equal to zero. I can produce the desired results on an amd64 when end = start + 0xFFFFFFFF00000000.

    Declaring n as size_t solves the problem.

    Comment by Santiago M. Mola — May 27, 2008 @ 3:52 am

  8. Well, 0xFFFFFFFF00000000 is just not doable. 0x100000000 is enough.

    Comment by Santiago M. Mola — May 27, 2008 @ 4:06 am

  9. That’s it. Pointer difference has type ptrdiff_t which is typedef’d to long on, at least, amd64 (it is to int on x86). You can ‘easily’ make n be 0 that way. The cast back to size_t wouldn’t be a problem since you already lost precision on the cast to int and n is already 0.

    The solution here is to declare the variable as ptrdiff_t, even though size_t works, the solution is, in some sense, incorrect. (Probably size_t and ptrdiff_t are typedef’d to the same base type these days).

    — ferdy

    Comment by Fernando J. Pereda — May 27, 2008 @ 8:21 am

  10. Nice one. :) Looks, I was close second time, but missed the obvious fact, that may sizeof(ptrdiff_t) > sizeof(int). Would you get any compiler warning with it?

    Comment by prak — May 27, 2008 @ 9:15 am

  11. I’m not getting any warnings here (on OSX), I shall try a newer gcc version on my exherbo installation later. I’m not sure it should emit a warning though.

    Let’s see if I can get a better explanation of the kind of environment one needs:

    * You need an architecture with 2-complement arithmetic (which would be virtually anything these days)
    * You need, as some of you have already pointed out, that sizeof(ptrdiff_t) > sizeof(int), which would be any 64 bit architecture.
    * An implementation that silently wraps around signed integer types (because we are overflowing a signed int). Linux and gcc work this way. I don’t know about other Unices, but I guess the BSDs are on the same track.

    For references about all this in the standard (WG14/N1124), look for sections: 6.3.1.3 (paragraph 3), 6.5.6 (paragraph 9) and 6.5.16.1.

    — ferdy

    Comment by Fernando J. Pereda — May 27, 2008 @ 10:20 am

  12. You need a difference of UINT_MAX+1. You need to introduce 4Gb of input data.

    Comment by Santiago M. Mola — May 27, 2008 @ 12:02 pm

  13. Just was able to test it: since gcc-4.3.0 (couldn’t check gcc-4.2.4) there is chance to be warned about it:
    g++-4.3.0-alpha20080118 -Wconversion -fdiagnostics-show-option -o sec_triv sec_triv.cc

    sec_triv.cc: In function ‘short int overflow_int()’:
    sec_triv.cc:9: warning: conversion to ‘short int’ from ‘size_t’ may alter its value [-Wconversion]

    I had to s/int/short int/ since I am on 32 bit machine.

    Also -Wsign-conversion works; both are disabled by default in C++, and not included in -Wall, -Wextra. That would suggest add this one to my fav flags…

    Comment by prak — May 27, 2008 @ 12:44 pm


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: