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)
|
who understands an exploit technique (overflow, UAF, IOCTL abuse, token stealing)
|
||||||
work at that level instead of on WinAPI struct layouts and Go plumbing.
|
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`,
|
- `Context` ([context.go](context.go)): `Arch` (x86/x64), `LogLevel`, `Timeout`,
|
||||||
`Newline`. Note: `P16/P32/P64` are little-endian only and do not read
|
`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`/
|
- Tube API ([tube.go](tube.go)): `Send*`/`Recv*`/`RecvRegex`/`RecvPred`/
|
||||||
`Interactive`/`Close`.
|
`Interactive`/`Close`.
|
||||||
|
|
||||||
## PE tooling — ✅
|
## PE tooling — [+]
|
||||||
|
|
||||||
- Parsing ([pe.go](pe.go), [sections.go](sections.go)): `ImageBase`,
|
- Parsing ([pe.go](pe.go), [sections.go](sections.go)): `ImageBase`,
|
||||||
`EntryPoint`, PE32 and PE32+ via a unified `header()` helper, per-section
|
`EntryPoint`, PE32 and PE32+ via a unified `header()` helper, per-section
|
||||||
@@ -52,13 +52,13 @@ Legend: ✅ done · 🚧 partial · ⬜ planned.
|
|||||||
`DisableTLSCallbacks`, `PatchBytes`/`PatchBytesAtOffset`, `RecalculateChecksum`.
|
`DisableTLSCallbacks`, `PatchBytes`/`PatchBytesAtOffset`, `RecalculateChecksum`.
|
||||||
|
|
||||||
Deferred:
|
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
|
growing the header area and recomputing `SizeOfImage`/`SizeOfHeaders`; needs
|
||||||
round-trip tests against real binaries.
|
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.
|
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.
|
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`/
|
`Modules`, `AllSymbols`; caches one `PEFile` per DLL. `SymbolVA`/
|
||||||
`ListLoadedModules` for one-off lookups. (Analogue of `p.libs`/`p.symbols`.)
|
`ListLoadedModules` for one-off lookups. (Analogue of `p.libs`/`p.symbols`.)
|
||||||
|
|
||||||
## Minidump — ✅
|
## Minidump — [+]
|
||||||
|
|
||||||
[minidump.go](minidump.go): `OpenMinidump` parses `MINIDUMP_HEADER`/
|
[minidump.go](minidump.go): `OpenMinidump` parses `MINIDUMP_HEADER`/
|
||||||
`MINIDUMP_DIRECTORY` and decodes `ModuleListStream` (`Modules()`) and
|
`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
|
context (`CONTEXT` blob) is not decoded — its layout is arch-specific with XSAVE
|
||||||
padding; use `RawStream`. Works without `GOOS=windows`.
|
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)):
|
[shellcode/asm/winexec_x64.asm](shellcode/asm/winexec_x64.asm)):
|
||||||
position-independent x64, resolves kernel32 via the PEB
|
position-independent x64, resolves kernel32 via the PEB
|
||||||
(`shellcode/asm/resolver.inc`), calls `WinExec` by name. Assembled ahead of
|
(`shellcode/asm/resolver.inc`), calls `WinExec` by name. Assembled ahead of
|
||||||
time with NASM; `.asm` source kept alongside the `.bin`.
|
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.
|
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
|
raw `WS2_32`, a standalone `LoadLibraryA`+`GetProcAddress` primitive (current
|
||||||
`find_export` only walks an already-loaded module).
|
`find_export` only walks an already-loaded module).
|
||||||
- ⬜ Token-stealing shellcode (see Driver/LPE).
|
- [-] Token-stealing shellcode (see Driver/LPE).
|
||||||
- ⬜ Encoders: alphanumeric and XOR bad-character avoidance.
|
- [-] Encoders: alphanumeric and XOR bad-character avoidance.
|
||||||
- ⬜ `asm`/assemble direction. Disassembly exists (`ROP.Disassemble` via
|
- [-] `asm`/assemble direction. Disassembly exists (`ROP.Disassemble` via
|
||||||
`x86asm`); assembling would use cgo bindings to the `keystone/` engine
|
`x86asm`); assembling would use cgo bindings to the `keystone/` engine
|
||||||
(walled off by its own `go.mod`, [keystone/go.mod](keystone/go.mod)).
|
(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
|
[debugger_windows.go](debugger_windows.go) wraps the Windows Debug API
|
||||||
(`DebugActiveProcess`/`WaitForDebugEvent`/`ContinueDebugEvent`/
|
(`DebugActiveProcess`/`WaitForDebugEvent`/`ContinueDebugEvent`/
|
||||||
@@ -128,21 +128,21 @@ padding; use `RawStream`. Works without `GOOS=windows`.
|
|||||||
- `Step()` in response to an `EventBreakpoint` behaves like `Continue` (resuming
|
- `Step()` in response to an `EventBreakpoint` behaves like `Continue` (resuming
|
||||||
past a software breakpoint already requires an internal single-step).
|
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
|
`DialPipe(name)`, both returning a `*Tube`. `ServePipe` uses
|
||||||
`PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED`; each `ReadFile`/`WriteFile` blocks
|
`PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED`; each `ReadFile`/`WriteFile` blocks
|
||||||
on `GetOverlappedResult`, so the handle behaves as a blocking
|
on `GetOverlappedResult`, so the handle behaves as a blocking
|
||||||
`io.ReadWriteCloser`. `pipeConn.Close()` is idempotent (`sync.Once`) because
|
`io.ReadWriteCloser`. `pipeConn.Close()` is idempotent (`sync.Once`) because
|
||||||
the duplex handle is used as both `Tube.stdin` and `Tube.stdout`.
|
the duplex handle is used as both `Tube.stdin` and `Tube.stdout`.
|
||||||
- ⬜ `Listen`: TCP listener tube for reverse shells (`net.Listen`).
|
- [-] `Listen`: TCP listener tube for reverse shells (`net.Listen`).
|
||||||
- ⬜ TLS transport (`Remote` with `tls.Dial`).
|
- [-] TLS transport (`Remote` with `tls.Dial`).
|
||||||
- ⬜ SSH transport (`golang.org/x/crypto/ssh`).
|
- [-] SSH transport (`golang.org/x/crypto/ssh`).
|
||||||
- ⬜ Process-tree cleanup: `Spawn` should kill the whole tree on `Close()` via a
|
- [-] Process-tree cleanup: `Spawn` should kill the whole tree on `Close()` via a
|
||||||
Windows Job Object.
|
Windows Job Object.
|
||||||
|
|
||||||
## CLI — 🚧
|
## CLI — [+-]
|
||||||
|
|
||||||
[cmd/winpwn/main.go](cmd/winpwn/main.go), additive to the library.
|
[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`.
|
mode), `disasm`, `exports`, `imports`, `heap`.
|
||||||
|
|
||||||
Open:
|
Open:
|
||||||
- ⬜ `winpwn asm`: blocked on the Keystone integration.
|
- [-] `winpwn asm`: blocked on the Keystone integration.
|
||||||
- ⬜ `winpwn template`: scaffold a new solve script (partly covered by
|
- [-] `winpwn template`: scaffold a new solve script (partly covered by
|
||||||
[cmd/pwninit](cmd/pwninit/main.go)).
|
[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
|
`(*Device).IOCTL(code, in)` over `DeviceIoControl`, handling output-buffer
|
||||||
sizing and error mapping.
|
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.
|
`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
|
Heap struct-parsing layer. All decoders work against any `io.ReaderAt`; only
|
||||||
finding a heap address in a live process needs Windows syscalls.
|
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
|
`_HEAP_ENTRY` + `DecodeHeapEntry` (XOR against `_HEAP.Encoding`). Offsets from
|
||||||
`dt ntdll!_HEAP` on build 10.0.26100; decode validated against `!heap -a` for
|
`dt ntdll!_HEAP` on build 10.0.26100; decode validated against `!heap -a` for
|
||||||
three allocations (see `TestDecodeHeapEntryMatchesLiveGroundTruth`).
|
three allocations (see `TestDecodeHeapEntryMatchesLiveGroundTruth`).
|
||||||
@@ -177,9 +177,9 @@ finding a heap address in a live process needs Windows syscalls.
|
|||||||
`EntriesWithUserData`, `AdjacentBusyPairs`. `UserSize` =
|
`EntriesWithUserData`, `AdjacentBusyPairs`. `UserSize` =
|
||||||
`Size*HeapEntrySize - UnusedBytes` (no separate header subtraction —
|
`Size*HeapEntrySize - UnusedBytes` (no separate header subtraction —
|
||||||
`UnusedBytes` already accounts for the header).
|
`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)`.
|
`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
|
`FindLFHBucket` (searches `BlockSize >= wantSize + 16`; LFH blocks include the
|
||||||
16-byte header), `ActiveSubsegment`, `AllSubsegments`, `ReadLFHSubsegment`
|
16-byte header), `ActiveSubsegment`, `AllSubsegments`, `ReadLFHSubsegment`
|
||||||
(BusyBitmap validated), `CalibrateLFHFirstBlockOffset`, `BlockAddress`,
|
(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);
|
`SegmentInfoArrays[bucket]` is populated (build-specific);
|
||||||
`_HEAP_USERDATA_HEADER.EncodedOffsets` is obfuscated —
|
`_HEAP_USERDATA_HEADER.EncodedOffsets` is obfuscated —
|
||||||
`CalibrateLFHFirstBlockOffset` uses a known-address calibration instead.
|
`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
|
`ReadSegmentHeap` (Signature/GlobalFlags + VS/LFH summaries), VS subsegment
|
||||||
walk, LFH bucket enumeration. VS `_HEAP_VS_CHUNK_HEADER.Sizes` and Segment LFH
|
walk, LFH bucket enumeration. VS `_HEAP_VS_CHUNK_HEADER.Sizes` and Segment LFH
|
||||||
`BlockOffsets.EncodedData` are XOR-encoded and not decoded. Adjacency is
|
`BlockOffsets.EncodedData` are XOR-encoded and not decoded. Adjacency is
|
||||||
handled by `AdjacentAddressPairs`/`FindAdjacentPair` on leaked addresses, which
|
handled by `AdjacentAddressPairs`/`FindAdjacentPair` on leaked addresses, which
|
||||||
needs no chunk-header decode.
|
needs no chunk-header decode.
|
||||||
- ✅ `winpwn heap <pid> [-walk]` CLI subcommand.
|
- [+] `winpwn heap <pid> [-walk]` CLI subcommand.
|
||||||
- ⬜ BSTR/client-spray generator (lower priority; `SprayAndFind` covers the
|
- [-] BSTR/client-spray generator (lower priority; `SprayAndFind` covers the
|
||||||
generic retry/search loop).
|
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.
|
goroutine pool.
|
||||||
- ⬜ Race-condition primitives: fire N goroutines at a target behind a barrier.
|
- [-] Race-condition primitives: fire N goroutines at a target behind a barrier.
|
||||||
- ⬜ Cancellable `Interactive`: propagate a `context.Context` into both copy
|
- [-] Cancellable `Interactive`: propagate a `context.Context` into both copy
|
||||||
goroutines so the stdin-forwarding goroutine does not outlive the tube.
|
goroutines so the stdin-forwarding goroutine does not outlive the tube.
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|||||||
Reference in New Issue
Block a user