鬱猫のつぶやき

鬱猫が日々思っていることをつづります

OSED 受験記

はじめに

Offensive Security 社のエクスプロイト開発の認定資格である OSED を受験して合格しました。

OSED 自体最近できたばかりの認定資格ということがあり、日本人による合格体験記がまだなさそうでしたので、ブログ記事としてまとめることにしました。

OSED について

もともと、OSCE という認定資格がありました。 この資格がつい最近になって終了となり、代わりとして以下の 3 つの資格が新規に作成されました (OSCE3 certification とよばれています。)。

  • OSED
  • OSEP
  • OSWE

www.offensive-security.com www.offensive-security.com www.offensive-security.com

OSED はもともと OSCE に含まれていたエクスプロイト開発に関連するコンテンツを拡充した上で、1 つの認定資格にしたものです。 ただ、OSCE と扱われている内容にいくつか違いがあり、例を挙げると

  • 脆弱性発見に関して、OSCE では Fuzzing で行うが、OSED ではリバースエンジニアリングを通じて行う
  • OSCE では DEP や ASLR バイパスがコンテンツに入っていなかったが、OSED ではこれらも対象

などがあるようです。 詳細に関しては OSCE と OSED の両方の資格を持っている方の以下のブログ記事に記載があります。

epi052.gitlab.io

試験の形式については OSCP と同じで、実技 + レポート提出となっており、実践的な知識が問われるものとなっています。 この実技には、エクスプロイト開発や Shellcode 開発、リバースエンジニアリングした上での脆弱性発見などが含まれます。

また、試験を受けるには Windows User Mode Exploit Development (EXP-301) というオンライントレーニングを受講する必要があります。 このオンラインコースを受講し、試験を受け 60 点以上をとれば晴れて OSED 取得となります。

受験に至った動機

きっかけはいくつかありますが、一番大きいのは体系的な知識の習得です。

エクスプロイト開発に関しては体系的にまとまった書籍がなく(それか古い)、しっかり習得できていないのではという不安が前からありました。 そういったことがあり、OSCE や OSEE を取得したいと前々から考えていました。 ちょうどそのタイミングで OSCE が廃止され、OSED が新しく認定資格として追加されたというニュースを聞き、受験するに至ったという感じです。

レーニングで扱う内容

シラバスここから確認できます。 扱う内容としては Windowsユーザーモードのエクスプロイト開発に関する技術のうち以下のものになります。

対象はスタックベースのもののみで、ヒープエクスプロイトは対象外となっています。 おそらく、ヒープエクスプロイトに関しては OSEE でカバーすることを考えているのだと思われます。

また、ネットワークベースのエクスプロイトしか扱っていません。 ファイルベースのものについては対象外となっているため、こちらは別の教材で補強して学ぶ必要があります。

レーニン

レーニングでは 600 ページぐらいの A4 PDF の教材とビデオが渡されます。 私は教材の方を隅から隅まで読み込むことで勉強しました。

教材に関しては、CPU やアセンブリの基礎から入るような教材となっており、リバースエンジニアリング未経験の方でも理解できるようになっています。 また、かなりの数の演習問題が用意されている点も特徴です。 教材の最後には Challenge 問題も用意されており、教材の内容をしっかり理解できているのか試せるようになっています。 そのため、エクスプロイト開発や脆弱性発見の経験が十分にある方は、先に Challenge 問題から取り組んで、そこから足りない部分を教材で補強するという形で勉強するのでも良いかもしれません。

私の場合は、教材を最初から最後まで順番に読んでいき、最後に Challenge 問題を解く流れで進めました。 一応 Challenge 問題を含め、すべての問題を解きました。 この中にはかなり難易度の高い問題も含まれています。 (実際、Discord のほかの受講者の様子を見る限りでも、皆同じ箇所で苦労しているようでした。)

最終的にはすべての問題を解けました。これは大きな自信につながりました。

レーニングにおいては実は大きな失敗をしています。 それは OSED の試験の詳細を読まずにトレーニングを始めてしまったことです。

OSED では試験中に使えるツールについての制約が書かれており、例えば、逆アセンブラとしては IDA Pro の Free 版しか使えない、デバッガーとしては WinDbg しか使えないなどの非常に重要な情報が書かれています。 それを知らないままでしたので、トレーニングの途中まで Ghidra や radare2 など、自分が使い慣れているツールで練習問題を解いてしまっていました。

このルールがあるため、試験において高機能なデコンパイラは基本的に使えません。

同じように OSED の取得を検討されている方は、トレーニングが始まる前の段階で試験の詳細にしっかり目を通すことをおすすめします。

