セキュリティキャンプ2018応募課題晒し

まえがき

セキュリティキャンプ全国2018の選考を通過したので,応募課題を晒そうと思います.一応注記しておきますが,私はプログラミングは大学に入ってから学び始め,低レベル開発経験などもゼロ.セキュリティ関係を学び始めたのは去年12月で,プロでもなんでもありません.そんなことはどうでもよく,今回は”プロじゃなくても受かる可能性はある”とのメッセージを込めて,恥ずかしながら応募課題を晒すことにしました.
応募したきっかけは,今年度が始まり,B2ながら大学の研究室にお邪魔させていただいたときの先輩方(プロ)のお言葉です.僕も(おそらく例に違わず)応募課題の問題の難易度や,晒された応募課題の質と量に圧倒され,当初は来年受けるつもりでいました.しかし,先輩方は「まあ受けてみなよ」「受かるでしょ」と背中を押してくださいました.応募課題に受かることはスタートラインでしかないのですが,感謝してもしきれません!m(_ _)m

選択したトラックについて

トラックA(脆弱性マルウェア解析トラック)を選択しました.なぜなら僕にとってはこの応募課題が一番易しい(あくまで比較的に)と思ったからです.具体的には,他のトラックの課題は「~の課題を述べよ」のように抽象的であり,ある程度経験がないと論述は厳しいと思った一方,トラックAの問題は割りと具体的で,経験は要求されず,書きやすく感じました.

課題晒しにあたって

原文はやや個人情報を含んでいるので,そのような部分は[---CUT---]と書いています.また,かなり支離滅裂な部分があると思います.ご容赦ください!

まずは問題を

#脆弱性・マルウェア解析トラック
設問ひとつあたり4,096文字以内で答えてください。
また正解がある設問については、"正解しているかどうか"より
"正解にたどり着くまでのプロセスや熱意"を重要視しています。
答えにたどり着くまでの試行錯誤や自分なりの工夫等を書いて、精一杯アピールしてください。

##問1
あなたが今まで作ってきたソフトウェアにはどのようなものがありますか?
また、それらはどんな言語やライブラリを使って作ったのか、
どこにこだわって作ったのか、たくさん自慢してください。

##問2
今までに解析したことのあるソフトウェアやハードウェアにはどのようなものがありますか?また、その解析目的や解析方法、工夫した点があればそれらも教えてください。

##問3
Twitterアカウント、Github、ブログ、公開している資料等がありましたら、URL等を記載してください。

##問4
あなたが今年のセキュリティ・キャンプで受講したいと思っている講義は何ですか?(複数可)
またそれらを受講したい理由を教えてください。

##問5
自分が最も技術的に興味を持った脆弱性をひとつ挙げ、技術的詳細(脆弱性の原因、攻略方法、対策方法など)
について分かったことや思ったこと、調査の過程で工夫したこと等を報告してください。
その際、書籍やウェブサイトを調べて分かったことはその情報源を明記し、
自分が独自に気付いたことや思ったことはそれと分かる形で報告してください。
また脆弱性の攻略方法を試す際は、他者に迷惑を掛けないように万全の措置をとってください。

##問6
以下にDebian 8.10(amd64)上で動作するプログラムchal00のmain関数の逆アセンブル結果があります
("objdump -d chal00"の出力結果のうち、main関数の箇所を抜粋しました)。
このプログラムは、コマンドライン引数としてある特定の文字列を指定されたときのみ実行結果が0となり、
それ以外の場合は実行結果が1となります。
この実行結果が0となる特定の文字列を探し、その文字列を得るまでに考えたことや試したこと、
使ったツール、抱いた感想等について詳細に報告してください。
```
00000000004003c0 <main>:
  4003c0:       48 b8 0f 0e 0d 0b 00    movabs $0xc0601000b0d0e0f,%rax
  4003c7:       01 06 0c
  4003ca:       83 ff 02                cmp    $0x2,%edi
  4003cd:       48 89 44 24 f0          mov    %rax,-0x10(%rsp)
  4003d2:       48 b8 04 05 08 0a 02    movabs $0x70903020a080504,%rax
  4003d9:       03 09 07
  4003dc:       48 89 44 24 f8          mov    %rax,-0x8(%rsp)
  4003e1:       b8 01 00 00 00          mov    $0x1,%eax
  4003e6:       75 59                   jne    400441 <main+0x81>
  4003e8:       48 8b 56 08             mov    0x8(%rsi),%rdx
  4003ec:       31 c0                   xor    %eax,%eax
  4003ee:       89 c1                   mov    %eax,%ecx
  4003f0:       48 ff c0                inc    %rax
  4003f3:       80 7c 02 ff 00          cmpb   $0x0,-0x1(%rdx,%rax,1)
  4003f8:       75 f4                   jne    4003ee <main+0x2e>
  4003fa:       83 f9 08                cmp    $0x8,%ecx
  4003fd:       b8 01 00 00 00          mov    $0x1,%eax
  400402:       75 3d                   jne    400441 <main+0x81>
  400404:       48 8b 32                mov    (%rdx),%rsi
  400407:       31 c0                   xor    %eax,%eax
  400409:       30 c9                   xor    %cl,%cl
  40040b:       48 89 f2                mov    %rsi,%rdx
  40040e:       48 d3 ea                shr    %cl,%rdx
  400411:       83 e2 0f                and    $0xf,%edx
  400414:       0f b6 54 14 f0          movzbl -0x10(%rsp,%rdx,1),%edx
  400419:       48 d3 e2                shl    %cl,%rdx
  40041c:       83 c1 04                add    $0x4,%ecx
  40041f:       48 09 d0                or     %rdx,%rax
  400422:       83 f9 40                cmp    $0x40,%ecx
  400425:       75 e4                   jne    40040b <main+0x4b>
  400427:       48 33 05 92 ff ff ff    xor    -0x6e(%rip),%rax        # 4003c0 <main>
  40042e:       48 ba 85 03 0e 67 b3    movabs $0x600967b3670e0385,%rdx
  400435:       67 09 60
  400438:       48 39 d0                cmp    %rdx,%rax
  40043b:       0f 95 c0                setne  %al
  40043e:       0f b6 c0                movzbl %al,%eax
  400441:       c3                      retq
```

