본문 바로가기
System Hacking/해커스쿨 L.O.B Hacking Zone

해커스쿨 L.O.B level 4 [goblin]

by En_Geon 2020. 5. 18.

goblin 로그인 후 파일을 확인한다.

 

goblin 파일

 

코드 파일을 열어 코드를 분석한다.

 

orc.c

 

extern

extern이란 외부변수의 선언이다.

 

함수를 작성할 때 원형 선언과 정의를 분리할 수 있는 것처럼, 변수도 외부변수 선언과 정의로 구분 할 수 있다.

그뿐만 아니라 함수처럼 외부변수 선언만 있고 정의가 빠지면 컴파일 오류가 아닌 링크 오류가 발생한다.

 

외부변수의 선언은 일반 변수의 선언과 같은 형식으로 기술하지만, 자료형 앞에 extern 예약어를 붙이면 된다.

그러나 절대로 초깃값을 명시하면 안 된다. 초깃값을 명시하면 '선언 및 정의'가 되어버리기 때문이다.

 

extern 예약어는 문법상 '기억 부류 지정자'다.

외부변수라는 말의 의미는 변수의 정의가 현재 파일이 아니라 다른 파일(외부 파일)에 존재한다는 의미다.

char **environ

main() 함수 인자 중 char **envp가 있다. 이 인자를 사용해도 되지만 전역변수로 char **environ 변수를 사용해도 된다.

char **envp를 사용할 때는 사용하지 않더라도 int argc, char **argv를 선언해야 한다.

char **environ을 사용하려면 extern으로 외부변수를 선언해야 한다.

 

getenv 함수는 F.T.Z와 L.O.B에서 많이 써오던 환경변수 하나의 주소만을 가져오는 함수다.

그러나 environ모든 환경 변수를 출력할 수 있다.

 

F.T.Z와 L.O.B에서 자주 다루던 환경변수를 왜 다루는가 하면 egghunter 반복문 때문이다.

memset

memset의 기본형은 "void *memset(void *dest, int c, size_t count);"다.

"dest" 초기화할 대상 메모리 주소

"c" 초깃값. 이 값이 0이면 메모리를 0으로 초기화.

"count" 초기화 대상 메모리의 바이트 단위 크기

 

memset() 함수는 첫 번째 인수로 전달된 메모리 주소를 시작으로 세 번째 인수로 전달된 바이트 단위 길이의 메모리에 두 번째 인수로 전달된 정보를 대입한다. 이때 두 번째 인수의 값은 0 ~ 255 사이의 숫자로 제한되는데 이유는 8bit로 표현할 수 있는 범위로 제한되기 때문이다.

 

memset() 함수가 사용되어 environ을 찾아서 0으로 초기화 한다. 즉, 모든 환경변수를 찾아 0으로 초기화 하는 반복문이다. 그러므로 환경변수를 사용해서는 문제를 풀 수 없다.

memset() 함수를 이해하지 못하면 이전 문제들과 같아서 환경변수를 등록해 해결하려고 한다. memset() 함수 때문에 환경변수를 등록해 해결하려고 하면 오류가 나게 되고 문제 풀이는 막히게 된다.

 

if 문은 첫 번째 인자의 47byte 자리에 "\xbf"가 들어가지 않으면 문자를 출력하고 프로그램이 종료된다.

이점을 유의해서 페이로드를 작성해야 한다.

 

코드 분석을 끝내고 gdb로 메모리를 분석한다.

이때 orc 실행파일을 gdb로 열면 gdb 실행 명령어인 "r"이 권한제한으로 오류를 내므로 goblin 권한으로 orc를 복사해 gdb로 열면 권한제한 문제는 없어진다.

 

gdb

 

우리에게 중요한 코드는 strcpy() 함수가 나오는 부분이다. main+189에서 나오고 copy가 일어나는 곳은 ebp-40이다.

다른 변수도 있고 다른 함수도 있지만 strcpy() 함수만 생각하는 것은 스택 구조를 알면 편하다.

스택 구조를 도식화하게 되면 이렇다.

 

int i 4byte
char buffer[40] 40byte
SFP 4byte
RET 4byte

 

스택 구조가 이렇게 되기 때문에 변수 i는 신경 쓰지 않아도 되고 buffer에 관한 것을 중심적으로 보면 되고 BOF를 44byte만큼 일으키면 되는 걸 알 수 있다.

 

더보기

 

RET에 BOF를 일으키기 위해서는 argv[2]의 주소를 알아야 한다.

strcpy() 함수에서 argv[1]을 사용해서 두 번째인 argv[2]를 사용해 RET에 BOF를 일으킨다.

 

gdb argv[2]

 

argv[2]의 주소를 알아오기 위해 main에 브레이크를 걸고 실행 명령어를 통해 두 개의 인수를 입력한다.

 

x/100x $esp

 

esp를 보고 입력값이 어디에 들어가 있는지 찾아보면 된다. 지금 argv[1]에는 "B"를 넣어 주었고 뒤에 "A"를 넣었다. 그러므로 "A"가 어디에서 시작하는지를 보면 argv[2]의 주소를 찾을 수 있다.

argv[2]의 주소는 "0xbffffc10"이 된다.

 

argv[2]의 주소를 힘들게 알아낸 이유는 환경변수를 통해 BOF를 일으킬 수 없기 때문인데 항상 사용하던 페이로드의 구성으로 본다면 buffer + SFP 크기인 44byte만큼 BOF를 일으키고 환경변수의 주소를 바로 입력해주었다. 그러나 환경변수를 사용하지 못하기 때문에 argv[2]를 환경변수처럼 사용하기 위함이다.

 

이번 문제의 페이로드 구성은 buffer + SFP 크기인 44byte만큼 BOF를 일으키고 환경변수 주소가 들어가야 할 자리에 argv[2]의 주소가 들어간다. 그리고 두 번째 인수에 셸 코드를 넣어주면 RET에 argv[2]의 주소가 들어있으므로 RET에 셸 코드가 들어가게 되는 것이다.

44byte만큼 BOF를 일으키면 if 문의 문제도 함께 해결된다.

주소가 "0xbffffc10"이므로 "\x10\xfc\xff\xbf"로 들어가게 되는데 "A"가 문자열로 들어간다. 문자열은 배열의 형태이므로 문자가 0번부터 들어가서 주소 시작이 \44로 시작하게 되어 주소 시작부터 \44\45\46\47이 되기 때문에 if 문의 문제도 함께 해결된다.

 

argv[2]의 주소와 구성을 알았으면 페이로드를 작성한다.

 

페이로드

 

구성과 같이 BOF를 일으키고 argv[2]의 주소가 들어가고 argv[1]이 끝난다. argv[2]에 셸 코드를 넣어주면 orc의 권한으로 셸이 실행된다.

 

 

 

댓글