VULN · آسیب‌پذیری‌ها تزریق سمت سرور بحرانی

SSTI — تزریق قالب سمت سرور

Server-Side Template Injection

تزریق قالب سمت سرور زمانی رخ می‌دهد که ورودی کاربر به‌جای داده، وارد خودِ قالب می‌شود و موتور قالب آن را اجرا می‌کند — مسیری که اغلب به اجرای کد می‌رسد.

تیم فنی پی‌هانتر

SSTI چیست و تمایز کلیدی آن کجاست؟

تزریق قالب سمت سرور (Server-Side Template Injection) که به‌اختصار SSTI خوانده می‌شود، زمانی رخ می‌دهد که ورودی کاربر داخل خودِ متن قالب الحاق شود، نه اینکه به‌عنوان داده به یک قالب ثابت پاس داده شود. تمام موضوع در همین تمایز است: render_template_string("Hello " + name) آسیب‌پذیر است، اما render_template("hi.html", name=name) نیست، چون در دومی name فقط داده است.

موتورهای قالب برای انعطاف، به گراف اشیای زبان و امکانات درون‌نگری (introspection) دسترسی دارند. وقتی مهاجم می‌تواند نحو قالب را تزریق کند، این گراف را می‌پیماید و به فراخوانی‌های سیستمی می‌رسد؛ به همین دلیل پیامد SSTI معمولاً اجرای کد از راه دور (RCE) است، نه صرفاً افشای داده.

روش تشخیص: از پروب پلی‌گلات تا شناسایی موتور

