Принцип взлома премиум тем для WordPress. И не только

Всего пару лет назад адаптировать понравившуюся тему для популярной CMS WordPress было очень просто: достаточно было базовых знаний программирования на php, или хотя бы понимание принципов программирования вообще и интернета под рукой. Но в наиболее «выигрышные» премиум-темы со временем стали встраивать защиту от умельцев.

Первые ласточки

Поначалу было несложно разгадать код: стоило выбросить ссылки на функцию, которая проверяет «правильность» установленной темы — и следующий кусок кода сам обнаруживал себя ошибкой в определённом месте. Как правило в нём была зашифрована сама функция проверки, причём не в чистом php-коде, а зашифрованная алгоритмом base64. Собственно, можно было и сразу поискать в файлах темы функции base64_decode(), раскодировать их в любом онлайн-раскодировщике, и дальше по полученному php-коду уже понять, что она делает.

Чаще всего зашифрованная функция просто проверяла наличие некоторых строк или переменных в шапке темы, блокируя работу сайта, если что-то было изменено назойливым сайтописателем. Почти всегда проблема решалась простым затиранием этой функции, независимо от того, перехватывала ли она вызовы каких-то стандартных подпрограмм самого WordPress’а, или нет. Пример такой последовательности действий описан на сайте selfbiz.ru.

Но время не стоит на месте, и в один прекрасный момент поиск base64 также перестал давать результаты. Что случилось?

Шахтёрский ребус

На мой взгляд, анализ кода новых тем не мог не привести к фрагменту явно закодированного блока в functions.php, да всего одна строчка в footer.php немного настораживала предупреждениями, что её стирать не надо (тема и правда начинала ругаться при их затирании, хотя обычный html-комментарий около этого кода легко убивал двух зайцев). Навязчивую строчку в подвале экранировать (хотя и не уничтожить полностью) удавалось, но всё же оставался непонятным механизм проверки взлома.

Интересным было и то, что среди шифрованного кода заветной фразы «base64» не было вовсе, только куча переменных. Но глаз-то не обманешь — после них шёл явно шифр base64. И вдруг догадка пришла сама собой.