試験に向けて行ったこと

仕事の関係で Lab の期間と試験の日程に開きがあったため、試験に向け追加でいくつか問題を解きました。

github.com

github.com

github.com

これらの問題の脆弱性発見から Exploit 開発までを一気通貫して行い、試験に備えました。

また、Exploit 開発をスムーズに行えるようにするため、独自でツールを作成し準備しておきました。 こうしたツール開発は、ほかの受講者の方も行っているみたいで、例えば、epi052 さんの osed-scripts などがあります。

github.com

試験

試験は 48 時間の試験 + 24 時間のレポート執筆という構成になっています。 問題は全部で 3 つ出題されます。

私の場合は 36 時間経過した時点で 3 つの問題を攻略済みで、残りの時間ではレポートに必要な情報を整理するといった感じでかなり余裕がありました。徹夜はせず、睡眠は通常通り 1 日 6 時間取っていたため、実質 24 時間で 3 つの問題を解き切りました。 個人差はあるかと思いますが、OSED は時間にかなり余裕がある試験になっていると感じました。

どちらかといえば、レポート執筆の方がつらかったです。 結局レポート執筆のために徹夜をすることとなり (泣)、ボロボロの状態でレポート締め切りの 3 時間前に提出しました。

レポートのページ数は 80 ページぐらいです。ほかの受験者の情報を見る限りでも、これぐらいの分量になるのが通常のようです。

レポート執筆に当たっては以下のテンプレートを使いました。

github.com

このテンプレートには注意点がありまして、それはコードの 1 行が長くなりすぎると、行の一部が切れてしまう点です (折り返した上で表示してくれるわけではありません)。 レポート提出直前に気づき、私は慌てて直しました。

問題内容の詳細は書けませんが、ほかの受験者が指摘しているように、変化球がくると思った方が良いです。

www.youtube.com

あとは、Exam Guide の内容をじっくり読むことをおすすめします。(ここが非常に重要です。)

おわりに

今回は私の OSED 受験記について紹介しました。 受験に至った動機である、「体系的な知識が身につけられているのか不安」というのが、OSED を取得し、少しですがなくすことができました。

全体的に OSED のトレーニングの教材はよくできており、教材を進めるために用意されている Lab 環境も非常に充実しています。 エクスプロイト開発を体系的に学びたいすべての方に自信をもって勧められる内容です。

次は OSEE にチャレンジしてみたいと思っていますが、実は OSEE に関してはトレーニングをオンラインで受講できないようになっています。 新型コロナの影響もあり、海外渡航も難しくいつ受けられるのやらといった感じです。 また、以下の記事によれば申し込むのだけでも大変みたいです。

ierae.co.jp

申し込み用のスクリプトを用意して待とうかと思っています。

Python 型アノテーション

はじめに

Python 3.5 から導入された型アノテーションについての自分用のメモになります。

アノテーション

Python は動的型付けの言語です。それに起因して、実行時に思わぬエラーに遭遇することが少なくありません。

実行時に遭遇するエラーをできるだけ最小にするために、最近の Python では型アノテーションをつけ、実行前に型の整合性が取れているのかを確認する機構が入っています。

具体的にコードで示しましょう。

import json
from typing import Dict


def hoge() -> str:
    return "hoge"


def int_to_str(i: int) -> str:
    return str(i)


def main() -> None:
    with open("test.json", "r") as fin:
        dat: Dict[str, str] = json.loads(fin.read())
    print(dat["a"])
    # print(dat[1]) # <-- index は str であるべきなのに、int になっている

    print(int_to_str(hoge())) # <-- hoge は str を返すが int_to_str の引数は int を受け取る

if __name__ == "__main__":
    main()

関数の宣言が少し変わっているのがわかると思います。

def int_to_str(i: int) -> str:
    return str(i)

上記は引数 i が int 戻り値が str になることを宣言しています。

引数、ローカル変数の方を指定する場合は変数のあとにコロン、そのあとに型をつければ OK です。 関数の戻り値の方を指定する場合は、関数宣言の後に -> type をつけます。

さて、これをそのまま実行するだけだと実行時にエラーが発生するだけで全く恩恵はありません。

Python では実行前に型の整合性が取れているのかチェックするプログラムが用意されています。それが mypy です。

mypy でチェック

pip で導入していない場合は、まず以下のコマンドで mypy を導入しましょう。

$ pip install mypy

さて、さっきの例を mypy で検査させてみましょう。test.py としてセーブして mypy の引数としてファイル名を指定します。

