본문 바로가기

코딩 문제/해커스쿨

FTZ : level20

728x90

hint를 확인한다.

tmp로 실행파일을 복사하여 gdb를 실행한다.

disas main 명령어가 먹히질 않는다.

심볼이 없다고 하니 일단 다시 level20 디렉토리로 돌아간다.

 

한번 실행파일을 실행시켜 보겠다.

%x를 4번 하면 입력한 AAAA가 나온다.

아마 저 4번째 구간부터 bleh[80]이 저장되는 곳 같다.

 

다시 hint를 살펴보니 fgets 함수이기 때문에 BOF 공격을 해야되는데

gdb 명령이 안되니 다른방법이 있지 않을까란 생각을 했다.

그러던중 printf함수의 모양이 이상하길래 인터넷 검색을 해보니

Format String 취약점이라는 것이 있었다.

 

[ Format String ]

일반적으로 사용자로부터 입력을 받아 들이거나 결과를 출력하기 위하여 사용하는 형식

ex. %d, %f, %c, %s 등

 

[ %n ]

%n이라는 서식문자가 나오기 전까지 출력된 값들을 전부 세서 그 다음 메모리 주소값에 그 숫자만큼 대입하는

서식문자이다.

ex. AAAA %1111x %n을 입력한다면

AAAA(4) + 공백(1) + %1111x(1111) + 공백(1) = 1117 이 count 되어서

0x41414141 주소에 들어가게 된다.

1117은 16진수로 45d이다.

즉 0x41414141 = 0x45d 가 된다.

 

[ Format String Attack ]

Format String과 이것을 사용하는 printf() 함수의 취약점을 이용하여 RET의 위치에 쉘코드의

주소를 write하여 쉘을 획득 하는 공격

 

[ BOF공격과 차이점 ]

BOF 공격은 RET의 위치 주소를 정확히 알아야 하지만

FSB 공격은 Overflow 없이 RET에 바로 write 할 수 있으므로 스택 가드를 우회한다.

 

[ .ctors / .dtors ]

gcc를 이용해서 컴파일 된 이진 프로그램에는 두가지 특수 테이블 섹션이 있다.

.ctors : 생성자를 위한 특수 테이블이다.( constructor )

-> main() 실행되기 전에 호출된다.

.dtors : 소멸자를 위한 특수 테이블이다.( destructor )

-> exit system call로 종료되기 직전에 호출된다.

-> 모든 실행 프로그램 안에는 __do_global_dtors() 함수가 있다.

이 함수는 .dtors섹션+4 번째에 있는 메모리 주소에 0이 아닌 값이 있을 경우 함수로 실행을 시킨다.

.dtors+4 번째에 있는 메모리 값은 기본으로 0이 되어있다.

-> __DTOR_END__ : .dtors + 4 번째 위치한 곳

 

자 그럼 .dtors의 주소를 구해보자.

2번째 구역과 3번째 구역이 동일하다면 .dtors가 맞다.

.dtors의 주소는 08049594라는 것을 알수 있다.

 

그럼 08049594+4인 __DTOR_END__의 메모리 값에 shellcode가 위치하는 주소를 입력해주면 되겠다.

 

먼저 환경변수에 shellcode를 등록시킨 후

evel19에서 사용한 쉘코드 주소를 확인하는 코드를 간단히 짠후 주소를 확인한다.

그럼 이제 공격코드를 한번 짜보자.

전체흐름은 4byte공간 + __DTOR_END__의 주소 + 4byte공간 + __DTOR_END__의 주소+2byte + %8x * 3 +

%(쉘코드 뒤 4byte)c + %n + %(쉘코드 앞 4byte)c + %n으로 하겠다.

 

공격코드에 대해 풀어보겠다.

 

%8x가 나온 이유는 아래 그림과 같다.

%x를 입력했을 때는 0x0000004f일 경우 "4f"만 출력이 되지만

%8x를 입력했을 때는 "      4f"로 출력이 된다.

즉. %8x를 사용하면 무조건 8byte로 출력을 해준다.

 

%8x를 3번 하는 이유는 아까 위에서 %x를 4번 했을때 입력한 AAAA가 저장되는

것이 보였으므로 %8x를 3번하여 4번째에 쉘코드를 덮어씌우는 것이다.

 

__DTOR_END__의 주소를 나누는 이유는

숫자가 너무 커서 메모리를 한번에 덮어쓸 수 없기 때문에

2byte씩 나누어 반씩 넣어주기 위함이다.

 

[ %정수(형식지정자) ]

%n이 인식하는 자릿수를 마음대로 정할 수 있는 방식

ex. %100d -> 공백이 100개 입력

즉. 쉽게 원하는 만큼의 입력이 가능해지게 하는 것이다.

 

[ 쉘코드를 나누는 이유 ]

쉘코드가 있는 주소 0xbffffbea를 10진수로 바꾸면 3221224426가 된다.

그러나 이 수는 CPU가 인식할 수 없는 큰 수 이기 때문에 반으로 나누어야 한다.

-> 뒤 4byte : 0xfbea = 64490

-> 앞 4byte : 0xbfff = 0x1bfff - 0xfbea = 50197

( 0xbfff가 0x1bfff와 같은이유 : 앞 4byte인 0xbfff에서 뒤 4byte인 0xfbea를 빼면

음수가 되므로 앞에 1을 붙여 1bfff로 변환한 다음 뒤 4byte를 빼준다. )

 

자그럼 필요한 것들을 정리해 보자.

-> __DTOR_END__의 앞주소 : 08049598

-> __DTOR_END__의 주소+2byte : 0804959a

-> 쉘코드 뒤 4byte : 64450

( 64490은 %n앞에 나온

AAAA(4byte) + __DTOR_END__의 앞주소(4byte) + AAAA(4byte) + __DTOR_END__의 주소(4byte) + %8x(8byte)*3이므로

16+24 = 40을 더해서 나온 수이므로 40을 뺀 64450이 된다. )

-> 쉘코드 앞 4byte : 50197

 

그럼 이제 한번 공격코드를 실행해보자.

 

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

FTZ : level19  (0) 2019.07.22
FTZ : level18  (0) 2019.07.20
FTZ : level17  (0) 2019.07.19
FTZ : level16  (0) 2019.07.14
FTZ : level15  (0) 2019.07.13