امتیاز موضوع:
  • 0 رأی - میانگین امتیازات: 0
  • 1
  • 2
  • 3
  • 4
  • 5
متغیرهای مقداردهی نشده، خوب یا بد؟
نویسنده پیام
godvb غایب
مدیر بازنشسته بخش نفوذ و امنیت
*****

ارسال‌ها: 430
موضوع‌ها: 77
تاریخ عضویت: اسفند ۱۳۸۶

تشکرها : 886
( 1338 تشکر در 343 ارسال )
ارسال: #1
متغیرهای مقداردهی نشده، خوب یا بد؟
سلامی دوباره، بعد از مدتها استراحت و بی حوصلگی در راستای تجزیه نشدن و همچنین جزیی از طبیعت قرار نگرفتن یه پستی میزارم تا این پست رو همانند پکت های Keep-Alive در نظر بگیرید.

خوب، قرار نیست نحوه اکسپلویت کردن و تکنیک هارو توضیح بدیم فقط میخواهیم کمی درباره خطرات مقداردهی نکردن متغیرها در برنامه و نحوه بوجود آمدن باگ صحبت کنیم، واس خیلی ها از جمله خودم پیش اومده که در زبان C/C++ متغیری مانند Integer رو مقداردهی اولیه انجام ندادیم ، یادمون رفته و یا برحسب عادت در زبان های برنامه نویسی دیگر مثل VB6.0 اینکار رو انجام ندادیم. بنظرم تنها دلیلی که بعضی ها هنوزم که هنوزه طرفدار VB6.0 هستند! همین مقداردهی اولیه نکردن متغیرهاست، اگر شما حتی نوع متغیر رو تعریف نکنین بطور خودکار متغیر از نوع Variant در نظر گرفته میشه و هر DATA Type بخواهید میتونید درون متغیر بریزید!!! اینها انسانهای خاص هستند و خاص هم که فحش نیست !؟

خوب از بحث اصلیمون دور نشیم،اگر استاد همون درس برنامه نویسی و یا افراد دیگری که ازشون این سوال “متغییر مقدار دهی نشده ولی دارای مقدار هست و خروجی به ما نشون میده” رو پرسیدین بهمون گفتن که وقتی یک نوع Integer رو مقدار دهی نمیکنیم ، مقداری Random درون متغیر ریخته میشه؟!؟ و اینم جز خیلی از حرفاس که وارد مغزمون کردن تا بیشتر ندونیم و نپرسیم. (انگاری دلم خیلی پره :دایی) بایک مثال خیلی ساده و راحت میخواهیم به صحت و سقم این دلیل پی ببریم. خوب سورس زیر رو کامپایل و اجرا میکنیم:


کد:
int main(void)
{
    int uninit_var ;
    printf("\n your uninitialized variable is :\t %x\n",uninit_var);
    return 0;
}

بعد از اجرا مقدار این متغیر رو داریم میبینیم که بایستی این یک مقدار تصادفی باشه و در هر بار اجرا مقداری مختلف ببینید، من درون سیستم خودم فقط این مقدار رو میبینیم و نتیجه میگیرم که مقدار Random نیست:

[تصویر:  Ouput.png]

حال برنامه را درون یار همیشه همراهمون باز میکنیم تا دلیل این امر رو کشف کنیم.


[تصویر:  debugging.png]

همانطور که ملاحظه میکنین ابتدا مقدار ECX درون پشته Push میشه و بنابراین عنصر بالای پشته خواهد بود و مقدار ESP یا همون Stack Pointer هم به عنصر بالای پشته اشاره میکنه. حال مقدار بالای پشته وارد رجیستر EAX میشه یعنی درواقع همون مقدار رجیستر ECX . نتیجه اینکه مقدار بالای پشته یا همون مقدار رجیستر ECX ، مقدار تصادفی در درون uninit_var (متغیر مقدار دهی نشده مان) می باشد.