متدولوژی تشخیص سه‌مرحله‌ای است: پروب، شناسایی موتور، سپس بهره‌برداری کنترل‌شده.

  1. پروب پلی‌گلات: ابتدا یک رشته‌ی چندنمادی مثل ${{<%[%'"}}%\ تزریق می‌شود تا با ایجاد خطا یا رفتار غیرعادی، وجود ارزیابی قالب آشکار شود.
  2. شناسایی موتور: با بارهای ریاضی، موتور را تفکیک می‌کنیم. {{7*7}} در Jinja2، Twig و Nunjucks ارزیابی می‌شود؛ ${7*7} در FreeMarker و Java EL؛ <%= 7*7 %> در ERB. یک تمایزگر کلاسیک، {{7*'7'}} است که در Jinja2 مقدار 7777777 و در Twig مقدار 49 می‌دهد.
  3. بهره‌برداری: پس از قطعیت موتور، مسیر رسیدن به فرمان مشخص می‌شود.

موتورهای پرکاربردی که باید بشناسید و نحو تشخیصی هرکدام:

موتورزبان/بسترنحو نمونه
Jinja2پایتون (Flask){{7*7}}
TwigPHP (Symfony){{7*7}} با تمایز {{7*'7'}}=49
FreeMarkerجاوا${7*7}
Velocityجاوا#set($x=7*7)
ERBروبی (Rails)<%= 7*7 %>

چون چند موتور نحو مشترک دارند (مثلاً {{...}} در Jinja2 و Twig)، تمایزگرهایی مثل {{7*'7'}} برای قطعیت لازم‌اند: نتیجه‌ی 7777777 به Jinja2 و 49 به Twig اشاره می‌کند. شناسایی دقیق موتور پیش‌شرط انتخاب مسیر بهره‌برداری است، چون هر موتور گراف اشیا و کلاس‌های ابزار متفاوتی دارد.

مسیر SSTI تا RCE

بهره‌برداری معمولاً از دل سلسله‌مراتب اشیا عبور می‌کند تا به کلاسی برسد که فرمان اجرا می‌کند. شکل مفهومی آن:

  • Jinja2: پیمایش از یک شیء تا کلاس پایه و یافتن زیرکلاسی که به اجرای فرایند می‌رسد (از طریق زنجیره‌ی __class__ و __mro__).
  • Twig: استفاده از قابلیت‌های داخلی محیط برای ثبت یک callback و فراخوانی exec.
  • FreeMarker: از طریق کلاس ابزار freemarker.template.utility.Execute.
  • ERB: از طریق بک‌تیک روبی که مستقیماً فرمان سیستم را اجرا می‌کند.

در یک تست نفوذ حرفه‌ای، این مسیر تنها تا حد اثبات وجود ارزیابی (مثل چاپ نتیجه‌ی یک عبارت ساده یا خواندن یک مقدار پیکربندی بی‌خطر) نشان داده می‌شود و از یک اثبات مفهوم غیرمخرب فراتر نمی‌رود؛ اجرای کامل زنجیره‌ی فرمان روی سامانه‌ی کارفرما، هم غیرضروری و هم پرریسک است.

سناریوی واقعی متعارف: یک قابلیت «قالب ایمیل سفارشی» یا «پیام خوش‌آمد قابل‌ویرایش» که متن واردشده‌ی کاربر را مستقیماً به render_template_string می‌دهد. کاربری که باید فقط متن بنویسد، ناگهان می‌تواند عبارت قالب تزریق کند و از داده به کد عبور کند. همین الگو در سامانه‌های ساخت فاکتور، گزارش‌ساز و صفحه‌ساز پویا نیز دیده می‌شود؛ هرجا که «قالب» یک ورودی کاربر باشد، باید به SSTI مشکوک شد.

کشف عملی این آسیب بخشی از تست نفوذ وب است.

تمایز از تزریق قالب سمت کلاینت

باور غلط رایج

«SSTI و تزریق قالب سمت کلاینت یک چیزند.» نه. در SSTI ارزیابی روی سرور رخ می‌دهد و پیامد آن معمولاً RCE است. در تزریق قالب سمت کلاینت (CSTI)، قالب در مرورگر توسط فریم‌ورک‌هایی مثل AngularJS/Vue ارزیابی می‌شود و پیامد آن معمولاً XSS است، نه اجرای فرمان روی سرور. نکته‌ی مشترک: در هر دو، کدگذاری HTML به‌تنهایی بی‌فایده است چون فریم‌ورک محتوا را پیش از اجرای نحو قالب رمزگشایی می‌کند.

سندباکس داخلی موتورها هم مرز امنیتی مطمئنی نیست؛ همان درسی که سندباکس AngularJS با فرارهای مکرر و حذف نهایی در نسخه‌ی ۱٫۶ به صنعت داد.

نمونه‌ی فنی و ارتباط با OWASP

شکل پروب تشخیصی و تمایز موتور:

# پروب پلی‌گلات (ایجاد خطا/رفتار غیرعادی)
${{<%[%'"}}%\

# تمایز موتور با یک عبارت
{{7*'7'}}
# Jinja2 => 7777777    |    Twig => 49

SSTI به‌عنوان یک تزریق سمت سرور در OWASP Top 10:2025 زیر دسته‌ی A05:2025 تزریق جای می‌گیرد و در راهنمای تست OWASP (WSTG) آزمون اختصاصی خود را دارد. چون مسیر متعارف آن RCE است، در گزارش تست نفوذ معمولاً شدت بالا یا بحرانی می‌گیرد. مرور دسته‌بندی در صفحه‌ی OWASP.

پرسش‌های متداول

چطور بفهمم برنامه به SSTI آسیب‌پذیر است؟

ابتدا یک پروب پلی‌گلات برای دیدن خطا یا رفتار غیرعادی تزریق کنید، سپس با عباراتی مثل {{7*7}} یا ${7*7} بررسی کنید که آیا نتیجه‌ی محاسبه در پاسخ ظاهر می‌شود. اگر بله، موتور در حال ارزیابی ورودی شماست.

تفاوت SSTI با تزریق قالب سمت کلاینت چیست؟

SSTI روی سرور ارزیابی می‌شود و معمولاً به RCE می‌رسد؛ تزریق قالب سمت کلاینت در مرورگر ارزیابی می‌شود و پیامد آن معمولاً XSS است. هر دو با کدگذاری HTML به‌تنهایی رفع نمی‌شوند.

کدام موتورهای قالب بیشتر در معرض SSTI هستند؟

Jinja2 در پایتون، Twig در PHP، FreeMarker و Velocity در جاوا، و ERB در روبی از پرتکرارترین‌ها هستند. هر کدام نحو و مسیر بهره‌برداری متفاوتی دارند و پس از شناسایی موتور مشخص می‌شوند.

پیشگیری / رفع

راهکارها به ترتیب اثربخشی:

  1. هرگز قالب را از ورودی کاربر نسازید. ورودی را فقط به‌عنوان داده به یک قالب ثابت پاس دهید (render_template(..., name=name)).
  2. در صورت امکان از قالب‌های بدون منطق (مثل Mustache) استفاده کنید که ارزیابی کد دلخواه ندارند.
  3. اگر قالب کاربرمحور یک نیاز محصولی است، آن را در یک سندباکس واقعی و فرایند مجزا بدون دسترسی به فایل‌سیستم و شبکه اجرا کنید — و به سندباکس داخلی موتور تکیه نکنید.
  4. سرویس را با حداقل‌ترین امتیاز اجرا کنید تا تأثیر اجرای احتمالی محدود بماند.
→ بازگشت به دانشنامه
PUT IT TO THE TEST

امنیت سامانه‌ی شما را
به مهاجمان واگذار نکنید

تیم پی‌هانتر با دیدِ یک مهاجم واقعی، این آسیب‌پذیری‌ها و ده‌ها مورد دیگر را روی دارایی‌های شما می‌سنجد.