-
[F.T.Z] Level 20Wargame/F.T.Z 2022. 10. 9. 07:41반응형
Level 20을 풀어보자.
마지막 문제이니 시간을 들여 천천히 풀기 위해 주말까지 기다렸는데, 예상보다 더 어렵다..
물론 개념을 미리 알고 있었으면 쉬웠을테지만, 그렇지 못하니 어려운 건 몸으로 부딪혀가며 알아가고 있다.
Level 20의 hint를 살펴보자.
소스 코드가 짧은 것을 보니, 어려운 문제다. (짧은게 더 어려운 것 같다.)
코드를 분석해보자.
먼저, char 형의 80 바이트 크기를 가지는 bleh 배열이 선언된다.
다음으로 setreuid를 선언해 권한을 변경한다.
그 다음 fgets 함수를 통해 bleh에 입력된 79바이트를 표준 입력으로 가져온다.
마지막으로 bleh를 출력하며 프로그램이 끝난다.
<Level 20의 hint>
굉장히 간단한 코드인데, 뭔가 이상하다.
fgets에서 가져오는 크기가 선언된 배열 bleh의 크기보다 작다.
어떻게 overflow를 시킬 수 있을까..??
여기서 새로운 공격 방법이 필요하다!!
코드를 자세히 보면, printf 함수가 무언가 이상하다.
원래 printf는 %s, %c, %d 등 서식 문자를 입력하고, 그것에 들어갈 값을 뒤에 입력해 출력해주는 함수이다.
하지만 이 문제의 소스 코드에서 printf 함수에는 서식 문자가 없다.
그런데도, 입력한 값에 대해서 제대로 출력을 해주는 것을 확인할 수 있다. (hi -> hi)
이상한 것을 느끼고, 구글링을 해보니 이것을 이용해 공격을 할 수 있다고 한다.
여기서, 새로운 공격 방법을 시도해보자.
FSB라고 불리는 Format String Bug를 이용한 공격이다.
앞에서 설명한 서식 문자가 Format String인데 이것을 지정해주지 않았을 때, 그 취약점을 이용할 수 있다.
printf 함수는 원래 printf(const char *format, ...); 의 형식으로 선언되어 있다.
즉, 포인터를 넣으면 포맷 스트링의 형식에 따라 그 위치의 값을 출력해주는 것이다.
그런데 여기서 만약 포맷 스트링만 지정하고, 그에 대응되는 주소를 지정하지 않으면 어떻게 될까?
아래 사진을 보면 그 결과를 알 수 있을 것이다.
<Format String Bug 결과>
aaaa라는 문자열이 입력한대로 나왔다.
그런데 뒤에 %08x 혹은 %p를 입력하자 16진수 형태의 4바이트 값이 나왔다.
그리고 그 값이 일정한 것을 알 수 있다.
이해를 위해 그림을 그려보자.
<stack 상황>
&bleh에 printf의 const *format이 들어간다고 생각하자.
앞에서 입력한 aaaa %p %p %p %p 가 현재 들어가있는 상태이다.
각 %p에 맞는 주소를 지정하지 않아, FSB에 의해 메모리가 출력된 것이다.
그런데, <Format String Bug 결과> 사진을 보면 4번째 %p로 인해 0x61616161이라는 값이 출력되었다.
이 값은 aaaa를 16진수로 표현한 ASCII 코드값이다.
이제 <stack 상황> 그림을 보면 왜 입력값 &bleh 위에 3칸이 있고, 그 위에 bleh[80]이 위치하는지 알 수 있다.
3개의 %p가 &bleh와 bleh[80] 사이의 더미 값의 메모리를 출력하고, 4번째 %p가 bleh[80]의 위치를 출력한다.
여기까지는 이해가 됐을텐데, 이것을 이용해 어떻게 공격을 하는 것일까?
포맷 스트링 중 %n 서식 문자가 있다.
이것은 %n이 나타날 때까지 출력한 문자 수를 지정된 인자에 넣을 때 사용한다.
이 %n을 이용해 특정 메모리에 원하는 수를 넣어 값을 덮어버리면서 공격을 할 수 있다.
문제를 풀 수 있을 정도로는 FSB에 대해 설명이 진행된 듯하다.
이제 이것을 이용해 문제를 풀어보도록 하자.
먼저 gdb로 RET의 위치를 알면 된다.
그런데.. gdb를 이용한 분석을 막아 놓은 듯하다. (구글링을 해보니 방어 기법 중 하나라고 한다.)
<gdb 분석 방어>
다시 구글링을 해보니, FSB를 이용한 공격에는 dtors라는 것을 이용한다고 한다.
간략히 설명하자면, GNU 컴파일러(gcc)는 컴파일 시 ctors와 dtors 두 세그먼트를 생성한다.
ctors는 constructor로 main 함수가 실행되기 전에 실행된다.
dtors는 destructor로 main 함수가 종료되고나서 실행된다.
main 함수가 끝나면서 이 .dtors 속성의 함수가 실행되는 것을 이용해 공격을 수행해보자.
먼저 .dtors가 메모리의 어디에 위치하고 있는지 확인을 해보자.
objdump 명령어와 readelf 명령어를 둘 다 사용해봤는데, 편한 것을 사용하면 될 것 같다.
주의할 점은 나타난 주소 0x08049594에서 +4를 해준 0x08049598이 필요한 주소라는 것이다.
완벽한 이해는 아니지만, .dtors가 실행되고 +4의 주소에 .dtors 내의 함수가 추가되어 실행된다고 알고 있다.
이제 .dtors의 함수가 들어가는 주소에 쉘 코드의 주소를 넣어 공격이 되도록 해보자.

