SECCON 2015 × CEDEC CHALLENGE
- 3. Sandbag – 不正なスコアの送信 – 手法
• http://api.sandbag2015.net/score/ranking に下記のJSON形式のデー
タをPOSTで送る
• {"uuid":"01234567-89ab-cdef-0123-456789abcdef",
"name":"kusano",
"point":2147483647}
>curl -X POST -d "{¥"uuid¥":¥"01234567-89ab-cdef-0123-
456789abcdef¥",¥"name¥":¥"kusano¥",¥"point¥":2147483647}"
http://api.sandbag2015.net/score/ranking
{"rank":"0"}
- 5. Sandbag – 不正なスコアの送信 – 対策案
• 通信のキャプチャにより容易に手法を発見できた
• 通信の暗号化
• アプリの難読化
• 完璧な対策は不可能
• ランキング対象を全てのユーザーではなく、ユーザーのフレ
ンドのみにするなど、チートされてもユーザーが嫌な思いを
しないようなゲーム設計にする
- 7. Sandbag – 通信が平文 – 影響度
• ユーザーの個人情報(名前、ポイント)が漏洩する危険性がある
• 他のユーザーになりすまされる危険性がある
• スマートフォンユーザーは公衆Wi-Fiなど傍受可能な手段で
インターネットに接続することが多い
• チートよりもこちらのほうが問題
- 8. Sandbag – 通信が平文 – 対策案
• 通信の暗号化
• 下記のような暗号化では不充分
• アプリ内に埋め込んだ鍵で暗号化
• アプリを解析されて鍵を抜き出される
• DH鍵交換など
• MITM攻撃に対して脆弱
• HTTPSを使うのが簡単
• エラーが発生したらちゃんとエラーにする
public void onReceivedSslError(WebView paramWebView,
SslErrorHandler paramSslErrorHandler,SslError paramSslError)
{
paramSslErrorHandler.proceed();
}
- 9. Sandbag – APIサーバーのXSS – 手法
• 名前を「<s>hogehoge」などとしてスコアを送信
• http://api.sandbag2015.net/score/ranking をブラウザで開く
※私が送信したデータではありません
- 10. Sandbag – APIサーバーのXSS – 影響度
• 例えば、http://bbs.sandbag2015.net/ というURLでユーザー
交流用の掲示板を運営し、 .sandbag2015.net でCookieを発行
していると、なりすましなどが可能になる
- 13. SUNIDRA – 裏技 – 手法
• 通常は
• HP: 100
• 攻撃力: 10
• 名前を「Warrior」にすると
• 攻撃力: 24
• 名前を「Phoenix」にすると
• HP: 999
• 名前を「Lunatic」にすると
• HP: 15
• 攻撃力: 5
- 15. SUNIDRA – 裏技 – 対策案
• 難読化はなされているが、文字列の暗号化が弱い
• 文字列ごとに決まった値と xor をすると復号できる
• 256通りなので全探索可能
• より強力な難読化ツールの導入
• 裏技の名前を直接コード中に書くのではなく、ハッシュ値を使う
resourceを0x07でxor
- 16. SUNIDRA – 不正なスコアの送信 – 手法
• 手法というか、スコア送信の仕様
• https://cedec2015.seccon.jp/cedec2015/GameCtrl/ にゲーム開始時とゲームクリア時にs, p, gを
form-urlencodeしてPOSTで送信
• s
• ゲーム開始時: Start
• ゲームクリア時: GameClear
• p
• 名前やスコアをgで暗号化
• 詳細は次ページ
• g
• pの暗号化に使用する鍵
• ゲーム開始時: Base64で復号すると32バイト以下になる文字列なら何でも良い
• 本アプリでは32バイトのBase64っぽい文字列(復号すると24バイト)
• ミス?
• 解析の攪乱のため?
• ゲームクリア時: ゲーム開始時にサーバーから送られてくる文字列
• ゲームクリア時のpはこの文字列で暗号化する
このせいで悩んだ(´・ω・`)
- 17. SUNIDRA – 不正なスコアの送信 – 手法(pの構成)
• 【ランダム英数字(/+)256文字】:【アプリのSHA1ハッシュ】,【プレイヤーHP】,【ボスHP】,
【残り時間】,【プレイヤー名】
• アプリのSHA1ハッシュ
• アプリのHSA1ハッシュをBase64エンコード
• g6J6KPwHXeLG+7aqoaUS+fvJDTo=
• アプリの改竄対策
• 修正版がランキングに対応していないのはこのため
• プレイヤHP
• ゲームクリア時に0以下だと弾かれる
• ボスHP
• ゲームクリア時に1以上だと弾かれる
• 残り時間
• ランキングに使われる
• ゲームクリア時に301以上だったり、-1以下だったりすると弾かれる
• プレイヤー名
• Base64エンコードして送信
• 何でも良い(後述)
- 18. SUNIDRA – 不正なスコアの送信 – 手法(pの暗号化)
• pはRijndael(AES)で暗号化して送信する
• 鍵長
• 256ビット
• ブロック長
• 256ビット
• なので、AESではない
• PyCryptoが対応していなくて面倒だった
• パディング
• ゼロ埋め
• 暗号利用モード
• ECB
• 一般的には良くないとされるが、このアプリでは特に悪用方法が思いつかなかった
• ECBなのでIVを指定する必要は無い
• 暗号化は2段階
k = crypt(key=g, plain="6789ABCDEFGHIJKLMNOPQRSTUVWXYZ")
p = crypt(key=k, plain=p)
なぜか32バイトではなく30バイト
ミス? 解析の攪乱?
- 20. SUNIDRA – 不正なスコアの送信 – 対策案
• チート手法の発見方法
• ルート証明書を端末に追加して、HTTPS通信の解析
• アプリが難読化されていると、解析の確認用に
実際の通信内容が欲しい
• アプリのコードの解析
• より強力な難読化ツールの導入
• (OSだけではなく)アプリ側でも証明書を検証する
- 21. SUNIDRA – 不正なスコアの送信 – 面倒だったこと
• ゲームオーバー時にはスコアの送信をしていない
• 一度ゲームをクリアする必要があった
• どの関数を呼び出しているかわからない
× obj = this.f(Encoding.ASCII.GetBytes(arg_37B_0),~
○ obj = this.f(Convert.FromBase64String(arg_37B_0),~