تزریق فرمان چیست و چگونه کار میکند؟
تزریق فرمان سیستمعامل (OS Command Injection) وقتی رخ میدهد که برنامه ورودی کاربر را — مستقیم یا غیرمستقیم — به یک مفسر فرمان سیستمعامل میرساند. اگر ورودی داخل یک شل اجرا شود، متاکاراکترهای شل مانند ;، &&، |، بکتیک و $( ) اجازه میدهند مهاجم فرمان جداگانهای به دستور اصلی بچسباند. پیامد، اجرای فرمان دلخواه با سطح دسترسی سرویس وب و اغلب رسیدن به اجرای کد از راه دور و سپس شل معکوس است.
این آسیب دو حالت کلی دارد: در نوع مستقیم (in-band) خروجی فرمان در پاسخ برنامه بازتاب میشود و بهرهبرداری آشکار است؛ در نوع کور (blind) هیچ خروجیای برنمیگردد. در حالت کور، تشخیص از سه راه انجام میشود: تأخیر زمانی کنترلشده (مثل sleep 10 یا ping -c 10 و سنجش زمان پاسخ)، کانال خارجباند (وادار کردن سرور به یک درخواست DNS یا HTTP به دامنهای تحت کنترل تستر)، یا نوشتن فایل و خواندن بعدی آن. تفکیک این حالتها در گزارش مهم است، چون شدت و قابلیت اثبات هرکدام متفاوت است.
تزریق آرگومان: خطری که بدون شل هم هست
مهمترین سوءتفاهم این حوزه، تصور مصونیت هنگام نبود شل است.
«ما شل صدا نمیزنیم (از APIهای execveمحور استفاده میکنیم) پس تزریق فرمان غیرممکن است.» نادرست است. تزریق آرگومان (Argument Injection) حتی بدون هیچ شلی کار میکند: وقتی ورودی مهاجم بهعنوان یک آرگومان به یک برنامه پاس داده میشود، میتواند رفتار آن برنامه را تغییر دهد. نمونهها: curl -o برای نوشتن فایل دلخواه، tar --checkpoint-action=exec، git --upload-pack، find -exec، و zip --unzip-command. هیچکدام به متاکاراکتر شل نیاز ندارند و دفاع «شل نداریم» را رد میکنند.
سوءتفاهم دوم مربوط به جاواست.
«Runtime.exec() در جاوا به زنجیرهسازی با ; آسیبپذیر است.» نادرست است. Runtime.exec() یک شل صدا نمیزند، پس متاکاراکترهای شل زنجیره نمیشوند. ریسک واقعی جاوا آنجاست که برنامه بهصراحت ProcessBuilder یا exec را با sh -c فراخوانی کند، و همچنین تزریق آرگومان.
تفاوت APIها بین زبانها تعیینکننده است؛ جدول زیر مرز «شل/بدونشل» را در چند زبان پرکاربرد نشان میدهد:
| زبان | درگیرکنندهی شل (پرخطر) | بدون شل (امنتر) |
|---|---|---|
| پایتون | os.system، subprocess با shell=True | subprocess.run([...], shell=False) |
| Node.js | child_process.exec | execFile، spawn |
| PHP | system، exec، shell_exec، بکتیک | proc_open با آرگومان آرایهای و کنترل |
| جاوا | ProcessBuilder با sh -c | ProcessBuilder/Runtime.exec با آرایهی آرگومان |
| روبی | بکتیک، system("str")، open("|cmd") | system(cmd, arg1, arg2) با آرگومانهای جدا |
اما توجه کنید که حتی ستون «امنتر» در برابر تزریق آرگومان بهخودیخود مصون نیست؛ اگر ورودی کاربر بتواند یک سوییچ برنامه شود، همچنان باید اعتبارسنجی شود.
سناریوی واقعی و کشف در تست نفوذ
سناریوی متعارف: یک ابزار «بررسی وضعیت شبکه» در پنل مدیریت، آدرس واردشده را به فرمان ping در یک شل میدهد. مهاجم بهجای آدرس، 127.0.0.1; id میفرستد و فرمان دوم اجرا میشود. در نمونهی بدونشل، یک قابلیت «دانلود از URL» که پشت صحنه curl صدا میزند، با تزریق سوییچ -o به نوشتن فایل روی سرور میانجامد.
روش کشف در تست نفوذ وب:
- شناسایی هر قابلیتی که با سیستمعامل یا ابزارهای خطفرمان تعامل دارد (پینگ، تبدیل تصویر، آرشیو، دانلود).
- تزریق پروب تأخیری یا خارجباند و سنجش پاسخ.
- بررسی احتمال تزریق آرگومان، حتی وقتی متاکاراکترها فیلتر شدهاند.
- مکمل با بازبینی کد منبع برای یافتن فراخوانیهای
exec/system.
هر یافته با یک اثبات مفهوم غیرمخرب (مثل تأخیر کنترلشده) مستند میشود، نه یک زنجیرهی بهرهبرداری واقعی.
تیمهایی که میخواهند توسعهدهندگانشان این الگوها را در کد پیشگیری کنند، از دورهی سازمانی توسعه امن بهره میبرند.
نمونهی فنی
شکل کد آسیبپذیر و نسخهی امن آن در پایتون:
# آسیبپذیر: ورودی وارد شل میشود
import os
os.system("ping -c 1 " + user_host) # ; id فرمان دوم را اجرا میکند
# امن: آرایه، بدون شل، بدون تفسیر متاکاراکتر
import subprocess
subprocess.run(["ping", "-c", "1", user_host], shell=False)حتی در حالت امن بالا، اگر user_host بتواند با - شروع شود، باید در برابر تزریق آرگومان اعتبارسنجی شود (مثلاً با فهرست سفید کاراکترها یا افزودن -- پیش از ورودی).
ارتباط با OWASP و CWE
تزریق فرمان سیستمعامل شناسهی CWE-78 را دارد و در OWASP Top 10:2025 زیر دستهی A05:2025 تزریق قرار میگیرد؛ در فهرست ۲۵گانهی CWE سال ۲۰۲۵ رتبهی نهم است. این آسیب یکی از مسیرهای متعارف رسیدن به RCE در برنامههای وب است. مرور دستهبندی کامل در صفحهی OWASP.
پرسشهای متداول
اگر از شل استفاده نکنیم، تزریق فرمان غیرممکن است؟
خیر. تزریق آرگومان بدون هیچ شلی کار میکند؛ ورودی مهاجم بهعنوان آرگومان به برنامههایی مثل curl، tar، git و find پاس میشود و رفتارشان را تغییر میدهد (مثلاً نوشتن فایل با -o).
آیا Runtime.exec در جاوا به زنجیرهسازی با نقطهویرگول آسیبپذیر است؟
خیر. Runtime.exec() شل را صدا نمیزند، بنابراین متاکاراکترهای شل مثل ; زنجیره نمیشوند. خطر واقعی زمانی است که برنامه بهصراحت sh -c اجرا کند یا در معرض تزریق آرگومان باشد.
تزریق فرمان کور چطور کشف میشود؟
وقتی خروجی بازتاب نمیشود، از تأخیر زمانی (مثل sleep)، کانال خارجباند (درخواست DNS یا HTTP به یک دامنهی تحت کنترل تستر) یا نوشتن و خواندن بعدی فایل برای اثبات اجرا استفاده میشود.