شما هنوز به سیستم وارد نشده اید.

#1 2018-03-19 17:16:14

Arcush
Moderator
ثبت شده: 2015-09-15
ارسال ها: 1,390

تفریح - حُقهٔ LD_PRELOAD

درود. در آستانهٔ سال نو، آرزوی تندرستی و شادی برای دوستانم دارم.

دیشب در گروه تلگرام، دربارهٔ اینکه چرا وجود برنامهٔ sudo در سیستم عامل لازم هست بحثی انجام شد؛ هم ازین جهت که سودبخش هست و هم ازین جهت که ممکنه خطرهایی هم به دنبال داشته باشه. من بخاطر کسب اطلاعات بیشتر در این باره، به جست و جو پرداختم و به اینجا برخوردم:
https://security.stackexchange.com/ques … udo/119413
گذشته ازینکه جوابی که تیک خورده بسیار عالی و زیبا نوشته شده، در لابلای بحث های اون صفحه به اینجا هم پیوند داده شده:
https://rafalcieslak.wordpress.com/2013 … -programs/
صفحه ای که علیرغم اینکه ارتباط خاصی با بحث sudo نداره ولی بسیار هیجان انگیز و قابل درنگ هست. هیجان انگیز بودنش در ادامه مشخص خواهد شد ولی دربارهٔ دومی، به این فکر افتادم که مطالعه ش ممکنه کمی باعث بشه نگرش نوجوانان جامعهٔ گنو/لینوکسمون نسبت به موضوع هک عوض بشه. نگرشی که در گروه های تلگرامی به شدت شاهدش هستیم. واقعن چگونه ممکنه کسی که نحوهٔ مانت کردن را هنوز نمیدونه و هتا قادر به تحقیق دربارهٔ انجام این کار نیست، در همون بازهٔ زمانی تصمیم بگیره و احساس کنه که میتونه یک هکر بشه (و این کار را هم با توزیع هایی مثل بلکآرچ و یا کالی یا توزیع های هم خانوادهٔ این دو از این دیدگاه انجام بده). در اینجا به این نتیجه میرسیم که تصور شخص دربارهٔ مفهوم واژهٔ هک نادرست بوده و نیاز به پیرایش داره. بی شک بسیاری از ما اینجا را دیدیم:
http://catb.org/~esr/faqs/hacker-howto.html
گرچهٔ تئوری خیلی زیبایی با جزئیات بیان شده ولی روشنه که تکنیکی نبوده و مثال های صریحی هم نداره و برداشت من اینه که در مورد گروه های سنیِ نوجوان به اندازه ای که فراگیر هست، تاثیرگذار نیست.


تصمیم گرفتم قسمت هایی ازون صفحه را با سلیقهٔ خودم به پارسی برگردان کنم تا تلاشی باشه برای بهبود این نگرش. قسمت هایی که برگردان نشده اند، حاوی کاربردهای ایده بوده و باتوجه به اینکه قرار هست صفحهٔ نصب آرچ در ویکی خودمون را کمی بومی تر کنیم، فرصتش برام محقق نشد. گرچه ذهن شخص علاقه مند، نمیتونه منتظر بمونه و خودش اون قسمت ها را مطالعه خواهد کرد، ولی از دوستانم تقاضا می کنم اگر مایل هستند، اون چند پاراگراف را هم به پارسی برگردانند.

من نه تخصصی در موضوع امنیت دارشته و نه تحصیلاتی در زمینهٔ علوم کامپیوتر دارم. ولی از دوستانی که در این موارد مطالعه و تخصص دارند، میخوام که در بهبود و بازیافت این نگرش بیمار به واژهٔ هک کمک کنند.
======
======

ایده: برنامه ای نوشته شده است که سورس آن در اختیار نیست. یک تابع هم نام ( = قلابی) با تابعی که در آن برنامه وجود دارد ( = واقعی) ساخته و آن را در یک کتابخانهٔ داینامیک قرار می دهیم. اکنون با استفاده از  متغیر محیطیِ LD_PRELOAD برنامه را مجبور به استفاده از کتابخانه کرده و بنابراین، تابع قلابی بجای تابع واقعی بکار بسته خواهد شد.

پیاده سازیِ اول- اجرای یک تابعِ هم نام بجای یک تابع و با نتیجهٔ کاملن متفاوت:

کد

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
 
int main(){
  srand(time(NULL));
  int i = 10;
  while(i--) printf("%d\n",rand()%100);
  return 0;
}

را با

gcc random_num.c -o random_num & ./random_num