وقتی که تعداد خط کدها زیادمیشن برنامه نویس ها فراموش می کنند که کدام متغییر مقداردهی نشده است و دلایل دیگر.شاید در لفظ و در نظر برنامه نویس مقدار دهی نکردن متغیر مشکل خاصی پیش نیاره و به هیچ وجه فکرشون به اکسپلویت شدن برنامه شون برسه! میتونن به CVE-2015-0240 باگ Samba که متغییر cred یک اشاره گرمقدار دهی نشده در تابع _netr_ServerPasswordSet() است، مراجعه کنند و مطالعه ای داشته باشند که نمونه ها زیاد است. نکته قابل ذکر در اکسپلویتینگ که جا داره در اینجا تکرار کنم : هر اکسپلویت از یک باگ سو استفاده میکنه ولی هر باگ قابل اکسپلویت نیست.
چون هدف فقط آشنایی با این باگ هست از یک مثال قدیمی و خوب استفاده میکنیم تا بتونیم بهتر این موضوع رو درک کنیم ( اصل کار مفهوم و نحوه رخ دادن باگ درون برنامه هاست و لینوکس و ویندوزی فرق نداره ).

کد:
//www.aha.4xmen.ir
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_USER 1024
#define MAX_PASS MAX_USER

#define ERR_CRITIC 0x01
#define ERR_AUTH   0x02

int do_auth(void)
{
    char username[MAX_USER], password[MAX_PASS];

    fprintf(stdout, "Please enter your username: ");
    fgets(username, MAX_USER, stdin);

    fflush(stdin);

    fprintf(stdout, "Please enter your password: ");
    fgets(password, MAX_PASS, stdin);

    printf("username at: 0x%x\n", &username);
    printf("password at 0x%x\n", &password);


#ifdef DEBUG
    fprintf(stderr, "Username is at: 0x%08x (%d)\n", &username, strlen(username));
    fprintf(stderr, "Password is at: 0x%08x (%d)\n", &password, strlen(password));

#endif
    if(!strcmp(username, "user") && !strcmp(password, "washere"))
    {
        return 0;
    }

    return -1;
}

int log_error(int farray, char *msg)
{
    char *err, *mesg;
    char buffer[24];

#ifdef DEBUG
    fprintf(stderr, "Mesg is at: 0x%08x\n", &mesg);
    fprintf(stderr, "Mesg is pointing at: 0x%08x\n", mesg);
#endif
    printf("mesg: 0x%x\n", mesg);
    memset(buffer, 0x00, sizeof(buffer));
    sprintf(buffer, "Error: %s", mesg);

    fprintf(stdout, "%s\n", buffer);
    return 0;
}

int main(void)                  
{
    switch(do_auth())
    {
    case -1:
        log_error(ERR_CRITIC | ERR_AUTH, "Unable to login");
        break;
    default:
        break;
    }
    return 0;
}

برنامه مورد نظرمون رو اجرا میکنیم :

[تصویر:  sample2.png]

آدرس متغیر ها را در حافظه نشان داده می شوند، تا به اینجا مشکل خاصی نیس. ورودی های برنامه User و Password است که آنها هم بدرستی دریافت می شوند. به تابع زیر از سورس کد برنامه توجه کنید:

کد:
int log_error(int farray, char *msg)
{
    char *err, *mesg;
    char buffer[24];

#ifdef DEBUG
    fprintf(stderr, "Mesg is at: 0x%08x\n", &mesg);
    fprintf(stderr, "Mesg is pointing at: 0x%08x\n", mesg);
#endif
    printf("mesg: 0x%x\n", mesg);
    memset(buffer, 0x00, sizeof(buffer));
    sprintf(buffer, "Error: %s", mesg);

    fprintf(stdout, "%s\n", buffer);
    return 0;
}

متغیر mesg از نوع لوکال و مقدار دهی نشده، ولی توسط تابع sprintf مورد استفاده قرار گرفته است. خوب این متغیر چه ربطی به ما دارد؟ فرض کنیم که این متغیر مقدار دهی نشده است، چگونه می تواند باعث ایجاد باگ شود؟
طول رشته ورودی برای User و Password حداکثر 1024 کاراکتر است. حال حداکثر کاراکتر را بعنوان ورودی به برنامه میدهیم و عکس العمل برنامه را نسبت به ورودی ها مشاهده می کنیم:

[تصویر:  sample2-1.png]

مقدار mesg با کاراکترهای ورودی ما بازنویسی شده است (همان مقدارهای تصادفی) ! ! !

