README
¶
CyberHeap
Fast triage of Java heap dumps for pentesters. Extract credentials, API keys, tokens and private keys out of HPROF files in seconds — no JVM required, no Eclipse MAT, no manual string grepping.
____ _ _ ___ ____ ____ _ _ ____ ____ ___
| \_/ |__] |___ |__/ |__| |___ |__| |__]
|___ | |__] |___ | \ | | |___ | | |
HPROF secret scanner · by clevergod
CyberHeap combines two complementary passes over the dump:
- Regex scan — runs ~40 tuned patterns (AWS / GCP / Azure / GitHub / GitLab / Slack / Stripe / OpenAI / Anthropic / HuggingFace / Telegram / SendGrid / Mailgun / Twilio / Discord / JWT / basic auth / JDBC URLs / private keys / emails and more) across the raw bytes.
- Structured scan — parses the HPROF format, indexes every class and
instance, and pulls credentials directly out of Java objects (Spring
DataSourceProperties, Hikari, Druid, Mongo, Apache Shiro, …) even when the cleartext isn't near any recognisable marker.
Recognised tokens are decoded inline (basic-auth shows user:password,
JWTs show their iss/sub/exp/scope claims) so triage is a one-step
read instead of a copy-paste-decode loop.
Features
- Single statically-linked Go binary. No JVM, no runtime deps.
- Scans local files or remote URLs (e.g.
https://target/actuator/heapdump). - Severity-bucketed output: CRITICAL / HIGH / MEDIUM / LOW / INFO.
- Inline decoding of base64 basic-auth credentials and JWT claims.
- Smart masking for client reports — domain of an email stays visible, password body is starred, JWT header is kept so the algorithm is readable.
- Persistent JSON reports (
-o DIR) with merge-on-rerun semantics, tracking first/last seen per finding for retests. - Class-aware structured findings carry the Java FQN and heap object ID so each reported password is traceable to its origin in the dump.
- Pretty terminal output with aligned columns and framed banner;
--no-colorfor scripts and--format=jsonfor pipelines.
Install
Build from source
Requires Go 1.22+.
git clone https://github.com/cleverg0d/cyberheap.git
cd cyberheap
make build
./bin/cyberheap --help
The binary lands at ./bin/cyberheap. Copy it anywhere on your $PATH.
Cross-compile
GOOS=linux GOARCH=amd64 go build -o cyberheap-linux-amd64 ./cmd/cyberheap
GOOS=linux GOARCH=arm64 go build -o cyberheap-linux-arm64 ./cmd/cyberheap
GOOS=darwin GOARCH=arm64 go build -o cyberheap-darwin-arm64 ./cmd/cyberheap
GOOS=windows GOARCH=amd64 go build -o cyberheap-windows.exe ./cmd/cyberheap
Commands
scan — find secrets in a dump
cyberheap scan <file.hprof | http(s)://host/path/heapdump>
Runs both regex and structured passes by default and merges the findings.
| Flag | Purpose |
|---|---|
--severity a,b,c |
Filter by severity: critical,high,medium,low,info |
--category a,b |
Filter by category: datasource,cloud,scm,jwt,auth,credentials,connection-string,private-key,payment-saas,personal,shiro |
--mask |
Mask secret values (for client-facing evidence) |
-v, --verbose |
Show byte offsets and the raw encoded value for decoded findings |
--max-value N |
Truncate raw values longer than N chars (0 = unlimited) |
--min-count N |
Drop findings seen fewer than N times in the dump |
--utf16 |
Also scan a squeezed view for UTF-16LE strings (JDK 8 char[]) |
--no-regex |
Skip the regex pass (structured pass only — ~1 s on a 100 MiB dump) |
--no-spiders |
Skip the structured pass (regex only) |
--no-header-check |
Scan any file as raw bytes (for corrupted or non-standard dumps) |
-o, --output DIR |
Save/merge findings as JSON into DIR/<target>.json |
--format f |
pretty (default) or json |
--no-color |
Disable ANSI colors |
--no-banner |
Suppress the banner |
info — inspect dump metadata
cyberheap info <file.hprof> # header only (instant)
cyberheap info <file.hprof> --deep # parse everything + class stats
cyberheap info <file.hprof> --deep --top=30 # top N populated classes
cyberheap info <file.hprof> --json # machine-readable
--deep parses the full dump and reports:
- HPROF version, ID size, timestamp, header length
- String table size, class count, instance count, sub-record count
- Per-tag record breakdown (
STRING_IN_UTF8,LOAD_CLASS, …) - Top N classes by instance count (useful for fingerprinting the stack)
batch — scan many dumps, get one summary
cyberheap batch dumps/*.hprof -o ./reports --severity=critical,high
Each dump is scanned independently; per-file JSON reports land in -o DIR.
Stdout shows a compact "filename → counts" table and aggregate totals
across all files.
Useful flags:
--fail-on-critical— exit non-zero on any CRITICAL (CI-friendly)--mask,--utf16,--no-regex,--no-spiders,--severity,--category— same semantics asscan
scan --diff-against — retest deltas
cyberheap scan dump-v1.hprof -o ./reports # baseline
cyberheap scan dump-v2.hprof --diff-against=./reports/dump-v1.json
Each finding in the new scan is prefixed:
+— new since the baseline=— unchanged (same pattern + value, or same spider/class/object)-— closed (was in baseline, gone now — shown in a dedicated "CLOSED since previous scan" block)
Exactly what you want driving a retest against an earlier evidence pack.
decrypt — offline decryption of common enterprise ciphers
# Jasypt — ENC(...) strings from application.properties
cyberheap decrypt jasypt --password=MASTER --value='ENC(...)'
cyberheap decrypt jasypt --auto --try-password=A --try-password=B --value='ENC(...)'
cyberheap decrypt jasypt --from-dump=./heap.hprof --value='ENC(...)' # harvest master from heap
# Shiro RememberMe cookies
cyberheap decrypt shiro --key=<base64-16> --cookie=<base64> --mode=cbc
cyberheap decrypt shiro --auto --cookie=<base64> # tries well-known defaults
cyberheap decrypt shiro --auto --cookie=<base64> --try-key=<base64>
cyberheap decrypt shiro --from-dump=./heap.hprof --cookie=<base64> # harvest key from heap
# JWT decode + optional HMAC verify
cyberheap decrypt jwt --token=<jwt> # just decode
cyberheap decrypt jwt --token=<jwt> --secret=<HMAC-secret> # decode + verify
cyberheap decrypt jwt --auto --token=<jwt> --try-secret=A --try-secret=B
Supported:
- Jasypt —
PBEWithMD5AndDES,PBEWithMD5AndTripleDES,PBEWithSHA1AndDESede. Reverses theStandardPBEStringEncryptor/PooledPBEStringEncryptorpipeline (PBKDF1-MD5, DES/3DES-CBC, PKCS#5). - Shiro RememberMe — AES-128/192/256 in both CBC (Shiro ≤ 1.2.4, CVE-2016-4437) and GCM (newer Shiro).
--autotries every hard-coded default key shipped with the vulnerable releases plus any extras theshirospider harvested from the heap. - JWT — full verification for HS256/384/512 (HMAC), RS256/384/512
(RSA-PKCS1v15), PS256/384/512 (RSA-PSS), ES256/384/512 (ECDSA over
P-256/P-384/P-521), and EdDSA (Ed25519). Public keys come from PEM
files (
--public-key FILE) or inline PEM blobs (--public-key-pem). Thealg:noneattack is always rejected. Claims are returned even when the signature fails, which mirrors the triage flow.
All subcommands accept --json for machine-readable output.
strings — placeholder for Phase 4 string-dump mode.
Examples
Quick scan of a local file
All sample values below are synthetic — placeholder credentials for illustration purposes, not real secrets.
$ cyberheap scan ./example-heapdump
╔════════════════════════════════════════════════════╗
║ ____ _ _ ___ ____ ____ _ _ ____ ____ ___ ║
║ | \_/ |__] |___ |__/ |__| |___ |__| |__] ║
║ |___ | |__] |___ | \ | | |___ | | | ║
║ ║
║ v0.1.1 · HPROF secret scanner · by clevergod ║
╚════════════════════════════════════════════════════╝
file ./example-heapdump
format HPROF 1.0.2 size 120.0 MiB id-size 8 bytes header 31 bytes
timestamp 2026-01-15 10:00:00 UTC
elapsed 3.0s
summary 6 findings (regex 3 + structured 3) ● 2 CRITICAL ● 1 HIGH ● 2 MEDIUM ● 1 LOW ● 0 INFO
───────────────────────────────────── HIGH ─────────────────────────────────────
jwt-token: iss=https://auth.example.com/realms/demo sub=user-0001 exp=2026-02-01 12:00:00Z
──────────────────────────────────── MEDIUM ────────────────────────────────────
basic-auth: demo-client:demo-secret
basic-auth: svc-account:ExamplePass-01 (x3)
───────────────────────────────────── LOW ──────────────────────────────────────
email-address: user@example.com (x2)
━━━━━━━━━━━━━━━━━━━━━━━━━━━ STRUCTURED (class-aware) ━━━━━━━━━━━━━━━━━━━━━━━━━━━
[CRITICAL] Spring DataSourceProperties
class: org.springframework.boot.autoconfigure.jdbc.DataSourceProperties object: 0xabcdef01
driverClassName org.postgresql.Driver
url jdbc:postgresql://db.internal/demo
username demo_app
password ExamplePassword-01
[HIGH] Spring PropertySource: applicationConfig: [classpath:/application.yml]
class: org.springframework.boot.env.OriginTrackedMapPropertySource object: 0xabcdef02
app.jwt.secret ExampleJwtSecret-01
spring.datasource.password ExamplePassword-01
Scan a live target over HTTP
cyberheap scan https://target.example.com/actuator/heapdump -o ./reports
The dump is streamed to a temp file, scanned, then deleted. The filename of
the saved report is derived from the hostname (target.example.com.json).
Generate a masked report for a client
cyberheap scan ./heapdump --mask -o ./client-reports/ --format=pretty > report.txt
Emails keep their domain, passwords are starred, JWT headers remain readable
so the algorithm (HS256, RS256) is documented in the finding.
Re-scan after remediation
cyberheap scan ./heapdump-after-fix -o ./reports
# saved ./reports/heapdump-after-fix.json (+0 new, 8 updated, 3 closed)
Findings are merged by (pattern, value) for regex hits and
(spider, class, object_id) for structured hits. Each record tracks
first_seen / last_seen / runs so you can diff retests.
Structured pass only (fast credential hunt)
cyberheap scan ./heapdump --no-regex --severity=critical,high
On a 100 MiB dump this runs in ~700 ms and surfaces DataSource/Shiro credentials without the regex noise.
Real-world validation: CVE-2016-4437 (Apache Shiro deserialization)
End-to-end walk-through against the public
vulhub/shiro/CVE-2016-4437
lab. This is the canonical Shiro rememberMe RCE — a baked-in default
AES key on Shiro ≤ 1.2.4 lets an attacker forge a serialized Java object
inside the cookie and trigger deserialization gadgets on the server.
1. Start the vulnerable app and capture a heap dump
git clone --depth 1 https://github.com/vulhub/vulhub /tmp/vulhub
cd /tmp/vulhub/shiro/CVE-2016-4437
docker compose up -d
# The image ships JRE only — use jattach (single static binary) for the dump.
curl -fsSL https://github.com/jattach/jattach/releases/download/v2.2/jattach-linux-x64.tgz \
| tar xz -C /tmp
docker cp /tmp/jattach cve-2016-4437-web-1:/tmp/jattach
docker exec cve-2016-4437-web-1 \
sh -c '/tmp/jattach $(pgrep -f java) dumpheap /tmp/shiro.hprof'
docker cp cve-2016-4437-web-1:/tmp/shiro.hprof ./shiro-ctf.hprof
2. Scan — cipher key falls out in ~1.4 s
$ cyberheap scan ./shiro-ctf.hprof --no-regex
summary 1 findings (regex 0 + structured 1) ● 1 CRITICAL
━━━━━━━━━━━━━━━━━━━━━━━━━━━ STRUCTURED (class-aware) ━━━━━━━━━━━━━━━━━━━━━━━━━━━
[CRITICAL] Shiro RememberMe cipher key (RCE primitive)
class: org.apache.shiro.web.mgt.CookieRememberMeManager object: 0x85fdfd70
encryptionCipherKey (base64) kPH+bIxk5D2deZiIxcaaaA==
encryptionCipherKey (hex) 90f1fe6c8c64e43d9d799888c5c69a68
encryptionCipherKey (bytes) 16 (AES-128)
algorithm AES
That base64 is the historical default key for every Shiro ≤ 1.2.4 install. CyberHeap ships it in the well-known-keys list as well, so even an empty heap dump would allow auto-recovery when paired with a live cookie.
3. Decrypt a captured rememberMe cookie in one command
Grab the cookie from a valid login (browser or curl), then:
$ cyberheap decrypt shiro \
--from-dump=./shiro-ctf.hprof \
--cookie='jTFMjfyULCJ8kT4u0xvz...long-base64...Lsn0w=='
harvested 1 Shiro cipher-key(s) from ./shiro-ctf.hprof
mode: CBC
key: kPH+bIxk5D2deZiIxcaaaA==
length: 377 bytes
java-serialized: true
hex preview: aced000573720032 6f72672e6170616368652e736869726f2e7375626a6563742e53696d706c655072696e636970616c436f6c6c656374696f6e ...
aced 0005— JavaObjectOutputStreammagic. The payload really is a deserialized object, exactly the primitive CVE-2016-4437 relies on.- Classname
org.apache.shiro.subject.SimplePrincipalCollection— this is the admin session principal, confirming the decryption matched.
The same --from-dump flag works for decrypt jasypt against Jasypt
master-password candidates harvested by the jasypt spider, so
ENC(...) values in application.properties can be reversed without
the analyst ever copy-pasting the master password.
Total time from docker compose up to serialized payload in hand: ~10
seconds. This used to be a 15-30 minute manual chain (locate the key in
Shiro sources, decode and split IV, run a Java one-liner) — CyberHeap
collapses it to two commands.
Output formats
pretty (default)
Human-readable, ANSI-coloured, grouped by severity. The STRUCTURED section
at the bottom carries class/object context for each class-aware finding.
json
Single JSON object with separate findings (regex) and structured_findings
(spider) arrays. Consume it with jq:
cyberheap scan ./heapdump --format=json | jq '.findings[] | select(.severity=="CRITICAL")'
markdown
GitHub-flavoured Markdown assessment report with severity summary table, per-pattern findings table, per-class structured tables, and CWE references — suitable to paste into a client deliverable:
cyberheap scan ./heapdump --format=markdown --mask > report.md
Under --mask all secret values are obfuscated while the class/key names
stay readable, making the output safe to share.
Persisted reports (-o DIR)
Pretty output to stdout and a merge-aware JSON report on disk. Each run
updates the report with new findings, keeps per-finding first_seen/last_seen
timestamps and a runs counter. Ideal for driving retests and evidence
packs.
What gets detected
Regex patterns (~40)
Credentials and assignment syntaxes:
- JDBC URLs with inline passwords
password = ...,pwd: ...,passphrase = ...- Spring Boot
spring.datasource.password= - RSA / EC / OPENSSH / ENCRYPTED / PGP private key headers
- URL-embedded credentials (
scheme://user:pass@host) - Jasypt
ENC(...)values
Cloud providers:
- AWS access key IDs, secret keys, MWS tokens
- Azure storage connection strings
- Google OAuth client IDs, access tokens, API keys
- Google service-account JSON markers
SCM and SaaS:
- GitHub classic + fine-grained PATs, OAuth tokens
- GitLab PATs, npm tokens
- Slack bot/user/webhook tokens, Discord webhook URLs, Telegram invites
- Stripe keys, Square tokens, Artifactory API tokens
AI and messaging:
- OpenAI keys (with the
T3BlbkFJinternal marker) - Anthropic keys (
sk-ant-*) - HuggingFace, SendGrid, Mailgun, Twilio, Cloudinary
Session and transport:
- Basic auth and Bearer tokens (with JWT claim decoding)
- JWTs with compact claim summary
- Redis, Mongo, AMQP, SMTP URLs
- HashiCorp Vault tokens
Personal / contextual:
- Email addresses (with Java stack trace noise filtered out)
Structured (class-aware) spiders
- datasource — Spring
DataSourceProperties, Hikari (HikariConfig,HikariDataSource), Druid, Apache DBCP2, Tomcat JDBC, Weblogic, MongoDBMongoClientURIandConnectionString. - shiro —
CookieRememberMeManagerandAbstractRememberMeManager(with all subclasses), extractscipherKey,encryptionCipherKey,decryptionCipherKeyplus the cipher algorithm name. The key is reported as base64, hex, and byte length for direct hand-off tocyberheap decrypt shiro. - propertysource — Spring
MapPropertySource,OriginTrackedMapPropertySource,PropertiesPropertySource,SystemEnvironmentPropertySourceand their subclasses. Walks the internalHashMap/LinkedHashMap/ConcurrentHashMap/Hashtable/Properties/SingletonMap/UnmodifiableMapbacking store and surfaces every key whose name matches a secret-carrying pattern (password,clientsecret,accesskey,jwt.secret, …). - redis — Spring Data Redis (
RedisStandaloneConfiguration,RedisSentinelConfiguration,RedisClusterConfiguration), Jedis (JedisShardInfo), Lettuce (RedisURIwithchar[]password). - env — JVM process environment (
java.lang.ProcessEnvironmentstatic fields). Filters by secret-shaped keys, drops trivial values (paths, booleans, bare integers) to avoid noise. - jasypt —
StandardPBEStringEncryptor,PooledPBEStringEncryptor, configuration classes and Spring Boot wrappers. Extracts the master password (char[] / byte[] / String) sodecrypt jasypt --autocan unlock everyENC(...)string in the dump. - cloudcreds — AWS SDK v1 & v2 (
BasicAWSCredentials,AwsBasicCredentials, session variants), Aliyun OSS & Core SDK credential classes, Alibaba credentials-java, Huawei OBS, Tencent COS. ReturnsaccessKey+secretKey+ optionalsessionToken.
A finding is only reported when genuine credential evidence is present
(a non-null password, or a URL with a scheme). Findings are deduplicated
across target classes so inheritance hierarchies (e.g. HikariDataSource
extending HikariConfig) don't double-report the same object.
HPROF compatibility
- Format 1.0.1 and 1.0.2
- 4-byte and 8-byte identifiers (auto-detected from the header)
- JDK 8
java.lang.String(char[] value) and JDK 9+ (byte[] value+coderfor LATIN1 / UTF-16LE) - Modified UTF-8 decoding for
STRING_IN_UTF8records HEAP_DUMPandHEAP_DUMP_SEGMENTcontainers- Sub-records:
CLASS_DUMP,INSTANCE_DUMP,OBJECT_ARRAY_DUMP,PRIMITIVE_ARRAY_DUMP, allROOT_*variants - Dotted field paths with automatic traversal of object references
Limitations
- Current release reads the full dump into memory. Tested on dumps up to 2 GiB; dumps above that benefit from mmap/streaming, planned for a future release.
- On actively-running pools, some Hikari pool objects null out their
password field after
pool.start(). The real credential is kept in the correspondingDataSourcePropertiesobject, which CyberHeap reports correctly; the dedup logic prevents double-counting. - Jasypt 3.x modes based on PBKDF2 + HMAC-SHA (
PBEWITHHMACSHA512ANDAES_256) are not yet supported — Jasypt 1.x defaults are covered.
Ethics
CyberHeap is intended for authorised penetration tests, red team engagements, bug bounty programs, and incident response on systems you own or have explicit permission to test. Using it against third-party systems without authorisation is illegal in most jurisdictions.
Exposed /actuator/heapdump endpoints are a well-documented class of
misconfiguration; CyberHeap automates triage once access is legitimate.
Prior art
CyberHeap's class-aware extraction model is inspired by JDumpSpider (Apache 2.0). The HPROF parser and spider framework are written from scratch in Go with a significantly expanded set of targets, inline decoders, severity modelling, masking and persistence layers.
License
MIT — see LICENSE.
Author
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
cyberheap
command
|
|
|
internal
|
|
|
decode
Package decode provides best-effort inline decoding of secrets that scanner.Match surfaces as raw strings.
|
Package decode provides best-effort inline decoding of secrets that scanner.Match surfaces as raw strings. |
|
decrypt
Package decrypt implements the cleartext-recovery primitives CyberHeap surfaces via the "decrypt" subcommand:
|
Package decrypt implements the cleartext-recovery primitives CyberHeap surfaces via the "decrypt" subcommand: |
|
heap
Package heap builds a navigable index over a parsed HPROF file.
|
Package heap builds a navigable index over a parsed HPROF file. |
|
spiders
Package spiders contains class-aware extractors that run over a parsed HPROF index.
|
Package spiders contains class-aware extractors that run over a parsed HPROF index. |