##問7
chal01.zipには、USBメモリのディスクイメージusb_disk_modified.img が入っています。
そのイメージファイルをマウントしようとしましたが、エラーが出てマウントできませんでした。
どうやらどこかが壊れているようです。
ファイルシステムの構造を調べ、壊れていると思った全ての箇所を修正し、
マウントツール(*1)でマウントできるようにした後、
ファイルシステム上に存在するPDFファイルを救い出してください。
* (*1)マウントツールの例
    * mount (for linux)
    * hdiutil (for macos)
    * FTK Imager(for Windows)

また、以下の設問の答えとその解き方を、ファイルシステムの調査結果を用いて説明してください。
答えのみの場合は正解とはみなしません。修正箇所が複数ある場合は、全て列挙してください。
1. このイメージの元のファイルシステムの名称は?
2. どのオフセットを何のバイト列に置き換えて直したのか?壊れていると判断した理由は?
3. そのオフセットの位置は何という名称の構造体の何というメンバがあるはずなのか?
4. PDFファイルのハッシュ値をすべてsha256で答えよ。

回答

グレー背景となっている部分が回答です.

  • 問1
①連立方程式計算機
大学ではじめにC言語を習ったので、それを実際に使ってプログラミングに慣れたいと思ったのと、線形代数学の授業や課題で連立方程式を扱うことが多く、私自身ケアレスミスがかなり多いので、自分で解いた問題の連立方程式を使う部分について、きちんと正解しているかを確認するために作った。
基本構造としては、ユーザがテキストファイルに解きたい連立方程式の各未知数の係数を書き込み、そのファイルから係数部分を読み取って、線形代数の講義で習った通り基本変形を行うことでプログラムが解き、たとえば
{A=3
{B=1
というふうにそれぞれの解をコマンドプロンプト上で表示させる。係数入力用テキストファイルには、もしA+2B=3、(-1/8)A-4B+C=0という3元一次連立方程式を解かせたいとすると、以下のように行列に似た形式で入力する。
1 2 0 3
-1/8 -4 1 0
ただし、線形代数の教科書(中高でもそうである)では整数でない解についてはもちろん分数で記載していたので、解は整数または分数で出力させるようにしてある。そのために、少々複雑になってしまったが、プログラムでは入力した係数のすべてを分数に直してから基本変形を行うようにした。
また、式の個数に対し未知数の個数が多ければ、どれかを適当な実数とおかなければならないので、それも実装した。係数行列の行、列数をそれぞれL,R(L<R)とすると、列交換を行わない限り第L+1,L+2,…,Rの未知数を任意の実数とおく。
また、答えが見やすいように表示方法にも次のような工夫をした。
・先頭の係数については、+符号をつけない。
・分数は括弧()でくくり、符号+-は括弧の前に置く。
・未知数を定数と置いた時には、最後に「(ただし、a,b,…は任意の定数)」と表示する。
係数入力ファイルにも、入力失敗を検知するためのしくみを設けた。
・未知数の個数がマクロ定義による制限を超えている。
・数字と、マイナスやスラッシュ以外の文字が入力されている。
・正しく行列形式になっていない。たとえば1行目には9個係数が書かれているのに2行目には8個しか書かれていないなど。
・小数点、スラッシュが1係数あたり2個以上入力されている。
・マイナスが係数の先頭にない。
・EOFの行を除き、まったく入力されていない行がある。
・分数の分母、分子として小数が入力されている。
最近は放置気味であったが、応募を機にまだ腕はないながらもGitHubアカウントを作ったので、今後は複数の連立方程式を一気に解いてくれるようにしたり、計算途中で分母分子が大きくなったときにオーバーフローが起きてまったく違う答えを出力しないように分数から浮動小数点数形式に直して計算したり、解けない問題をもっと探して修正をしたり、入力制限やエラー検出を増やしたりして、クオリティを上げて、後輩に使わせてあげたりしたい。

②自分で脆弱性を持つWebサイトを作り、攻撃と防御を経験する。
昨年12月から、CTFや様々な本、[---CUT---]で攻撃手法やアセンブリ言語を勉強していたものの、セキュリティに関しては初心者である今の状態から、あとたった数年でセキュリティのプロとして新しい技術を瞬時に吸収できるようになったり、ディジタルフォレンジックや脆弱性の検証などができるまでに成長するまでの道筋がどうしてもつかめなかったので、今年4月にセキュリティに詳しい先輩方がいらっしゃるという研究室に訪問させていただき、相談をしたところ、「攻撃の練習をCTFでするだけではなく、実際に脆弱性のあるWebサービスを作ってみて、どうやってWebページが作られているのかだとか、攻撃を防ぐ機構を実際に体験して知ったほうがいいよ」との助言をいただいたので、localhost上で練習用サイトを構築しはじめた。具体的には、色んな脆弱性を持つ、例えばデータベースに登録された名前を入力して、パスワードを確認するなどの簡素なWebページを作って、実際にSQLインジェクションなどが成功するのかを検証している。それをやってみようと思ったときに、どうやってlocalhost上でサーバを立てるのか、どこにhtmlやphpのファイルを置くのかといった初歩的なこともわからず、そしてphpでどのようにMySQLなどのデータベース管理システムに接続するのかも、実際にCTFでSQLインジェクションをやったことがあるにもかかわらず知らなかったのがとても衝撃的だった。少し調べると、xamppという、ApacheやMySQLなどが含まれるlocal環境でのWebサービス開発用ソフトウェアがあったので、それを利用した。今のところ、SQLに関連した脆弱性では「' or '1'='1」などの入力を利用したパスワード認証の突破や、Union-based-SQL-injectionができるページを作った。これからは、htmlspecialchars()関数などによるエスケープを施してみたり、バインド機構を実装して、その防御効果を検証していく。また、XSSについても、クッキーの抜き出しや、UTF-7と認識させてエスケープ処理を回避するような基本的なXSSを試してみたい。また、Pythonでソケットプログラミングを習い始めたので、Time-based-blind-SQL-injectionなどのブラインドSQLインジェクションを試しながら、ソケットプログラミングの練習をしようと思っている。

書いてて苦しかったです.他の応募課題ではみんな自作OSとかコンパイラ作成とか高度なことをやっているので,経験ではまったく及ばないからです.来年受けようと思ったのも,これが理由です.アレを見た方はご存知かと思いますが,僕はプログラミング言語のベストプラクティスさえできていません.ただ,雑魚なりに色々書きました.しかしこんな僕でも受かったということは,他の応募課題がすごすぎて打ちひしがれている方々にも十分チャンスがあるということなのです!

  • 問2
これまでに何かを自主的に解析したことはなかったが、2月にNeverLAN CTFというCTFで、hashcatというオープンソースソフトウェアを用いて、WPA2-PSKの4wayハンドシェイクのパケットから辞書攻撃によってパスワードクラックをするという問題があったことを思い出したので、家で10年ほど前から使用しているWi-Fiアクセスポイントの暗号化方式は推奨されるものとなっているか、私でもできるほど簡単にクラックできてしまわないかをこの機会に調べてみることにした。
通信の暗号化方式は、アクセスポイント(親機)に書いてあり、128bitのWEPであった。
WEP暗号化方式は脆弱であり、104bitのWEP鍵については2008年に神戸大と広島大が鍵の解読を瞬時に行えることを発表しており、WEPからWPA/WPA2へと暗号化方式を移行するように推奨している。
今回は、http://ra66itblog.hateblo.jp/entry/2016/05/29/203015を参考に、Kali-linux 2018.1を利用して自宅Wi-Fiアクセスポイントのパスワードの解析を試みた。
まず、iwconfigコマンドを打ち、動作している無線LANインターフェイスの情報を確認した。しかし、次のように表示されてしまった。
eth0     no wireless extensions.
lo       no wireless extensions.
この表示について調べてみたところ、原因は、私のPCのKali LinuxはVirtualbox仮想化ソフトを利用して、ホストOSのWindows 10 proにゲストOSとしてインストールされているが、ネットワークアダプタの設定がブリッジ接続であるために、無線LANアダプタが認識されていないからであるということがわかった。
そこで、この表示について検索してみると、compat-wireless-2010-06-26というドライバを用いるとwlan0インターフェイスを作成できることが分かった。それをダウンロードしたのち、当該ディレクトリ内でmake loadコマンドを打つと、無線LANインターフェイスとしてwlan0が認識された。
wlan0が認識されたところで、次にairmon-ng start wlan0と打ち、解析に用いるパケットを収集できるようにwlan0をモニタモードに変更した。iwconfigコマンドで、wlan0についてMode:Monitorとなっており、モニタモードになっていることが確認できた。また、無線LANインタフェース名がwlan0monと変化した。
次に、airodump-ng wlan0monコマンドによって、近くにあるアクセスポイントを探し、それぞれのBSSIDや暗号化方式、通信の強度などを表示させようとした。ここで、本来なら自宅アクセスポイントを含む最低1つのアクセスポイントの情報が表示されるはずであったが、何も表示されなかった。
同じ症状にあっている人はいないかgoogle検索をすると、動画(https://www.youtube.com/watch?v=jpzZe7PO8TY)を見つけた。そのコメント欄にはいくつかの対処法が書かれていた。
(1)VirtualBoxの機能拡張パッケージをダウンロードし、USB規格の設定をUSB2.0またはUSB3.0に変更する。
(2)Kali Linuxにデフォルトで搭載されているwifiteという別のアプリでWEP解析を行う。
しかしながら、(1)は効果がなく、(2)ではairodump-ngと同様に、アクセスポイントが発見されなかったため解析することができなかった。
もっと調べると、compat-wireless-2010-06-26を使ってwlan0インターフェイスを認識させて同じ手順を踏み、airodump-ngコマンドを実行して同じ問題に当たったという動画が見つかり、その次の動画(https://www.youtube.com/watch?v=ZdKUd1XaMYg)では、次の3つの対処法が紹介されていた。
(1)VirtualBoxなどの仮想環境上ではなく、デュアルブート環境でKali Linuxを動かす。
(2)外部から無線LANアダプタを取り付ける。
(3)マシンのアップデートを行う。
というように、今までのcompat-wirelessを利用した方法ではWEP解析はできないことがわかった。
この問題にとりかかったときには課題提出締め切りまであと3日というところだったので、これまでに書いたこと程度のことしか行えなかったが、今度はUSBの無線LANアダプタを使用することで、多くのWEP解析方法紹介サイトと同様な環境下で自宅のアクセスポイントのWEP解析ができるのかどうかを試そうと思っている。また、aircrack-ngでは飛び交っているパケットを傍受してWEP解析を行うことから、Wiresharkで得たパケットのみで解析する方法もあると思うので、もう少し調べたい。

これが一番苦し紛れです.この問題の回答を書き始めたのが締め切り3日前だったのはしょうがないとして,問題がプロ諸氏にとってはお茶の子さいさいなもの,それでいてメインの部分の前でつまづき,終了.これはいかに熱意重視とはいえ,落選決定だと思いました.恥ずかしい……!!!次の問題に移りましょうか()

  • 問3
[Twitterアカウント]
[---CUT---]

[GitHubアカウント]
https://github.com/Ciruela-Xcyba17her

[はてなブログ]
http://verliezer93764.hatenablog.jp/

Githubアカウントを急遽作ったんですが,今まで述べましたように,無知が露呈しています.しかし,他のアカウントも含め,自分がどういう人物か知ってもらうため,正直に載せました.この行為が吉と出たか大凶と出たか,僕にはわかりません.

  • 問4
【A1~3】「インシデントレスポンスで攻撃者を追いかけろ」
今回セキュリティキャンプに応募した理由の一つとして、セキュリティ技術者が現場でどのような技術を用いて、どのようにして人々の情報を守り、犯人の追跡をするのかを知ることによって、「初心者である今からセキュリティ技術者として人々を守る力を持つようになるまでの道筋・勉強方法がつかめない」という最大の悩みを解決し、これからの大学・大学院生活における学び方に生かしていきたいというものがある。この講義では、マルウェア解析、デジタルフォレンジック、ログ解析などを仮想的であってもインシデントが発生した環境で手を動かして経験することができる絶好の機会なので、セキュリティ技術者となるためには自分は何をどのように学ぶべきなのか、今後の学び方を身に着けたい。

【A4】「IN-DEPTH STATIC MALWARE ANALYSIS」
今年2月からアセンブリ言語を自習しはじめ、CTFでも、何回か変数と数値を比較しているだけであったり、コードがせいぜい30行程度であるようなプログラムに関する易しめの問題をだんだん解けるようになってきた。しかしながら、メガバイト単位の大きいプログラムであったり、数~数十キロバイトのサイズでも、何も出力せず、ウィンドウも表示しないようなプログラムには手が出ない状況である。この講義では、攻撃者が作るプログラムの静的解析を経験することによって、長いマルウェアコードの中からどのようにして素早く核となるコードを見つけ、その挙動を見抜くか、そしてもともと攻撃者の使うコードにはどのような関数が使われているのかを学び、その後CTFなどで解析の練習を重ね、静的解析の力をつけていくきっかけにしたい。

【E5】「Linuxカーネル脆弱性入門」
問5では、EternalBlueについて調べたが、途中で「非ページプールでのバッファオーバーフローを起こす」ことについて、まず非ページプールとはどこのことなのか、またそれはどのように使われるのかについてかなりの時間を費やし、最後にはなんとか脆弱性攻撃のおおまかな原因や方法はつかめたが、実際の攻撃のイメージがわかなかった。でも、攻撃者たちはそのようなコンピュータの仕組みについてはもちろん熟知しており、その知識をもとにマルウェアを作るはずである。よって、守る側の自分もLinuxカーネルなどについての知識についてはつけておかなければならないと強く感じたので、低いレイヤを対象とした脆弱性についてもすぐに理解できるように、この講義を通してカーネルの基本や学び方を身に着けたいと思っている。

【C6】「パターン認識とセキュリティ」
去年に人工知能分野が最近急速な成長を遂げつつあることを知り、それを何らかの形でセキュリティ分野に応用すれば、増え続けるサイバー攻撃に対する防御側の不足を補えるのでは?という思いはあった。日経bpの「すべてわかるセキュリティ大全2018」という本では、マカフィー社のMcafee Endpoint Threat Defenceやシマンテック社のSymantec Endpoint Protectionなどのウイルス対策ソフトでは機械学習アルゴリズムを導入していることが紹介されており、[---CUT---]、機械学習には非常に興味を持っている。この応募課題が終わり次第機械学習に入門していこうと思っているので、この講義に参加することで、機械学習をどのようにしてセキュリティ技術に応用できるのかを知りたい。

【A7】「本当にわかるSpectreとMeltdown」
私は、CPUはただ演算命令に従ってALUを動かし、演算をこなすだけのものと思っていたので、セキュリティには無関係であると思っており、CPUに脆弱性が見つかったときには、どうして命令をこなすだけのCPUが脆弱性を持つのかと思い、非常に驚いた。今回、この講義を利用して、今まで学んでこなかったCPUにはただ演算をこなすだけではなく、どんな役割や能力を持っているのかを知り、SpectreとMeltdownはどういうところにつけこんで攻撃をするのか、どんな被害が想定されるのかを確実に理解して、コンピュータの設計に関する知識として備えたいと思っている。

ただ思っていることを書きました.特に,僕自身セキュキャンは今後何をどう学んでいくかの参考にしたかったので,その講義をセキュキャン修了後にどのように生かしていきたいのかを書きました.

  • 問5
調べた脆弱性:EternalBlue
脆弱性を調べる上で、はじめはそれを概観しながらわからない単語を調べることで確実に理解を進めていき、次々と深くまで、単語の意味を調べつつ、進めていくことにした。
EternalBlueのCVE識別番号は、CVE-2017-0143である。まずは、EternalBlueは具体的にどのような脆弱性なのかを概観した。[1]のCurrent Descriptionという見出しによると、この脆弱性は、「遠隔地にいる攻撃者が、作成した(特殊な)パケットを送ることで、WindowsのSMBv1(SMB version 1.0)サーバ上で任意のコードを実行(RCE)できる」というものである。[2]によると、SMBとは、Server Message Blockの略称で、Windows OSで構成されたLAN上でファイルを共有するサービスの基礎となっているプロトコルである。OSI参照モデルではL6,7に位置しており、クライアント-サーバ型の構造をしている。
次に、そのSMBv1サーバ上でどのようにして任意コードを実行させるのか、より詳しい記述を探した。その結果、[3]が見つかった。かなり詳しく書かれていたので、このページの理解を目指すことにした。[3]によると、EternalBlueはSMBv1の3つのバグを使う。

(1)"Wrong Casting Bug"
SMBプロトコル上で、OS2 FEAをNT FEAに変換する過程にバグがある。
Os2FeaList構造体にSizeOfListInBytesというメンバがあり、SrvOs2FeaListSizeToNTという関数は何バイトのOS2 FEAをNT FEAに変換するかを計算しこの変数にその結果を格納する。その計算の過程は本来DWORD型の範囲で行われるべきであるが、上位2バイトを考えずにWORD型の範囲で行われてしまうことがある。一方、返り値としてもNT FEAのサイズは計算されるが、それは正しく行われる。よって、[3]にあげられている例のように、変換前にSizeOfListInBytesの値が0x10000であった場合に、変換すべきOS2FEAのサイズが0x1ff5d(バグがなければ0xff5d)となっており、これは変換後の本来のNTFEAのサイズよりも大きくなる。これはout-of-bounds write(領域外メモリへの書き込み)を発生させる。

(2)"Wrong Parsing Function Bug"
SMBプロトコルを通じてファイルを送信する際、SMB_COM_TRANSACTION2とSMB_COM_NT_TRANSACTという、データに関するサブコマンド群を使う。単一のパケットとして送信するにはデータの送信量が膨大すぎるときは、それぞれのサブコマンドの末尾に_SECONDARYを付けたSECONDARYサブコマンドを使用する。ここで、SMB_COM_TRANSACTION2とSMB_COM_NT_TRANSACT、そしてそれぞれのSECONDARYサブコマンドではどちらも送信できるデータ量の上限をヘッダに定義するが、前者ではWORD型とする一方、後者ではDWORD型で設定することとなっており、差異がある。また、最初にSMB_COM_NT_TRANSACTコマンド(データ量の最大値はDWORD型で設定)を打ち、その後SMB_COM_TRANSACTION2_SECONDARYコマンド(データ量の最大値はWORD型)で送信することも可能である。これにより、送られてきたOS2 FEAのサイズをWORD型で計算させて領域外メモリへの書き込みを行わせる(1)のバグを起こすことができる。

(3)"Non-paged Pool Allocation Bug"
SMBクライアントがサーバに対し認証され、接続およびセッションを確立するにはSMB_COM_SESSION_SETUP_ANDXリクエストを送る必要がある。SMB_COM_SESSION_SETUP_ANDXリクエストのフォーマットには2種類あり、どちらの認証でもリクエストはSMB_ParametersとSMB_Dataという2つの部分に分かれる。また、サーバ側では、それらが揃っているのかをSrvValidateSmbという関数が確認する。その中で、BlockingSessionSetupAndXという関数がSMB_Dataのデータを抽出するが、クライアントとサーバの間でリクエストフォーマットの認識の齟齬が生じることがあり、ByteCountというSMB_Dataのサイズを示すパラメータを違ったオフセットから読んでしまい、ByteCountを本来と異なる値で読んでしまう(本来より大きく計算される)ことがある。それによって、小さなパケットでも、非ページプールでの大きい領域の確保が行われることがある。

RCEを行うためには、非ページプール上で今までに述べた3つのバグを利用し、MDL Overwriteなどを行う。MDLとは、I/O命令に使用され、実メモリ上では一群のデータはバラバラに配置されることに対し、連続的にデータが配置される仮想メモリからポインタで実メモリを指すための構造である。SRVNETバッファのヘッダにはMDL構造があり、その前の領域からバッファオーバーフローを起こすことができれば、MDLを書き換えることができる。そのためには、まず複数のSRVNETバッファを生成させるカーネルグルーミングや、領域の解放を行い、バグ(3)で確保させておいた実質の空き領域とSRVNETバッファを連続させる。ただし、SRVNETバッファはSMBv2を実現するsrvnet.sysによって、SRVバッファはSMBv1を実現するsrv.sysによって生成され、カーネルグルーミングによって発生させるSRVNETバッファについては(1)と(2)のバグは起こさせない。また、HAL’s Heapという、アドレス位置が不変なカーネル構造体があり、それはWindows 8以前のバージョンで実行権限を持つ。つまり、MDL上のポインタをHAL's Heapに書き換えれば、HAL’s Heapに任意のシェルコードを書き込ませてRCEを実行することができる。そのためには、(1)と(2)のバグによって、バグ(3)により確保された空き領域上でSRVバッファによるオーバーフローを起こさせ、MDLを書き換える。

次に、私の環境でこの脆弱性のエクスプロイトが実験できるかどうか調べたところ、多くのWebページや動画がMetasploitという脆弱性のペネトレーションテストソフトを用いていた。また、EternalBlueに加え、バックドアを設置するDoublePulsarを併用したMetasploit用コードがGitHub上で公開されており(https://github.com/ElevenPaths/EternalBlue-Doublepulsar-Metasploit)、それを利用した方法もあった。今回はそれを利用してみる。方法は、まずGithubなどから入手した。Metasploitを起動した後、use xxx(xxxは攻撃用コード)として攻撃方法を設定して、setコマンドで攻撃に要する各パラメータを設定する。exploitコマンドで攻撃を開始し、テストターゲットとしてhttps://developer.microsoft.com/en-us/microsoft-edge/tools/vms/からWindows7の仮想環境"IE8 on win7"をダウンロードして使用し、攻撃はKali Linux 2018.1を使用した。また、間違いを防ぐため、攻撃の際には、このPC全体が外部アクセスポイントに接続しないようにした。また、Windows7の方ではホストオンリーアダプタの設定を行い、IPアドレスを固定した。
しかし、私の環境では攻撃はうまくいかなかった。auxiliary/scanner/smb/smb_ms17_010を用いると、脆弱性を攻撃できそうなときにそれを意味する表示がされるが、その表示もされなかった。どこかで設定が間違っているか、"IE8 on win7"がすでに対策をしていると考えられるが、今回は断念した。この脆弱性の対策としては、Microsoft社の出しているパッチを適用すればよい。EternalBlueについて調べてみて、正直にいってその原理は私にとってとても難しいものであった(カーネルに関する知識を持っていなかった)。しかしながら、実際の攻撃はMetasploitによって簡単に行えてしまう。これは、知識のない者でも簡単に攻撃を行えることを意味している。サイバー犯罪を抑えるためには、OSSも含め、攻撃能力をもつツールの公開を制限したり、ダークウェブ上での流通にも大いに警戒する必要があると思った。

参照ページ:
[1] https://nvd.nist.gov/vuln/detail/CVE-2017-0143「CVE-2017-0143 Detail」
[2] http://www.atmarkit.co.jp/ait/articles/0410/29/news103.html「第20回 ファイル共有プロトコルSMB/CIFS(その1) (1/3)」
[3] https://research.checkpoint.com/eternalblue-everything-know/#bugb「EternalBlue ? Everything There Is To Know」

SpectreとMeltdownが気になっていたが,脳内で話題だったEternalBlueについて調べました.これがまた難しかった.SMBって何?サブコマンドって??非ページプールって??と,いちいち出てくる用語がわからない状態.
出てくる単語の多くがわからなくて,ググりまくりました.しかし実際に設定等をいじることはなかったので,今でもはっきりは理解できていないと思います.しかもソースが読み慣れてない英語で,ますます理解に時間がかかりました.いちいち調べていった結果,文字数はどんどん膨れ上がり,気づいたら6000文字オーバー.削りまくった結果,特に最後のあたりは詰め込んだ感じになっています.ただ,最後まで一定の理解はできるように頑張りました
この問題の影響で,いわゆる低レイヤと呼ばれる部分に深い興味を持っています!30日でOSを作るアレ,やってみたい.

  • 問6
アドレスAより割り当てられている命令について、「Aの行の命令」と書くことにする。まずは、ジャンプ命令の場所を抜き出した。すると400e36と4003f8と400402と400425の行にジャンプ命令があり、そのうち4003e6と400402の行のものは、main関数終了に向かってジャンプを行わせるものとなっており、その前には必ずraxに1を格納するmov命令がある。一般に、関数の返り値を格納するためにはraxが使用される。それらのジャンプを行わなかった場合、40043bの行にsetneという命令があるが、これは400438の行のcmp命令でゼロフラグが立つ、つまり等しいという結果が出ればalに0を格納し、そうでなければ1を格納するものである。したがって、400438の行までプログラムが進行し、raxとrdxの値が等しいという条件を満たせばプログラムは0を返すということがわかる。
ここからは、命令を1つ1つ確認していくことにしたが、早速4003caの行のcmp命令に困惑した。ここでediの値が数値2と等しくなければ先ほど述べた通り返り値は1になってしまう。ところが、このcmp命令以前には、ediに操作をする命令が書かれていない。そこで、問題文が「プログラムが0を返すようなコマンドライン引数を見つけよ」という趣旨だったことを参考に、Ubuntu 16.04 LTS上で、コマンドライン引数を指定したときどんな逆アセンブルコードが生成されるのか調べるために、以下のサンプルCプログラム(test.c)を作った。
//プログラム先頭
#include<stdio.h>
int main(int argc,char *argv[]){
	return 0;
}
//プログラム終了
与えられた逆アセンブル結果ではediと2の比較はほぼ先頭で行われていたので、gdbを用いて逆アセンブルし、main関数の前でrdiの値を調べた。その結果、rdiの値は、設定するコマンドライン引数の個数によって変化し、常にrdiの値が(コマンドライン引数の個数)-1となった。よって、rdiの値は、実行用文字列「./test」を含んだすべてのコマンドライン引数の個数で、main関数に入る前に代入されると考えた(なお、https://simple-teq.net/assembly/assm-14.htmlにその旨が書いてあったことを後に見つけた)。
したがって、このcmp命令は指定したコマンドライン引数の個数が1個であることを確認する働きをしていると結論した。
さて、アセンブリコードに戻る。4003c0の行から4003dcの行にかけては、raxを用いてスタック構造にに順次値をコピーしている。Debianではバイトオーダとしてリトルエンディアンを採用しているので、ここでは(espの値-16)のアドレスから(espの値-1)のアドレスの16バイトにかけて0f,0e,0d,…,03,09,07という数値が代入されていったことになる。
4003eeの行から4003f8の行にかけては、4003f3 の行のcmpb命令、4003f8の行のjne命令によるループ構造になっている。
ループの中で、(rdx-1+rax*1)というアドレスの計算が行われているが、そのベースとなっているrdx、すなわち4003e8の行でrdxに代入したrsi+8というアドレスは何を意味するかが分からなかった。rsi:ソースインデックスレジスタは、一般的にはベースとなるアドレスからのオフセットを表したり、文字列操作のための文字列のコピー元アドレスを格納したりする役目を持つが、この「文字列」というものが、問題にある「コマンドライン引数に指定する文字列」にあたるのではないかと考えたので、再びUbuntu 16.04 LTSで、rsiの指すアドレスとコマンドライン引数のアドレスの関係を調べるためのサンプルプログラムを書いた。
//プログラム先頭
#include<stdio.h>
int main(int argc,char *argv[]){
	printf(“%p\n”,argv[1]);
	printf(“%p\n”,argv[2]);
	printf(“%p\n”,argv[3]);
}
//プログラム終了
gdbを用いて、set args ABCD EFGH IJKLコマンドで3つのコマンドライン引数を指定し、最初のprintfの前でプログラムを止め、x/20x $rsiコマンドによって確認した。その結果は以下の通りである。
0x7fffffffe27e: 0x41 0x42 0x43 0x44 0x00 0x45 0x46 0x47
0x7fffffffe286: 0x48 0x00 0x49 0x4a 0x4b 0x4c 0x00 0x58
0x7fffffffe28e: 0x44 0x47 0x5f 0x56
この結果から、rsiはコマンドライン引数のアドレスを指すために使われ、各コマンドライン引数の終端には、0x00(文字列終端を示す)が格納されると考えた。すなわち与えられた逆アセンブルコードの4003f3の行では、(rdx-1+rax*1)のrdxにrsi+8を代入して(rsi+7+rax*1)というアドレスの中身(1バイト)と0を比較しているが、これはコマンドライン引数について、その終端であるかどうかをraxをカウントとして用いることで1バイトずつ確認していると考えられる。また、rsi+8というオフセット付きのアドレス指定となっているのが気になったが、rsiの指すアドレス以降には、”./test”というような実行用文字列が格納されることがわかった。よって、4003eeの行から4003f8の行までのループは、後のjne命令によりecxの値が8でなければmain関数が1を返してしまうことを考え、コマンドライン引数の長さが8バイトであることを確認する働きをしていると結論した。
400404の行ではrsiに先ほどの8バイトのコマンドライン引数を代入している。
400407の行ではeaxの値を0に、400409の行でカウントの値を0にしている。40040bの行から400425の行はループとなっており、400419の行でecxに4を加え、400422の行でecxの値と0x40を比較し、等しければループを抜けることから、ecxがカウンタの働きをし、このループは計16回繰り返される。
X回目のループの中で何を行っているのかをまとめる。まず、40040bの行から400411の行にかけてはコマンドライン引数を16進で表した場合の下から第X桁目を抽出する。それを仮にAとおく(0<=A<=15)。次に、初めに定義したスタック構造について、(rsp-16+A)のアドレスに位置する1バイトを取り出す。取り出した値を仮にBとおく。このとき、rsp-0x10というアドレスからrsp-1というアドレスまで16バイトにかけて0f 0e 0d 0b 00 01 06 0c 04 05 08 0a 02 03 09 07という並びで定数が格納されていることに注意する。すなわち、Bの取りうる値は0,1,2,…,e,fの,1桁の16進数である。400419の行から40041fの行にかけては、raxの下位から第X桁に対し、Bとor演算を行わせている。ここで、ループに入る前にはraxの値は0であるから、ループを経るごとにraxの値が下位から1桁ずつ決定されることがわかる。
ループの先を読んでみる。400427の行では、raxにrip-0x6e(mainの先頭を指す)のアドレスの内容とraxの内容とのXOR演算の結果を格納する。ここで、rip-0x6eというアドレスを参照した場合、単純に考えればニーモニックで命令を示す48とb8も含まれてしまい、仮にchal00がCなど高級言語で書かれているとしたら、はたして命令を示す機械語を利用するプログラムを書けるのかが疑問だった。しかし、あくまでも処理はアセンブリ言語における命令に厳格に従うと考え、リトルエンディアンを考慮して、rip-0x6eを参照した値は、0x01000b0d0e0fb848であるとした。400438の行ではraxとrdxの比較を行い、40043bでは最初に述べたsetne命令を実行する。ここでalが0に設定されればよいので、rax=rdx=0x600967b3670e0385となるようにしなければならない。したがって、ループ直後にはraxの値が0x01000b0d0e0fb848 XOR 0x600967b3670e0385 = 0x61096cbe6901bbcdとなればよい。
すなわち、ループ内で行っていることは考えると、コマンドライン引数として指定するのは、16進表記で、上位から1桁目が[rsp-0x10+0x6]=6より6、2桁目が[rsp-0x10+0x5]=1より5、3桁目が[rsp-0x10+0x4]=0より4、4桁目が[rsp-0x10+0xe]=9よりe、…と求めてゆき、0x654e67316e453372である。これは1バイトの文字列”eNg1nE3r”をASCII Codeで変換したものである。したがって、コマンドライン引数を”eNg1nE3r”とした場合のみchal00は数値0を返す。

とりあえず1行ずつ読んでいき,適宜命令についてググりました.また,資料が見つからなければ,簡単に実験をして近い内容をつかもうとしました.難しそうでも思い切って取り組んでみると,この問題はできるかもしれません.スタックポインタの動きについてはあっているのかわかりません.

  • 問7
Windows 10 Proからこの問題を解いた。

設問1.
拡張子偽装などはないとする。拡張子が.imgであるので、これはMacによって作られたディスクイメージファイルである。なので、まずはMac OSがサポートするファイルシステムを調べた。しかしながらそれだけでは何の情報もつかめなかったので、とりあえず与えられたイメージディスクファイルをバイナリエディタStirlingで開き、マジックナンバーや、先頭・末尾に近い部分から情報を得ようとした。その結果、ファイルシステム一覧のWebページにも載っていたNTFSというASCII文字列が0x6fバイト目からみられた。NTFSは、Windows NT系で多く使われるファイルシステムである。Macでは、"NTFS-3G for Mac OS X"や、“Paragon NTFS for Mac OS X"というドライバをインストールすれば読み書きが可能になる。
また、文字列"NTFS"が見つかった、イメージファイルの先頭部分をFTK Imagerは"MBR"と識別していた。問題で与えられたのは"usbディスクイメージ"であるから、この部分はMBRではなくてPBRなのではないか、また、FTK Imager上でMBRとなっているのは、ここの内容が壊れているために正しく読めていないからではないかと考えた。したがって、まずはPBRの修正を試みることにした。
ファイルシステムは、NTFSであると予想した。しかしながら、PBRに偶然にNTFSという特定の4バイトの文字列が入るとは考えにくいものの、決定的な証拠はわからなかった。

設問2と設問3.
設問1を通して、まずはPBRが破損しているかどうかを確かめることにした。PBRはBIOS Parameter Block(BPB), Extended BPB, Boot Code, End of sector Marker(内容はブートセクタ終了を示す0xAA55)の4つの部分で構成されている。各構造体やメンバの名称はhttp://ultradefrag.sourceforge.net/doc/man/ntfs/ntfs_layout.h.htmlを参照した。以降、修復場所を示していく。

1.オフセット0x3から始まる4バイト
これは、"NTFS_BOOT_SECTOR"構造体の"oem_id"というサイズが8バイトのメンバで、ファイルシステムがNTFSであることを示すために必要である。本来"NTFS"という4バイトのASCII文字列と0x20が4バイト分の合計8バイトが入るが、"NTFS"となる部分がすべて0になってしまっている。したがって、"NTFS"を表す"4E 54 46 53"に書き直す。その後の4バイトにはすべて0x20が入るが、これはそのまま存在している。

2.オフセット0xbから始まる2バイト
これは、"BIOS_PARAMETER_BLOCK"構造体の、"bytes_per_sector"というサイズが2バイトのメンバで、1セクタあたりのバイト数を示す。HDDのセクタサイズには、512バイトと、4Kバイト(4096バイト)のものが存在する。後者はHDDの大容量化によって2010年ごろから現れ、アドバンスド・フォーマットという規格が2009年11月に定められている。ただ、NTFSのセクタサイズは512バイトが一般的であるため、512(0x200)バイトと考えた。ところで、オフセット0x28から始まる8バイトは"BIOS_PARAMETER_BLOCK"構造体の"number_of_sectors"というメンバであり、パーティションのセクタの数を示す。したがって、"bytes_per_sector"と"number_of_sectors"の値を掛け合わせると、このディスクイメージファイルのサイズと等しくなるはずである。エクスプローラによると、このディスクイメージファイルのサイズは104857600バイトである。一方、bytes_per_sector=0x200(バイト)、number_of_sectors=0x31fffであることから、掛け合わせた値は104857088となり、もう512(バイト)を加えれば104857600となる。このことから、bytes_per_sectorの値は0x200でよく、また、PBRはセクタとしては数えられないものと考えられる。したがって、bytes_per_sector=0x200(バイト)、number_of_sectors=0x31fffが正しい値と考え、"00 00"になっているところを、リトルエンディアンを考慮し"00 02"と書き直した。

3.オフセット0xdから始まる1バイト
これは、"BIOS_PARAMETER_BLOCK"構造体の、"sectors_per_cluster"というサイズが1バイトのメンバで、1クラスタあたりのセクタの数を示す。その値は0x08であることが多い。ここで、オフセット0x30には8バイトのサイズを持つ"NTFS_BOOT_SECTOR"構造体の"mft_lcn"というメンバがあり、これはMFTの割り当てられている最初のクラスタの番号を示す。すなわち、バイナリエディタ上で、MFTの内容が始まるアドレスと、(bytes_per_sector)*(sectors_per_cluster)*(mft_lcn)という積で求められる値は等しくなるはずである。結果としては、MFTの先頭アドレスは0x2155000であった一方、積の値は0x200*0x8*0x2155=0x2155000であり、等しくなった。よって、bytes_per_sector=0x0200,sectors_per_cluster=0x08,mft_lcn=0x2155が正しい値であると考えられる。
与えられたディスクイメージファイルでは、0x08という値が入るところが、0x00になっている。したがって、"08"と書き直す。

ここまで修正して、FTK Imagerがファイル構造を認識するようになった。また、[root]ディレクトリに、iir_vol37.pdfとiir_vol38.pdfというpdfファイルがあった。しかし、前者はサイズなどの情報や内容が認識されていたのに、後者はファイル名とファイルタイプが"$I30 INDX Entry"となっていること以外に何も情報がなく、内容を確認できなかった。しかし、ファイル情報がこれだけ確認されているということは、ファイル情報を管理するMFTに何か問題があると思ったので、iir_vol38.pdfのMFT情報を見てみることにした。その場所をバイナリエディタで探すためには、ファイル名を用いた。MFTにおいてはファイル名の情報はUnicodeで掲載されているため、検索することができる。そこで、MFT情報を見つけ、さらに次のように修正した。

4.バイナリエディタ上でアドレス0x2165000から始まる4バイト
これは、MFT_RECORD構造体の、magicという4バイトのメンバで、シグネチャとも呼ばれる。その値は、ASCII文字で"BAAD"か"FILE"のいずれかである。通常は"FILE"であるが、ファイルエントリが壊れている場合、"BAAD"となる。さて、"iir_vol38.pdf"のMFT情報が先ほど見つかったが、その先頭は、"BAAD"となっており、壊れていることになっている。ここではiir_vol38.pdfの内容を見たいので、"FILE"に直す。
直した結果、FTK Imager上でiir_vol37.pdfと同様に、サイズや内容などの情報が表示されるようになった。

ここで、FTKからディスクイメージのマウントを試みると、マウントは成功した。しかしながら、Windows10に搭載されているマウント機能ではなぜかマウントできず、疑問が残った。

設問4.
HashSumというハッシュ値計算ソフトウェアを用いて、以上の手順で手に入れたiir_vol37.pdfとiir_vol38.pdfのSHA-256ハッシュ値を求めた。その結果、iir_vol37.pdfのハッシュ値は5d62f82532d62645cd67b312546a599d90d15ea09c5ee5dbd61c5d90fc945ad3であり、iir_vol38.pdfのハッシュ値は86eb9f1fe34087f709a659b051d4d06930078c72d420683f2bf89455f31eb55fであった。ここで、私はこの問題がほかの問題よりも単純に感じたため、念のためにpdfファイルにもディスクイメージファイルが壊れたことによる内容の変化が起きなかったのかについて考えることにした。SHA-256は弱衝突耐性を持ち、1ビットでも内容が狂えば、本来導出されるべきハッシュ値と大きく異なってしまう。iir_vol37.pdfやiir_vol38.pdfをgoogle検索したところ、同じ内容のpdfファイルがあり、公開されていた。そこで、それらをダウンロードし、HashSumでハッシュ値を調べたところ、先ほどの2つのハッシュ値と同じ値であった。したがって、救い出した2つのファイルは公開されているものと内容が同一であり、また、公開されているこの2つのファイルがダウンロードされてから私が救い出すまでに改ざんがなく、ディスクイメージファイルの故障の影響を受けなかったと考えられる。すなわち、救い出したpdfファイルのSHA-256ハッシュ値は上にあげた通りで正しいと考えられる。

ファイルシステムの名称がわかったら比較的早く進みました.この問題も,少しずつ調べながら進めていけば,解けるかもしれません(この問題を見たとき,正直に言うと自分は「ファイルシステムって何??」という状態でした笑).ただ,なぜWindows10が搭載するマウント機能でマウントできなかったのかが謎で,それを考えるのに時間を無駄に使ってしまいました.

最後に

セキュキャンが自分の丈にあっているのかは正直なところとても不安です.でも,そんな態度では決して最前線には立てないので,恐れずに挑戦して,セキュキャンをきっかけに,自分自身で考えて成長できるようになりたいと思います!