roadmap edit
This commit is contained in:
+39
-39
@@ -7,9 +7,9 @@ Scope: winpwn is built for a local Windows-only CTF. The goal is to let a player
|
||||
who understands an exploit technique (overflow, UAF, IOCTL abuse, token stealing)
|
||||
work at that level instead of on WinAPI struct layouts and Go plumbing.
|
||||
|
||||
Legend: ✅ done · 🚧 partial · ⬜ planned.
|
||||
Legend: [+] done · [+-] partial · [-] planned.
|
||||
|
||||
## Core ergonomics — ✅
|
||||
## Core ergonomics — [+]
|
||||
|
||||
- `Context` ([context.go](context.go)): `Arch` (x86/x64), `LogLevel`, `Timeout`,
|
||||
`Newline`. Note: `P16/P32/P64` are little-endian only and do not read
|
||||
@@ -29,7 +29,7 @@ Legend: ✅ done · 🚧 partial · ⬜ planned.
|
||||
- Tube API ([tube.go](tube.go)): `Send*`/`Recv*`/`RecvRegex`/`RecvPred`/
|
||||
`Interactive`/`Close`.
|
||||
|
||||
## PE tooling — ✅
|
||||
## PE tooling — [+]
|
||||
|
||||
- Parsing ([pe.go](pe.go), [sections.go](sections.go)): `ImageBase`,
|
||||
`EntryPoint`, PE32 and PE32+ via a unified `header()` helper, per-section
|
||||
@@ -52,13 +52,13 @@ Legend: ✅ done · 🚧 partial · ⬜ planned.
|
||||
`DisableTLSCallbacks`, `PatchBytes`/`PatchBytesAtOffset`, `RecalculateChecksum`.
|
||||
|
||||
Deferred:
|
||||
- ⬜ `AddSection`: append a new section for payload injection. Requires
|
||||
- [-] `AddSection`: append a new section for payload injection. Requires
|
||||
growing the header area and recomputing `SizeOfImage`/`SizeOfHeaders`; needs
|
||||
round-trip tests against real binaries.
|
||||
- ⬜ `DynPE` (analogue of `DynELF`): given one memory-read primitive, walk a
|
||||
- [-] `DynPE` (analogue of `DynELF`): given one memory-read primitive, walk a
|
||||
loaded module's export directory at runtime to resolve symbols.
|
||||
|
||||
## Live-process introspection — ✅
|
||||
## Live-process introspection — [+]
|
||||
|
||||
Reads a PE inside a running process, resolving a module base via the PEB.
|
||||
|
||||
@@ -78,7 +78,7 @@ Reads a PE inside a running process, resolving a module base via the PEB.
|
||||
`Modules`, `AllSymbols`; caches one `PEFile` per DLL. `SymbolVA`/
|
||||
`ListLoadedModules` for one-off lookups. (Analogue of `p.libs`/`p.symbols`.)
|
||||
|
||||
## Minidump — ✅
|
||||
## Minidump — [+]
|
||||
|
||||
[minidump.go](minidump.go): `OpenMinidump` parses `MINIDUMP_HEADER`/
|
||||
`MINIDUMP_DIRECTORY` and decodes `ModuleListStream` (`Modules()`) and
|
||||
@@ -87,25 +87,25 @@ Reads a PE inside a running process, resolving a module base via the PEB.
|
||||
context (`CONTEXT` blob) is not decoded — its layout is arch-specific with XSAVE
|
||||
padding; use `RawStream`. Works without `GOOS=windows`.
|
||||
|
||||
## Shellcode & encoding — 🚧
|
||||
## Shellcode & encoding — [+-]
|
||||
|
||||
- ✅ `ShellcodeWinExec(cmd)` ([shellcraft.go](shellcraft.go),
|
||||
- [+] `ShellcodeWinExec(cmd)` ([shellcraft.go](shellcraft.go),
|
||||
[shellcode/asm/winexec_x64.asm](shellcode/asm/winexec_x64.asm)):
|
||||
position-independent x64, resolves kernel32 via the PEB
|
||||
(`shellcode/asm/resolver.inc`), calls `WinExec` by name. Assembled ahead of
|
||||
time with NASM; `.asm` source kept alongside the `.bin`.
|
||||
- ✅ `ExecuteShellcode` ([shellcode_exec_windows.go](shellcode_exec_windows.go)):
|
||||
- [+] `ExecuteShellcode` ([shellcode_exec_windows.go](shellcode_exec_windows.go)):
|
||||
runs a template locally for validation.
|
||||
- ⬜ More templates on the same resolver base: `MessageBoxA`, reverse shell via
|
||||
- [-] More templates on the same resolver base: `MessageBoxA`, reverse shell via
|
||||
raw `WS2_32`, a standalone `LoadLibraryA`+`GetProcAddress` primitive (current
|
||||
`find_export` only walks an already-loaded module).
|
||||
- ⬜ Token-stealing shellcode (see Driver/LPE).
|
||||
- ⬜ Encoders: alphanumeric and XOR bad-character avoidance.
|
||||
- ⬜ `asm`/assemble direction. Disassembly exists (`ROP.Disassemble` via
|
||||
- [-] Token-stealing shellcode (see Driver/LPE).
|
||||
- [-] Encoders: alphanumeric and XOR bad-character avoidance.
|
||||
- [-] `asm`/assemble direction. Disassembly exists (`ROP.Disassemble` via
|
||||
`x86asm`); assembling would use cgo bindings to the `keystone/` engine
|
||||
(walled off by its own `go.mod`, [keystone/go.mod](keystone/go.mod)).
|
||||
|
||||
## Debugger — ✅
|
||||
## Debugger — [+]
|
||||
|
||||
[debugger_windows.go](debugger_windows.go) wraps the Windows Debug API
|
||||
(`DebugActiveProcess`/`WaitForDebugEvent`/`ContinueDebugEvent`/
|
||||
@@ -128,21 +128,21 @@ padding; use `RawStream`. Works without `GOOS=windows`.
|
||||
- `Step()` in response to an `EventBreakpoint` behaves like `Continue` (resuming
|
||||
past a software breakpoint already requires an internal single-step).
|
||||
|
||||
## Networking & transports — 🚧
|
||||
## Networking & transports — [+-]
|
||||
|
||||
- ✅ Named pipes ([pipe_windows.go](pipe_windows.go)): `ServePipe(name)`/
|
||||
- [+] Named pipes ([pipe_windows.go](pipe_windows.go)): `ServePipe(name)`/
|
||||
`DialPipe(name)`, both returning a `*Tube`. `ServePipe` uses
|
||||
`PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED`; each `ReadFile`/`WriteFile` blocks
|
||||
on `GetOverlappedResult`, so the handle behaves as a blocking
|
||||
`io.ReadWriteCloser`. `pipeConn.Close()` is idempotent (`sync.Once`) because
|
||||
the duplex handle is used as both `Tube.stdin` and `Tube.stdout`.
|
||||
- ⬜ `Listen`: TCP listener tube for reverse shells (`net.Listen`).
|
||||
- ⬜ TLS transport (`Remote` with `tls.Dial`).
|
||||
- ⬜ SSH transport (`golang.org/x/crypto/ssh`).
|
||||
- ⬜ Process-tree cleanup: `Spawn` should kill the whole tree on `Close()` via a
|
||||
- [-] `Listen`: TCP listener tube for reverse shells (`net.Listen`).
|
||||
- [-] TLS transport (`Remote` with `tls.Dial`).
|
||||
- [-] SSH transport (`golang.org/x/crypto/ssh`).
|
||||
- [-] Process-tree cleanup: `Spawn` should kill the whole tree on `Close()` via a
|
||||
Windows Job Object.
|
||||
|
||||
## CLI — 🚧
|
||||
## CLI — [+-]
|
||||
|
||||
[cmd/winpwn/main.go](cmd/winpwn/main.go), additive to the library.
|
||||
|
||||
@@ -151,25 +151,25 @@ integer), `hex`/`unhex`/`hexdump`, `rop` (`-search`/`-regex`; no unfiltered dump
|
||||
mode), `disasm`, `exports`, `imports`, `heap`.
|
||||
|
||||
Open:
|
||||
- ⬜ `winpwn asm`: blocked on the Keystone integration.
|
||||
- ⬜ `winpwn template`: scaffold a new solve script (partly covered by
|
||||
- [-] `winpwn asm`: blocked on the Keystone integration.
|
||||
- [-] `winpwn template`: scaffold a new solve script (partly covered by
|
||||
[cmd/pwninit](cmd/pwninit/main.go)).
|
||||
|
||||
## Driver / LPE (Ring 0) — ⬜
|
||||
## Driver / LPE (Ring 0) — [-]
|
||||
|
||||
- ⬜ Device handle + IOCTL wrapper: `OpenDevice(name)` over `CreateFileA`,
|
||||
- [-] Device handle + IOCTL wrapper: `OpenDevice(name)` over `CreateFileA`,
|
||||
`(*Device).IOCTL(code, in)` over `DeviceIoControl`, handling output-buffer
|
||||
sizing and error mapping.
|
||||
- ⬜ Token-stealing shellcode (x86 and x64), parameterized by a
|
||||
- [-] Token-stealing shellcode (x86 and x64), parameterized by a
|
||||
`KernelOffsets{Process, ActiveProcessLinks, Token}` struct.
|
||||
- ⬜ Kernel info-leak helpers over the common `NtQuerySystemInformation` classes.
|
||||
- [-] Kernel info-leak helpers over the common `NtQuerySystemInformation` classes.
|
||||
|
||||
## Heap exploitation — 🚧
|
||||
## Heap exploitation — [+-]
|
||||
|
||||
Heap struct-parsing layer. All decoders work against any `io.ReaderAt`; only
|
||||
finding a heap address in a live process needs Windows syscalls.
|
||||
|
||||
- ✅ NT Heap foundation ([heap.go](heap.go)): `_HEAP`/`_HEAP_SEGMENT`/
|
||||
- [+] NT Heap foundation ([heap.go](heap.go)): `_HEAP`/`_HEAP_SEGMENT`/
|
||||
`_HEAP_ENTRY` + `DecodeHeapEntry` (XOR against `_HEAP.Encoding`). Offsets from
|
||||
`dt ntdll!_HEAP` on build 10.0.26100; decode validated against `!heap -a` for
|
||||
three allocations (see `TestDecodeHeapEntryMatchesLiveGroundTruth`).
|
||||
@@ -177,9 +177,9 @@ finding a heap address in a live process needs Windows syscalls.
|
||||
`EntriesWithUserData`, `AdjacentBusyPairs`. `UserSize` =
|
||||
`Size*HeapEntrySize - UnusedBytes` (no separate header subtraction —
|
||||
`UnusedBytes` already accounts for the header).
|
||||
- ✅ `ListProcessHeaps(pid)` ([heap_windows.go](heap_windows.go)): PEB walk via
|
||||
- [+] `ListProcessHeaps(pid)` ([heap_windows.go](heap_windows.go)): PEB walk via
|
||||
`NtQueryInformationProcess(ProcessBasicInformation)`.
|
||||
- ✅ NT Heap LFH ([heap_lfh.go](heap_lfh.go)): `ReadLFHBuckets`,
|
||||
- [+] NT Heap LFH ([heap_lfh.go](heap_lfh.go)): `ReadLFHBuckets`,
|
||||
`FindLFHBucket` (searches `BlockSize >= wantSize + 16`; LFH blocks include the
|
||||
16-byte header), `ActiveSubsegment`, `AllSubsegments`, `ReadLFHSubsegment`
|
||||
(BusyBitmap validated), `CalibrateLFHFirstBlockOffset`, `BlockAddress`,
|
||||
@@ -187,22 +187,22 @@ finding a heap address in a live process needs Windows syscalls.
|
||||
`SegmentInfoArrays[bucket]` is populated (build-specific);
|
||||
`_HEAP_USERDATA_HEADER.EncodedOffsets` is obfuscated —
|
||||
`CalibrateLFHFirstBlockOffset` uses a known-address calibration instead.
|
||||
- ✅ Segment Heap outer layer ([heap_segment.go](heap_segment.go)):
|
||||
- [+] Segment Heap outer layer ([heap_segment.go](heap_segment.go)):
|
||||
`ReadSegmentHeap` (Signature/GlobalFlags + VS/LFH summaries), VS subsegment
|
||||
walk, LFH bucket enumeration. VS `_HEAP_VS_CHUNK_HEADER.Sizes` and Segment LFH
|
||||
`BlockOffsets.EncodedData` are XOR-encoded and not decoded. Adjacency is
|
||||
handled by `AdjacentAddressPairs`/`FindAdjacentPair` on leaked addresses, which
|
||||
needs no chunk-header decode.
|
||||
- ✅ `winpwn heap <pid> [-walk]` CLI subcommand.
|
||||
- ⬜ BSTR/client-spray generator (lower priority; `SprayAndFind` covers the
|
||||
- [+] `winpwn heap <pid> [-walk]` CLI subcommand.
|
||||
- [-] BSTR/client-spray generator (lower priority; `SprayAndFind` covers the
|
||||
generic retry/search loop).
|
||||
|
||||
## Go-native additions — ⬜
|
||||
## Go-native additions — [-]
|
||||
|
||||
- ⬜ `Pool`: fan out one exploit across N parallel connections via a bounded
|
||||
- [-] `Pool`: fan out one exploit across N parallel connections via a bounded
|
||||
goroutine pool.
|
||||
- ⬜ Race-condition primitives: fire N goroutines at a target behind a barrier.
|
||||
- ⬜ Cancellable `Interactive`: propagate a `context.Context` into both copy
|
||||
- [-] Race-condition primitives: fire N goroutines at a target behind a barrier.
|
||||
- [-] Cancellable `Interactive`: propagate a `context.Context` into both copy
|
||||
goroutines so the stdin-forwarding goroutine does not outlive the tube.
|
||||
|
||||
## Testing
|
||||
|
||||
Reference in New Issue
Block a user