دوباره برنامه را درون یار همیشه همراه اجرا می کنیم و ورودی ها را حداکثر کاراکتر خواهیم داد. اینجا نحوه ساختار پشته در هنگام Call شدن یک function رو توضیح نمیدم، اگر دوستان نیاز دیدن میتونن از لینک زیر کتاب سرریز بافر رو دانلود و مطالعه کنید:

کد:
https://www.exploit-db.com/docs/30179.zip

قبل از اینکه وارد تابع log_error بشویم مقادیر پشته رو نگاهی میاندازیم، میبینیم که متغیرهای User و Pass مقدار دهی شده اند و درون پشته ذخیره گردیده اند (Local Variable).


[تصویر:  sample2-2.png]

پشته رو به بالا رشد پیدا میکنه، حال وارد Call میشویم تا ادامه روند اجرا را بررسی نماییم:

[تصویر:  sample2-3.png]

مقدار ESP منهای 20 می شود! یعنی درون پشته Overlap رخ خواهد داد.

کد:
00401130  /$ 83E>SUB ESP,20


در ادامه نیز مقدار رجیستر ESI نیز تغییر می کند و آدرس mesg درون آن قرار می گیرد:
کد:
0040113F  |. 8B7>MOV ESI,DWORD PTR SS:[ESP+4]

خوب میبینیم که استک Overlap شده است و به دلیل اینکه متغیر mesg مقدار دهی نشده است همان مقداری که از قبل (پشته تابع قبلی) درون استک قرار دارد را مدنظر قرار می گیرد و ما می توانیم براحتی مقدار این متغیر را تحت کنترل داشته باشیم. حال روند اجرا را ادامه می دهیم. در تصویر زیر میبینیم که 24 بایت در پشته با مقدار 0x00 پر میشود و این همان متغیر char buffer[24];است. خوب حال توسط دستور sprintf مقادیر آدرس ESI در آدرس EAX کپی می شود. پس دلیل رخ دادن خطا در برنامه را فهمیدیم که آدرس نا معتبر است.

سوال: اگر مقدار ESI یک آدرس معتبر باشد چه اتفاقی می افتد؟
جواب: مقادیر آن آدرس درون متغیر Buffer کپی می شوند.
سوال: آیا طول مقادیر چک می شود؟
جواب: خیر، طول مقادیر چک نمی شود و شرط پایان کپی نمودن مقادیر در درون Buffer وجود کاراکتر Null است.

از این سوال و جواب به این نتیجه میرسیم که براحتی می توانیم با کپی نمودن داده بیشتر از 24 کاراکتر در درون متغیر Buffer آدرس بازگشت (Return Address) را بازنویسی نمایم.بدین صورت که من بجای مقدار 0xDDDD آدرس بخشی از پشته که مقدار متغیر User است را میدهم و نتیجه را میبینیم :

[تصویر:  sample2-4.png]

آدرس ESI رو مقدار 0x0012FA00قرار دادم و چون طول کاراکتر ها بیش از 24 بود آدرس بازگشت بازنویسی شده و مقدار EIP هم 0x41414141 شده است. کمی آدرس های پشته درون تصاویر رو مقایسه کنین میبینین که چه اتفاقی رخ داده است.

موفق باشید.

آنچه توانسته ایم انجام دهیم، لطف پرودگار بوده است.

XMen For Ever
۱۳-تير-۱۳۹۴, ۰۳:۰۸:۱۰
ارسال‌ها
پاسخ
تشکر شده توسط : babyy, saeedsmk, behzady, lord_viper, mahdi321, nImaarek


موضوعات مرتبط با این موضوع...
موضوع نویسنده پاسخ بازدید آخرین ارسال
  [سوال] باز نشدن سايت ايراني فيلتر نشده ؟ علي برقي 2 3,992 ۳۰-شهریور-۱۳۹۰, ۱۰:۱۶:۵۹
آخرین ارسال: ha_60

پرش به انجمن:


کاربرانِ درحال بازدید از این موضوع: 1 مهمان

صفحه‌ی تماس | IranVig | بازگشت به بالا | | بایگانی | پیوند سایتی RSS