본문 바로가기

코딩 문제/해커스쿨

FTZ : level11

728x90

평소처럼 로그인 후 힌트를 확인한다.

힌트를 보니 C코드가 나온다.

우선 이 C코드를 해석해 보자.

setreuid의 함수 원형 

strcpy의 함수 원형

 

해석은 끝났으니 다른 것을 건드려 보자.

우선 tmp 폴더에는 아무것도 없다.

attackme 실행파일은 인자를 주지 않으면 Segmentation fault가 발생하고

인자를 주면 앞에 입력한 인자가 붙는다.

 

Segmentation fault는 왜 발생하는 것일까?

 

그렇다면 과연 어디가 잘못된 것일까?

인터넷을 검색해보니 GDB라는 디버거가 있었다.

 

GDB란. 다른 프로그램 수행 중에 그 프로그램 내부에서 무슨 일이 일어나고 있는지 보여주거나

프로그램이 잘못 실행되었을 때 무슨 일이 일어나고 있는지 보여주는 것이다.

Python의 기본 모듈에 포함되어 있는 기본적인 Python 디버깅 툴이다.

 

흔히 쓰는 GDB명령어는

run or r : 프로그램을 시작한다.

quit : GDB에서 빠져나간다.

disas : 함수명이나 주소를 입력하면 디스어셈블링 한다.

< 디스어셈블러 : 기계어를 어셈블리어로 변환하는 컴퓨터 프로그램 >

ex. disas main

info b : 브레이크포인트를 확인한다.

x : 특정 메모리 값을 확인한다.

ex. x $r1 or x/10i

i r : 각종 레지스터의 정보를 확인한다.

 

GDB를 사용해서 run을 해보니 다음과 같은 오류가 뜬다.

인터넷에 이 오류를 찾아보니 현재파일에 setuid가 설정되어 있으면

GDB가 작동되지 않는다고 한다.

그래서 tmp폴더에 복사하여 다시 GDB를 사용해보겠다.

자 그럼 한번 분석을 해보자.

 

우선 push, mov, sub 이런것들은 명령어이고

%ebp, %esp, %eax 이런것들은 레지스터들이다.

 

[ 명령어 ]

- push : 스택에 특정 값을 넣고, esp-4

 

- pop : 특정 값에 esp가 가리키는 값을 넣고, esp+4

 

- nop : 아무것도 하지 않는다.

< 사용이유 : 메모리에 빈 공간이 없게 해야 시스템에서 바이너리를 읽을 때 오류가 나지 않는다. >

 

- mov : 우변(or 상수)의 값을 좌변에 입력한다.

 

- lea : 우변의 주소값을 좌변(레지스터만 가능)에 입력한다.

 

- sub a, b : a에서 b를 빼서 결과를 b에 저장

< 즉 좌측의 값만큼 공간만 할당된 쓰레기값이 생성 >

 

- add a, b : a와 b를 더해서 결과를 a에 저장

 

- cmp : 뒤에 나오는 두 수를 비교하는 것이다.

 

-dec : 값을 1 감소시킨다.

 

- inc : 값을 1 증가시킨다.

 

- jne : 비교값이 다를 때 점프

 

- call : 해당 프로시저 호출

< 리턴값이 스택에 저장됨 >

 

- jmp : 해당 주소로 점프

 

[ 레지스터 ]

eax : 함수 실행 후 리턴 값

 

esp : 스택 포인터

< 스택의 가장 상단의 주소를 가진 레지스터 >

 

ebp : 지역변수를 가리키며 이전 함수로 돌아가기 위해 ebp의 주소값을

esp를 통해 함수 시작지점의 바로 윗 부분에 저장시킨다.

< 스택의 가장 밑바닥의 주소를 가진 레지스터 >

 

[ 스택 ]

정의 : 메모리 공간

 

기본 구조 : Buffer + SFP[4byte] + RET[4byte]

 

Buffer : 데이터가 저장되는 공간

SFP : leave 시켜 RET로 돌아갈 주소를 EBP에 불러온다

RET : Return

 

그렇다면 위의 sub $0x108, %esp는

0x108 크기의 공간이 생긴다는 것이다.

0x108 = 10진수로 264이다.

초기에 str은 256byte로 선언하였기 때문에

나머지 8byte는 쓰레기 값이다.

 

즉. 메모리 구조는 Buffer[256byte] + Dummy[8byte] + SFP[4byte] + RET[4byte]이다.

 

정리해보면 Segmentation fault가 발생한 이유가 버퍼 오버플로로 인해 생겼기 때문에

Segmentation fault가 발생하지 않게 하기 위해서

RET를 이용하는 것임을 알 수 있다.

 

< 버퍼 오버플로 : 데이터를 버퍼에 저장할 때 프로그래머가 지정한 곳 바깥에 저장되는 것 >

 

인터넷을 찾아보니 쉘코드라는 것이 있었다.

 

< 쉘코드 : 작은 크기의 코드로 소프트웨어 취약점 이용을 위한 내용부에 사용

일반적으로 어셈블리어로 작성되고 기계어로 변경된다 >

 

인터넷에서 구한 쉘코드는 위와 같다.

그럼 계산을 해보자.

RET에는 Return값을 넣어줘야 되니 4byte를 잡고

나머지 영역에서 쉘코드 25byte를 잡으면

남는 영역이 있다.

남는 영역에는 \x90을 넣어 주고 쉘코드를 A, RET영역도 \x90을 넣어주었다.

< NOP : 아무런 수행을 하지 않는 코드, \x90으로 표현 >

위의 명령어를 해석하자면

python -c 'print "\x90"*243+"A"*25+"\x90\x90\x90\x90" '는 python을 이용하여

인자의 길이를 길게 집어 넣을 수 있는 방법이다.

 

그리고 `(백틱)은 쉘 커멘드의 출력 값을 리턴해주는 용도로 쓴다.

 

python의 c옵션을 사용하면 Command Line에서 출력할 수 있다.

Command Line : cmd, 콘솔 등 텍스트 기반 응용 프로그램으로 파일 보기, 처리,

조작이 가능하다. >

그런다음 메모리 값을 확인해본다.

x/268x $esp 명령어의 의미는

esp주소부터 268byte만큼 출력한다는 의미이다.

 

자세히 보면 0x90909090이 실행되는 곳이 보일 것이다.

0xbffffb30를 RET의 주소로 잡고 Buffer Overflow 공격을 해보겠다.

 

다시 level11로 넘어와 attackme를 실행하면서 인자로 python -c를 이용하여

입력한 코드대로 변환해서 272byte를 넣어준다.

 

"\x90"를 243개 넣은 후 위의 쉘코드를 입력해준다.

마지막으로 아까 잡은 RET의 주소를 입력해준다.

< RET의 주소를 넣을때 자세히 보면 주소는 bf ff fb 30이지만 입력한 순서는

\x30 \xfb \xff \xbf 로 입력 된 것을 볼 수 있다 >

 

입력한 후 sh 모드로 변경이 되었다.

id를 확인하면 level12권한이 된것을 확인할 수 있다.

'코딩 문제 > 해커스쿨' 카테고리의 다른 글

FTZ : level13  (0) 2019.07.08
FTZ : level12  (0) 2019.07.07
FTZ : level10  (0) 2019.01.22
FTZ : level9  (0) 2019.01.19
FTZ : level8  (0) 2019.01.16