$ mypy test.py
test.py:19: error: Argument 1 to "int_to_str" has incompatible type "str"; expected "int"
Found 1 error in 1 file (checked 1 source file)

int_to_str の引数として int が期待されているけども、実際は str になっているとエラー出しているのがわかります。 確かに型の検査が実行前に行われていることが確認できますね。

bear を使った compile_commands.json 生成

はじめに

clangd を使う際、JSON Compilation Database (compile_commands.json) が必要になります。CMake を使ってビルドするプロジェクトの場合は、CMAKE_EXPORT_COMPILE_COMMANDS=1 を指定すれば簡単に export できますが、そうでない場合は別のツールを使って生成する必要があります。

今回は、汎用的に compile_commands.json を生成するツール Bear の Mac への導入方法について紹介します。

Homebrew があれば以下のコマンドで入ります。

$ brew install bear

ただ、このままだと Bear が動いてくれません。この後、以下の二つの手順を踏む必要があります。

  • System Integirty Protection の無効化
  • 標準ライブラリのヘッダファイルの再インストール

System Integrity Protection の無効化

Bear のリポジトリにも注意点として挙げられていますが、library preload を無効化しているシステムでは Bear は使えません。Macだと (私は Majave を使っていますが、それ以降でもおそらく) デフォルトで library preload ができないようになっているため、Bear が出力する compile_commands.json が空ファイルになります。

この library preload を許可するためには、System Integrity Protection を無効化してやる必要があります。デフォルトではこれは有効になっています。

$ csrutil status | grep 'System Integrity Protection'
System Integrity Protection status: enabled.

System Integrity Protection の無効化ですが、以下の手順で行えます。

  1. OS の再起動
  2. Cmd-R を押しっぱなしにして、リカバリーモードに入る
  3. ユーティリティをクリック
  4. Terminal をし、Terminal を起動
  5. csrutil disable を打ち込む
  6. OS の再起動

再起動した後、再度 Terminal を開き、System Integrity Protection が無効になっていることを確認します。

$ csrutil status | grep 'System Integrity Protection'
System Integrity Protection status: disabled.

標準ライブラリのヘッダファイルの再インストール

次は標準ライブラリのヘッダファイルの再インストールです。これをやらないとこのイシューで挙げられている問題点に遭遇し、標準ライブラリのヘッダーが見つからずコンパイルできなくなります。

解決策としてはイシューのスレッドのこれにしたがって、ヘッダーの再インストールを行います。

JSON Compilation Database を生成してみる

これで compile_commands.json が生成できるようになりました。適当な C 言語のプロジェクトで compile_commands.json を早速作成してみましょう。

今回は高機能逆アセンブラである radare2 の compile_commands.json を生成してみます。

$ git clone https://github.com/radareorg/radare2.git
$ cd radare2
$ ./configure
$ bear make all
$ ls compile_commands.json
compile_commands.json

以下のような compile_commands.json ができていることが確認できます。