کامپایل کرده و اجرا کنید که ده عدد تصادفی بدست خواهد داد. اکنون فرض کنید سورس فایل اجرایی کد را در اختیار نداریم. هدف ما این است که رفتار این فایل اجرایی را بدون دست زدن به سورس و یا کامپایل دوباره، تغییر بدهیم. این کار را با ساختن یک dynamic library با اجرای کد

int rand(){
    return 42; //the most random number in the universe
}

توسط

gcc -shared -fPIC unrandom.c -o unrandom.so

شروع می کنیم.

پس ما یک فایل اجرایی  به نام random_num داریم که اجرای هر بار آن ده عدد تصادفی غیریکسان به دست می دهد و همچنین یک لایبرری با نام unrandom.so ساخته ایم که حاوی تابعی با مقدار ثابت 42ی rand است.

اکنون فایل اجرائیِ  random_num را با دستور

LD_PRELOAD=$PWD/unrandom.so ./random_num

اجرا کنید. خروجی، ده عدد ۴۲ خواهد بود. میتوان ابتدا با زدن

export LD_PRELOAD=$PWD/unrandom.so

فقط

./random_num

را زده و با هیجان بیشتری، این خروجی را تولید کرد. smile

شاهده می کنیم که خروجیِ یک فایل اجرایی بدون دست زدن به سورس آن، در یک فرآیند ظاهرن طبیعی توسط (و به نحو مطلوب) یک تکه کد که در یک کتابخانه قرار داده بودیم، تغییر کرد. درحقیقت، برنامهٔ اجراییِ ما بجای اینکه با تابع "واقعی" rand کار کرده و ۱۰ عدد تصادفی تولید کند، با تابع "ساختگی" rand موجود در کتابخانه ای که درست کرده ایم کار کرد و ۱۰ بار عدد ثابت ۴۲ را تولید کرد. نکته اینجاست: آنچه هتا قبل از ایجاد کتابخانه به برنامه گفته بودیم، این بود که با تابعی به نام rand ده بار کار کرده و ده عدد تولید کند. اما پس از ایجاد کتابخانه، یک تابع دیگر rand نیز بوجود آمده است.

با زدن دستور

$ ldd random_num
	linux-vdso.so.1 (0x00007ffc7b30b000)
	/home/esa/Desktop/fun/unrandom.so (0x00007f15b68a4000)
	libc.so.6 => /usr/lib/libc.so.6 (0x00007f15b64ed000)
	/lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007f15b6ca8000)

می توانیم لیست کتابخانه هایی را که برنامهٔ random_num در زمان اجرا به آنها نیاز داشته است، مشاهده کنیم. در این لیست، نام کتابخانهٔ libc.so.6 نیز دیده می شود که درواقع، حاوی همان تابع "واقعی" rand است (این موضوع را با زدن

$ nm -D /usr/lib/libc.so.6 | grep rand

تحقیق کنید).

با استفاده از متغیر محیطیِ  LD_PRELOAD می توان برنامه را وادار کرد تا از برخی کتابخانه ها به اجبار استفاده کند، حتی اگر خود برنامه قبلن این را نخواسته باشد. در این مثال، ما با استفاده از  LD_PRELOAD برنامهٔ random_num را مجبور کردیم تا از کتابخانهٔ unrandom.so استفاده کند (همان کتابخانه ای که تابع قلابی rand را در آن ساخته ایم). این موضوع را با زدن

$ LD_PRELOAD=$PWD/unrandom.so ldd random_num
	linux-vdso.so.1 (0x00007ffd015cd000)
	/home/esa/Desktop/fun/unrandom.so (0x00007fb2d599b000)
	libc.so.6 => /usr/lib/libc.so.6 (0x00007fb2d55e4000)
	/lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007fb2d5d9f000)

تحقیق کنید. ملاحظه می کنید کتابخانه ای که ساخته ایم، در این لیست دیده می شود. پس، وقتی برنامهٔ random_num اجرا می شود، جاییکه تابع rand خود C را صدا کند، ما تابع rand قلابی را به آن میدهیم.

پیاده سازیِ دوم- کمی عمیق تر - اجرای یک تابع با ایجاد وضعیتی که خروجیِ بیشتری از خروجیِ خود تابع تولید کند:

