Format String 이란 

int num = 5;

printf("ABC%d", num); 같은 형태의 코드가 있을 때 %d가 바로 포맷 스트링입니다

보통 프로그래머들이 코딩을 할 때 저런식으로 입력하지않고 편리성을 위해서 

fgets(num , 10 , stdin);

printf(num)

이러한 식으로 입력을 하는 경우가 있는데 만약 저기 num안에 문자열을 넣으면 문자열로 정상적으로 인식하지만 서식문자를 넣었을 경우에는 서식문자로 인식합니다 .

그 후에 넣어진 서식문자를 만나면 메모리의 다음 "4바이트"를 그 서식자의 기능대로 출력하게 됩니다 . 

주로 알려지고 쓰이는 서식문자들의 역할입니다 

%d : 정수형 10진수 상수                   %lf : 실수형 상수(double)
%f : 실수형상수 (float)                      %c :  문자 값 (char)
%s : 문자열                                    %u : 양의 정수 (10진수)
%o: 양의 정수 (8진수)                       %x : 양의 정수 (16진수)
%n:*int(쓰인 총 바이트 수)                 %hn : %n의 반인 2바이트 단위 

자 여기서 서식문자를 만나면 메모리의 다음 "4바이트"를 그 서식자의 기능대로 출력하게 된다고 말씀드렸는데요 과연 여기서 다음 "4 바이트"가 무엇을 의미하는 것일까요 ?

(* 본 소스는 FTZ-LEVEL20의 소스를 예제 삼아서 진행하고 있습니다 .
문제의 풀이를 보기 싫으신분은 다른 글을 참고해주세요!)


fgets 함수를 통해서 printf(bleh)으로 바로 호출하네요

printf로 바로 출력을하면 포맷스트링어택이 가능하게 됩니다 ! 

자 그럼 여기서 아까 말씀드린 다음 4바이트가 뭐인지 설명해드리겠습니다 .

AAAA라는 문자열을 친 후에 %x(양의 정수)라는 서식문자를 사용했습니다!

분명히 %x를 입력했을텐데 지금 나오는 숫자들은 무슨 숫자들일까요 ?

4f를 10진수로 바꾸면? 79입니다 

4212ecc0 은 무슨 뜻일까요 ? 

stdin이라고 뜨는군요! 그렇다면 지금 fgets(bleh,79,stdin) 에서 79와 stdin이 나왔네요?

4207a750 은 무엇을 의미할까요 ? 

strrchr이 나왔네요 ? 의미는 임의의 문자열이 시작되는 위치를 구합니다 .

그리고 마지막으로 41414141은 제가 입력한 A의 아스키코드입니다 

그렇다면 메모리 구조는 

| printf | | size of (bleh) | | stdin | | strrchr | | bleh | | dummy | | sfp | | ret |

이렇게 되잇겠네요!

고로 다음 "4바이트" 가 의미하는것은 | ~ | 공간을 하나씩 출력한다고 보시면 되겠습니다   

자 그럼 이 서식문자를 통해서 공격을 해야되는데요 ! 

여기서 가장 중요한 서식문자 %n을 알아보도록 하겠습니다! 

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

만약에 AAAA %1111x %x %x %n 를 입력햇을 경우에는 현재 %n의 위치가 원래 41414141이 있던곳입니다 .

고로 AAAA(4) + 공백(1) + %1111x(1111)+ 공백(1) + %x(1) + 공백 (1) + %x(1) + 공백(1) = 4 + 1 + 1111 + 1 + 1 + 1 + 1 = 1120 이므로  0x41414141 = 0x460(1120) 이 됩니다 . 

방금 위에서 설명한거로 알 수 있는것은 %1111x 를 넣었더니 1111만큼 주소값이 증가한것을 확인 할 수 있습니다 .
그렇다면 원하는 흐름의 주소를 알아낸다음에 그 만큼 주소 값을 올려서 원하는 메모리 주소에 덮어 쓸 수 있다면(ex - ret )? 공격이 가능하겠습니다!

