Ключ к тому, почему это происходит, заключается в том, что buffer_one
находится после buffer_two
в памяти.Это означает, что когда вы переполняете buffer_one
, вы не переполняетесь в buffer_two
.Вместо этого вы переполняете стековую память, используемую для хранения других вещей, таких как сохраненный указатель ebp
и, самое главное, адрес возврата.
И это именно то, что вы хотите, чтобы случиться при попытке переполнения буфераэксплуатируют!Когда программа выполняет strcpy(buffer_one, argv[1]);
, первые четыре байта из argv[1]
попадают в память, выделенную для buffer_one
.Но затем следующие 12 начинают переполняться памятью, используемой для других целей, в конечном итоге перезаписывая адрес возврата.Не видя машинного кода, я не могу точно сказать, какие именно байты точно переполняют адрес возврата.Но я предполагаю, что значение EIP во время SIGABRT составляет 0x31323334 или что-то подобное (шестнадцатеричное представление «1234»).Ключ к пониманию того, что, перезаписывая адрес возврата, вы управляете EIP.А когда вы управляете EIP, вы управляете системой .(несколько преувеличено, но в большинстве случаев не за горами) Когда вы управляете EIP, вы контролируете, какие инструкции процессор будет выполнять дальше (оставляя в стороне тот факт, что ОС / ядро фактически стоят между ними).
Теперь, если вы найдете точно, какие восемь байтов перезаписывают адрес возврата, вы можете заменить эти байты адресом вашего буфера (0x00007fff5fbff8e0
) и вместо возврата к исходному вызывающему объекту (в данном случае libc), программа начнет выполнениеинструкции, которые вы предоставили (AKA шеллкод).Обратите внимание, что вам придется заполнить подразумеваемые 0 в наиболее значимых местах и указать адрес в виде фактических непечатаемых символов ASCII (0x00 0x00 0x7f 0xff 0x5f
и т. Д.), А не фактических цифр / символов 7ff5
и т. Д. При использовании x86В архитектуре -64 вам также нужно будет принимать во внимание порядок байтов и задавать его задом наперед - 0xe0 0xf8 0xbf
и т. Д. Обеспечение этих непечатаемых символов проще всего выполнить с помощью обратных галочек и подстановки команд с помощью краткого сценария Python или Perl:
./overflow `python -c 'print "AAAAAAAAAAAAAAAA\xe0\xf8\xbf\x5f\xff\x7f"'`
(А дополняют, чтобы переполнить буфер.) К сожалению, вы не сможете предоставить 2 дополнительных \x00
, необходимых для адреса.Один из этих NULL будет помещен для вас к strcpy
, но вам придется повезти с последним NULL и надеяться, что перезаписываемый адрес уже начался с 0x00
(что на самом деле весьма вероятно).Теперь, когда вы выполните это с правильным числом A, вы, вероятно, по-прежнему получите ошибку сегментации или даже, возможно, недопустимую инструкцию, поскольку теперь вы перейдете к заглавной букве A и выполните их как действительные машинные инструкции (0x41
=>inc ecx
).
Затем, наконец, последняя часть вставляет фактический шелл-код.Учитывая ваш ограниченный размер буфера, будет очень трудно предоставить что-нибудь полезное всего в 12 байтах или около того.Поскольку в этом случае вы пишете код, возможно, проще всего будет увеличить буфер.Если это не вариант, то вы можете либо A) использовать buffer_two
, но и еще 16 байтов, так как он предшествует buffer_one
или B), предоставить шелл-код в переменной среды и перейти к нему вместо этого.
Если вы хотите написать реальный шелл-код самостоятельно, вам необходимо знать, как выполнять системные вызовы и что такое соглашения о вызовах и как их использовать, а также как избежать байтов NULL в шелл-коде.Другой альтернативой является использование генератора полезной нагрузки, такого как тот, который входит в состав Metasploit, который сделает его намного проще (хотя вы не будете учиться почти так же много).
Это технически единственные части, которые вам нужны, тем более что у вас есть хорошее представление о том, каким будет адрес.Тем не менее, часто (особенно когда адрес шелл-кода неизвестен), так называемые салазки NOP будут помещаться перед шелл-кодом, так что вам не нужно будет точно определять адрес.Сани NOP (сокращение от No Operation) - это просто от сотен до тысяч инструкций NOP (0x90), которые вы можете перейти в середину и не иметь никакого эффекта до тех пор, пока выполнение не продолжится в шеллкод.в GDB и выполнение правильно переходит к шелл-коду, но вы все равно получаете нарушения доступа, вероятно, потому что бит NX установлен на странице стека, что означает, что процессор откажется выполнять данные из стека в качестве инструкций.Я не уверен, включен ли execstack
в OSX или нет, но если это так, вы можете использовать его в целях тестирования, чтобы отключить бит NX (execstack -s overflow
).
Я прошу прощения за стенутекст, но я не был уверен, как далеко вы хотите пойти изучать переполнения буфера.Есть и другие руководства, которые вы можете проверить, такие как архетипический справочник Aleph One, «Разбивая стек ради удовольствия и прибыли» . Справочник по кодировщику оболочек - хорошая книга для ознакомления, и я уверен, что другие могут добавить рекомендации.
TL; DR: Короче говоря, вы переполняете буфер и перезаписываете сохраненные указатели и адреса возврата мусором.