Writing An Interpreter In GoをRustでやった

おひさしぶりです、morimolymolyです。

Writing An Interpreter In GoをRustでやりました。
github.com

もともとはRustのチュートリアルであるところのTRPLをこなしたあとに、そこそこ量のあるコードを書きたくなったのでなにか探していたところ、インタプリタを書いてみるのも悪くないなあと思って書いてみました。
doc.rust-lang.org

最初は、Rustというか関数型言語っぽいやりかたがみにつかず、Anyトレイト連発❗みたいなクソコードを量産しましたが、時期にやり方を覚えいい感じにこーどがかけるようになりました。
ちなみにGolangの実装のままにAnyトレイト連発のクソコードは以下のブランチからみることができます。
github.com

Pratt Parserを書く部分とかも楽しかったし、おまけのマクロの実装も楽しかった。
コードを書いていくと次第に理解が深まり、本を読まずとも、機能の追加方法が思いつく瞬間があり、とても気持ちのいい体験でした。

これからは続きのWriting a Compilier in GoをRustでやってみようかなあ、と思っています。
まだrmonkeyも汚いコードを残しているので整理はしたいと思っていますが……。
なんせ休学して暇な身なのでメンタルさえ安定していれば無限にコードが書けるのでね。

今、いろいろと面白いことを考えてやっている最中なので、時期にコンパイラインタプリタ以外のおもしろいこともここで発表できたらいいなあと思っています。
まずはメンタルを安定させてお休みするところから始めていきます。

それでは短いですが、次回の機会で。

ゼロから始めるHyper-Vのアーキテクチャ

はじめに

こんにちは,morimolymolyです.
皆さん,Hyper-V脆弱性報酬プログラムはごぞんじですか?
https://www.microsoft.com/en-us/msrc/bounty-hyper-v

なんとHyper-VのRCE(Remote Code Execution)を発見するだけで$250,000も貰えてしまうのです!!
最近では360 Core Securityの研究者がVMEscapeの脆弱性をみつけて$200,000をゲットしています.

$250,000も貰えたら働かずに毎日ごろごろしていても数年は余裕で持ちますね.
というわけでHyper-V脆弱性を発見する前に,これのアーキテクチャを学ぼうというわけです.

Hypervisor概要

ハイパーバイザにはType-1(Baremetal)とType-2(Host)の2種類あります.
Type-1はハイパーバイザがハードウェア上で直接動作します.
例: XenKVM,BitVisor,Bareflank
Type-2はホストOS上のプロセスとして動作するハイパーバイザです.
例: VirtualBoxVMWareQEMUBochs

Hyper-VはType-1ハイパーバイザにあたります.

Hyper-Vアーキテクチャ概要

f:id:morimolymoly:20190125210041p:plain
上図がHyper-Vアーキテクチャです.
Hyper-VはAttack Surfaceをなるべくへらすために,コード量を最小限にするという設計理念があります.
そのためデバイス仮想化などの機能はHyper-Vには実装していません.
Hyper-VがインストールされたホストOSがデバイス仮想化などのあらゆるサービスを担当します.
そのおかげでHyper-VはホストOSに実装されているデバイスドライバをそのまま利用できます.

Hyper-VにはPartitionという単位でVMが存在します.
Hyper-VがインストールされたホストOSをRoot Partitionと呼び,これは仮想マシンの管理,ゲストのメモリの操作,デバイスエミュレーションサービスの提供,実デバイス操作を行います.
XenでいうとDom0(特権ドメイン)です.

ゲストマシンはPartitionと呼びます.
ゲストマシンは別のPartitionの物理メモリにアクセスできませんし,ハードウェアの直接アクセスができません.
すべてはRoot Partitionと通信してデバイスの操作などを行ってもらいます.
またこの通信は厳格に定義されたインターフェースを通じてのみ行なえます.

このアーキテクチャから分かる通り,セキュリティ野郎が注目するべきはRoot Partitionです.
詳しくは以下の記事を参照.
docs.microsoft.com

Hyper-Vのメモリ

Hyper-Vのメモリは以下のように定義されます.

  • PA(Physical Address) 物理メモリ空間
  • SPA(System Physical Address) RootPartitionの物理メモリ空間
  • SVA(System Virtual Address) RootPartitionの仮想メモリ空間
  • GPA(Guest Physical Address) ゲストの物理メモリ空間
  • GVA(Guest Virtual Address) ゲストの仮想メモリ空間

SPA,SVAは他のハイパーバイザだとHPA(Host Physical Address),HVA(Host Virtual Address)と呼ばれることが多い気がします*1

