ধারণার প্রমাণ
[email protected]:~/PwnKit-Exploit$ make
cc -Wall exploit.c -o exploit
[email protected]:~/PwnKit-Exploit$ whoami
debian
[email protected]:~/PwnKit-Exploit$ ./exploit
Current User before execute exploit
[email protected]$whoami: debian
Exploit written by @luijait (0x6c75696a616974)
[+] Enjoy your root if exploit was completed succesfully
[email protected]:/home/debian/PwnKit-Exploit# whoami
root
[email protected]:/home/debian/PwnKit-Exploit#
ঠিক করুন
আদেশ | ব্যবহার করুন |
---|---|
sudo chmod 0755 pkexec |
CVE 2021-4034 ঠিক করুন |
ইনস্টলেশন এবং ব্যবহার
git clone https://github.com/luijait/PwnKit-Exploit
cd PwnKit-Exploit
make
./exploit
whoami
আদেশ | ইউটিলিটি |
---|---|
make clean |
পরিবর্তিত কোড পরীক্ষা করার জন্য বিল্ড পরিষ্কার করুন |
ব্যাখ্যা
ভিত্তিক ব্লগ । গুণ _ com
pkexec এর main() ফাংশনের শুরু কমান্ড-লাইন আর্গুমেন্টগুলিকে প্রক্রিয়া করে (লাইন 534-568), এবং প্রোগ্রামটি চালানোর জন্য অনুসন্ধান করে, যদি এর পাথ পরম না হয়, PATH এনভায়রনমেন্ট ভেরিয়েবলের ডিরেক্টরিতে (লাইন 610-640) ):
435 main (int argc, char *argv[])
436 {
...
534 for (n = 1; n < (guint) argc; n++)
535 {
...
568 }
...
610 path = g_strdup (argv[n]);
...
629 if (path[0] != '/')
630 {
...
632 s = g_find_program_in_path (path);
...
639 argv[n] = path = s;
640 }
দুর্ভাগ্যবশত, যদি কমান্ড-লাইন আর্গুমেন্টের সংখ্যা argc হয় – যার মানে যদি আর্গুমেন্ট লিস্ট argv যা আমরা execve() এ পাস করি সেটি খালি থাকে, অর্থাৎ {NULL} – তাহলে argv[0] NULL হয়। এটি আর্গুমেন্ট লিস্টের টার্মিনেটর। অতএব:
লাইন 534 এ, পূর্ণসংখ্যা n স্থায়ীভাবে 1 এ সেট করা হয়েছে; লাইন 610 এ, পয়েন্টার পাথ argv[1] থেকে সীমার বাইরে পড়া হয়; লাইন 639 এ, পয়েন্টার s লেখা হয় সীমার বাইরে argv[1] তে। কিন্তু ঠিক কি থেকে পড়া এবং লেখা হয় এই সীমার বাইরের argv[1]?
এই প্রশ্নের উত্তর দেওয়ার জন্য, আমাদের সংক্ষিপ্তভাবে বিস্তৃত হতে হবে। যখন আমরা একটি নতুন প্রোগ্রাম execve() করি, তখন কার্নেল আমাদের আর্গুমেন্ট, এনভায়রনমেন্ট স্ট্রিং এবং পয়েন্টার (argv এবং envp) নতুন প্রোগ্রামের স্ট্যাকের শেষে কপি করে; উদাহরণ স্বরূপ:
|---------+---------+-----+------------|---------+---------+-----+------------|
| argv[0] | argv[1] | ... | argv[argc] | envp[0] | envp[1] | ... | envp[envc] |
|----|----+----|----+-----+-----|------|----|----+----|----+-----+-----|------|
V V V V V V
"program" "-option" NULL "value" "PATH=name" NULL
স্পষ্টতই, কারণ argv এবং envp পয়েন্টার মেমরিতে সংলগ্ন, যদি argc 0 হয়, তাহলে সীমার বাইরের argv[1] আসলে envp[0], আমাদের প্রথম পরিবেশ পরিবর্তনশীল, “মান” এর পয়েন্টার। অতএব:
লাইন 610 এ, প্রোগ্রামের পাথটি argv[1] (যেমন envp[0]) থেকে সীমার বাইরে পড়া হয় এবং “মান” নির্দেশ করে; লাইন 632 এ, এই পাথ “মান”টি g_find_program_in_path() এ পাস করা হয়েছে (কারণ “মান” একটি স্ল্যাশ দিয়ে শুরু হয় না, লাইন 629 এ); তারপর, g_find_program_in_path() আমাদের PATH এনভায়রনমেন্ট ভেরিয়েবলের ডিরেক্টরিতে “মান” নামে একটি এক্সিকিউটেবল ফাইল অনুসন্ধান করে; যদি এই ধরনের একটি এক্সিকিউটেবল ফাইল পাওয়া যায়, তাহলে এর সম্পূর্ণ পাথ pkexec এর main() ফাংশনে (লাইন 632 এ) ফেরত দেওয়া হয়; অবশেষে, লাইন 639-এ, এই সম্পূর্ণ পথটি argv[1] (অর্থাৎ envp[0]) এর সীমার বাইরে লেখা হয়েছে, এইভাবে আমাদের প্রথম পরিবেশ পরিবর্তনশীলকে ওভাররাইট করা হয়েছে। সুতরাং, আরও স্পষ্টভাবে বলা হয়েছে:
যদি আমাদের PATH এনভায়রনমেন্ট ভেরিয়েবল “PATH=name” হয়, এবং যদি “name” ডিরেক্টরিটি বিদ্যমান থাকে (বর্তমান কাজের ডিরেক্টরিতে) এবং “value” নামে একটি এক্সিকিউটেবল ফাইল থাকে, তাহলে “name/value” স্ট্রিংটিতে একটি পয়েন্টার লেখা হয়। envp[0] এর সীমার বাইরে; বা
যদি আমাদের PATH হয় “PATH=name=.”, এবং যদি ডিরেক্টরি “name=.” বিদ্যমান এবং “মান” নামে একটি এক্সিকিউটেবল ফাইল রয়েছে, তারপর “নাম=./মান” স্ট্রিংটির একটি পয়েন্টার envp[0]-এ সীমার বাইরে লেখা হয়। অন্য কথায়, এই সীমার বাইরে লেখা আমাদেরকে pkexec এর পরিবেশে একটি “অনিরাপদ” পরিবেশ পরিবর্তনশীল (উদাহরণস্বরূপ, LD_PRELOAD) পুনরায় প্রবর্তন করতে দেয়। প্রধান() ফাংশন কল করার আগে এই “অনিরাপদ” ভেরিয়েবলগুলি সাধারণত SUID প্রোগ্রামের পরিবেশ থেকে (ld.so দ্বারা) সরানো হয়। আমরা নিম্নলিখিত বিভাগে এই শক্তিশালী আদিম শোষণ করব।
শেষ মুহূর্তের নোট: Polkit এছাড়াও Solaris এবং *BSD- এর মতো নন-লিনাক্স অপারেটিং সিস্টেমগুলিকে সমর্থন করে , কিন্তু আমরা তাদের শোষণের তদন্ত করিনি। যাইহোক, আমরা লক্ষ্য করি যে OpenBSD শোষণযোগ্য নয়, কারণ এর কার্নেল argc 0 হলে একটি প্রোগ্রাম execve() করতে অস্বীকার করে।