$rHaN='4';$VqrC='s';$NRoFK='e';$DYxma='_';$tNTdm='b';$hqZJRO='d';
$dXUGlnu='6';$OJWdVR='e';$SmsCe='e';$UjaUX='o';$DEYPS='c';$yVKLW='d';$vGCkjJc='a';
$olVOigbC=$tNTdm.$vGCkjJc.$VqrC.$OJWdVR.$dXUGlnu.$rHaN.$DYxma.$hqZJRO.$NRoFK.$DEYPS.$UjaUX.$yVKLW.$SmsCe;$TwUY='g';$pDpWoq='a';$NUuRmO='n';$edaMRVh='t';$MhkU='l';$LaaPNe='i';$VXQfspm='f';$iVzdTp='e';$veIN='z';$uZIiZkvz=$TwUY.$veIN.$LaaPNe.$NUuRmO.$VXQfspm.$MhkU.$pDpWoq.$edaMRVh.$iVzdTp;$Fdiq='r';$heIC='r';$aflnkj='_';$jORVFNd='s';$upLthOA='t';$rfMnKzq='o';$VYNYS='t';$ZLti='3';$xdOy='1';$mfYQuUeL=$jORVFNd.$VYNYS.$heIC.$aflnkj.$Fdiq.$rfMnKzq.$upLthOA.$xdOy.$ZLti;$JvasOQV='s';$bgsiq='r';$UJQwNeq='t';$jQqlL='r';$lGnUc='e';$nReiM='v';$Qudgjdcq=$JvasOQV.$UJQwNeq.$jQqlL.$bgsiq.$lGnUc.$nReiM;
eval($uZIiZkvz($olVOigbC($mfYQuUeL($Qudgjdcq('C8/Kami/+//+9iCIT9eAc0PCd9CYnC7VaRYKORhYDBsi81  [...]

Не так страшен чёрт

Поиск переменных в приведённом выше фрагменте показал, что в коде одно и то же имя встречается два раза, причём один раз переменной присваивается один символ, а дальше её значение используется в конкатенации через точку. Кстати, чуть глубже в шифрованном коде была функция eval() — а это уже больше, чем ключ к вопросу: php выполняет что-то как код (а это, кстати, небезопасно!).

Решение оказалось простым: заменить вызовы переменных на их значения. Первая же группа переменных со значениями 4, s, e, _, b, d, 6, e, e, o, c, d, a — это… да что там угадывать: это же base64_decode, с переставленными местами буквами, чтобы мы не догадались. Убираем в коде первые 13 переменных — теперь мы знаем, что $olVOigbC — это base64_decode. Осталось понять, что закодировано.

Анализируем следующий набор переменных. Угадайте, что означает g, a, n, t, l, i, f, e, z ? Ответ: это gzinflate — функция, которая распаковывает сжатую строку. Вот вам второй ключ: что-то сжато, закодировано, и выполняется кодом. Идём дальше.

r, r, _, s, t, o, t, 3, 1 — это, конечно же, str_rot13 (иногда догадка приходит раньше, чем успеваешь сложить значения переменных в цепочку).

s, r, t, r, e, v — ещё проще — strrev (ну ладно, даже если не знаете такой функции, всё равно буква за буквой сложить из исходных переменных слово — дело времени и Ctrl+F).

Ну всё, дальше по коду — eval. Что имеем на данный момент?

$olVOigbC=base64_decode;
$uZIiZkvz=gzinflate;
$mfYQuUeL=str_rot13;
$Qudgjdcq=strrev;
eval($uZIiZkvz($olVOigbC($mfYQuUeL($Qudgjdcq('C8/Kami/+//+9iCIT9eAc0PCd9CYnC7VaRYKORhYDBsi81 [...]

Нужно ли объяснять, что это на самом деле:

eval(gzinflate(base64_decode(str_rot13(strrev('C8/Kami/+//+9iCIT9eAc0PCd9CYnC7VaRYKORhYDBsi81+ [...]

Если вы поняли все предыдущие действия, дальнейший рассказ превращается в банальную прозу. Остаётся найти онлайн-функцию strrev(), чтобы перевернуть строку… и так далее, пока не получится читабельный код.

Почти всё…

Беда в том, что после раскодировки может получиться снова закодированная строка, вернее eval(gzinflate… и так далее. Это означает, что программисту было не лень одну и ту же строку тридцать раз закодировать аналогичным методом (иногда набор функций в цепочке варьируется). Если делать все преобразования вручную — конечно, надоест. Но никто не мешает создать php-файл, который будет выполнять цепочку функций (без eval, конечно) пошагово (например, раскодировал — снова вставил, нажал «выполнить» и т.д. Хотя бы по бедности сделать php, в котором будет echo base64_decode(rot13… — куда можно будет снова вставлять полученный результат и снова исполнять.

И вроде бы счастье когда-то наступит. Но иногда программисты решают ещё больше заморочить голову. Тогда после раскодировки получается новая последовательность:

$jh__j='4';$qt____________y='s';$to__d='e';$en_____________t='_';$ne____________l='b';$bp_____________p='d';$zb____j='6';$nt______________j='e';$ga________q='e';$xk______l='o';$se___________j='c';$de_____________c='d';$je___n='a';

Тут и ежу понятно: внутри ещё раз та же 4, s, e, _, b, d, 6, e, e, o, c, d, a — base64_decode. Тогда придётся пройти ещё один круг (Кстати, одна из переменных состояла из переменных $zb____________y=f.u.n.c.t.i.o.n._.e.x.i.s.t.s; — так что расслабляться не стоит). И так — пока не доберёшься до результата.

Стоит ли оно того? Возможно. Так, в одном из разобранных кодов я увидел прямое обращение к файлу с проверкой на таймаут… и это в eval’е! А если интернет тормознёт в момент чтения — получается, тема не заработает? И потом, если хостинг брался «в обрез», то два-три-четыре десятка обращений к php-функциям сказываются явно не в лучшую сторону на производительности сайта, правда? Тогда уж точно придётся ломать, чтобы починить…

Выводы

Разумеется, я написал всё это не для того, чтобы взять — и в лоб взламывать что-то. Алгоритм кодирования можно применить, например у себя на сайте, или, скажем, в коде, за который могут не заплатить. Решать вам.

Методики засекречивания кода продолжают развиваться, и ещё вчера простой и открытый php теперь свободно превращается во что-то нечитаемое простым глазом.


Protected by Copyscape Online Plagiarism Test
Вы не можете высказаться или оставить ссылку здесь...

Обсуждение закрыто.

Powered by WordPress | Thanks to NewWpThemes | Александр Божок