در مرحلهٔ قبلی یک تابع قلابی را بجای یک تابع ساختگی قرار داده و برنامه را مجبور کردیم که از آن استفاده کند. ولی نتیجه آنقدر متفاوت بود که براحتی میتوانستیم متوجه وجود یک علامت سؤال (= که در اینجا مجبور کردن برنامه برای استفاده از یک تابع دیگر بود) شویم. اگر بخواهیم این علامت سؤال را کم رنگ کرده و مخفی کاریِ بیشتری داشته باشیم، چه؟ درحقیقت، فرض کنید میخواهیم کدی به یک فایل اجرایی تحمیل کنیم بگونه ای که رفتاری غیرطبیعی مشاهده نشود (یا کمتر از مثال بالا مشاهده شود) و  این گونه به ذهن متبادر شود که از همان تابع واقعی استفاده می کنیم. در ادامه، برای پیاده سازیِ چنین ایده ایْ از تابع open استفاده خواهیم کرد. درحقیقت، یک تابع قلابیِ open خواهیم ساخت که علاوه بر انجام کار تابع واقعی open، کار دیگری نیز انجام دهد.

کد

#define _GNU_SOURCE
#include <dlfcn.h>
 
typedef int (*orig_open_f_type)(const char *pathname, int flags);
 
int open(const char *pathname, int flags, ...)
{
    /* Some evil injected code goes here. */
 
    orig_open_f_type orig_open;
    orig_open = (orig_open_f_type)dlsym(RTLD_NEXT,"open");
    return orig_open(pathname,flags);
}

را درنظر بگیرید. خط

#define _GNU_SOURCE

که بالای برنامه قرار میگیرد، ماکروی _GNU_SOURCE برای استفاده از پرچم غیر استاندارد (از نظر استانده های possix) RTLD_NEXT از هدر dlfcn.h توسط کامپایلر لازم است. این پرچم در تابع dlsym استفاده خواهد شد. در قسمت typedef نیز یک alias با دقیقن همان آرگومان های تابع "واقعی" open ساخته ایم.

اکنون بدنهٔ تابع open را ببینید. از اشاره گر تابعیِ orig_open برای اشاره به تابع واقعی open استفاده شده است. درنهایت، این تابع با همان آرگومانهایی که به تابع قلابی open پاس داده شده بودند، صدا زده شده و مقدار آن گرفته شده است.

اکنون موقع آن است تا با استفاده از کدی که نوشته ایم، هدف خود را پیاده کنیم. فرض کنید هدف ما در اینجا نشان دادن آدرس فایل باز شده باشد. بنابراین، با افزودن خط

printf("The victim used open(...) to access '%s'!!!\n",pathname); //remember to include stdio.h!

که به

#include <stdio.h>

نیز نیاز دارد، کد بالا را بصورت

#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
 
typedef int (*orig_open_f_type)(const char *pathname, int flags);
 
int open(const char *pathname, int flags, ...)
{
  printf("The victim used open(...) to access '%s'!!!\n",pathname); //remember to include stdio.h!

    orig_open_f_type orig_open;
    orig_open = (orig_open_f_type)dlsym(RTLD_NEXT,"open");
    return orig_open(pathname,flags);
}


int main () {
  return 0;
  }

تغییر می دهیم و آن را با

gcc -shared -fPIC  inspect_open.c -o inspect_open.so -ldl

بعنوان یک کتابخانه داینامیک کامپایل می کنیم. پرچم ldl برای لینک به libdl.so.2 استفاده شده که حاوی dlsym است:

$ nm -D /usr/lib/libdl.so.2 | grep dlsym

خب. آنچه اکنون در اختیار داریم، یک کتابخانهٔ داینامیک است که تابعی قلابی به نام open در آن پیاده سازی شده بگونه ای که رفتارش دقیقن شبیه به رفتار تابع واقعی open بوده و علاوه بر آن، یک قابلیت دیگر نیز دارد: نشان دادن آدرس فایل ها.

مثلن

$ LD_PRELOAD=$PWD/inspect_open.so firefox

را بزنید.

کنون ممکن است قانع شده باشیم که این روش چقدر می تواند برای دیباگ کردن برنامه ها و یا هتا مشاهدهٔ رفتار برنامه های ناشناخته مفید باشد. همچنین درمورد این مثال خاص، اگر میخواهید کار را کامل تر انجام دهیدْ باید یک تابع قلابیِ open64 هم برای تابع استاندارد دیگر واقعی open64 ایجاد کنید.

پ.ن: دستورهایی وجود دارند که در ترمینال کار مشابهی با همین مثال را انجام میدهند. همینطور آدرس های  libc.so.6 و libdl.so.2 در توزیع های بجز آرچ (بیس) ممکن است متفاوت باشد که با مثلن whereis میتوان از آدرس آنها مطلع شد.

آفلاین

پانوشت انجمن

پشتیبانی توسط تیم آرچ لینوکس ایران و نیرو گرفته با FluxBB