Root Partitionの役割

f:id:morimolymoly:20190125213147p:plain
上図はRoot Partitionで提供されるサービスの例です.
以下に用語を説明します.

  • VDEV(Virtual Device) Rootのユーザ空間で動くエミュレートまたは準仮想化されたデバイス
  • VSP(Virtualization Service Provider) Rootのカーネル空間でVDEVと協調して動く準仮想化されたデバイス
  • IC(Integration Component) ゲストがアクセスできるRootのユーザ空間で動くコンポーネント

また次のようなサービスが提供されています.

f:id:morimolymoly:20190125220823p:plain

このようにRootPartitionのユーザ空間でホストされるサービスもあれば,RootPartitionのカーネル空間でホストされるサービスもあります.

ゲストのデバイス操作の流れ

f:id:morimolymoly:20190125214731p:plain
上図はゲストがデバイスを操作するときの概要図です.
流れは以下のとおりです.

  1. ゲスト内のVSC(Virtualization Service Client)がvmbusを通じてRoot Partition内のVSPに操作を依頼
  2. VSPがホストOSのデバイスドライバに操作を依頼
  3. デバイスドライバがデバイスを操作

でばストレージ操作のVSPをみてみましょう.
f:id:morimolymoly:20190125221027p:plain


ユーザ空間のVSPも見てみましょう.
f:id:morimolymoly:20190125213849p:plain
VMWPとはVirtual Machine Worker Processの略で仮想マシンごとに用意されるVSPを提供するためのユーザ空間のプロセスです.
この例ではユーザ空間にSMBのサービスであるVSMBを動作させています.

Hyper-VLinuxでの実装

当然このような仮想化支援技術はゲストカーネルに実装されていなければなりません.
Linuxでは以下のようなディレクトリにコードがあるようです.
f:id:morimolymoly:20190125222048p:plain

実際にヘッダファイルを眺めてみるとなかなか面白いです.
github.com

おわりに

簡単でしたが,以上がHyper-Vの基本的なアーキテクチャになります.

予告

次回はHyper-Vリバースエンジニアリング環境を構築します.
また既知の脆弱性についても解説したいと思います*2

*1:ほんま統一してくれ

*2:クローズドソースなので当時のバイナリをビルドして実験できないから解説する意味があるのかわからんが

メールアドレスが不正使用されてしまいました

こんにちは,morimolymolyです.

2019/01/11の午前2時から3時にかけて私のメールアドレスを使い,私を騙るものが5ちゃんねるや爆サイ,その他掲示板に書き込みをしていたようです.現在,その書き込みを見た方からたくさんメールが届いて迷惑しています.

私はtwitter(@morimolymoly),XMPP(moly420[at]exploit.im),slack(HyperVillage,seccamp,MMA,バイト先)以外でmorimolymolyというスクリーンネームは使用していません.また上記アカウント以外から能動的にコンタクトを取ることはありません.

もし,今後私を騙るようなものがコンタクトをしてきても無視をしてください.
所属している界隈故,マルウェアなどを用いたサイバー攻撃に発展していく恐れもあります.
くれぐれも私を騙るものからのメッセージに記載されたURLなどは決して踏まないように気をつけてください.

以下,私を騙る書き込みのスクリーンショットを公開し,この記事を終わります.

f:id:morimolymoly:20190112125221p:plain
f:id:morimolymoly:20190112125217p:plain
f:id:morimolymoly:20190112125211j:plain
f:id:morimolymoly:20190112125204j:plain
f:id:morimolymoly:20190112125201j:plain

f:id:morimolymoly:20190112125157j:plain
犯人の生IPです

f:id:morimolymoly:20190112130015p:plain
2019/01/12 13:00追加

Linuxのsyscallハンドラのエントリポイントがたくさんあるんだが -> 完全に理解した

はじめに

久しぶりの更新です,morimolymolyです.

最近,私はRing-1に興味を持ちいろいろなhypervisorを触って遊んでいます研究しています.

さて,皆さんはsyscallにはかなり馴染み深いですよね?

システムソフトウェアを書くときはもちろんのこと,exploitするときにガジェットをあーでもないこーでもないとつないでROPして呼び出したりしますよね?

私も最近syscallをRing-1から監視したりしているのですが,そこで気になったことがあったのです.

というわけでこの記事では,x86_64のマルチコアプロセッサLinux環境でのsyscall命令についてみていきます.

syscall命令

x86_64ではシステムコールを行う際には,引数をレジスタに設定して,syscall命令を呼び出します.

