سفر من در رمزگشایی درخواست های رمزگذاری شده
بخش صفر: شروع مسیر
وقتی فقط یک IP از هدف داشته باشید، چه میکنید؟
چند روز پیش از من خواسته شد تا یک وب اپلیکیشن که با استفاده از سیستم ساز برسا پیاده سازی شده بود را بررسی کنم. تنها چیزی که از این اپلیکیشن میدانستم، IP یک نمونه نصب شده از آن بود. طبق معمول Burp Suite را باز کرده و گشت و گذار در این وب اپلیکیشن (مثل یک کاربر عادی) را شروع کردم. چیز خاصی به غیر از یک صفحه لاگین وجود نداشت :(
برای گرفتن درخواست لاگین، نام کاربری و رمز عبور را عبارت admin را وارد کردم ولی با چنین درخواستی مواجه شدم:
چند روز پیش از من خواسته شد تا یک وب اپلیکیشن که با استفاده از سیستم ساز برسا پیاده سازی شده بود را بررسی کنم. تنها چیزی که از این اپلیکیشن میدانستم، IP یک نمونه نصب شده از آن بود. طبق معمول Burp Suite را باز کرده و گشت و گذار در این وب اپلیکیشن (مثل یک کاربر عادی) را شروع کردم. چیز خاصی به غیر از یک صفحه لاگین وجود نداشت :(
برای گرفتن درخواست لاگین، نام کاربری و رمز عبور را عبارت admin را وارد کردم ولی با چنین درخواستی مواجه شدم:
Encrypted Request Body |
Encrypted Response Body |
Request/Response
Body رمزگذاری شده بود! با دیدن این Request/Response اولین فکری که به
ذهن من رسید این بود: رمزگذاری از سمت کاربر انجام شده بود! کنجکاو بودم
بدانم که اپلیکیشن درحال مخفی کردن چه دیتایی از کاربر بود، پس به بررسی
بیشتر پرداختم.
بخش اول: پیدا کردن الگوریتم رمزنگاری
خواندن
فایلهای JS که توسط وب اپلیکیشن بارگذاری شده بود را شروع کردم و با جستجو
کردن کلمههای Encrypt و Decrypt بین این فایلها، به کد زیر رسیدم:
Obfuscated JS File |
کل
کد مبهم سازی شده بود (Obfuscated) و تقریبا غیر ممکن بود که بتوانم از
خواندن این کد به نتیجهای برسم. پس فعلا بررسی این کدها را به تعویق
انداختم و به تستهای بیشتر روی صفحه ورود مشغول شدم.
مدتی گذشت تا اینکه یکی از بهترین هکرهایی که میشناسم این ترفند را به من یاد داد:
In the browser, I picked a form that I can put my text in and submit, like the login form for example.
then I set breakpoints in the browser developer-tool for Mouse > onClick event, So as soon as I click on "login" the breakpoint triggers and then from there just keep going ("step in") until the code reaches the point that it needs to encode/encrypt the request before sending it to the server.
then I set breakpoints in the browser developer-tool for Mouse > onClick event, So as soon as I click on "login" the breakpoint triggers and then from there just keep going ("step in") until the code reaches the point that it needs to encode/encrypt the request before sending it to the server.
برای انجام دادن این عمل روی مرورگر Firefox، مراحل زیر را با من دنبال کنید:
- در صفحهی هدف دیتای مورد نظر را وارد میکنیم (مانند نام کاربری و رمز عبور).
- در صفحه راست کلیک کرده و روی گزینهی Inspect کلیک میکنیم.
- به تب Debugger میرویم.
- از قسمت Filter by event types، گزینهی click را از منوی Mouse کلیک میکنیم.
- حالا
با کلیک کردن روی عمل موردنظر (مانند گزینهی Login)، مرورگر به حالت
Debug رفته و از آن لحظه به بعد میتوانیم تک تک کدهایی را که ورودی ما را
پردازش میکند را بررسی کنیم.
توجه
داشته باشید که مراحل بالا در هر مرورگر مدرنی قابل انجام هستند. یکی دیگر
از مزایای استفاده از دیباگر مرورگر در چنین مواردی، امکان دسترسی به کد
Deobfuscated بدون نیاز به هیچ ابزار جانبی و پیچیده است.
با انجام دادن مراحل بالا، به خط زیر رسیدم:Clear-text JS Code |
اپلیکیشن با استفاده از تابع Encrypt2 ورودی کاربر را قبل از ارسال، رمزگذاری میکند:
Encryption Function |
قسمتهای مهم این تابع را باهم بررسی میکنیم:
- خطوط ۱۱، ۱۹ و ۲۴ برای حالتی شبیه Debug هستند که احتمالا Developer فراموش کرده آنها را از کد حذف کند. کاری که این خطوط می کنند این است که یک گزارشی از تغییرات اعمال شده روی متن را در Console مرورگر ثبت میکنند.
- مهم ترین قسمت کد در خط ۱۳ اتفاق میافتد؛ جایی که پس از کد گذاری کردن متن ورودی با UTF-8 ، برنامه نویس با استفاده از تابع map سراغ تک تک کاراکترهای متن ورودی رفته و عملیات زیر را روی هر کاراکتر انجام میدهد:
(c + n) % 256
به
طور خلاصه، این تابع ASCII Code متناظر هر کاراکتر را با یک شیفت یا آفست
(که بصورت Hardcode مقداردهی شده است) جمع کرده و باقیماندهی این حاصل جمع
با عدد 256، عدد نهایی را تشکیل میدهد.
علت استفاده از عدد 256 این است که حاصل جمع عدد c و n از یک بایت بیشتر نشود.
علت استفاده از عدد 256 این است که حاصل جمع عدد c و n از یک بایت بیشتر نشود.
1 byte = 2^8 = 256 unique values
- سپس در خط ۲۱، این اعداد به کاراکتر متناظر خودشان در جدول ASCII تبدیل شده و متن رمزگذاری شدهی را میسازند.
فرض
کنیم که ما تابع Decrypt را نداشتیم. با کمی تفکر روی الگوریتم رمزگذاری
حدس میزنیم که کاراکترها را میتوانیم بصورت زیر Decrypt کنیم:
(c - n) % 256
در
این معادله، c معادل ASCII کاراکتری از متن رمزگذاری شده و n همان آفستی
هست که کاراکتر اصلی را شیفت داده بود. همانند بالا علت استفاده از
باقیمانده عدد 256 این است که حاصل تفریق عدد c و n از یک بایت بیشتر نشود.
حال به عملکرد کد برنامه نویس برای نوشتن تابع Decrypt میپردازیم:
Line that starts the decryption |
Decryption function |
همانطور
که در خط 39 میبینیم، تقریبا حدسمان درست بود با این تفاوت که کد برنامه
نویس یک عمل جمع با عدد 256 اضافه دارد. حدس میزنیم که برنامه نویس برای
اینکه عدد حاصل از تفریق c و n همیشه یک عدد مثبت صحیح بدست بیاید این کار
را انجام داده است.
ولی
همونطور که گفتیم، عددی که از معادلهی رمزگذاری بدست میآید، همیشه بین 0
و 255 قرار میگیرد و میتوان گفت که عملگر جمع در کد برنامه نویس اضافی
محسوب میشود.
نکتهی
حائز اهمیت این است که این الگوریتم رمزگذاری، یک روش متداول ناامن برای
پیادهسازی رمزگذاری خودتان است و توسعه دهندگان باید از استفاده از آن
اجتناب کنند.
در رمزنگاری، به این نوع روش رمزگذاری کردن، رمز سزار (به انگلیسی: Caesar cipher) گفته میشود.
در این نوع روش، هر حرف از متن ساده (Plain Text) با حرف دیگری با یک فاصلهی ثابت جایگزین میشود. برای اطلاعات بیشتر دربارهی رمزگذاری سزار میتوانید به این صفحه از ویکیپدیا مراجعه کنید.
همچنین باید توجه داشت که این اپلیکیشن روش رمزگذاری امنتری را نیز در اختیار برنامه نویس قرار میدهد که براساس روشهای مرسوم رمزگذاری دادههای JSON پیاده سازی شده است.
یکی از این روشها استفاده از فریمورک JOSE است که مخفف عبارت JSON Object Signing and Encryption است. JOSE چارچوبی است که روشی را برای انتقال امن ادعاها (Claims) بین طرفین فراهم میکند. چارچوب JOSE از چندین مشخصات برای این منظور تشکیل شده است (برگرفته از این نوشته):
در این نوع روش، هر حرف از متن ساده (Plain Text) با حرف دیگری با یک فاصلهی ثابت جایگزین میشود. برای اطلاعات بیشتر دربارهی رمزگذاری سزار میتوانید به این صفحه از ویکیپدیا مراجعه کنید.
همچنین باید توجه داشت که این اپلیکیشن روش رمزگذاری امنتری را نیز در اختیار برنامه نویس قرار میدهد که براساس روشهای مرسوم رمزگذاری دادههای JSON پیاده سازی شده است.
یکی از این روشها استفاده از فریمورک JOSE است که مخفف عبارت JSON Object Signing and Encryption است. JOSE چارچوبی است که روشی را برای انتقال امن ادعاها (Claims) بین طرفین فراهم میکند. چارچوب JOSE از چندین مشخصات برای این منظور تشکیل شده است (برگرفته از این نوشته):
- JWK, فرمت و مدیریت کلیدهای رمزنگاری را در JOSE شرح می دهد.
- JWS, تولید و مدیریت پیام های امضا شده را توصیف می کند.
- JWE, تولید و مدیریت پیام های رمزگذاری شده را توصیف می کند.
- JWA, الگوریتم های رمزنگاری مورد استفاده در JOSE را توصیف می کند.
- JWT, نمایش ادعاهای کدگذاری شده در JSON و محافظت شده توسط JWS یا JWE را شرح می دهد.
بخش دوم: Fun with Python
با
داشتن الگوریتم رمزگذاری، به یک اسکریپت ساده پایتون برای رمزگذاری و
رمزگشایی دادهها نیاز داشتم. چالشی که با آن در این بخش رو به رو بودم این
بود که این الگوریتم رمزگذاری، باعث بوجود آمدن کاراکترهایی میشد که خارج
از محدودهی قابل پرینت(32 تا 126) قرار میگرفتند، مانند "اعداد":
In [13]: ord('1')
Out[13]: 49
In [14]: (ord('1') + 100) % 256
Out[14]: 149
Out[13]: 49
In [14]: (ord('1') + 100) % 256
Out[14]: 149
به
همین علت هنگامی که پیلود رمزگذاری شده را از محیط Burp Suite به محیط
ترمینال ویندوز منتقل میکردم، بعضی از این کاراکترها منتقل نمیشدند و در
نتیجه باعث رمزگشایی ناقص میشدند. اولین ایدهای که برای حل این مشکل به
ذهن من رسید استفاده از فایل به عنوان ورودی/خروجی بود.
تابعی که برای رمزگشایی پیاده سازی کردم:
Python Decryption Function |
تابعی که برای رمزگذاری درخواستها پیاده سازی کردم:
Python Encryption Function |
بعد از رمزگشایی درخواست POST ، به نتیجهی زیر رسیدم:
{"un":"whoami","pwd":"whoami","otp":"","cap":"9bhcg","thm":"Gray","lng":"us-en","capId":"b830ed05-c651-4e76-86f8-fa207ddcd74b","action":"/api/auth/login"}
سپس، پاسخ سرور را رمزگشایی کردم:
Plain Text Server Response |
بخش سوم: حل مشکل بزرگ
استفاده
از اسکریپت پایتونی که در قسمت قبل مشاهده کردیم برای تست وب اپلیکیشن عمل
مشکلی بود. سادهتر بود اگر درخواستهای ما به صورت خودکار رمزگذاری و
رمزگشایی میشد. به همین علت افزونههای Burp Suite معقولترین گزینه برای
این کار بود.
آشنایی با Burp Extender
Burp Extender به شما امکان می دهد عملکرد Burp Suite را به طرق مختلف گسترش دهید.
با استفاده از Burp Extender به یک API توسعهپذیری بسیار غنی و قدرتمند مجهز میشویم که به برنامههای افزودنی ما اجازه میدهد کارهای مفید متعددی را انجام دهند. با استفاده از این API شما میتوانید:
با استفاده از Burp Extender به یک API توسعهپذیری بسیار غنی و قدرتمند مجهز میشویم که به برنامههای افزودنی ما اجازه میدهد کارهای مفید متعددی را انجام دهند. با استفاده از این API شما میتوانید:
- درخواست ها و پاسخ های HTTP را برای همه ابزارهای Burp پردازش و اصلاح کنید.
- به دادههای کلیدی زمان اجرا، مانند تاریخچه پروکسی، نقشه سایت هدف، و Issue های اسکنر دسترسی داشته باشید.
- تب های سفارشی و آیتم های جدیدی را به منوی رابط کاربری Burp اضافه کنید.
- ویرایشگر پیام HTTP Burp را برای مدیریت فرمتهای دادهای که Burp به طور بومی پشتیبانی نمیکند، سفارشی کنید و…
- امکان استفاده در ۳ زبان جاوا، پایتون و روبی.
ساختار یک افزونه ساده Burp Suite
یک
نکته کلی را قبل از اینکه به جزئیات افزونهها در زبان پایتون بپردازیم
باید در نظر داشته باشید: قبل از شروع کار یک افزونه، Burp به دنبال کلاسی
با نام BurpExtender در کدهاتی آن افزونه میگردد و سپس تابعی با نام
registerExtenderCallbacks را صدا میزند. در حقیقت این یک نقطه شروع برای
افزونههای شماست و به شما این امکان را میدهد که امکانات و تواناییهای
افزونه خود را به Burp معرفی کنید.
در زبان پایتون، Burp بر Jython متکی است. پروژه Jython پیاده سازی پایتون را در جاوا فراهم میکند و مزایای اجرای بر روی JVM و دسترسی به کلاس های نوشته شده در جاوا را در اختیار پایتون قرار می دهد (منبع).
برای ادامه کار نیاز به فایل Standalone Jar داریم که لینک دانلود آن در این لینک فراهم شده است.
پس از دانلود، این فایل را در مسیر مشخصی قرار میدهیم. پس مسیر فایل Standalone Jar را به برنامه Burp Suite معرفی میکنیم. برای انجام این کار، از تب Extender به قسمت Options رفته و مانند تصویر زیر، مسیر فایل Standalone Jar را به Burp معرفی میکنیم.
در زبان پایتون، Burp بر Jython متکی است. پروژه Jython پیاده سازی پایتون را در جاوا فراهم میکند و مزایای اجرای بر روی JVM و دسترسی به کلاس های نوشته شده در جاوا را در اختیار پایتون قرار می دهد (منبع).
برای ادامه کار نیاز به فایل Standalone Jar داریم که لینک دانلود آن در این لینک فراهم شده است.
پس از دانلود، این فایل را در مسیر مشخصی قرار میدهیم. پس مسیر فایل Standalone Jar را به برنامه Burp Suite معرفی میکنیم. برای انجام این کار، از تب Extender به قسمت Options رفته و مانند تصویر زیر، مسیر فایل Standalone Jar را به Burp معرفی میکنیم.
Setting JAR Path in Burp |
حال فایلی را با اسم دلخواه و پسوند py میسازیم و محتوای زیر را در آن قرار داده و فایل را ذخیره میکنیم (منبع):
Plugin Structure |
سپس
به تب Extensions بروید و یک پلاگین جدید اضافه کنید. نوع پسوند "Python"
را انتخاب کرده و محل فایل خود را مشخص کنید. این مثال هیچ کاری انجام نمی
دهد، اما باید بدون هیچ خطایی در Burp بارگیری شود.
افزونه Hello World
به تکه کد زیر توجه کنید (برخی توضیحات با کمک ChatGPT صورت گرفته است):Hello World Plugin |
- پلاگین را با import کردن IBurpExtender شروع میکنیم. IBurpExtender یک رابط ارائه شده توسط Burp Suite است که باید توسط افزونه های سفارشی پیاده سازی شود.
- کلاسی را با نام BurpExtender میسازیم که وارثی از رابط IBurpExtender است. این بدان معناست که BurpExtender درحال پیاده سازی متدهای لازم برای استفاده از رابط IBurpExtender است. باید توجه داشته باشیم که نام این کلاس باید حتما BurpExtender باشد (منبع).
- تابع registerExtenderCallbacks پس از بارگذاری افزونه صدا زده میشود. این تابع یک Instance از آبجکت IBurpExtenderCallbacks را ثبت میکند که به وسیلهی آن ما به متدهایی که ممکن است در طول نوشتن افزونه به آنها نیاز پیدا کنیم، دسترسی مییابیم.
- در ادامه با استفاده از متد setExtentionName نام افزونه خود را ثبت میکنیم.
- در نهایت متن "!Hello World" را در خروجی چاپ میکنیم.
تبدیل کد پایتون به افزونه
در ابتدایی ترین نسخه، نیاز داشتم تا دادهی رمزگذاری و رمزگشایی شده را در کنار درخواست اصلی مشاهده کنم. در این صفحه از وب سایت PortSwigger نمونههایی وجود دارد که یکی از آنها توجه من را به خود جلب کرد:
این نمونه کد به زبان پایتون در این GitHub قرار دارد.
در ابتدا به بررسی این نمونه کد میپردازیم و سپس کدهای پایتون خود را به آن اضافه میکنیم.
در ابتدا به بررسی این نمونه کد میپردازیم و سپس کدهای پایتون خود را به آن اضافه میکنیم.
بررسی کد نمونه
- در ابتدایی ترین خطوط این کد شاهد بارگذاری کردن ۳ کلاس جدید هستیم:
کلاس IMessageEditorTabFactory
این
کلاس برای ثبت یک تب جدید در اپلیکیشن Burp Suite استفاده میشود. برای
ثبت یک تب جدید خط زیر را در قسمت registerExtenderCallbacks افزونه خود
اضافه میکنیم:
callbacks.registerMessageEditorTabFactory(self)
کلاس IMessageEditorTab
افزونههایی که خود را به عنوان یک IMessageEditorTabFactory ثبت میکنند، باید یک آبجکت را از این کلاس برگردانند (که در این خط از نمونه کد به وسیلهی تابع createNewInstance انجام شده است)
کلاس IParameter
این کلاس رابطی است که اطلاعات پارامترهای یک درخواست HTTP را نگهداری میکند.
- در ادامه پس از ثبت کردن اسم افزونه و همچنین ثبت کردن افزونه به عنوان یک IMessageEditorTabFactory، آبجکتی از کلاسی که قرار است مسئول تب جدید ما باشد را به وسیلهی تابع createNewInstance برمیگردانیم.
کلاس IMessageEditorController: این رابط توسط کلاسی از جنس IMessageEditorTab استفاده میشود که اطلاعاتی را دربارهی پیامی که درحال حاضر درحال نمایش داده شدن است بدست آورد.
با استفاده از extender، ما به متدهایی که کلاس IBurpExtenderCallbacks در اختیارمان قرار میدهد، دسترسی پیدا میکنیم.
editable از نوع boolean است و نشان دهنده این است که آیا پیام در حال مشاهده قابل ادیت است یا خیر.
به وسیله تابع createTextEditor، یک نمونه از ویرایشگر متن Burp Suite میسازیم که در UI تب جدید افزونه خود از آن استفاده کنیم.
- حال به معرفی متدهایی که در کلاس Base64InputTab استفاده شدهاند میپردازیم:
متد getUiComponent: برنامهی Burp از این متد برای گرفتن محتویاتی که قرار است در تب جدید نمایش داده شود استفاده میکند.
متد isEnabled: این متد مشخص میکند که آیا تب جدید باید برای پیام درحال مشاهده نمایش داده شود یا خیز.
متد setMessage: برای نمایش پیام HTTP در تب جدید استفاده میشود.
متد getMessage: برای گرفتن پیام HTTP که در حال نمایش است استفاده میشود که این پیام ممکن است توسط user تغییر داده شده باشد.
متد isModified: یک مقدار boolean برمیگرداند که نشان دهنده این است که آیا پیام توسط کاربر ادیت شده است یا خیر.
متد getSelectedData: این متد دیتایی را که در حال حاضر توسط کاربر انتخاب شده است را برمیگرداند.
اضافه کردن کد پایتون به افزونه
من برای رسیدن به اولین ورژن از افزونه، باید تغییراتی را روی کدهای افزونه نمونهی Burp انجام میدادم.
تغییرات تابع isEnabled
این تابع تعیین کنندهی فعال بودن یا نبودن تب جدید برای هر پیام HTTP است.
من در ابتدا تغییراتی را روی این تابع اعمال کردم تا افزونه، تب جدید را برای تمامی درخواستهای POST ای فعال کند که همزمان دو شرط زیر را دارا باشند:
من در ابتدا تغییراتی را روی این تابع اعمال کردم تا افزونه، تب جدید را برای تمامی درخواستهای POST ای فعال کند که همزمان دو شرط زیر را دارا باشند:
- تایپ Body درخواست از نوع JSON بوده.
- کلید "Parameters" در بدنه درخواست موجود باشد.
isEnabled Function |
در تابع بالا ابتدا چک میکنیم که آیا پیام HTTP مورد بررسی، پیام درخواست (Request) است.
در ادامه بایتهای پیام درخواست را که در متغیر content قرار گرفته است را به تابع analyzeRequest میدهیم. این تابع درخواست HTTP را آنالیز میکند و اطلاعات مختلفی را از آن استخراج کرده و بصورت یک آبجکتی از کلاس IRequestInfo برمیگرداند.
سپس با استفاده از تابع getMethod (که یکی از متدهای کلاس IRequestInfo است) متد درخواست را بررسی میکنیم که از نوع متد POST باشد.
برای گرفتن بدنهی درخواست POST ابتدا offset بدنه را به وسیلهی تابع getBodyOffset بدست میآوریم و با استفاده از آن و با استفاده از بایتهای پیام درخواست، بدنه درخواست را جدا میکنیم.
در نهایت بررسی میکنیم که آیا بدنهی درخواست POST از تایپ JSON باشد و در غیر این صورت، تب برای آن درخواست فعال نمیشود.
تغییرات تابع setMessage
این تابع تعیین میکند تا چه پیامی در تب جدید نمایش داده شود.تغییرات این بخش را به دو قسمت تقسیم Request و Response تقسیم میکنیم تا هر یک را با جزئیات بررسی کنیم.
بخش اول - Request
setMessage - Request Part |
همانند کاری که در قسمت قبل انجام دادیم، در این تابع نیز ابتدا offset بدنه درخواست را بدست میآوریم و به وسیله آن بدنه را از درخواست جدا میکنیم.
بدنه را به یک آبجکت JSON تبدیل میکنیم و چک میکنیم که کلید "parameters" در آن موجود باشد و در غیر این صورت پیغام "There is no parameters in request" را در تب نمایش میدهیم. در ادامه، مقدار متناظر با کلید "parameters" را به تابع decrypt میدهیم. حال ما به متن ساده درخواست دسترسی داریم و باید آن را در تب جدید نمایش دهیم.
نمایش سادهی افزونه (بدون Indent) خواندن آن را کمی دشوار میکرد، به همین علت از متد json.dumps استفاده میکنیم و متن ساده را همراه indent با مقدار ۴ را به این متد میدهیم.
در نهایت با استفاده از تابع setText، متن سادهی رمزگشایی شده را در تب جدید نمایش میدهیم.
بخش دوم - Response
setMessage - Response Part |
در این مرحله از کار نیاز است که به یک نحوی پاسخ های سرور مورد نظر را از بقیهی پاسخها جدا کنیم. برای انجام این کار میتوانیم از تارگتهای In Scope برنامه Burp Suite استفاده کنیم. به این صورت که Origin پیام پاسخ را بدست آورده و بررسی میکنیم که آیا این Origin در In Scope موجود است یا خیر. بدین منظور برای بدست آوردن Origin پیام پاسخ از تابع getHttpService استفاده میکنیم تا به جزئیات پیام همانند Protocol، Host و Port دسترسی پیدا کنیم.
تابع getHttpService، سرویس HTTP را برای پیام درخواست/ریسپانس به صورت آبجکتی از کلاس IHttpService برمیگرداند.
در ادامه با فراخوانی تابع isInScope بررسی میکنیم که پیام پاسخ در بخش In Scope برنامه موجود است یا خیر و در غیر این صورت، پیامی را در تب جدید نمایش میدهیم که بیانگر این است که این Origin در Scope وجود ندارد.
در ادامه بایتهای پیام پاسخ را به تابع analyzeResponse میدهیم. این تابع برای آنالیز کردن پیام پاسخ و دریافت اطلاعات از آن استفاده میشود و این اطلاعات را در قالب آبجکتی از کلاس IResponseInfo برمیگرداند.
پس از بدست آوردن Offset بدنهی پیام پاسخ و جدا کردن آن از بایتهای پیام، آن را به رشته (String) تبدیل کرده و سپس رشتهی بدست آمده را با استفاده از UTF-8 کد گذاری میکنیم.
نکته دیگری که در بررسی کدهای JavaScript بدست آمده بود این بود که پیامهای پاسخ رمزنگاری شده توسط سرور با کاراکترهای "$%$" شروع میشوند. از این نکته استفاده میکنیم و بررسی میکنیم که آیا بدنهی استخراج شده از بایتهای پیام پاسخ، با این کاراکترها شروع میشوند.
سپس بدنه پیام را به تابع decrypt میدهیم و خروجی این تابع را با استفاده از متد setText در تب جدید نمایش میدهیم.
Encrypted Req/Res |
Plain-Text Req/Res |
تکمیل افزونه
برای تکمیل افزونه و استفاده از آن به یک سری ویژگیها نیاز داشتیم که در ادامه به جزئیات هر یک میپردازیم:- امکان ویرایش متن رمزنگاری شده و ارسال آن به سرور
- اضافه کردن پنجرهای برای تنظیمات با قابلیتهای زیر
- رمزنگاری کردن خودکار تمامی درخواستهای خروجی Burp
- رمزگشایی کردن خودکار تمامی پاسخهای ورودی به Burp
- رمزنگاری کردن خودکار درخواستهای خروجی از قسمت Burp Intruder
ویرایش متن رمزگذاری شده و ارسال آن
به قابلیتی نیاز داشتیم تا تغییرات کاربر در تب جدید، به صورت خودکار رمزگذاری شده و روی درخواست اصلی اعمال گردد. برای پیاده سازی چنین ویژگیای، از تابع getMessage در کلاس IMessageEditorTab استفاده میکنیم:getMessage Function |
در ابتدای این تابع با استفاده از تابع isTextModified
بررسی میکنیم که آیا متن رمزگشایی شده (در تب جدید) توسط کاربر تغییر
داده شده است یا خیر و در صورت صحیح بودن این شرط، وارد بلاک if میشویم و
در غیر این صورت پیام Original درخواست را برمیگردانیم.
در بلاک if، ابتدا با استفاده از تابع getText پیام تغییر داده شده توسط کاربر را بدست میآوریم و آن را با فراخوانی تابع bytesToString به رشته تبدیل میکنیم و در نهایت آن را با فراخوانی تابع encrypt رمزگذاری میکنیم.
در ادامه پیام اصلی را بدست آورده و هدرهای HTTP و بدنهی آن را جدا میکنیم. علت انجام این کار این است که میخواهیم پیام جدید را همانند پیام قبلی ساخته و هدرها و یا دیگر اجزای بدنه پیام (مانند "action") را تغییری ندهیم.
بدنه پیام را به آبجکتی از JSON تبدیل کرده و کلید "action" و مقدار متناظر آن را در متغیری ذخیره میکنیم. سپس بدنه پیام را ساخته و با فراخوانی تابع stringToBytes آن را به بایت تبدیل میکنیم.
در نهایت با فراخوانی تابع buildHttpMessage و ارسال هدرها و بدنهی جدید ساخته شده به این تابع، پیام جدید HTTP را میسازیم و آن را return میکنیم.
تابع buildHttpMessage دو ورودی headers (از نوع لیستی از رشتهها) و body (از نوع آرایهای از بایتها) را به عنوان پارامتر دریافت کرده و پیام HTTP متناظر این ورودیها را برمیگرداند.
در بلاک if، ابتدا با استفاده از تابع getText پیام تغییر داده شده توسط کاربر را بدست میآوریم و آن را با فراخوانی تابع bytesToString به رشته تبدیل میکنیم و در نهایت آن را با فراخوانی تابع encrypt رمزگذاری میکنیم.
در ادامه پیام اصلی را بدست آورده و هدرهای HTTP و بدنهی آن را جدا میکنیم. علت انجام این کار این است که میخواهیم پیام جدید را همانند پیام قبلی ساخته و هدرها و یا دیگر اجزای بدنه پیام (مانند "action") را تغییری ندهیم.
بدنه پیام را به آبجکتی از JSON تبدیل کرده و کلید "action" و مقدار متناظر آن را در متغیری ذخیره میکنیم. سپس بدنه پیام را ساخته و با فراخوانی تابع stringToBytes آن را به بایت تبدیل میکنیم.
در نهایت با فراخوانی تابع buildHttpMessage و ارسال هدرها و بدنهی جدید ساخته شده به این تابع، پیام جدید HTTP را میسازیم و آن را return میکنیم.
تابع buildHttpMessage دو ورودی headers (از نوع لیستی از رشتهها) و body (از نوع آرایهای از بایتها) را به عنوان پارامتر دریافت کرده و پیام HTTP متناظر این ورودیها را برمیگرداند.
اضافه کردن پنجرهای برای شخصی سازی افزونه
در این قسمت به پنجرهای جدید در برنامه Burp نیاز داریم تا افزونه را با توجه به نیازمان تغییر دهیم:- گزینهای برای رمزگذاری کردن خودکار درخواستها
- گزینهای برای رمزگشایی خودکار پاسخها
- گزینهای برای رمزگذاری خودکار درخواستهای Intruder
در ادامه به معرفی و توضیح این کلاسها میپردازیم و در نهایت به بررسی کلاس نوشته شده میپردازیم.
یا همان AWT بزار اصلی پنجرهها، گرافیکها و ابزارهای رابط کاربری وابسته به پلتفرم جاوا است که این کلاسها از این پکیج در حال بارگذاری شدن هستند:
کلاس BorderLayout
یک Container (ظرف) میسازد و آن را طوری تنظیم میکند که در پنج منطقه قرار گیرند: شمال، جنوب، شرق، غرب و مرکز.
کلاس Font
کلاس Font نشان دهنده فونت هایی است که برای نمایش متن به شکل قابل مشاهده استفاده می شود.
کلاس Insets
یک شیء Insets نمایشی از مرزهای یک Container است. فضایی را که یک Container باید در هر یک از لبه های خود بگذارد را مشخص می کند. این فاصله می تواند یک حاشیه، یک فضای خالی یا یک عنوان باشد.
کلاس JCheckBox
یک چک باکس را ایجاد میکند -- موردی که می تواند انتخاب یا از حالت انتخاب خارج شود و وضعیت خود را به کاربر نمایش می دهد.
کلاس JPanel
ظرفی است که میتواند گروهی از اجزا را ذخیره کند. وظیفه اصلی JPanel سازماندهی کامپوننت ها است، طرح های مختلفی را می توان در JPanel تنظیم کرد که سازماندهی بهتر اجزا را فراهم می کند.
کلاس EmptyBorder
کلاسی که یک مرز خالی و شفاف را فراهم می کند که فضا را اشغال می کند اما چیزی را ترسیم نمی کند.
کلاس ITab
این کلاس برای معرفی پنجره جدید به UI برنامه Burp استفاده میشود.پکیج Abstract Window Toolkit
برای معرفی کلاسهای بارگذاری شدهی Java، از داکیومنت Oracle و ویکیپدیا کمک میگیریم.یا همان AWT بزار اصلی پنجرهها، گرافیکها و ابزارهای رابط کاربری وابسته به پلتفرم جاوا است که این کلاسها از این پکیج در حال بارگذاری شدن هستند:
کلاس BorderLayout
یک Container (ظرف) میسازد و آن را طوری تنظیم میکند که در پنج منطقه قرار گیرند: شمال، جنوب، شرق، غرب و مرکز.
کلاس Font
کلاس Font نشان دهنده فونت هایی است که برای نمایش متن به شکل قابل مشاهده استفاده می شود.
کلاس Insets
یک شیء Insets نمایشی از مرزهای یک Container است. فضایی را که یک Container باید در هر یک از لبه های خود بگذارد را مشخص می کند. این فاصله می تواند یک حاشیه، یک فضای خالی یا یک عنوان باشد.
پکیج Swing
یک کتابخانه JFC و یک افزونه از AWT است. Swing عملکرد بسیار بهبود یافته ای را نسبت به AWT همراه اجزای جدید، ویژگی های اجزای توسعه یافته و مدیریت رویداد عالی با پشتیبانی از کشیدن و رها کردن (Drag and Drop) ارائه می دهد. این کلاسها از این پکیج در حال بارگذاری شدن هستند:کلاس JCheckBox
یک چک باکس را ایجاد میکند -- موردی که می تواند انتخاب یا از حالت انتخاب خارج شود و وضعیت خود را به کاربر نمایش می دهد.
کلاس JPanel
ظرفی است که میتواند گروهی از اجزا را ذخیره کند. وظیفه اصلی JPanel سازماندهی کامپوننت ها است، طرح های مختلفی را می توان در JPanel تنظیم کرد که سازماندهی بهتر اجزا را فراهم می کند.
کلاس EmptyBorder
کلاسی که یک مرز خالی و شفاف را فراهم می کند که فضا را اشغال می کند اما چیزی را ترسیم نمی کند.
بررسی کلاس نوشته شده
Custom Tab Class |
ابتدا در نقطه شروع کلاس (تابع __init__) آبجکتی از کلاس JPanel و آبجکتهایی را از کلاس JCheckBox با کپشنهای مورد نیازمان و نامهای متناظر میسازیم.
تابع getTabCaption مسئول برگرداندن نام تب جدید (پنجره جدید) است.
برنامه Burp از تابع getUiComponent برای دستیابی به محتویات تب جدید استفاده میکند که در این تابع ما از تابع نوشتهشده توسط خودمان به نام createUI استفاده میکنیم.
در شروع تابع createUI فونت نوشتاری چکباکسها را با ساختن آبجکتی از کلاس Font تشکیل میدهیم و سپس با فراخوانی متد setFont، این فونت را روی چکباکسها اعمال میکنیم.
در ادامهی این تابع با استفاده از کلاس Insets، فاصلههای چکباکسها از مرزها را تعیین میکنیم و این فاصلهها را با استفاده از کلاس EmptyBorder و فراخوانی متد setBoarder روی چکباکسها اعمال میکنیم و در نهایت تمامی این کامپوننتها را در ظرفی از نوع کلاس JPanel میریزیم.
حال برای استفاده از این کلاس در پلاگین خود، کد زیر را در تابع registerExtenderCallbacks از کلاس BurpExtender اضافه میکنیم:
ابتدا آبجکتی از کلاس جدیدمان میسازیم و با فراخوانی تابع addSuiteTab، تب جدید را به UI برنامه Burp اضافه میکنیم.
اسکرینشات این تب در تصویر زیر قابل مشاهده است:
Custom Tab |
اعمال تغییرات خودکار روی همه درخواست های ورودی/خروجی Burp
با استفاده از کلاس IHttpListener میتوانیم روی پیامهای HTTP ورودی و خروجی از Burp نظارت داشته باشیم و تغییرات مورد نیاز خود را روی این پیامها انجام دهیم. برای انجام این عمل، ابتدا کلاس IHttpListener را از پکیج burp بارگذاری میکنیم و سپس آن را به یکی از والدین کلاس BurpExtender اضافه میکنیم.از کلاس IHttpListener برای ثبت کردن افزونه خود به عنوان یک HTTP Listener استفاده میکنیم. برای انجام این کار، در تابع registerExtenderCallbacks خط زیر را اضافه میکنیم:
حال افزونهی ما به عنوان یک HTTP Listener در Burp ثبت شده است.
برای دیدن این پیامهای HTTP ورودی و خروجی از Burp از تابع processHttpMessage استفاده میکنیم.
تابع processHttpMessage سه ورودی را به عنوان پارامتر دریافت میکند:
- toolFlag: عددی که نشان میدهد پیام HTTP از کدام قسمت Burp منتشر شده است.
- messageIsRequest: یک Boolean است که نشان میدهد که پیام HTTP یک درخواست است یا یک پاسخ.
- messageInfo: حاوی اطلاعات پیام HTTP درخواست یا پاسخ در حال بررسی است.
بخش اول - رمزگذاری همهی درخواستها
processHttpMessage |
در
صورتی که نوع پیام HTTP در حال بررسی درخواست باشد و همچنین تیک چکباکس
گزینهی "رمزگذاری خودکار همهی درخواستها" خورده شده باشد، وارد بلاک
علامت گذاری شده با عدد ۱ میشویم.
در ابتدای این بلاک چک میکنیم که نوع متد درخواست HTTP از نوع POST باشد. سپس هدر و بدنهی درخواست POST را جدا کرده و در متغیرهای جداگانه ذخیره میکنیم.
سپس بدنه درخواست را به آبجکت JSON (یا همان dict در پایتون) تبدیل میکنیم و برای ادامهی کد بررسی میکنیم که کلید "parameters" (قسمتی که باید رمزگذاری شود) در آبجکت JSON وجود داشته باشد.
در این قسمت ۲ حالت را برای نوع مقدار "parameters" بررسی و پیاده سازی میکنیم:
اگر نوع "parameters" آبجکتی از dict باشد:
در این صورت مقدار متناظر با کلید "parameters" را از بدنه درخواست جدا میکنیم و آن را با فراخوانی تابع json.dumps به یک رشته تبدیل میکنیم چرا که اپلیکیشن در سمت سرور پس از رمزگشایی این عبارت، انتظار یک رشته را دارد.
سپس رشتهی بدست آمده را با استفاده از تابع encrypt، رمزگذاری میکنیم.
اگر نوع "parameters" آبجکتی از رشته (str) باشد
در این صورت مقدار متناظر کلید "parameters"را با استفاده از تابع encrypt رمزگذاری میکنیم.
در ادامه، بدنهی جدید پیام درخواست را میسازیم و با فراخوانی تابع json.dumps آن را به رشته تبدیل میکنیم و در نهایت با استفاده از تابع buildHttpMessage پیام HTTP درخواست جدید را میسازیم و آن را به وسیلهی تابع setRequest جایگزین پیام HTTP اصلی میکنیم.
در ابتدای این بلاک چک میکنیم که نوع متد درخواست HTTP از نوع POST باشد. سپس هدر و بدنهی درخواست POST را جدا کرده و در متغیرهای جداگانه ذخیره میکنیم.
سپس بدنه درخواست را به آبجکت JSON (یا همان dict در پایتون) تبدیل میکنیم و برای ادامهی کد بررسی میکنیم که کلید "parameters" (قسمتی که باید رمزگذاری شود) در آبجکت JSON وجود داشته باشد.
در این قسمت ۲ حالت را برای نوع مقدار "parameters" بررسی و پیاده سازی میکنیم:
اگر نوع "parameters" آبجکتی از dict باشد:
در این صورت مقدار متناظر با کلید "parameters" را از بدنه درخواست جدا میکنیم و آن را با فراخوانی تابع json.dumps به یک رشته تبدیل میکنیم چرا که اپلیکیشن در سمت سرور پس از رمزگشایی این عبارت، انتظار یک رشته را دارد.
سپس رشتهی بدست آمده را با استفاده از تابع encrypt، رمزگذاری میکنیم.
اگر نوع "parameters" آبجکتی از رشته (str) باشد
در این صورت مقدار متناظر کلید "parameters"را با استفاده از تابع encrypt رمزگذاری میکنیم.
در ادامه، بدنهی جدید پیام درخواست را میسازیم و با فراخوانی تابع json.dumps آن را به رشته تبدیل میکنیم و در نهایت با استفاده از تابع buildHttpMessage پیام HTTP درخواست جدید را میسازیم و آن را به وسیلهی تابع setRequest جایگزین پیام HTTP اصلی میکنیم.
بخش دوم - رمزگذاری درخواستهای Intruder
تنها تفاوتی که این بخش با قسمت قبلی دارد در این دو خط تصویر بالاست.
در شرط اول بررسی میکنیم که آیا تیک چکباکس عبارت "رمزگذاری فقط درخواستهای Intruder" خورده شده است یا خیر و در صورت صحیح بودن این شرط، بررسی میکنیم که آیا درخواست POST از سمت Intruder آمده است یا خیر و در صورت صحیح بودن این شرط، تمامی مراحل بلاک علامت گذاری شده با عدد ۱ بخش قبل را انجام میدهیم.
در شرط اول بررسی میکنیم که آیا تیک چکباکس عبارت "رمزگذاری فقط درخواستهای Intruder" خورده شده است یا خیر و در صورت صحیح بودن این شرط، بررسی میکنیم که آیا درخواست POST از سمت Intruder آمده است یا خیر و در صورت صحیح بودن این شرط، تمامی مراحل بلاک علامت گذاری شده با عدد ۱ بخش قبل را انجام میدهیم.
بخش سوم - رمزگشایی خودکار تمامی پاسخهای HTTP
processHttpMessage - Response Part |
در
صورتی که نوع پیام مورد بررسی از نوع پاسخ باشد و همچنین تیک چکباکس
گزینهی "رمزگذاری خودکار همهی درخواستها" خورده شده باشد، در تصویر بالا
وارد بلاک علامت گذاری شده با عدد ۱ میشویم.
در ابتدای این بلاک هدر و بدنهی پاسخ HTTP را جدا کرده و در متغیرهای جداگانه ذخیره میکنیم.
سپس بررسی میکنیم که محتویات بدنهی پاسخ HTTP با عبارت "$%$" شروع شود و در صورت صحیح بودن این شرط، محتویات بدنه پاسخ HTTP را به وسیلهی تابع decrypt رمزگشایی میکنیم، پیام HTTP جدید را میسازیم و در نهایت این پیام جدید را جایگزین پیام اصلی میکنیم.
در صورت اینکه محتویات بدنهی پاسخ HTTP با عبارت "$%$" شروع نشود، به وسیلهی Regex زیر سعی میکنیم تا اگر در بدنهی پاسخ HTTP محتویات رمزگذاری شدهای وجود داشته باشد، آن را رمزگشایی کرده و همانند بالا، پیام HTTP جدید را ساخته و جایگزین پیام اصلی میکنیم.
Regex استفاده شده در تصویر بالا:
در ابتدای این بلاک هدر و بدنهی پاسخ HTTP را جدا کرده و در متغیرهای جداگانه ذخیره میکنیم.
سپس بررسی میکنیم که محتویات بدنهی پاسخ HTTP با عبارت "$%$" شروع شود و در صورت صحیح بودن این شرط، محتویات بدنه پاسخ HTTP را به وسیلهی تابع decrypt رمزگشایی میکنیم، پیام HTTP جدید را میسازیم و در نهایت این پیام جدید را جایگزین پیام اصلی میکنیم.
در صورت اینکه محتویات بدنهی پاسخ HTTP با عبارت "$%$" شروع نشود، به وسیلهی Regex زیر سعی میکنیم تا اگر در بدنهی پاسخ HTTP محتویات رمزگذاری شدهای وجود داشته باشد، آن را رمزگشایی کرده و همانند بالا، پیام HTTP جدید را ساخته و جایگزین پیام اصلی میکنیم.
Regex استفاده شده در تصویر بالا:
مرور نسخه نهایی افزونه
در
این نسخه نهایی، ما میتوانیم از قابلیتهای Burp Suite در بررسی این وب
اپلیکیشن استفاده کنیم. قابلیتهایی مانند استفاده از Repeater و Intruder
که بدون این افزونه، استفاده از این ابزارها غیر ممکن بود. بعد از همه این
مراحل، تازه امکان ارزیابی امنیتی و شناسایی مشکلات امنیتی احتمالی در نرم
افزار فراهم میشود! نسخه نهایی این افزونه از لینک زیر قابل دریافت است و
از آن میتوان برای بررسی نسخه های جاری نرم افزار استفاده کرد.
و در نهایت از استادم جناب آقای کشفی که در نگارش این مقاله به طور مستمر مرا راهنمایی و یاری نمودند کمال تشکر را دارم.
نظرات
ارسال یک نظر