Case: akira-ransomware
| Field | Value |
|---|---|
| SHA-256 | 101bd45ab673fd6547386dff4339f1649e61b9f5c318260d365f766fbdc2d3c9 |
| MD5 | 427eba9f6ff2cd312c72770f13e279c1 |
| File name | 101bd45ab673fd6547386dff4339f1649e61b9f5c318260d365f766fbdc2d3c9.exe |
| Type / Arch | PE32+ / x86-64, MSVC 14.37 C++20, GUI subsystem |
| Size | 1,043,180 bytes mapped (on-disk size not recorded) |
| Compiled | 2023-12-28 14:49:57 UTC (PE timestamp) |
| Family (assessed) | Akira ransomware (self-branded: .akira, akira_readme.txt, Akira onion URLs) |
| Verdict | Malicious |
| Confidence | High: our own static analysis plus five controlled live runs; no external reporting used |
| Analyst | Claude Code + Ghidra MCP + x64dbg MCP |
| Opened | 2026-10-11 |
| Closed | 2026-10-11 |
Executive summary
The sample is a self-contained 64-bit C++ ransomware that brands itself Akira. It:
- takes its targets from a path (
--encryption_path=), a share list, or a sweep of the first five drive letters; - deletes Volume Shadow Copies through PowerShell launched hidden by WMI;
- force-closes processes that lock files using Restart Manager, sparing 20 system processes;
- encrypts files in place with ChaCha8 and KCipher-2, in one of three modes chosen by extension and size (Full, Spot = first p%, Part = striped);
- seals each file’s keys with an embedded RSA-4096 public key in a 512-byte footer, and renames
the file to
.akira.
It has no C2, persistence or exfiltration. Its key weakness: every per-file key comes from a fresh Yarrow-256 seeded only with the uptime counter (QPC × 100 ns), so keys can be brute-forced without the RSA key. Our decryptor does this on the sample’s real output in all three modes.
Capability summary
| Capability | Present | Evidence |
|---|---|---|
| Persistence | No | none in code |
| Privilege escalation | No | none in code |
| Defense evasion / anti-analysis | Minimal | per-string mod-127 obfuscation of 3 string groups; hidden WMI-spawned PowerShell; no anti-debug (01-triage/) |
| Credential access | No | – |
| Discovery | Yes | drive and share classification, process list (WTSEnumerateProcessesW, RmGetList), CPU count (03-static/02-…) |
| C2 / exfiltration | No | no sockets; the data-theft claim in the note is not carried out by this file |
| Payload delivery | No | single PE |
| Destruction / ransom | Yes | shadow-copy deletion, ChaCha8 + KCipher-2 + RSA-4096 encryption, .akira, note in every folder; live-confirmed (03-static/05-…, 04-dynamic/) |
Key IOCs
Full set in 02-strings-iocs/01-strings-iocs.md and artifacts/iocs.csv.
- Onions:
akiral2iz6a7qgd3ayp3l6yub7xx2uep76idk3u2kollpj5z3z636bad.onion(blog),akiralkzxzq2dsrzsrvbr2xgbbu2wgsmxryd4csgfameg52n7efvr2id.onion(chat); code6188-EA-ABYW-YGRG - Files:
.akira,.arika(532-byte journal),akira_readme.txt,Log-DD-MM-YYYY-HH-MM-SS.txt - Command line:
powershell.exe -Command "Get-WmiObject Win32_Shadowcopy | Remove-WmiObject"(parentWmiPrvSE.exe) - RSA-4096 public key DER SHA-256
a40a55f2600b9199181bc6c0778e64b2e75145c4e6f715da6d23a72407539aaa
ATT&CK techniques
See 05-detections/01-detections.md: T1486, T1490, T1489, T1047, T1059.001, T1564.003, T1027,
T1057, T1083, T1135, T1082.
Recovery
06-decryptor/akira_decrypt.py (Python 3.8+, standard library only; optional numpy + numba engine akira_fast.py):
decryptwith--keys,--seedsor the operator’s--private-key;--journalfor interrupted files.recoverbrute-forces the uptime seeds from a known header.benchmarkestimates the cost of a search window.- Blind recovery of real files (run 5,
04-dynamic/06-run5-blind-real-files.md): 4 of 4. A PDF, a DOCX and two PNGs were encrypted by the live sample with no seed capture. Each was recovered from its mtime, the live QPC offset and type-derived known plaintext, and decrypted byte-identical to the original SHA-256. Three took 28–31 min each in pure Python (20–26 s each on the numba engine). The fourth had a seed-gap outlier (26,718 ticks) and was found by a second pass withrecover --anchor iv2 --gap 8001:30000(21 min; 16 s on numba). - Independent blind runs (run 6 on this build, b2run1 on build 2): 6 of 7 in-scope files hash-exact each. In both, the
PDF over 262 KB came back with only its KCipher-2 chunks decrypted, because the zeros/text stage-2 check rejected the
right ChaCha8 key. Stage-2 PDF fallback (2026-10-14):
recovernow retries a.pdfwith two PDF-specific tests (exact object headers from the xref; continuing a page-content stream’s deflate). Both PDFs are recovered hash-exact with the default command line (06-decryptor/stage2-investigation/,artifacts/stage2-integrated-recover-2026-10-14.txt), so 7 of 7 in-scope files on each build are recoverable. The blind results stay 6 of 7. - Validated on the sample’s real output: selftest 8/8, real outputs 15/15 (including seed recovery,
two native-run files, an mtime-based recover the run-5 outlier PNG via
--anchor iv2, and both stage-1 engines), round trip 29/29, PDF stage-2 fallback 3/3 (06-decryptor/test_pdf_stage2.py, synthetic). Results inartifacts/test-run-2026-10-12.txtandartifacts/test-run-2026-10-14.txt. - Hash evidence for the five earlier
.akirafixtures (SHA-256 before/after, negative controls):artifacts/decryption-evidence-existing-2026-10-12.txt. - Demo on the real
.akirafiles, with negative controls andrecoverruns:artifacts/decryptor-demo-2026-10-11.txt. - Flow diagrams:
artifacts/diagrams/encryption-flow.png,artifacts/diagrams/decryption-flow.png. - Native seed gap ~0.4 ms (run 4), so the default
--gap 3800:8000covers 20 of 21 measured gaps. - Speed (stage 1, 20-thread host):
- pure Python: about 150k candidate pairs/s;
- numba engine (
akira_fast.py, picked automatically whennumpy+numbaare installed): about 40–45M pairs/s sustained, 97M/s in a short burst.
A default window (378M pairs) takes about 20 s end to end, against 30 min before. Building the Yarrow table (1,500 SHA-256 calls per seed, about 2k seeds/s per core) is now the larger cost. It sets the floor for very wide windows, such as about ±0.5 s after a reboot.
- Seed window from victim timestamps (
04-dynamic/05-timestamp-seed-anchors.md): an.akirafile’s last-write time is +0.45..+5.36 ms after its key2 seed (21 of 21 run-4 files). With the QPC offset measured on the live victim,recover --qpc-offset … --mtime …needs no debugger data. After a reboot the standard boot-time sources are only good to a few hundred ms (+352 ms on the VM), so the first file costs days; that solve then pins the offset for the rest of the boot.
Folder map
| Path | Contents |
|—|—|
| report.md | full analysis report (export of the Claude Docs report, rev 56 of 2026-10-14, which covers run 6, build 2 and the PDF stage-2 fallback; the diagrams are embedded from artifacts/diagrams/). HTML/PDF copies are no longer kept; python export/process_report_export.py export/report-rev56 --html-pdf regenerates them |
| 00-intake/ … 05-detections/ | case notes per the workspace layout |
| 03-static/ | 01 main flow · 02 targeting · 03 pre-encryption · 04 modes · 05 crypto · 06 file format · 07 string obfuscation · 08 function map · 09 API calls per behaviour |
| 04-dynamic/ | run 1 (escape) · run 2 (crypto validation) · run 3 (end-to-end capture) · run 4 (native seed gap, with seed-gap/ CSVs) · 05 timestamp seed anchors (timestamps/ probes + analysis) · 06 run 5 blind recovery of real files (run5-blind/: probes, .akira inputs, decrypted outputs) |
| 05-detections/ | detections write-up + akira_win64_encryptor_101bd45a.yar |
| 06-decryptor/ | decryptor, README, tests, real .akira fixtures + captures.txt; stage2-investigation/ (how the run-6 PDF’s ChaCha8 key was found) |
| blog/ | akira-recovery-blog.html, the public blog draft (private artifact); the GitHub download links are placeholders until the repository is live |
| scripts/ | first-pass validation models (akira_crypto.py, akira_ciphers.py) |
| artifacts/ghidra/ | raw Ghidra outputs (decompile, 1.65 MB disassembly, .rdata dump, imports, strings, metadata) |
| artifacts/decoded/ | decoded DB-extension list + per-table keys |
| artifacts/diagrams/ | execution, encryption and decryption flow diagrams (.png renders + .widget.jsx sources) |
| artifacts/ | RSA public key (.der / .pem), ransom note, iocs.csv, test run log, decryptor demo log |
Open questions
- RESOLVED (run 4): the natural seed-to-seed gap is ~0.4 ms (key2 → iv2 median 4,121 ticks,
range 3,889–27,109). It was measured with an in-process QPC hook and no seed breakpoints, and checked by
decrypting with the logged seeds. See
04-dynamic/04-run4-native-seed-gap.md. - Decryption via the operator’s private key is tested only with our own RSA key.
--share_fileand Part mode with percent < 50 were not exercised live.- The YARA rule has not been run by a YARA engine or against a benign corpus.
- VM: revert the snapshot (run 5 encrypted
samples\data; Desktop holdsinv.ps1,post.ps1,inv_pre.txt,post_run5.txtand the operator log). Ghidra/decrypt_testraw imports can be deleted. - RESOLVED (2026-10-12): victim timestamps place key2 to within a few ms when the QPC offset is measured on the same boot. Still untested: hosts at the default 15.6 ms clock tick, large files (where encryption time widens mtime − key2), and trace files kept from the attack boot that might hold an exact QPC↔clock pair after a reboot.
- RESOLVED (2026-10-12): run 5’s first PNG had a key2→iv2 gap of 26,718 ticks. It was recovered hash-exact by
an iv2-anchored search with gaps 8,001–30,000 (
recover --anchor iv2, 1,240 s). See addendum 4 ofartifacts/run5-blind-decryption-evidence-2026-10-12.txt.