
Exploit en Python para CVE-2019-5096, una vulnerabilidad de use-after-free en el manejador de carga del servidor web GoAhead, que provoca denegación de servicio mediante un doble free.
python TriggerDOS.py ip

[https://github.com/embedthis/goahead.git] Enlace de GitHub de GoAhead

En el código, localizamos upload.c:370; se puede ver que antes de
wp->currentFile=0
se ejecuta:
typedef struct WebsUpload {
char *filename; /**< Local (temp) name of the file */
char *clientFilename; /**< Client side name of the file */
char *contentType; /**< Content type */
ssize size; /**< Uploaded file size */
} WebsUpload;
...
typedef struct Webs {
...
WebsUpload *currentFile;
...
}Webs;
...
processContentData(Webs *wp){
...
file = wp->currentFile;
...
hashEnter(wp->files, wp->uploadVar, valueSymbol(file), 0);
defineUploadVars(wp);
wp->currentFile=0;
...
}
La función hashEnter añade un elemento a la tabla hash, lo que genera múltiples referencias a wp-currentFile. La estructura WebsUpload dentro de wp->files (tabla hash) se liberará con free cuando la sesión HTTP termine (al finalizar el ciclo de vida de Webs) y se llame a termWebs.
static void termWebs(Webs *wp, int reuse)
{
...
#if ME_GOAHEAD_UPLOAD
if (wp->files >= 0) {
websFreeUpload(wp);//遍历hashtable 取出WebsUpload结构体free掉。
}
#endif
}
A continuación, examinemos otro punto donde se llama a free:
...
processUploadHeader(Webs *wp, char *line)
{
while (key && stok(key, ";\r\n", &nextPair)) {// 这是以 ; 为分割符解析 upload 头部
...
else if (scaselesscmp(key, "filename") == 0) {
...
freeUploadFile(wp->currentFile);
file = wp->currentFile = walloc(sizeof(WebsUpload));
...
}
}
}
Observamos que si la cabecera upload contiene un campo filename, se libera wp->currentFile con free y, a continuación, se reserva un WebsUpload con walloc.
Dado que sizeof(WebsUpload) está dentro del tamaño de global_max_fast, el chunk del heap se asignará en orden LIFO (último en entrar, primero en salir), por lo que el chunk recién liberado se asigna de inmediato. Después, en la función processContentData, se vuelve a añadir a la tabla hash. En ese momento, la tabla hash ya tiene dos referencias a ese chunk, por lo que al llamar a termWebs se produce un double free y un abort.
En una sola petición se añaden dos cabeceras upload:
Después de la función processContentData, se vuelve a entrar en processUploadHeader. Es decir, la llamada se produce en el siguiente orden:
websProcessUploadData (bucle) # máquina de estados de subida; cada iteración determina un estado
->initUpload()
->processContentBoundary()
->processUploadHeader()
->processContentData()
->processContentBoundary()
->processUploadHeader()
->processContentData()
->return;
...