<.dtors의 주소>
많이 해왔던대로 먼저 쉘 코드를 실행할 수 있는 환경변수를 선언하도록 하자.
그 후 컴파일을 위한 소스 코드 파일을 만들고, 컴파일한 후 프로그램을 실행한다.
환경변수의 주소 0xbffffef5가 나왔다.

<환경변수 선언 후 알아낸 주소>
이제 페이로드만 작성하면된다.
먼저 정리를 해보자.
공격에 필요한 정보인 .dtors의 주소와 쉘 코드를 넣은 환경변수의 주소는 알고 있다.
이 .dtors의 함수 주소를 환경변수의 주소로 변조하면 공격은 성공이다.
페이로드를 작성해보자.
1. "\x90" * 4 : NOP를 4개 채워 계산하기 편하게 길이를 맞춰주고, %c를 출력할 수 있도록 한다.
2. "\x98\x95\x04\x08" : .dtors의 앞주소이다. 여기에 쉘 코드의 뒤 2바이트 주소를 입력해준다.
3. "\x90" * 4 : 1번과 같다.
4. "\x9a\x95\x04\x08" : .dtors의 뒷주소이다. 여기에 쉘 코드의 앞 2바이트 주소를 입력해준다.
5. "%p%p%p" : 3개의 더미값을 지나 4번째 포맷 스트링인 6번부터 출력되도록 한다.
6. "%65229c" : 환경변수의 뒤 2바이트 주소 0xfef5는 10진수로 65,269이다. 여기에 40을 뺀 값이다.
7. "%n" : 앞의 문자열 수만큼 3번 주소에 넣어 변조한다.
8. "%49418c" : 환경변수의 앞 2바이트 주소 0xbfff에 보수를 취해 0x1bfff에서 65,269를 뺀 값이다.
9. "%n" : 앞의 문자열 수만큼 4번 주소에 넣어 변조한다.
1번과 3번은 6번과 8번의 %[숫자]c의 숫자만큼의 크기를 출력하기 위해 넣었다.
2번과 4번을 보면 2바이트씩 주소를 나누었다.
그 이유는 쉘 코드가 담긴 환경변수의 주소가 0xbffffef5인데, 이것은 10진수로 3,221,225,205이다.
%n은 int 형식인데 이미 int의 범위를 훌쩍 넘어선다.
그래서 반으로 나눠 2바이트씩 변조할 수 있도록 하기 위해 stack에서 .dtors의 주소를 2바이트씩 바꾸었다.
그리고 환경변수의 뒤의 2바이트 주소를 먼저 변조하는 건 littel endian 방식으로 뒤부터 입력되기 때문이다.
6번에서 40을 뺀 것은 위에서 설명했듯 %n은 앞에 출력된 문자만큼 지정한 인자에 입력을 하기 때문이다.
앞의 8(NOP) + 8(.dtors의 앞, 뒤 주소값) + 24(%p*3) = 40이다.
그래서 65,229에 40만큼 더한 값인 65,269(0xfef5)가 %n에 의해 0x08049598에 입력될 것이다.
8번에서 보수를 취한 것은 0xbfff의 10진수 값 49,151에서 앞의 문자 길이 65,269를 빼면 음수가 되기 때문이다.
그래서 0xbfff의 앞에 1을 넣어 0x1bfff에서 65,269를 빼 49,418 길이 만큼의 문자를 출력하도록 한다.
<페이로드 입력>
그렇게 페이로드를 작성해 입력하면 페이로드에서 입력한 길이만큼 공백이 나와 화면이 길게 넘어갈 것이다.
그리고 id를 입력하면 uid가 clear로 나타난다.
my-pass를 입력하면 clear 비밀번호가 나온다. (clear로 접속했지만 별 거 없었다.)
putty의 Appearance에서 Font settings를 영어 보기에 편하게 설정했더니 한글이 깨져있다.
clear 문구는 다른 블로그에도 많으니 생각나면 확인해봐도 될 것 같다.
<나타난 비밀번호>
마지막 문제답게 난이도가 참 높았다고 생각한다.
잘 아는 사람들한테는 기본적인 공격이겠지만, 아직 시작 단계에서 해메는 사람으로서는 힘든 문제였다.
그래서 구글링도 많이 하고, 다른 블로그의 답들도 많이 참고했다.
그럼에도 아직 완벽히 이해하지 못한 부분들도 있고, 더 공부를 해야겠다는 생각이 든다.
이렇게 F.T.Z를 완료했다.
시스템 해킹의 기초 중의 기초라고 생각하니 막막하지만, 그래도 첫 단추를 무사히 꿴 것 같아 기분이 좋다.반응형'Wargame > F.T.Z' 카테고리의 다른 글
[F.T.Z] Level 19 (0) 2022.10.05 [F.T.Z] Level 18 (0) 2022.10.05 [F.T.Z] Level 17 (0) 2022.10.01 [F.T.Z] Level 16 (1) 2022.09.30 [F.T.Z] Level 15 (0) 2022.09.29