2017. 6. 8. 14:24ㆍSystemHacking/System
원재아빠님의 Format String 강좌를 보면서 개인공부를 위해 작성하였습니다.
포맷 스트링이란 변수의 형태를 지정해주는 것입니다
종류에는
%d : 정수형 상수 int
%c : 문자형 char
%f : 실수형 상수 float
%x : 16진수
%o : 10진수
등이 있으며, 중요한 %n 도 있습니다
[ 포맷스트링 예제 ]
포맷스트링의 빈틈은 다음과 같은 소스코드에서 발생합니다
#vi test2.c
#gcc -o test2 test2.c
[ 그림1 ]
printf()함수에서 받아들인 변수 put을 스트링의 여과 없이 그대로 출력해주고 있습니다
여기서 문제가 발생합니다. 특정 스트링을 임의로 사용자가 넣으면 특이한 현상이 발생합니다
[ 그림2 ]
fgets()를 통해서 put에 문자열을 입력할 때 " %x " 스트링을 추가하였더니 해당프로그램의 메모리 구조가 출력됩니다
포맷스트링을 통해 이렇게 주소값을 보여주는 것에서부터 특정값을 변경시킬 수도 있게 됩니다 ( %n 을 사용해서 )
6번째 %8x 에서 우리가 입력한 AAAA 가 저장되있는 곳을 확인할 수 있었습니다
FSB(Format String Bug)공격은 " %n " 스트링을 이용합니다. 사용법에 대해서 알아봅시다
#vi test3.c
[ 그림3 ]
아래그림은 실행결과입니다
[ 그림4 ]
" %n "스트링은 %n 앞의 글자수(byte)의 값을 할당된 주소에 덮어씌운다. ( 다른 스트링들과는 작동방향이 반대라고 생각하면 된다 )
즉, 특정 주소번지에 있는 값을 " %n "스트링을 통해서 원하는 값으로 변경시킬 수 있다 !
포맷스트링 공격법의 관점으로 보면 " RET "에 있는 값을 %n을 통해서 원하는 다른 주소로 변질시키는 것이다
그럼 특정 주소에 주소값을 넣는 방법에 대해 알아보겠습니다.
#vi test4.c
[ 그림5 ]
[ 프로그램 실행과정 ]
먼저 변수 " i "를 0x00000014 로 초기화시킵니다
변수 " k " 는 변수 " i "의 주소값입니다
65281을 " i의 시작주소 " 에 덮어씌운다.
65281+49406 = 114687을 " i의 시작주소 + 2 " 지점에 덮어씌운다.
그리고 " i "의 값을 출력해보자 ( %x : 16진수로 )
#gcc -o test4 test4.c
#./test4
[ 그림6 ]
" i "의 값은 " bfffff01 "
어떻게 해서 i 의 값이 변하였을까? i 의 값이 변화되는 과정을 살펴보도록 합시다
0x02e139a8 : 14 00 00 00 => 0x00000014
그리고 i의 시작주소(k)에 %n 스트링을 이용해서 65281을 입력시켰습니다. (= 0xff01 )
( *참고 : 리틀 엔디안 방식으로 인하여 1byte씩 역순으로 저장됩니다 )
0x02e139a8 : 01 ff 00 00
그리고 " i "의 시작주소+2byte한 주소(k+2)에 두번째 %n 스트링을 이용해서 65281+49406 = 114687을 입력시켰습니다. (= 0x1bfff )
0x02e139a8 : 01 ff ff bf => " 0xbfffff01 "
0x02e139ab : 10 00 00 00 : 쓰레기값
위처럼 메모리 주소를 값으로 넣기위해서는 매우 큰 수가 적용되므로 나누어서 집어넣는 방법을 사용합니다
이해가 잘 안될 수 있으니 실전으로 예제를 들어보겠습니다
[ 포맷스트링 실전 ]
# 임의의주소(0xbffffa6b)에 우리가 원하는값(0xbffffb30)을 넣는 방법을 알아보자
$vi test6.c
[ 그림7 ]
dumpcode.h는 ohhara님 께서 만드신 헤더라고 합니다. 해당 주소에 들어있는 값을 출력해주는 함수가 있어서 매우 편리합니다
( 구글링하면 나와요 )
1. 입력한 값의 위치 찾기 ㅡ 그림8
[ 그림8 ]
네번째의 " %8x " 스트링에서 AAAA가 저장되 있는 것을 확인 할 수 있다
즉 문자열을 입력하면 메모리 공간의 4번째 위치에 해당 문자열이 저장됨을 알 수 있다
그럼 " AAAA " 대신 메모리값을 넣어보도록 합시다
2. 메모리값 넣기 ㅡ 그림9
메모리값을 넣으려면 16진수형태로 넣어주어야 하기때문에 python 이나 perl 또는 printf 함수를 이용해야합니다
( printf함수에서 %%8x 는 파이프 연결로 인해 %8x 와같은 역할을 합니다 )
[ 그림9 ]
4번째 " %%8x " 에서 입력한 메모리 값이 저장되어 있음이 확인됩니다
임의의주소 " 0xbffffa6b " 를 메모리공간에 저장시켰습니다. 이를 응용해서 +2byte한 주소 또한 저장시킬 수 있습니다
[ 그림10 ]
그림에서는 0xbffffb30을 넣엇지만 실제 우리가 사용할 주소는 0xbffffb6d 입니다 ( 임의의주소에 +2byte한 주소 )
$(printf"\x41\x41\x41\x41\x6b\xfa\xff\xbf\x41\x41\x41\x41\x6d\xfb\xff\xbf%%8x%%8x%%8x%%c%%8x%%c%%8x";cat)|./test6 |
%%8x 스트링에 의해 메모리공간에 있는 쓰레기값들이 출력되어지고
%%c 스트링에 의해 " \x41\x41\x41\x41 " 문자열이 "A"라는 문자로 출력되어지고
%%8x 스트링에 의해서 " 0xbfffab6b " 가 출력되고
%%c 스트링에 의해 " \x41\x41\x41\x41 " 문자열이 "A"라는 문자로 출력되어지고
%%8x 스트링에 의해서 " 0xbfffab6d " 가 출력되어 졌습니다
메모리 구조를 표로 작성해보겠습니다 ( 이해를 돕기위해 임의적으로 작성 )
첫번째 %%8x |
두번째 %%8x |
세번째 %%8x |
%c |
%%8x |
%c |
%%8x |
쓰레기값 |
쓰레기값 |
쓰레기값 |
AAAA |
0xbffffa6b |
AAAA |
0xbffffa6d |
각각의 칸은 메모리의 공간을 차지하고 있고 스트링을 사용하여 해당 공간안의 값을 출력해준 것
즉, 메모리 공간안의 5번째 칸에는 " 0xbffffa6b " 가 위치하고 있다는 뜻 => 해당 값을 %%8x로 출력하여 보여준것
그럼 %%8x 스트링을 " %%n " 스트링으로 바꿔서 작성하면 어떤 변화가 생길것인가 ?
[ 그림 11 ]
0xbffffa6b의 값이 29 00 2a 00 으로 변했습니다 ! why ??
우리가 지금 %%n 스트링을 사용한 지점이 어디일까? 바로 " 0xbffffa6b " 와 " 0xbffffa6d " 가 위치해있는 메모리 공간이다
따라서 %%n 스트링은 " 0xbffffa6b " 와 " 0xbffffa6d " 지점에 값을 덮어 씌워서 변질시킨 것이다 ★★★
먼저 처음에 위치한 " %%n "을 봐보겠습니다. %%n 스트링 이전에 문자의개수는 4+4+4+4+8+8+8+1 = 41 = 0x29
그리고 두번째 위치한 " %%n "을 보시면 41개의 문자에 " %%n과 %%c " 2문자를 더한 43 = 0x2a 입니다
즉, 문자의 개수를 늘려주면 %%n의 값이 변하겟죠? 그럼 우리가 원하는 16진수값을 해당 0xbffffa6b지점에 덮어씌울 수 있습니다 !
넣고자 하는값 : " 0xbffffb30 "
첫번째 %%n 스트링에 " fb30 " 을 집어넣어주고 두번째 %%n 스트링에 " bfff " 를 넣어주어야 합니다.
그런데 " bfff "가 작은 수인데 어떻게 뒤에 나오는 문자의 개수를 맞춰줄까 생각이 들겁니다
" bfff " 대신 " 1bfff " 를 얻으면됩니다. 스택의 특성을 이용하는 것입니다
0xbffffa6b : 30 fb ff bf => " 0xbffffb30 " 0xbffffa6c : ff bf 01 00 0xbfffa6d : fa ff bf 01 |
실제로 해당 값을 해당 메모리 공간에 넣어보도록 하겠습니다
0xfb30 = 64304byte입니다. 이때 " %%c "스트링 앞에 있는 문자의 개수는 40(4+4+4+4+8+8+8)개입니다 ( 64304-40 = 64264 )
따라서 첫번째" %%c "스트링을 " %%64264c "으로 바꿔서 입력해보도록 하겠습니다.
$(printf"\x41\x41\x41\x41\x6b\xfa\xff\xbf\x41\x41\x41\x41\x6d\xfb\xff\xbf%%8x%%8x%%8x%%64264c%%n%%c%%n";cat)|./test6 |
[ 그림 12 ]
해당 지점(0xbffffa6b)에 원하는 값이 들어가 있음을 확인 할 수 있습니다.
다음으로 두번째 %%n 스트링에 들어갈 문자개수를 조절하여 " 0xbffffa6d "주소에 값을 덮어씌우면 성공입니다
0x1bfff = 114687byte입니다. 114687 - 64264- 40 = 50383byte개의 문자가 필요합니다
[ 그림 13 ]
원하는 메모리 공간에 원하는 값을 넣었습니다.
# 임의의 메모리 공간을 A 라고 했을 때 왜 A주소에 +2byte한 지점에 값을 집어넣는지 이해가 안될 수도 있다
# 16진수의 메모리 주소는 총 4byte이다
주소 |
1 byte |
1 byte |
1 byte |
1 byte |
값 |
0xbffffa6b |
30 |
fb |
ff |
bf |
0xbffffb30 |
0xbffffa6c |
fb |
ff |
bf |
01 |
0x01bffffb |
0xbffffa6d |
ff |
bf |
01 |
00 |
0x0001bfff |
# 처음 %%n에 의해서 " 0xbffffa6b "지점은 " 0x0000fb30 " 값을 가지고 있다
# " 0xbffffa6d "지점에 " 0x0001bfff "값으로 덮어씌움으로써 " 0xbffffa6b "지점의 처음 2byte부분을 변질시킨 것
# 메모리 주소값을 대입하기에 너무 큰 수를 사용해야 하므로 위처럼 2byte씩 끈어서 대입시키는 방법을 사용한다
( 정수가 표현할 수 있는 범위를 벗어나버린다 )
FTZ 11번문제에서 해당개념을 응용하여 사용해보도록 하겠고 이상으로 포맷스트링의 개념에 대한 설명을 끝마치도록 하겠습니다.
'SystemHacking > System' 카테고리의 다른 글
[8] Use After Free / Double Free Bug (0) | 2017.06.16 |
---|---|
[7] PLT / GOT / objdump활용법 (0) | 2017.06.14 |
[5] Heap Buffer Overflow ( Heap기반의 버퍼오버플로우 ) (0) | 2017.06.07 |
[4] 환경변수를 이용한 버퍼 오버플로우 공격 ( 에그쉘) (1) | 2017.06.05 |
[3] Buffer Overflow 공격 (0) | 2017.06.03 |