
Python-эксплойт для CVE-2019-5096, уязвимости use-after-free в обработчике загрузки веб-сервера GoAhead, вызывающей отказ в обслуживании через двойное освобождение.
python TriggerDOS.py ip

[https://github.com/embedthis/goahead.git] GoAhead githublink

В коде находим upload.c:370, видим, что перед
wp->currentFile=0
выполняется
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;
...
}
Функция hashEnter добавляет элемент в хеш-таблицу, что приводит к множественным ссылкам на wp->currentFile. Структура WebsUpload в wp->files (хеш-таблица) будет освобождена при вызове termWebs по завершении HTTP-сессии (завершении жизненного цикла Webs).
static void termWebs(Webs *wp, int reuse)
{
...
#if ME_GOAHEAD_UPLOAD
if (wp->files >= 0) {
websFreeUpload(wp);// обход хеш-таблицы, освобождение структур WebsUpload.
}
#endif
}
Теперь рассмотрим другой момент освобождения:
...
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));
...
}
}
}
Обнаружено, что если в заголовке upload есть поле filename, то освобождается wp->currentFile, а затем выделяется новый WebsUpload через walloc. Поскольку sizeof(WebsUpload) попадает в диапазон global_max_fast, куча будет выделяться по принципу LIFO, и только что освобождённый блок сразу же перераспределяется, а затем снова добавляется в хеш-таблицу в функции processContentData. В результате в хеш-таблице оказывается две ссылки на один и тот же чанк, что при вызове termWebs приводит к double free и аварийному завершению.
Добавляем два заголовка upload в одном запросе:
после processContentData снова входим в processUploadHeader. То есть последовательность вызовов:
websProcessUploadData (цикл) # конечный автомат загрузки, на каждой итерации определяется состояние
->initUpload()
->processContentBoundary()
->processUploadHeader()
->processContentData()
->processContentBoundary()
->processUploadHeader()
->processContentData()
->return;
...