syscall命令はMSR(Model-specific Register)のIA32_LSTAR(0xC0000082)からシステムコールのエントリポイントのアドレスを読み出し,RIPにセットします.

神秘的なRing3からRing0の移動が,この一つの命令でできるわけです.

驚きですね.

ではIA32_LSTARの中身を読み出してみましょう.

以下のLKM(Loadable Kernel Module)はRDMSR命令を用いてIA32_LSTARを読み出してprintkしています.

gist.github.com

このLKMをビルドして実行するとカーネルメッセージにIA32_LSTARの中身が表示されるというわけです.

それではこのLKMをおもむろに何度も実行してみましょう!(なぜ)

dmesg | grep MOLY

するとどうでしょう.

何故かアドレスが異なっています!! (結果はめんどいのでのせない)

syscallハンドラが気まぐれでメモリ空間を散歩しているのでしょうか?

いえ,違います.

この挙動はマルチコアプロセッサのKPTI(Kernel Page-Table Isolation)が有効な環境においては正常な動作なのです!!

syscall_init関数

さて,IA32_LSTARLinuxのboot時,syscall_init関数によって設定されます.

/* May not be marked __init: used by software suspend */
void syscall_init(void)
{
    extern char _entry_trampoline[];
    extern char entry_SYSCALL_64_trampoline[];

    int cpu = smp_processor_id();
    unsigned long SYSCALL64_entry_trampoline =
        (unsigned long)get_cpu_entry_area(cpu)->entry_trampoline +
        (entry_SYSCALL_64_trampoline - _entry_trampoline);

    wrmsr(MSR_STAR, 0, (__USER32_CS << 16) | __KERNEL_CS);
    if (static_cpu_has(X86_FEATURE_PTI))
        wrmsrl(MSR_LSTAR, SYSCALL64_entry_trampoline);
    else
        wrmsrl(MSR_LSTAR, (unsigned long)entry_SYSCALL_64);

ここの関数で行っている処理は,X86_FEATURE_PTIが有効になっている場合,つまりKPTIが有効になっている場合に,wrmsr命令でIA32_LSTARSYSCALL64_entry_trampolineを書き込んでいます.

SYSCALL64_entry_trampolineは,ご覧の通り,cpuのidによってアドレスが異なるようです.

つまりcpuのコアごとにIA32_LSTARの値が異なることは正解なわけです.

また,先程のLKMでアドレスが異なっていたのも,実行するコアが異なっていたからですね.

entry_SYSCALL_64_trampoline

さて,entry_SYSCALL_64_trampolineでは何をしているのでしょうか.

 .pushsection .entry_trampoline, "ax"

/*
 * The code in here gets remapped into cpu_entry_area's trampoline.  This means
 * that the assembler and linker have the wrong idea as to where this code
 * lives (and, in fact, it's mapped more than once, so it's not even at a
 * fixed address).  So we can't reference any symbols outside the entry
 * trampoline and expect it to work.
 *
 * Instead, we carefully abuse %rip-relative addressing.
 * _entry_trampoline(%rip) refers to the start of the remapped) entry
 * trampoline.  We can thus find cpu_entry_area with this macro:
 */

#define CPU_ENTRY_AREA \
    _entry_trampoline - CPU_ENTRY_AREA_entry_trampoline(%rip)

/* The top word of the SYSENTER stack is hot and is usable as scratch space. */
#define RSP_SCRATCH    CPU_ENTRY_AREA_entry_stack + \
            SIZEOF_entry_stack - 8 + CPU_ENTRY_AREA

ENTRY(entry_SYSCALL_64_trampoline)
    UNWIND_HINT_EMPTY
    swapgs

    /* Stash the user RSP. */
    movq %rsp, RSP_SCRATCH

    /* Note: using %rsp as a scratch reg. */
    SWITCH_TO_KERNEL_CR3 scratch_reg=%rsp

    /* Load the top of the task stack into RSP */
    movq CPU_ENTRY_AREA_tss + TSS_sp1 + CPU_ENTRY_AREA, %rsp

    /* Start building the simulated IRET frame. */
    pushq    $__USER_DS           /* pt_regs->ss */
    pushq    RSP_SCRATCH          /* pt_regs->sp */
    pushq    %r11             /* pt_regs->flags */
    pushq    $__USER_CS           /* pt_regs->cs */
    pushq    %rcx             /* pt_regs->ip */

    /*
    * x86 lacks a near absolute jump, and we can't jump to the real
    * entry text with a relative jump.  We could push the target
    * address and then use retq, but this destroys the pipeline on
    * many CPUs (wasting over 20 cycles on Sandy Bridge).  Instead,
    * spill RDI and restore it in a second-stage trampoline.
    */
    pushq    %rdi
    movq $entry_SYSCALL_64_stage2, %rdi
    JMP_NOSPEC %rdi
END(entry_SYSCALL_64_trampoline)

    .popsection

コメントにもある通り,このcpu_entry_area's trampolineはコアごとにremapされるようです.

そして,syscall_init関数でIA32_LSTARに書き込んでいたのは,RIPベースの相対アドレスの計算を行って導き出した,実際にマップされたコードのアドレスというわけです.

またentry_SYSCALL_64_trampolineはその名の通り,いくつかの段階をへて本来のシステムコールを行う前のトランポリンのようなコードです.

まあ今回は各段階のコードは読みませんが.

さて,ではなぜこのような複雑なマッピングを行っているのでしょうか?

コアごとにハンドラを配置する必要性がわかりません.

コアごとにハンドラがマッピングされるワケ

ぐぐると,以下のようなLKMLのパッチ情報にたどり着きました. LKML: Thomas Gleixner: [patch 19/60] x86/entry/64: Create a per-CPU SYSCALL entry trampoline

syscallが行われる際,FLAGS以外のすべてのレジスタとともにハンドラへ突入します.

当然,ユーザ空間のRSPをどこかに保存してカーネルのスタックのアドレスを示してやる必要がありますね.

通常,per-CPUdataセクション(コアごと)と呼ばれる空間にユーザ空間のRSPは格納されことになっています.

その後にentryの際にSWAPGSをしてGSレジスタベースでper-CPUdataセクションへアクセスします.

しかし,KPTIが導入された場合大きな問題が生じてきます.

KPTIによりユーザ空間とカーネル空間は別のページテーブルにマップされるからです.

つまり,CR3レジスタカーネル用とユーザ空間用とで書き換えなければならないのです!!(CR3レジスタに入るのはページディレクトリのベースアドレス.ページングに詳しくない人はアドレス解決に必要な基本のアドレスと思ってもらえれば良き!)

従来どおりGSレジスタベースでアクセスすればいいと思いますが,GSベースのアクセスの場合,ユーザ空間用のページに重要なperCPUdataがマッピングされていなければならないので,Meltdownなどの脆弱性によっていろいろと情報が盗み出されてしまう恐れがあります!!!!(CR3をカーネル用に書き換えたいのに特権がないとアクセスできないデータ……しんどい……)

CR3レジスタは即値で代入できず,レジスタを介する必要がるのだが,syscallハンドラで自由に使えるレジスタは大変少ないのです.

ではエントリ時のレジスタをみてみよう.

  • rax system call number
  • rcx return address
  • r11 saved rflags(これはばんばん変動する)
  • rdi arg0
  • rsi arg1
  • rdx arg2
  • r10 arg3 (CのABIによるとRCXに移す必要がある)
  • r8 arg4
  • r9 arg5
  • (note: r12-r15, rbp, rbx はABIで呼び出し先で自由に使えるアドレスと定義されている)

少なすぎィッ!! ということでどうにかしてperCPUdataセクションへユーザ空間のRSPを保存しつつ,CR3を書き換えなければならない.

さて,ここでcpu_entry_area's trampoline(syscallはんどら)をCPUごとに一定間隔でマッピングしてみると……なんとハンドラのアドレスから相対的にperCPUdataセクションにアクセスすることができるのです!!

つまりGSレジスタベースでアクセスする必要がなくなった!!やったあ! 実際はRSPを介してCR3のスイッチングを行っているみたいですね. いやあ複雑なマッピングをしてまでセキュリティを向上させなければ行けないのは大変ですねえ.

まとめ

syscallハンドラがKPTI環境でCPUコアごとにマッピングされているのは,重要なデータをユーザ空間とおなじページにマッピングされないようにしながら,CR3(ページディレクトリのベースアドレス)をスイッチングする必要があったからです!! セキュリティのコストってすごいねえ……

告知

twitter復活しました!

twitter復活しましたのでフォローおねがいします.

twitter.com

チームHyperVillage

また,仮想化技術関連のお話ができるslack(Hypervillage)をつくりました.
私一人しかいなくて寂しいのでだれか入ってください.
あといろいろと相談とか乗ってください……

Slack

とゆーか,仮想化技術関連とかで盛り上がっているコミュニティがあったら誘ってください……. よろしくおねがいします.

Dentoo.LT #21に登壇します

2018/11/11(日)に電通大で最もハッキーなサークルMMAが主催するDentoo.LTに登壇します! ハイパーバイザーとかBareflankな話をしようと思ってるのでぜひきてくれよな!!

【ポエム】WEBフロントエンド入門してみた

こんにちは,morimolymolyです.
レポート提出間近なのになんとなくwebフロントエンド入門してみました.

早速今回作ったページはこちら.

https://github.com/morimolymoly/dead-github.iogithub.com


入門する前の僕にとってのwebフロントエンドといえば,サーバーサイドでレンダリングしたページのDOMをごにょるために,スパイス程度にjqueryを使う程度でした.しかしふと,javascriptでクールにSPAっぽいの作れるようになりたいよなあと思って1日digってみました.*1
結論から言うと,1日程度では大したことはできなかったのですが*2,最近のjavascript事情とかはそこそこ把握できたし,ES6がどうのとかwebpackは〜みたいな話にもちょっとはついていけるくらいの知識がつきました.

さて,早速今回digった技術をご紹介します.

  • Vue.js
  • ES6とか新しめのjs
  • webpack
  • babel

まあご紹介しますと言いましたが,技術的な話を書くつもりはありません.
ガーッとまとめるなら,javascriptがそこそこまともな言語(ES6~)になって,でもまともなjavascriptを動かすには互換性の問題があるのでトランスパイル(ES5とかほとんどのブラウザで実行できるものに変換)するのにbabelとかつかって,散らばったモジュールをくっつけるのにwebpackを使って,めちゃくちゃ便利なVueなどのフレームワークを使ってアプリをつくるって感じです.今回は直接babelやwebpackのコマンドを叩くことはあまりせず,vue-cliというVue.jsが誇る激強テンプレート作成コマンドを使って開発しました.Vue.jsが強すぎるしハマったので,フレームワークに関してはよほど変化がない限りこれを使っていくと思います.

「webフロントエンドとか新しいもの好きが色々飛びついてメンテナンス性を失ってブツブツ言うアレだろ?」みたいな認識だったのですが,一日digってみると,どうして彼らが新しい技術を追求し,さすらい続けるのかが理解できました.おそらくwebフロントエンドは片手間にちょいちょいつまむ程度になると思いますが,さらなる環境の進化をまったり楽しんでいこうと思いました.

おわり

*1:浅はかなり

*2:成果を見れば一瞬でわかる

OSXで自作OSのための環境を構築する

  • はじめに
  • ながれ
  • GMP&MPFR&libmpc&gccのインストール
  • binutilsのインストール
  • libiconvのインストール
  • i686向けgccをビルドする
  • objconvのインストール
  • GRUB2のインストール
  • bareboneを動かす
  • さいごに
  • 参考にしたサイト様
続きを読む

UARTで懲りずにルーターをハックした。あと間違えて殺した。2

  • はじめに
  • 下調べ
    • 怪しいポートを探す
    • UARTのピンアウトとボーレートを特定する
      • GNDを探す
      • VccとTxを探す
      • ボーレートを当てる
      • Rxを探す
  • UART Exploit
    • Linuxのシェルを取る
    • U-bootのシェルを取る
  • まとめ
  • 最後に
  • 参考
続きを読む

UARTでルーターのハッキングを試みてぶっ殺した話

  • はじめに
  • UARTとは?
  • 下調べ
    • ボード観察
    • UARTピンの見極め
    • ボーレートを当てる
  • Bus PirateでUARTを扱ってみる
    • bus pirateとボードを接続する
    • UART通信をしてみる
    • 通信結果
  • flash romに対するglitch attack
    • 攻撃方法
    • アドレス入力ポートへの攻撃
    • データI/Oに対する攻撃
    • めちゃくちゃにglitch!
  • 最後に
  • 参考
続きを読む

セキュリティ・キャンプ2017全国大会に参加したという話

  • はじめに
  • セキュリティ・キャンプ全国大会とは?
  • 参加するまで
  • 参加決定から当日までの過ごし方
  • キャンプ一日目
    • セキュリティ基礎
    • 特別講義1
    • 特別講義2
    • チューター紹介
  • キャンプ二日目
  • キャンプ3日目
    • B4.Embedded System Reverse Engineering 101
    • B5.信じて送り出した家庭用ルータがNetBSDにドハマリしてloginプロンプト を返してくるようになるわけがない
    • BoF
  • キャンプ4日目
    • D6. LSMから見た Linux カーネルのセキュリティ
    • A7.ファジング実習
  • キャンプ5日目
    • グループワーク
  • さいごに

はじめに

こんにちは。
morimolymolyです。
セキュリティ・キャンプに参加したのでその感想文を投下します。
技術的な内容というよりかはポエム寄りです。

続きを読む