امروز روز گرد و خاک پاک کردن بود. گوگل ریدرم رو مرتب کردم. پلاگینهای اضافی وردپرس رو پاک کردم و یک سری از اونها رو به روز کردم. مرحلهی آخرم، آپدیت کردن وردپرس بود که به دلیل تنبلی، از مدتها قبل تصمیم به آپگرید اون داشتم (:
روی بهروزرسانی وردپرس فارسی کلیک میکنم. همه چیز خوب پیش میره و من یک لبخند که از آرامش خاطرم ایجاد شده، بر لبم دارم که ناگهان با خطایی مبنی بر عدم توانایی در ایجاد و پاک کردن چند فایل مواجه میشم. اولین کاری که بعد از این رخداد کردم این بود که به بلاگ رفتم و بله! با صفحهی خالی رو به رو شدم. رسما بلاگ پکیده بود!
به خودم گفتم هیچ مشکلی نیست! به صورت دستی به روز میکنیم! کارهای لازم برای بهروزرسانی وردپرس به صورت دستی رو انجام دادم ولی مشکل همچنان سر و پا بود. مانند یک دندان درد!
خب صرفا تنها چیزی که بهم امید میداد(در حد نور چوب کبریت!) این بود که دیتابیس سالم هست و تغییر نکرده! پس خیالم از بابت محتویات بلاگ راحت بود!
تنها راهحلی که داشتم کندن دندان از ریشه بود! کلا همه چیز رو پاک کنم و از اول کپی و دوباره نصب کنم. خب خدا رو شکر میکنم که از روی سرور، دانلود کردن یک وردپرس تازه کار مشکلی نیست! و مثل همیشه آماده میشم که به وسیلهی SSH به سرور وصل شم که همون لحظه تصویری از جلوی چشمم با سرعت خیلی زیادی رد شد! “ما در مرحلهی آزمایشی اینترنت ملی هستیم! SSH نداریم!”. یک مقدار لبخند صورتم کج میشه.
با توجه به جوی که در اون قرار داشتم، به ذهنم نرسید که باز هم میتوان از PHP کمک گرفت ولی همونطور که گفتم با توجه به شرایط به ذهنم نرسید و مجبور شدم به سراغ FileZilla بروم و از FTP کمک بگیرم!
شروع میکنم به پاک کردن کل فایلها! تقریبا یک ربعی طول میکشد و در آخر با چند Operation Not Permitted که مانند ریشههای دندان هستند، مواجه میشم!
اینجاست که به سراغ PHP میرم! صورت مساله چی بود دقیقا؟ من پسوورد یکی از یوزرهای sudoer سرور رو داشتم! به علاوه از exec پیاچپی هم میتونستم استفاده کنم!
حالا اینجا یک مسالهای وجود داشت! پسوورد sudo رو چطوری وارد کنم؟! چون داخل ترمینال نیستم که! پس کمی جستوجو در manpageها و اینترنت، به سوییچ stdin یا -S برای sudo میرسم که دقیقا همون کاری رو که من میخوام، انجام میده!
بعد از تست انواع سوییچها و روشها به این نتیجه رسیدم که همواره این sudo برای www-data اجرا میشه نه یوزری که من پسووردش رو دارم! پس به سمت استفاده از su میرم. مطمئن میشم که su راهکار من هست و روی سیستم خودم نیز تست میکنم! خوشحال و خندان در حال خوندن manpage مربوط به su بودم که ناگهان قلبم ریخت! در su دیگر سوییچی مانند -S وجود نداشت! از طرفی، به خاطر دلایل امنیتی، استفاده از pipe برای پاس دادن پسوورد از طریق stdin، در su بسته شده بود و با ارور su: must be run from a terminal مواجه میشدیم!
با کلی جستوجو به نتیجهای که قبلا هم با اون مواجه شده بودم میرسم. اون نتیجه این بود که expect میتونه چاره ساز باشه! دقیقا همینکاری رو که من میخواستم انجام میداد. تعیین میکردیم که اگر از ما مثلا پسوورد درخواست کرد، فلان چیز رو send کن! روی سیستم خودم هم تست کردم و با کلی خوشحالی اومدم که روی سرور پیاده کنم! همونطور که احتمالا حدس زدید، متوجه شدم که expect روی سرور نصب نیست و من نیز به دلیل بسته بودن SSH و نداشتن دسترسی کافی، قادر به نصب اون نیستم! پس expect کلا منحل شد!
ناامید در حال جستوجو کردن بودم که به یک وبلاگ جالب که مال یک هکر لینوکسی بود رسیدم! میتونم بگم وبلاگ خاص و جالبی داشت و سبکش مقداری با سبک بلاگهای مرتبط با هکینگ دیگه کمی فرق داشت.
خب این شخص روشی برای دور زدن این محدودیت امنیتی su پیشنهاد کرده بودند! مساله اصلی ما این بود که su نمیگذاشت که پسوورد رو از طریق pipe کردن و امثالهم به stdin وارد کنیم!
خب میشه کار تقریبا کار expect رو کرد. یعنی انگار یک نفر داره پسوورد رو وارد میکنه!(به صورت فیزیکی). راهکاری که ایشون پیشنهاد داده بودن استفاده از تابع ioctl و TIOCSTI در زبان C بود. قابل ذکره که manpage این تابع نیز وجود داره. TIOC مخفف عبارت Terminal IO Ctl و STI مخفف Stimulate Terminal Input هست.
خب پس ما به وسیلهی این تابع میتونیم برنامهی سادهای بنویسیم که ورودی خودش رو به صورت input به ترمینال میده (به عبارتی اجراش میکنه منتها به این شکل انگار یک نفر اون رو به صورت فیزیکی وارد کرده)
#include <sys/ioctl.h> #include <string.h> #include <stdio.h> main(int argc, char *argv[]) { char c[30]; int i; sprintf(c, "%s\\n", argv[1]); for(i = 0 ; i < strlen(c) ; i++) ioctl(0 , TIOCSTI , c+i); }
کامپایلش میکنم با اسم مثلا code_1 و بعدش تست روی سیستم خودم. تقریبا شبیه به این نمونهی جالب میشه :
$ (sleep 1; ./code_1 Passw0rD; ) & su - [1] 3486 Password: sh-3.1# /* It worked */
بله! جواب میده کاملا! خب حالا کد PHPم رو جوری تنظیم کردم که بیاد این اسکریپت رو همراه با دستوراتی که میخوام اجرا کنه. تقریبا چیزی شبیه به این:
echo exec("(sleep 1; ./code_1 PASSWORD; ) & su USER -c \'rm -rf CONTENTS TO DELETE\' - 2> ErrorLog");
پس از اجرا میبینم که همان نتیجه sudo برقرار است و برای www-data اجرا شده و نه یوزری که من میخوام و یک لحظه دقت میکنم و میبینم که در داخل execی که داخل php استفاده میکنیم، اصولا بحث input و … پیش نمیاد! من ابله با این اسکریپت دقیقا کجا دارم با ioctl پسوورد مینویسم؟! اگر داخل ترمینال بودم، اونوقت این داخل ترمینال رخ میداد ولی در اینجا که اصلا خبری از ترمینال نیست! و میبینم که تقریبا ۴ ساعت از وقتم رو تلف کردم!
با خودم میگم ۲ ساعت دیگه هم میذارم روی این ۴ ساعت بدین ترتیب کردم همه چیز رو پاک میکنم و بعد با php، وردپرس نسخهی جدید رو دانلود میکنم. سپس با php اون رو extract میکنم و دوباره کانفیگش میکنم(کانفیگش واقعا آسون بود. تقریبا از من چیزی نپرسید. از چیزهای باقی مونده از ورژن قبلی استفاده کرد).
و این بود داستان و بدبختی ما. نکتهی جالب توجه اینه که من واقعا حداقل ۶ ساعت دربارهی این موضوع وقت صرف کردم در حالیکه اگر دسترسی ssh داشتم به ۲۰ دقیقه هم نمیرسید!
پینوشت : فولدر آپلود ورژن قبلی کلا پاک شد و در نتیجه عکسهای مربوط به یکسری از پست ها نابود شدند :دی
پینوشت : از تابع ftp_exec پی اچ پی هم استفاده کردم که نتیجهای در بر نداشت! ارور عجیب میداد که آخرش هم درست نشد!
منابع( + , + )