asprintf

Since I’ve already gone 2 post without blogging about security I figured it’s time to do another security related one. It’ll be another this-api-is-so-broken post.

asprintf is basicly very simular to sprintf and snprintf, but unlike those 2 calls it dynamically allocates memory and hence the programmer doesn’t have to worry about boundschecking. Programmers seem to misunderstand that and think that they’re in the clear if they just use asprintf. Nothing could be further from the truth. See, there’s a corner case. What if malloc (which it uses internally) fails ?

Before I go into that, let’s first see how asprintf works:
int asprintf(char **strp, char *fmt, …)
Basicly you give it a pointer to a pointer as first argument and then a formatstring with some arguments.

so what if asprintf fails ? It returns -1. However, the content of strp upon failure is “undefined” according to the linux manpage. the OpenBSD manpage says the following about it:
“The asprintf() and vasprintf() functions [...] If sufficient space cannot be allocated, these functions will return -1. The value of ret in this situation is implementation-dependent (on OpenBSD, ret will be set to the null pointer, but this behavior should not be relied upon).”

in reality, all the BSD’s set strp to NULL and the glibc implementation (used on most linux distributions) will not change it.

So in linux this makes it interesting for exploitation. Suppose one uses asprintf() and forgets to check the return value (which is very common btw, but more on that later). it leaves strp uninitialized. in most cases strp will be on the stack. Meaning that it’s realistic to assume a user has control over this uninitialized variable (more on this can be found at http://www.felinemenace.org/papers/UBehavior.zip and http://www.blackhat.com/presentations/bh-europe-06/bh-eu-06-Flake.pdf) . So depending on what the application does next with this pointer it might be exploitable. Common scenario’s are passing this pointer to free() at some point (so you don’t get a memory leak) or for example having it point to some path that’ll get used in chmod/chown/open/rename/…. Definatly not a good thing to have. Often (even when asprintf() is used correctly) people tend to forget to free() memory obtained with asprintf(). Sometimes people also incorrectly handle asprintf failure. Since everyone seems to be looking at google code search for vulnerable example’s I did the exact same thing and quickly spotted a nice example of incorrect failure handling:
static char *
gen_dbsuffix(char *db_name, char *sfx)
{
char *dbsuffix;

if (sfx == NULL)
sfx = “.ok”;

asprintf (&dbsuffix, “%s%s”, db_name, sfx);
if (dbsuffix == NULL) {
fprintf (stderr, “gen_dbsuffix: out of memory\n”);
exit(1);
}
return dbsuffix;
}
that’s in MIT kerberos btw. Spotting the error should be obvious (considering what I just said about asprintf). While doing these google code searches I found out that the following abuse asprintf in one way or another:
- gnu radius
- heimdal kerberos
- samba (I’m shocked I tell you !)
- glibc’s pt_chown
- mailutils
- gnu sasl
- MIT kerberos

rsync used to have a nasty bug related to asprintf aswell, but that one recently got fixed.

edit: 2 things I forgat to mention, the first being, that last time I checked dietlibc has simular behavior to glibc’s (with regard to asprintf failing). The 2nd that using asprintf in return into libc exploits can be very usefull, since you get to allocate memory, And put data on it, without needing to check any registers.

2nd edit: It seems I made a terrible mistake. fefe just emailed me and told me dietlibc’s asprintf has always set strp to NULL on failure. I do apolgise for this. Must have confused it with something else.

Tags:

Leave a Reply