FTZ level20 Write-Up

2019. 7. 24. 06:38
728x90
728x90
728x90

FTZ level20 풀어보자.

 

[사진 1]

hint를 보면 bleh 변수의 크기를 80bytes로 선언한 후 clear 계정의 권한을 획득한다. 그런데 fgets 함수에서 79bytes만큼만 입력받기 때문에 버퍼 오버플로우 공격이 불가능하다. 하지만 printf 함수에서 포맷스트링을 사용하지 않고 bleh 변수를 출력하고 있으므로 포맷스트링 공격을 시도해보자.

 

 

[사진 2]

일단 attackme 파일을 /home/level20/tmp/ 디렉터리에 복사하고 gdb로 main 함수의 어셈블리어 코드를 확인해보려 하였다. 그러나 막혀있다. 따라서 bleh 변수가 ebp로부터 얼마나 떨어져 있는지 알기 어렵다.

 

 

[사진 3]

printf 함수에서 포맷스트링을 지정해주지 않으면 위와 같이 메모리 영역을 볼 수 있다. "ABCD" 문자열을 입력해본 결과로는 printf 함수의 주소로부터 12bytes 떨어진 곳에서 입력한 문자열 값들을 확인할 수 있다.

 

 

[사진 4]

그리고 포맷스트링 %n, %hn을 이용하면 printf 함수를 통해 메모리 영역에 값을 쓰는 것도 가능하다.

위와 같이 attackme 파일을 실행하고 %08x 포맷스트링을 5번 반복하여 출력된 값은 총 20bytes의 값이다. 다시 %08x 포맷스트링을 4번 반복하고 %n 포맷스트링을 입력하면 Segmentation fault가 일어난다. 왜냐하면 %n 포맷스트링은 %n이 나오기 전까지의 출력된 값들의 bytes 크기를 다음에 나오는 주소에 bytes 수만큼 저장한다.

즉, %08x 포맷스트링을 4번 반복하였으므로 출력된 값들의 크기는 16bytes이고 16이라는 값이 다음에 위치한 0x38302520 주소에 저장되었기 때문이다. 당연히 오류가 날 수 밖에 없는 것이다. 이 점을 이용하여 shellcode의 주소를 저장해야 한다.

 

 

[사진 5]

먼저 환경변수로 shellcode를 입력하자. 사용한 shellcode는 다음과 같다.

\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\x89\xc2\xb0\x0b\xcd\x80

shellcode 앞에 "\x90" nop 6bytes를 입력하였는데, 이 것은 shellcode가 다른 opcode와 섞이지 않도록 분리하기 위함이다. 환경변수로 입력된 shellcode의 주소를 알아내는 방법은 다음 링크를 참고하면 된다.

참고 링크: https://jade9reen.tistory.com/48

 

리눅스 환경에서 환경변수로 저장한 shellcode 주소 알아내기

bof 문제를 풀다보면 스택에 ASLR이 걸려있어 스택에 shellcode를 입력할 경우 shellcode의 주소를 정확히 알아내기 매우 어렵다. 이럴 때 환경변수에 shellcode를 저장해두고 getenv 함수로 shellcode의 주소를..

jade9reen.tistory.com

 

 

[사진 6]

ret의 위치를 정확히 알 수 없으므로 변조가 어렵다. 이럴 때 사용할 수 있는 것이 있는데, .dtors 영역이다.

gcc로 컴파일하게 되면 .ctors와 .dtors 영역을 생성하게 된다. .dtors 영역은 main 함수 종료 후에 실행되는 특징을 가지고 있다. 다라서 .dtors 영역에 shellcode의 주소를 넣어주면 main 함수가 종료된 후 shellcode가 실행될 것이다.

.dtors 영역은 위와 같이 objdump 명령어로 찾을 수 있는데 0x08049598에 shellcode의 주소를 입력해주면 된다.

 

다음으로 앞서 확인한 %n 포맷스트링으로 shellcode의 주소를 입력해주기 위해서는 shellcode의 주소를 10진수로 바꿔야 하고, shellcode의 주소 0xbffffefe를 상위 4bytes와 하위 4bytes로 구분해서 2개의 값을 계산해야 한다.

첫 번째 값 : 하위 2bytes 0xfefe => 0d65278

두 번째 값 : 상위 2bytes 0xbfff => 0d49151 - 첫 번째 값 = 음수

여기서 만약 상위 2bytes 값 - 하위 2bytes 값의 결과가 음수라면 상위 2bytes에 0x10000을 더하고 다시 계산한다. 따라서 변환된 값은 다음과 같다.

첫 번째 값 : 하위 2bytes 0xfefe => 0d65278

두 번째 값 : 상위 2bytes 0x1bfff => 0d114687 - 첫 번째 값 => 49409

 

 

[사진 7]

페이로드는 다음과 같다.

"\x90"*4 + .dtors 영역의 하위 주소 "\x98\x95\x04\x08" + "\x90"*4 + .dtors 영역의 상위 주소 "\x9a\x95\x04\x08" + "%08x"*3 +"%65238d%hn"(0d65278 - 앞의 40bytes) + "%49409d%hn"

페이로드의 원리는 다음과 같다.

 

 

[사진 8]

[사진 3]에서 확인한 결과 입력한 문자열은 printf 함수로부터 12bytes 떨어진 0x78383025부터 저장된다.

[사진 7]의 페이로드를 입력하게 되면 0x78383025 값은 "\x90" 4bytes 값으로 변조되고 0x38302520 값은 .dtors 영역의 하위 주소 "\x98\x95\x04\x09" 4bytes 값으로 변조될 것이다. 그리고 0x30252078 값은 "\x90" 4bytes 값으로 변조되고 0x25207838 값은 .dtors 영역의 상위주소 "\x9a\x95\x04\x09" 4bytes 값으로 변조될 것이다.

그리고 "08x" 포맷스트링을 3번 반복하여 앞에서 변조한 맨 처음 "\x90" 4bytes 값을 가리킬 것이고, "%65238d%hn" 포맷스트링은 맨 처음 "\x90" 4bytes 값을 출력하고 다음 위치인 .dtors 영역의 하위 주소 "\x98\x95\x04\x09"에 2bytes 0xfefe 값을 입력할 것이다. 다음으로 "%49409d%hn" 포맷스트링은 두 번째 "\x90" 4bytes 값을 출력하고 다음 위치인 .dtors 영역의 상위주소 "\x9a\x95\x04\x09"에 앞에서 출력한 65,278bytes와 49,409bytes의 합인 0x1bfff 값을 2bytes 0xbfff 값으로 입력할 것이다.

따라서 .dtors 영역의 주소는 shellcode 주소로 변조될 것이고 main 함수가 종료한 후 shellcode가 실행될 것이다.

 

 

[사진 9]

위와 같이 FTZ 20개 level이 끝났다.

728x90
728x90

'Wargame > FTZ' 카테고리의 다른 글

FTZ level19 Write-Up  (0) 2019.07.24
FTZ level18 Write-Up  (0) 2019.07.24
FTZ level17 Write-Up  (0) 2019.07.24
FTZ level16 Write-Up  (0) 2019.07.24
FTZ level15 Write-Up  (0) 2019.07.24

+ Recent posts