[ Full analysis of multiple Icecast 1.3.11 remotely exploitable overflows ] Pardon my lousy formatting..but this was just a quick writeup Ok..I did some digging into where the exact overflow occurs. And came up with the following results: The bad voodoo happens when the following line of code is executed. I tracked down the how and why and will try to explain it in this text. int diff = tree->cmp (item, p->data, tree->param); [ other exploitable bugs ] This line is called from avl_find() (avl.c) when it is called from get_alias() (alias.c) which is in turn called from find_mount_with_req() (source.c). It should be noted that a bit further down the road a sprintf() call in find_mount_with_req() could also be exploited if and when the discussed bug in the avl routine is fixed. This problem regards the following line: sprintf(pathbuf, "%s:%d%s", req->host[0] ? req->host : "localhost", req->port, req->path); Pathbuf being 8192 bytes. req->path also being able to get up to 8192 user-supplied bytes thus you'd be able to overflow pathbuf bounds by strlen(req->host) + req->port bytes. Which, if you take the default "localhost" and a high port is enough to reach and overwrite ebp and eip located behind pathbuf[BUFSIZ]. [ the exploited bug in question ] Ok onto the particulars of the bug I exploit... avl_find() is called as follows from within get_alias() if (!res) { search.name = req; res = avl_find (info.aliases, &search); } [ structures ] search.name is a member from an alias_t structure which looks like this: typedef struct alias_St { request_t *name; request_t *real; } alias_t; request_t looks like this: typedef struct request_St { char path[BUFSIZE]; char host[BUFSIZE]; int port; } request_t; so search.name is just a request_t structure. which is assigned the user supplied req (which contains the supplied overflowstring req->path); So now search.name.path == req.path. [ avl_find's guts ] avl_find()'s prototype is: void * avl_find (avl_tree *tree, const void *item) It is called as: avl_find (info.aliases, &search); The avl_tree structure looks like this: typedef struct avl_tree { #if PSPP struct arena **owner; /* Arena to store nodes. */ #endif avl_node root; /* Tree root node. */ avl_comparison_func cmp; /* Used to compare keys. */ int count; /* Number of nodes in the tree. */ void *param; /* Arbitary user data. */ mutex_t mutex; /* to protect the tree */ } avl_tree; [ pointing to an evil function ] This structure contains a function pointer called cmp. In the case of info.aliases, which is the first avl_tree struct argument to avl_find(), this function pointer is a pointer to compare_aliases() which is located in avl_functions.c. It's prototype is as follows: int compare_aliases (const void *first, const void *second, void *param) From within avl_find() compare_aliases() is called via the function pointer tree->cmp in the following manner: int diff = tree->cmp (item, p->data, tree->param); Item being the search structure, which we established contained the full user supplied request path in the form of search.name.path; [ inside compare_aliases() ] Now if we take a look at compare_aliases we see the following: int compare_aliases (const void *first, const void *second, void *param) { alias_t *a1 = (alias_t *) first, *a2 = (alias_t *) second; char full[BUFSIZE], full2[BUFSIZE]; if (!a1 || !a2 || !a1->name || !a2->name || !a1->name->host || \ !a1->name->path || !a2->name->host || !a2->name->path) { write_log (LOG_DEFAULT, "WARNING: NULL pointers in \ comparison"); return -1; } sprintf (full, "%s:%d%s", a1->name->host, a1->name->port, \ a1->name->path); sprintf (full2, "%s:%d%s", a2->name->host, a2->name->port, \ a2->name->path); return ice_strcmp (full, full2); } a1 == item. a1->name->path == search.name.path which equals the user supplied request path. So in essence we have the same situation that I warn about earlier in this txt. An sprintf that will allow a buffer overflow of full[BUFSIZE] with user (remotely) supplied data. Which results in ebp and eip being overwritten and the execution of arbitrary code. [ conclusion ] Phew..this has been quite the witchhunt..but I hope this has shed some more light on the how, the what and the exact location of the Icecast bug. As I said this situation occurs many times throughout the icecast source and I would recommend replacing all unsafe string functions with more bounds aware variants to prevent any future problems. With regards, diz -- #temp