CTF/포너블

[Space Heroes CTF] Guardians of the Galaxy - 포너블 / 포맷스트링

SecurityMan 2022. 4. 8. 21:00

 

이번 대회에서 중급 난이도로 분류되었던 포너블 문제이다.

 

가디언즈 오브 갤럭시 컨셉의 문제였다.

 

반응형

 

문제 설명에서 Can Starlord find a successful distraction format?

 

이라고 언급하면서 이 문제가 Format String 취약점을 이용해서 푸는 문제라는걸 암시하고 있다.

 

문제를 직접적으로 풀어볼 수 있는 주소(0.cloud.chals.io) 와 포트(12690)이 주어지고,

 

로컬에서 테스트해 볼 수 있도록 guardians 라는 바이너리 파일도 같이 주어진다.

 

 

일단 먼저 문제 서버에 접속해본다.

 

nc(netcat) 명령어를 이용해서 위와 같이 접속할 수 있다.

 

netcat은 네트워크를 연결해서 데이터를 읽고쓸수 있게 해주는 도구이다.

 

nc 0.cloud.chals.io 12690 이라고 입력하면 된다.

 

접속하면 Does Quill manage to win the dance battle? 라는 메세지가 출력되고,

 

사용자의 입력을 받는다.

 

hello 라고 입력해봤는데

 

Oh no, Ronan has seen throught the distraction! 이라는 문구가 출력되면서

 

내가 입력했던 hello가 밑에 그대로 출력되게 된다.

 

 

IDA 디스어셈블러를 이용해서 주어진 guardian 바이너리 파일을 열어본다.

 

main 함수에서 f5를 눌러서 수도코드를 살펴보면

 

puts로 지정된 문구를 출력하고, fgets 로 사용자의 입력을 받는다.

 

그리고 입력받은 값을 printf 함수를 이용해서 출력하는데, 여기서 문제가 발생한다.

 

사용자에게 입력을 받아들여서 결과를 출력할때 포맷 스트링이라는 것을 사용하는데

 

기호로 다음과 같이 표시한다.

 

%d : 정수형 10진수 상수
%f : 실수형 상수
%c : 문자
%s : 문자 스트링
%u : 양의 정수(10진수)
%o : 양의 정수(8진수)
%x : 양의 정수(16진수)
%p : 포인터

 

main 함수에있는 printf 함수의 경우 위와 같은 형식 지시자를 사용하지 않기 때문에

 

printf(format) 에 있는 format은 문자열로 해석되는것이 아니라 형식 지시자를 포함한 포맷스트링으로 인식된다.

 

 

프로그램을 실행시키고 aaaa %p %p %p %p %p %p %p %p %p %p %p ... 라고 입력해본다.

 

사용자의 입력값을 그대로 받아서 출력하는 프로그램이기 때문에

 

aaaa %p %p %p %p %p %p %p %p %p %p %p 를 입력했다면

 

당연히 aaaa %p %p %p %p %p %p %p %p %p %p %p 가 출력되어야 하지만

 

위 사진과 같이 전혀 다른 이상한 값이 출력되는것을 볼 수 있다.

 

이게 %p가 문자열로 해석되지 않고 포맷스트링으로 해석되었기 때문에

 

포인터 주소들이 줄줄이 출력되고 있는 것이다.

 

aaaa는 ascii 코드 값으로 0x61인데 뒤쪽에 보면

 

맨 처음 입력했던 aaaa도 0x61616161 형태로 출력된 것을 확인할 수 있다.

 

 

다시한번 main 함수의 수도코드를 본다.

 

char format[32] 변수를 선언(rsp+10h)한 다음char s[40] 변수를 선언(rsp+30h)한 것을 확인할 수 있다.

 

그리고 중간에서 fopen 으로 flag.txt 파일을 읽어 그 내용을 stream에 저장하고

 

fgets로 stream의 내용을 s에 저장하는것을 볼 수 있다.

 

 

아까 aaaa %p %p %p %p %p %p %p %p %p %p %p 를 입력했을 때 보면

 

aaaa가 8번째 출력값에 들어가 있는 것을 볼 수 있다.

 

char format 변수 안에 aaaa가 들어있으니 format 변수가 출력된 것이라고 생각하면,

 

char s 변수는 format 변수 뒤에 있으니 s 변수의 내용을 출력하려면 8번보다 뒤쪽을 봐야할 것이다.

 

 

입력값을 aaaa %12$p 로 줘 보았다.

 

이렇게 하면 해당 인덱스(12)에 있는 값만 골라서 출력할 수 있다.

 

%12$p, %13$p, %14$p, %15$p를 차례로 입력해보면 6d 69 7b 66 74 이런식으로

 

6 또는 7로 시작하는 값들이 나오는것을 볼 수 있다.

 

 

이런 값들은 ascii 코드에서 소문자에 해당하는 영역이다.

 

출력값들을 다 한곳에 모아본다.

 

4개의 값을 모아서 적은 후에 맨 뒤에 있는 숫자부터 역순으로 다시 정렬해준다.

 

이렇게 하는 이유는 리틀 엔디언(Little-endian) 방식이기 때문이다.

 

메모리에 저장되는 순서라고 생각하면 된다.

 

예를들어 0x12345678 이라는 값이 있다면 리틀 엔디언일 경우 맨 뒤부터 두자리씩 잘라서

 

78 56 34 12 이런식으로 저장된다.

 

위 사진도 같은방식으로 거꾸로 숫자를 다시 쓴것이다.

 

 

거꾸로 써놓은 숫자들을 복사해서 ascii 코드로 해석해보면

 

플래그를 획득할 수 있다.

반응형