먼저 쉘코드를 실행 시키는게 관점이겠습니다 . 본 글에서는 전에 풀이했던 FTZ LEVEL20의 풀이를 가져와서 다시 한번 설명하겠습니다  

먼저 에그쉘을 이용해서 쉘코드를 구했습니다 .

그 다음엔 ret 주소를 구합니다  > 08049598 

자 근데 0xffffffff를 32bit 시스템에서는 받아들일 수 없습니다! 
0xffffff를 10진수로 표현하면 43억 가까이 되기 때문이지요! 그래서 먼저 쉘코드의 주소를 2바이트씩 나눠서 입력하는 방법을 선택해야 합니다 .
bfff / f2cb 이렇게 나누어서 10진수로 바꾼 후 공격하는 것이지요 

근데 쉘 코드를 ret에 넣어줘야 합니다. 그럼 ret에는 2바이트씩 나눠져서 입력을 하기 떄문에 
f2cb 는 08049598 bfff 는 0804959a 에 입력을 해줘야겠습니다! 
(리틀 엔디안 방식을 이용해야 하기 떄문에 낮은자리를 먼저 입력합니다 )

자! 그럼 페이로드를 대충 구성해보죠! 

AAAA\x98\x95\x04\x08AAAA\x9a\x95\x04\x08%8x%8x%8x%f2cb의 10진수%n%bfff의 10진수%n

이렇게 되겠습니다! 

자 여기서 왜 AAAA를 써줘야 하느냐 ? %n 의 위치와 ret의 위치를 맞춰주기 위해서입니다!

서식문자는 4바이트를 참고한다고 햇지요 ? 쉘코드를 각자 나눠서 입력한 서식문자가 AAAA 에 들어가게되고 %n에는 쉘코드의 주소를 넣어놧으니 ret에 들어가도록 하기 위해서입니다! 

%8x를 굳이 쓴 이유가 무엇이냐 ?

| printf | | size of (bleh) | | stdin | | strrchr | | bleh | | dummy | | sfp | | ret |

아까 위에서 이러한 메모리구조를 말씀 드렸습니다 

%8x 를 쓴 이유는 저 메모리의 각 부분들이 각자 8자리만큼 차지하기 때문이고 
%8x를 3번 사용해준 이유는 bleh 부문으로 넘어가게 해서 값들이 정상적으로 ret에 들어가게 하기 위해서입니다 .

자 여기서 이제 f2cb 와 bfff를 10진수로 나타내면 f2cb = 62115 bfff = 49151 이겠습니다.

근데 %n은 여태까지 입력된 자리를 모두 계산한다고 하였지요 ?
AAAA(4)+ ret(4) + AAAA(4) + ret(4) + 8 + 8 + 8 = 40이 되겠습니다

그렇다면 62125 라는 숫자가 되기 위해서는 62125 - 40 = 62085 라는 숫자를 %c에 넣어줘야지 여태까지 나온 숫자값들을 모두 합산해서 62125가 되겠네요!

어라 근데 bfff = 49151 이네요 ? 여태까지 모두 나온 숫자들을 빼줘버리면 음수가 되고 그렇다고 가만히 내비두자니 ret의 주소에 접근할 수가 없습니다.

이럴때 방법은 bfff 앞에 1을 붙이면 1bfff:114687이 됩니다. 

여기서 62125 만큼 빼주면 52562입니다 . 고로 페이로드는 

AAAA\x98\x95\x04\x08AAAA\x9a\x95\x04\x08%8x%8x%8x%62085c%n%52562c%n 이렇게 되겠습니다 .  이렇게 각 쉘코드가 ret의 주소에 들어가게 되고

권한을 취득할 수 있게됩니다 

'시스템 기법 정리' 카테고리의 다른 글

Fake EBP  (0) 2016.06.02
PLT , GOT [상세히] ,GOT Overwrite  (0) 2016.06.02
ASLR를 해제하는 여러가지 방법  (0) 2016.05.29
plt , got , rtl chain , ASLR  (0) 2016.05.28
버퍼 오버플로우 - 2 RTL (개념 정리, 기초)  (0) 2016.05.27

+ Recent posts