[
    {
        "arguments": [
            "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang",
            "-c",
            "-fno-common",
            "-fPIC",
            "-g",
            "-Wall",
            "-D__UNIX__=1",
            "-DR2_PLUGIN_INCORE",
            "-I/Users/utsuneko/Documents/radare2/libr/..//shlr",
            "-I/Users/utsuneko/Documents/radare2/libr/../shlr/zip/include",
            "-I/Users/utsuneko/Documents/radare2/libr",
            "-I/Users/utsuneko/Documents/radare2/libr/include",
            "-fvisibility=hidden",
            "-I../../shlr/sdb/src//src",
            "-DHAVE_FORK=1",
            "-o",
            "chmod.o",
            "chmod.c"
        ],
        "directory": "/Users/utsuneko/Documents/radare2/libr/util",
        "file": "chmod.c"
    },
...

まとめ

Bear の Mac への導入方法についてまとめました。

compile_commands.json があると、VSCode の補完がいい感じで効くようになったりします。他にも最近リリースされた Sourcetrail を C 言語のプロジェクトで使う場合も compile_commands.json が必要になります。   この compile_commands.json の作成、地味に苦労したので、誰かの役に立てればと思い、ブログに記事としてまとめました。

AArch64 アセンブリ UBFM UBFIZ UBFX

はじめに

最近はArmv8アセンブリを読むことが多いのが、ビット演算系の命令を頻繁に忘れて何回もググっているので、一回まとめておく。 公式ドキュメント、擬似コード (or 具体例) を載っけて欲しい。

ubfm

Unsigned Bit Field Move の頭文字をとって ubfm と呼ぶらしい。 他のビット演算系の命令を見る上で基本となる。

ubfm Xd, Xn, #imm6r, #imm6s
  • imm6s には source register から移動させる一番左のビット番号を指定
  • imm6r には destination register に挿入させる位置の一番右のビット番号を指定

右とか左とか言っているが、LSB first の場合になる。

擬似コードで書けば以下のような感じ。

Xd[:] = 0;
Xd[(64 - imm6r + imm6s + 1):(64 - imm6r)] = Xn[imm6s + 1:0];

Pythonで同じ操作に当たるコードを書くとこんな感じ。

Xd = (Xn & ((1 << (imm6s + 1)) - 1)) << (64 - imm6r)

まず Xn の下位 imm6s + 1 bitを取り出す。その取り出したbitを 64 - imm6r bit 分だけ左シフトしたものをXdに代入する。

例がこのサイトに上がっていたので載っけておく。

 x2    : 823456789ABCDEF0
 x5    : 5555555555555555
 UBFM X5, X2, #60, #23 --> 000000000BCDEF00
# x2 のうち下位24bitsを取り出す (今回だとBCDEF0) 
# そのあと、64 - 60 = 4 bits だけ左シフトしてx5に代入すれば完成

ubfiz

Unsigned Bit Field Insert in Zero の頭文字をとって ubfiz。

ubfiz Xd, Xn, #lsb, #width

これは以下と同じになる。

ubfm Xd, Xn, #(-lsb MOD 64), #(width-1)

source register の内容のうち、下位 width bit を取り出す。これを lsb 分左シフトしたものを作成し、destination register に格納する。

擬似コードで書くとこんな感じ。

Xd[:] = 0;
Xd[width + lsb:lsb] = Xn[width:0]

これも具体例で考えてみる。

 x2    : 823456789ABCDEF0
 x5    : 5555555555555555
 UBFIZ X5, X2, #4, #24 --> 000000000BCDEF00

ubfm の具体例を ubfiz で書き直した例を作ってみた。lsb = 4, width = 24 としたら、さっきの ubfm の具体例と一致することがわかると思う。

ubfx

Unsigned Bit Field eXtract を省略して ubfx

これは以下と同じになる。

ubfm Xd, Xn, #lsb, #(lsb+width-1)

擬似コードでかくとこんな感じ。

Xd[:] = 0;
Xd[width:0] = Xn[lsb + width:lsb];

具体例を一個載っけておく。

x2    : 823456789ABCDEF0
x5    : 5555555555555555
UBFX X5, X2, #4, #24 --> 00000000000BCDEF

おわりに

UBFM UBFIZ UBFX について擬似コード付きで説明した。 新しく命令勉強したら更新していくことにする。

RISC-V Day Tokyo 2019 に参加してきた

日立製作所立馬場記念ホール にて行われたRISC-V Day Tokyo 2019に参加してきた。聞いて思ったことを忘れないうちに書き残しておこうと思い、記事にした。

個人的に気になったこと

まずは、将来的にRISC-Vが機能をスイスに移すことを計画しているとの話。Calista Redmondさんの発表において出てきた(正確には、発表あとの質疑応答のところ)。

ISAを非営利財団であるRISC-V Foundationが管理しているが、機能がアメリカにある以上、Huaweiへ行われたような措置が今後起きないとも限らない。スイスに拠点が移ることにより、こうした危険性はなくなり中国メーカーにとって追い風になりそう。

次にヘネペタの翻訳の苦労話。原著は結構な分量である上、殴り書きに近いところがあったため翻訳に際しては相当の苦労があったとのこと。編集者とのトラブルもあったそうで、その苦労の末生まれたのがヘネパタの日本語訳本だったという話。

私は大学の選考がComputer Scienceでも電気電子工学でもないので、パタヘネすらまだ読めていないが、コンピューターアーキテクチャの定番の教科書・バイブル的存在とのことで是非とも読破しておきたい。

その他、パネルディスカッション。

印象に残っているのは「セキュリティモジュールをオープンソースで作ることのメリットは?」という質問。ソフトウェアと同じでリーナスの法則 (「十分な目ん玉があれば、全てのバグは洗い出される」) が働くこと、問題がおきた場合に自力での検証ができること (これがArmやIntelのものだとできない) というのがメリットとの話が出ていた。

ただ、リーナスの法則が働くのは十分にアクティブなプロジェクトにおいてのみなので、オープンであることが必ずしもプラスに働くわけではないことは注意が必要。

例えば、オープンにして逆にサプライチェーン攻撃の標的になるケースもある。例えば、npmパッケージのevent-streamでは、悪意のある人がOSSのメンテナーとなり、悪意のあるモジュールをパッケージ内に埋め込んだという事件がおきている。

また、「RISC-Vを採用してよかった点。悪かった点。」という質問もあった。

よかった点として

  • シンプルなISAで本質的でない部分に煩わされることがない
  • 一通りソフトウェアスタックが整っている
  • ツールセットがよくできている
  • 新しいアイデアがすぐに試されて、実装が公開される

悪かった点として

  • デバッグ環境が整っておらず苦労する
  • libcとかにまだバグがあったりする
  • Tile Linkの仕様が一気に変わったりして、アップデートしたら突然動かなくなったりする
  • Chiselできる人があまりいない

とかが挙げられていた。

libcとかのバグはこれまで踏んだことがないけど、最近のものはだいぶ修正されたということなんかな。

Chisel ひとまず最低限かけるようになってきたが、まだまだな気がするな(依然としてRocket Chipのコードリーディングに苦しんでいるレベルなので。)。

所感

本日の発表を聞いていて、初めに思ったことが「日本でも商用利用がそろそろ始まる気配があるな」というところかなと。他のアジア諸国に比べて2--3年ほど遅れとの話があったが、今年が商用利用の元年になるかもしれない。

ただ、あくまで気配でしかないかな。日本企業の発表で具体的な採用事例を紹介しているところは少なくとも現時点ではなく、まだ検討段階を脱していない。カスタマイズ可能・低消費電力を実現しやすい・ライセンスフリーであることに目をつけてようやく検討しだしたと感じ。

あと、既存の製品からの移行よりは新規参入者をターゲットとしているサービスがRISC-Vは多めと今日聞いていて感じた。すでに市場に入り込み広く使われているプロセッサの置き換えをすることがどれだけ大変なのか、それは歴史が証明しているわけで、そこに狙いを定めるのはなんか正しい気がする。(ただ、DTSインサイト SiFiveと日本市場の正規代理店となったというプレスリリースが先週あったけど、これにより既存の製品からの移行の支援の体制が整ってきてはいる。)

あと、スポンサーセッションが多めなのは気になった。もう少しテクニカルな話を聞きたかったというところはある。アカデミック・産業半々ぐらいの構成にしてほしい。(ちなみに、海外のRISC-V Workshopとかをみていると発表の内容がアカデミック・産業半々ぐらいになっている。)日本でRISC-Vに関係する研究を行なっている研究者は0ではないはずで、そういった研究者を招いて最先端の研究内容を発表するセッションを設けても良いのではと感じた。

総じて勉強になることが多かった。来年も都合が合えば参加したい。

分子動力学シミュレーションコードをGoで書いてみる

はじめに

仕事でGoを時々使う。静的型付け言語で比較的動作も高速・書きやすいというのがあり重宝している。

高速と書いたが、あるアプリケーションを別々の言語で実装して速度を比較した場合、ざっくりと

C/C++, Fortran, Rust > Go > Java > Python, Ruby

みたいになるイメージを持っていたんだけども、これって本当なのかなとふと思った。

そこで、大学にいた頃にやっていた短距離古典分子動力学法のシミュレーションコードをGoで書いてみて、C++の実装と比較してみることにした。

ソースコードは以下にあげた。

github.com

比較対象としたC++の実装はkaityo256さんの以下のリポジトリのコード。

github.com

計測の条件は以下の通り

時間がなかったので、ひとまずstep1のBrute Forceで相互作用の計算を行う場合の性能の比較だけ行なった。

結果

Go C++
実行時間 18.43s 19.99s

あれ? Goの実装の方が速かった。なんかミスっている気がするな。。。 後日、真面目にプロファイリングしてみることにする。

Embenchを試そうとしたが動かせなかった

Dhrystone に変わるフリーのCPUを性能を測るベンチマークとして Embench が David Patterson によって提案されているのを、RISC-V Workshop Zurich で知った。発表スライドとビデオはこのリンクの"Embench TM: A Free Benchmark Suite for Embedded Computing from an Academic-Industry Cooperative (Towards the Long Overdue and Deserved Demise of Dhrystone)" を参照。

GitHubソースコードがすでに公開されているので早速試そうとしたのだが、結論から言うと実行することができず orz。

riscv64 向けに一応ビルドするところまではできたのだが、ベンチマークを実行しその結果を出力するところまではできなかった。

リポジトリにあるISSUEを見る限り同じ問題に困っている人がいるらしく、ビルドスクリプトからして書き直しを検討しているらしいので、修正されるのを待つことにするか。 github.com

riscv64 向けにビルドできるようにしたものを置いた。 github.com