更新: 359 个文件 - 2026-03-16 23:30:01
这个提交包含在:
@@ -30,6 +30,16 @@ import time
|
||||
from typing import Dict, Optional, Tuple, List
|
||||
import sys
|
||||
import re
|
||||
from pathlib import Path
|
||||
import contextlib
|
||||
import io
|
||||
|
||||
|
||||
SCRIPTS_DIR = Path(__file__).resolve().parents[2] / "scripts"
|
||||
if str(SCRIPTS_DIR) not in sys.path:
|
||||
sys.path.insert(0, str(SCRIPTS_DIR))
|
||||
|
||||
from tool_contract import add_common_args, emit_report, ensure_authorized, make_report, write_evidence # noqa: E402
|
||||
|
||||
|
||||
class Colors:
|
||||
@@ -307,57 +317,33 @@ def main():
|
||||
parser.add_argument("--kid-injection", default="/dev/null", help="KID 注入值")
|
||||
parser.add_argument("--analyze", action="store_true", help="分析 JWT")
|
||||
parser.add_argument("-v", "--verbose", action="store_true", help="详细输出")
|
||||
add_common_args(parser, include_network=False)
|
||||
|
||||
args = parser.parse_args()
|
||||
ensure_authorized(args, parser)
|
||||
|
||||
cracker = JWTCracker()
|
||||
|
||||
print(f"\n{Colors.BOLD}{'=' * 60}{Colors.END}")
|
||||
print(f"{Colors.BOLD}JWT Cracker & Analyzer{Colors.END}")
|
||||
print(f"{Colors.BOLD}{'=' * 60}{Colors.END}\n")
|
||||
|
||||
try:
|
||||
header, payload, _ = cracker.decode(args.token)
|
||||
|
||||
print(f"{Colors.CYAN}Header:{Colors.END}")
|
||||
print(f" {json.dumps(header, indent=2)}")
|
||||
print(f"\n{Colors.CYAN}Payload:{Colors.END}")
|
||||
print(f" {json.dumps(payload, indent=2)}")
|
||||
if args.format == "text":
|
||||
print(f"{Colors.CYAN}Header:{Colors.END}")
|
||||
print(f" {json.dumps(header, indent=2)}")
|
||||
print(f"\n{Colors.CYAN}Payload:{Colors.END}")
|
||||
print(f" {json.dumps(payload, indent=2)}")
|
||||
|
||||
except Exception as e:
|
||||
cracker.print_result("ERROR", str(e))
|
||||
sys.exit(1)
|
||||
|
||||
if args.analyze:
|
||||
print(f"\n{Colors.CYAN}Analysis:{Colors.END}")
|
||||
analysis = cracker.analyze(args.token)
|
||||
|
||||
if "issues" in analysis:
|
||||
for issue in analysis["issues"]:
|
||||
color = (
|
||||
Colors.RED
|
||||
if issue["severity"] == "HIGH"
|
||||
else Colors.YELLOW
|
||||
if issue["severity"] == "MEDIUM"
|
||||
else Colors.BLUE
|
||||
)
|
||||
print(f" {color}[{issue['severity']}]{Colors.END} {issue['issue']}")
|
||||
print(f" {issue['description']}")
|
||||
|
||||
if args.attack:
|
||||
print(f"\n{Colors.CYAN}Attack: {args.attack}{Colors.END}")
|
||||
|
||||
if args.attack == "none":
|
||||
forged = cracker.attack_none_algorithm(args.token)
|
||||
cracker.print_result("SUCCESS", f"Forged Token (none): {forged}")
|
||||
|
||||
elif args.attack == "kid":
|
||||
forged = cracker.attack_kid_injection(args.token, args.kid_injection)
|
||||
cracker.print_result("SUCCESS", f"Forged Token (kid): {forged}")
|
||||
|
||||
elif args.attack == "confusion":
|
||||
forged = cracker.attack_algorithm_confusion(args.token)
|
||||
cracker.print_result("INFO", "需要公钥来利用算法混淆攻击")
|
||||
analysis = cracker.analyze(args.token) if args.analyze else {"issues": []}
|
||||
forged = None
|
||||
if args.attack == "none":
|
||||
forged = cracker.attack_none_algorithm(args.token)
|
||||
elif args.attack == "kid":
|
||||
forged = cracker.attack_kid_injection(args.token, args.kid_injection)
|
||||
elif args.attack == "confusion":
|
||||
forged = cracker.attack_algorithm_confusion(args.token)
|
||||
|
||||
wordlist = None
|
||||
if args.wordlist:
|
||||
@@ -368,24 +354,62 @@ def main():
|
||||
cracker.print_result("ERROR", f"字典文件不存在: {args.wordlist}")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"\n{Colors.CYAN}Cracking...{Colors.END}")
|
||||
stdout_buffer = io.StringIO()
|
||||
capture = contextlib.redirect_stdout(stdout_buffer) if args.format != "text" else contextlib.nullcontext()
|
||||
start = time.time()
|
||||
secret = cracker.crack(args.token, wordlist, args.verbose)
|
||||
with capture:
|
||||
secret = cracker.crack(args.token, wordlist, args.verbose and args.format == "text")
|
||||
elapsed = time.time() - start
|
||||
|
||||
if secret:
|
||||
cracker.print_result("FOUND", f"密钥破解成功: {secret}")
|
||||
cracker.print_result("INFO", f"耗时: {elapsed:.2f}s")
|
||||
|
||||
forged = cracker.encode(header, payload, secret, header.get("alg", "HS256"))
|
||||
cracker.print_result("SUCCESS", f"可以伪造任意 Token")
|
||||
else:
|
||||
cracker.print_result(
|
||||
"WARNING",
|
||||
f"未能破解密钥 (尝试了 {len(wordlist) if wordlist else len(cracker.common_secrets)} 个)",
|
||||
)
|
||||
|
||||
print(f"\n{Colors.BOLD}{'=' * 60}{Colors.END}\n")
|
||||
evidence_refs = []
|
||||
ref = write_evidence(
|
||||
args,
|
||||
"jwt-analysis.json",
|
||||
{
|
||||
"header": header,
|
||||
"payload": payload,
|
||||
"analysis": analysis,
|
||||
"attack": args.attack,
|
||||
"secret_found": bool(secret),
|
||||
"captured_stdout": stdout_buffer.getvalue()[-1000:],
|
||||
},
|
||||
)
|
||||
if ref:
|
||||
evidence_refs.append(ref)
|
||||
status = "verified" if secret or forged else "needs-review"
|
||||
severity = "high" if secret else "medium" if analysis.get("issues") else "info"
|
||||
report = make_report(
|
||||
tool="jwt-cracker",
|
||||
mode="jwt-analysis-and-weak-secret-test",
|
||||
target="jwt-token",
|
||||
status=status,
|
||||
severity=severity,
|
||||
payload_or_probe={
|
||||
"header": header,
|
||||
"payload_keys": sorted(payload.keys()),
|
||||
"issues": analysis.get("issues", []),
|
||||
"attack": args.attack,
|
||||
"secret_found": bool(secret),
|
||||
},
|
||||
request_summary={"wordlist": args.wordlist or "builtin-common", "elapsed_seconds": round(elapsed, 2)},
|
||||
evidence_refs=evidence_refs,
|
||||
destructive_risk="low",
|
||||
args=args,
|
||||
extra={"forged_token_present": bool(forged)},
|
||||
)
|
||||
text_lines = [
|
||||
"=" * 60,
|
||||
"JWT Cracker & Analyzer",
|
||||
"=" * 60,
|
||||
f"Token Alg: {header.get('alg', 'unknown')}",
|
||||
f"Issues: {len(analysis.get('issues', []))}",
|
||||
f"Secret Found: {'yes' if secret else 'no'}",
|
||||
f"Status: {status}",
|
||||
]
|
||||
emit_report(args, report, text_lines)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
在新工单中引用
屏蔽一个用户