검수요청.png검수요청.png

"포맷스트링 공격"의 두 판 사이의 차이

위키원
이동: 둘러보기, 검색
잔글 (같이 보기)
1번째 줄: 1번째 줄:
'''포맷스트링 공격'''<!--포맷 스트링 공격-->(Format String Attack)이란 포맷스트링과 이것을 사용하는 printf() 함수의 취약점을 이용하여 RET의 위치에 셸 코드의 주소를 읽어 셸을 획득하는 [[해킹]] 공격이다.
+
'''포맷스트링 공격'''<!--포맷 스트링 공격-->(Format String Attack)이란 포맷스트링과 이것을 사용하는 printf() 함수의 취약점을 이용하여 RET의 위치에 셸 코드의 주소를 읽어 셸을 획득하는 [[해킹]] 공격이다. 기존에 널리 사용되던 [[버퍼오버플로우]](Buffer Overflow) 공격 기법에 비교되는 강력한 해킹 기법이다.
  
 
== 개요 ==
 
== 개요 ==
포맷스트링 공격은 기존에 널리 사용되고 있던 [[버퍼 오버플로]](buffer overflow) 공격 기법에 견줄 만한 강력한 해킹 기법이다. 이 해킹 기법이 발표되고 나서 그 동안 별 문제 없었던 각종 프로그램들에 대한 취약점이 속속 발표되고 해당 프로그램을 제작했던 회사들은 이 취약점을 해결하기 위해 분주해졌다.
+
포맷스트링 공격은 기존에 널리 사용되고 있던 [[버퍼 오버플로]](buffer overflow) 공격 기법에 견줄 만한 강력한 해킹 기법이다. 이 해킹 기법이 발표되고 나서 그 동안 별 문제 없었던 각종 프로그램들에 대한 취약점이 속속 발표되고 해당 프로그램을 제작했던 회사들은 이 취약점을 해결하기 위해 분주해졌다. 포맷스트링 공격 기법에 대해 이해하기 위해서는 포맷스트링이라는 것과, C프로그램에서 어떻게 처리되는지를 이해해야 한다.
 
+
 
== 특징 ==
 
== 특징 ==
 
=== 포맷스트링 ===
 
=== 포맷스트링 ===
[[포맷스트링]]<!--포맷 스트링-->(format string)이란 일반적으로 사용로부터 입력을 받아들이거나 결과를 출력하기 위하여 사용하는 형식이다. [[C 언어]]에서는 일반적으로 사용하는 기호로 다음과 같은 것들이 있다.
+
[[포맷스트링]]<!--포맷 스트링-->(format string)이란 일반적으로 사용로부터 입력을 받아들이거나 결과를 출력하기 위하여 사용하는 형식이다. 포맷스트링을 사용하는 함수에 대해 형식이나 형태를 지정해주는 문자열을 의미한다. [[C 언어]]에서는 일반적으로 사용하는 기호로 다음과 같은 것들이 있다.
 
* %d : 정수형 10진수 상수
 
* %d : 정수형 10진수 상수
 
* %f : 실수형 상수
 
* %f : 실수형 상수
18번째 줄: 18번째 줄:
  
 
%n 은 이전까지 입력되었던 [[문자열]]의 길이(byte) 수 만큼 해당 [[변수]]에 저장시키기 때문에 [[메모리]]의 내용도 변조 가능하다. 이를 이용해 문자열의 길이를 내가 변조시키고 싶은 값의 길이만큼 만든 후 %n을 써주게 되면 메모리상에 내가 원하는 값을 넣을수 있게 된다.<ref>myPPT, 〈[https://mydaum00.tistory.com/entry/%ED%8F%AC%EB%A7%B7-%EC%8A%A4%ED%8A%B8%EB%A7%81 포맷 스티링]〉, 《티스토리》, 2017-07-28</ref>
 
%n 은 이전까지 입력되었던 [[문자열]]의 길이(byte) 수 만큼 해당 [[변수]]에 저장시키기 때문에 [[메모리]]의 내용도 변조 가능하다. 이를 이용해 문자열의 길이를 내가 변조시키고 싶은 값의 길이만큼 만든 후 %n을 써주게 되면 메모리상에 내가 원하는 값을 넣을수 있게 된다.<ref>myPPT, 〈[https://mydaum00.tistory.com/entry/%ED%8F%AC%EB%A7%B7-%EC%8A%A4%ED%8A%B8%EB%A7%81 포맷 스티링]〉, 《티스토리》, 2017-07-28</ref>
 +
 +
=== 포맷스트링의 문제점 ===
 +
포맷스트링공격은 다른 공격 기법처럼 취약점이나 버그들처럼 프로그래머의 작은 실수를 이용해서 공격을 하는 기법이다. 포맷스트링으로 인한 취약점을 이용해서 공격하는 것을 포맷스트링 공격 이라고 한다.<ref name="문제점">haks2198, 〈[http://blog.naver.com/PostView.nhn?blogId=haks2198&logNo=220840244540&categoryNo=0&parentCategoryNo=0&viewDate=&currentPage=1&postListTopCurrentPage=1&from=postView [Format String Attack] 포맷 스트링 공격이란?]〉, 《네이버 블로그》, 2016-10-19 </ref> 예를 들어 프로그래머 두명이 같은 프로그램을 짠다고 가정해보면 첫번째 프로그래머는
 +
printf("%s", str);
 +
이렇게 작성할 수 있다. 두 번째 프로그래머의 경우
 +
printf(str);
 +
두 가지 모두 틀린 것은 아니다. 오히려 두번째 코드의 경우 소스코드를 더 적게 사용하여 현명하다고 판단할 수도 있다. 그렇지만 해커에게는 이렇게 작성된 코드에서 프로그램의 흐름을 바꿀 수 있는 방법을 찾게 만든다. 우선 아래와 같이 코드를 작성한다.
 +
#include<stdio.h>
 +
int main(){
 +
      char str[15] = "Hello, World!\n;
 +
      printf("%s", str);      //1번 예시
 +
      printf(str);            //2번 예시
 +
      return 0;
 +
작성한 코드를 실행시킨다.
 +
 +
Hello, World!
 +
Hello, World!
 +
위와 같이 실행되는 것을 확인할 수 있다. 문제없이 실행된다. 그러나 2번 예시의 경우 printf함수에 의해서 해석되는 문자열"str"은 문자열이 아니라 형식 지시자를 포함한 포맷스트링으로 인식되게 된다. 이처럼 소스코드의 양을 줄이려고 하다가 되어 해커에게 공격의 기회를 줄 수 있다.
 +
 +
출력하려는 문자열 내에 "%d"와 같은 지시자가 들어있으면 형식지시자의 개수만큼 인자들이 스택에서 추출되어진다. printf함수에 전달된 인자의 개수와는 상관없이 지시자의 개수만큼 포맷스트링 문자열 다음에 저장되어 있는 스택의 내용을 추출하게 되는 것이다. 두번째 예시로 코드를 작성할 경우 입력에 따라 지시자의 수가 달라질 수 있게 되어서 악의적으로 스택의 내용을 확인할 수 있다.<ref name="fs">pRoneeR, 〈[https://proneer.tistory.com/entry/FormatString-%ED%8F%AC%EB%A7%B7%EC%8A%A4%ED%8A%B8%EB%A7%81Format-String-Attack [FS] 포맷스트링(Format String) Attack]〉, 《티스토리》, 2007-02-02</ref>
 +
 +
 +
== 버퍼 오버플로 ==
 +
포맷 스트링 공격은 기존에 널리 사용되던 버퍼오버플로 공격과 비교된다. 버퍼 오버플로는 쉽게 말하자면 입력받는 값이 버퍼를 채우다 못해 흘러넘쳐 발생하는 현상이다. 이는 프로그래머가 데이터를 다루는 데에 오류가 발생해서 오동작하게 되는 취약점이다.<ref name="초코">ChocoPeanut, 〈[https://copycode.tistory.com/92 버퍼 오버플로&포맷 스트링 공격]〉, 《티스토리》, 2017-04-29</ref> 프로그램을 작성할 때 문자열에 할당된 메모리 용량보다 큰 공간을 할당했을 때를 예시로 들 수 있다.
 +
8바이트 문자열을 넣을 수 있는 버퍼가 존재할 때, 이 메모리 공간 뒤에는 중요한 코드가 존재한다고 가정하자. 이 8바이트 공간 안에 8바이트를 넘는 문자열을 입력할 경우 중요한 코드가 손상되어 오동작을 유발할 수 있다.
 +
 +
버퍼 오버플로는 C언어 데이터 무결성 문제로 처음 등장했다. 단순 프로그램상 문제로 인식되었다. 후에 악성코드의 원조격인 [[모리스 웜]]이 버퍼오버플로를 사용했다는 것이 알려지면서 공격기법으로써 인식되기 시작했다.<ref name="초코"></ref>
 +
 +
== 활용 ==
 +
포맷스트링 공격으로 할 수 있는 일에는 여러가지가 있다.
 +
=== 프로그램 다운 ===
 +
첫 번째로 프로그램을 다운시킬 수 있다.
 +
char buf[512]
 +
read(0, buf, 512);
 +
printf(buf);
 +
이와 같은 코드가 있을 경우 해커가 "%s%s%s%s%s%s" 라는 입력을 넣게되면 프로그램은 다운된다. printf함수가 %s를 만나면 스택에 있는 메모리 주소로 인식하고 주소에 접근한다. 이 때 %s%s%s%s%s%s 를 입력하게되면 스택에서 위로 24바이트를 읽어서 주소로 인식하게된다. 하지만 원래 스택에 있는 값은 문자열의 주소값이 아니기 때문에 잘못된 주소에 접근하게 되는데, 이 때 세그멘테이션 폴트가 일어나고 프로그램이 다운된다.
 +
 +
=== 프로세스 스택 확인 ===
 +
두 번째로 프로세스 스택을 볼 수 있다. %d나 %x는 스택에서 4바이트 값을 읽어와서 출력한다. 즉 스택의 4바이트를 보고싶다면 %x를 입력하면 된다. 이렇게 메모리 스택을 볼 수 있으면 공격자는 프로세스의 메모리 구조를 파악할 수 있게 된다.
 +
 +
=== 메모리 임의의 위치 보기 ===
 +
포맷스트링 공격으로 공격자는 메모리의 원하는 부분을 볼 수 있다.
 +
char buf[512]
 +
int a;
 +
int b;
 +
read(0, buf, 512); //표준입력에서 읽어들임
 +
printf(buf);
 +
위와 같은 코드가 있을 경우 buf에 자신의 데이터를 입력한다. 만약 buf의 처음 4바이트에 보기를 원하는 주소를 넣는다면 해커는 8바이트만큼 스택을 거슬러 올라가서 그 주소에 접근할 수 있다. 예시를 든다면 해커가 0x0a0b0c0d를 보고자 한다면 "\x0d\x0c\x0b\x0a\ %08x %08x %s" 를 입력해야 한다. %08x가 두개 있기 대문에 8바이트를 거슬러 올라가서 문자열이 있는 버퍼까지 간다. 그 다음에는 %s가 있기 때문에 스택에서 4바이트를 읽고 주소로 인식한다. 마지막으로 printf함수는 0x0a0b0c0d번지에서 NULL값이 나올 때까지 읽는다. 이처럼 해커는 원하는 부분을 볼 수 있다.
  
 
{{각주}}
 
{{각주}}
23번째 줄: 71번째 줄:
 
== 참고자료 ==
 
== 참고자료 ==
 
* myPPT, 〈[https://mydaum00.tistory.com/entry/%ED%8F%AC%EB%A7%B7-%EC%8A%A4%ED%8A%B8%EB%A7%81 포맷 스티링]〉, 《티스토리》, 2017-07-28
 
* myPPT, 〈[https://mydaum00.tistory.com/entry/%ED%8F%AC%EB%A7%B7-%EC%8A%A4%ED%8A%B8%EB%A7%81 포맷 스티링]〉, 《티스토리》, 2017-07-28
 +
* haks2198, 〈[http://blog.naver.com/PostView.nhn?blogId=haks2198&logNo=220840244540&categoryNo=0&parentCategoryNo=0&viewDate=&currentPage=1&postListTopCurrentPage=1&from=postView [Format String Attack] 포맷 스트링 공격이란?]〉, 《네이버 블로그》, 2016-10-19
 +
* ChocoPeanut, 〈[https://copycode.tistory.com/92 버퍼 오버플로&포맷 스트링 공격]〉, 《티스토리》, 2017-04-29
 +
* pRoneeR, 〈[https://proneer.tistory.com/entry/FormatString-%ED%8F%AC%EB%A7%B7%EC%8A%A4%ED%8A%B8%EB%A7%81Format-String-Attack [FS] 포맷스트링(Format String) Attack]〉, 《티스토리》, 2007-02-02
 +
 +
  
 
== 같이 보기 ==
 
== 같이 보기 ==
28번째 줄: 81번째 줄:
 
* [[레인보우 테이블]]
 
* [[레인보우 테이블]]
 
* [[패스워드 크래킹]]
 
* [[패스워드 크래킹]]
 +
* [[버퍼 오버플로]]
  
{{보안|토막글}}
+
{{보안|검토 필요}}

2019년 7월 29일 (월) 11:25 판

포맷스트링 공격(Format String Attack)이란 포맷스트링과 이것을 사용하는 printf() 함수의 취약점을 이용하여 RET의 위치에 셸 코드의 주소를 읽어 셸을 획득하는 해킹 공격이다. 기존에 널리 사용되던 버퍼오버플로우(Buffer Overflow) 공격 기법에 비교되는 강력한 해킹 기법이다.

개요

포맷스트링 공격은 기존에 널리 사용되고 있던 버퍼 오버플로(buffer overflow) 공격 기법에 견줄 만한 강력한 해킹 기법이다. 이 해킹 기법이 발표되고 나서 그 동안 별 문제 없었던 각종 프로그램들에 대한 취약점이 속속 발표되고 해당 프로그램을 제작했던 회사들은 이 취약점을 해결하기 위해 분주해졌다. 포맷스트링 공격 기법에 대해 이해하기 위해서는 포맷스트링이라는 것과, C프로그램에서 어떻게 처리되는지를 이해해야 한다.

특징

포맷스트링

포맷스트링(format string)이란 일반적으로 사용로부터 입력을 받아들이거나 결과를 출력하기 위하여 사용하는 형식이다. 포맷스트링을 사용하는 함수에 대해 형식이나 형태를 지정해주는 문자열을 의미한다. C 언어에서는 일반적으로 사용하는 기호로 다음과 같은 것들이 있다.

  •  %d : 정수형 10진수 상수
  •  %f : 실수형 상수
  •  %lf : 실수형 상수
  •  %c : 문자값
  •  %s : 문자 스트링
  •  %u : 양의 정수(10진수)
  •  %o : 양의 정수(8진수)
  •  %x : 양의 정수(16진수)
  •  %n : 쓰인 총 바이트 수

%n 은 이전까지 입력되었던 문자열의 길이(byte) 수 만큼 해당 변수에 저장시키기 때문에 메모리의 내용도 변조 가능하다. 이를 이용해 문자열의 길이를 내가 변조시키고 싶은 값의 길이만큼 만든 후 %n을 써주게 되면 메모리상에 내가 원하는 값을 넣을수 있게 된다.[1]

포맷스트링의 문제점

포맷스트링공격은 다른 공격 기법처럼 취약점이나 버그들처럼 프로그래머의 작은 실수를 이용해서 공격을 하는 기법이다. 포맷스트링으로 인한 취약점을 이용해서 공격하는 것을 포맷스트링 공격 이라고 한다.[2] 예를 들어 프로그래머 두명이 같은 프로그램을 짠다고 가정해보면 첫번째 프로그래머는

printf("%s", str);

이렇게 작성할 수 있다. 두 번째 프로그래머의 경우

printf(str); 

두 가지 모두 틀린 것은 아니다. 오히려 두번째 코드의 경우 소스코드를 더 적게 사용하여 현명하다고 판단할 수도 있다. 그렇지만 해커에게는 이렇게 작성된 코드에서 프로그램의 흐름을 바꿀 수 있는 방법을 찾게 만든다. 우선 아래와 같이 코드를 작성한다.

#include<stdio.h>
int main(){
      char str[15] = "Hello, World!\n;
      printf("%s", str);       //1번 예시
      printf(str);             //2번 예시
      return 0;

작성한 코드를 실행시킨다.

Hello, World!
Hello, World!

위와 같이 실행되는 것을 확인할 수 있다. 문제없이 실행된다. 그러나 2번 예시의 경우 printf함수에 의해서 해석되는 문자열"str"은 문자열이 아니라 형식 지시자를 포함한 포맷스트링으로 인식되게 된다. 이처럼 소스코드의 양을 줄이려고 하다가 되어 해커에게 공격의 기회를 줄 수 있다.

출력하려는 문자열 내에 "%d"와 같은 지시자가 들어있으면 형식지시자의 개수만큼 인자들이 스택에서 추출되어진다. printf함수에 전달된 인자의 개수와는 상관없이 지시자의 개수만큼 포맷스트링 문자열 다음에 저장되어 있는 스택의 내용을 추출하게 되는 것이다. 두번째 예시로 코드를 작성할 경우 입력에 따라 지시자의 수가 달라질 수 있게 되어서 악의적으로 스택의 내용을 확인할 수 있다.[3]


버퍼 오버플로

포맷 스트링 공격은 기존에 널리 사용되던 버퍼오버플로 공격과 비교된다. 버퍼 오버플로는 쉽게 말하자면 입력받는 값이 버퍼를 채우다 못해 흘러넘쳐 발생하는 현상이다. 이는 프로그래머가 데이터를 다루는 데에 오류가 발생해서 오동작하게 되는 취약점이다.[4] 프로그램을 작성할 때 문자열에 할당된 메모리 용량보다 큰 공간을 할당했을 때를 예시로 들 수 있다. 8바이트 문자열을 넣을 수 있는 버퍼가 존재할 때, 이 메모리 공간 뒤에는 중요한 코드가 존재한다고 가정하자. 이 8바이트 공간 안에 8바이트를 넘는 문자열을 입력할 경우 중요한 코드가 손상되어 오동작을 유발할 수 있다.

버퍼 오버플로는 C언어 데이터 무결성 문제로 처음 등장했다. 단순 프로그램상 문제로 인식되었다. 후에 악성코드의 원조격인 모리스 웜이 버퍼오버플로를 사용했다는 것이 알려지면서 공격기법으로써 인식되기 시작했다.[4]

활용

포맷스트링 공격으로 할 수 있는 일에는 여러가지가 있다.

프로그램 다운

첫 번째로 프로그램을 다운시킬 수 있다.

char buf[512]
read(0, buf, 512);
printf(buf);

이와 같은 코드가 있을 경우 해커가 "%s%s%s%s%s%s" 라는 입력을 넣게되면 프로그램은 다운된다. printf함수가 %s를 만나면 스택에 있는 메모리 주소로 인식하고 주소에 접근한다. 이 때 %s%s%s%s%s%s 를 입력하게되면 스택에서 위로 24바이트를 읽어서 주소로 인식하게된다. 하지만 원래 스택에 있는 값은 문자열의 주소값이 아니기 때문에 잘못된 주소에 접근하게 되는데, 이 때 세그멘테이션 폴트가 일어나고 프로그램이 다운된다.

프로세스 스택 확인

두 번째로 프로세스 스택을 볼 수 있다. %d나 %x는 스택에서 4바이트 값을 읽어와서 출력한다. 즉 스택의 4바이트를 보고싶다면 %x를 입력하면 된다. 이렇게 메모리 스택을 볼 수 있으면 공격자는 프로세스의 메모리 구조를 파악할 수 있게 된다.

메모리 임의의 위치 보기

포맷스트링 공격으로 공격자는 메모리의 원하는 부분을 볼 수 있다.

char buf[512]
int a;
int b;
read(0, buf, 512); //표준입력에서 읽어들임
printf(buf);

위와 같은 코드가 있을 경우 buf에 자신의 데이터를 입력한다. 만약 buf의 처음 4바이트에 보기를 원하는 주소를 넣는다면 해커는 8바이트만큼 스택을 거슬러 올라가서 그 주소에 접근할 수 있다. 예시를 든다면 해커가 0x0a0b0c0d를 보고자 한다면 "\x0d\x0c\x0b\x0a\ %08x %08x %s" 를 입력해야 한다. %08x가 두개 있기 대문에 8바이트를 거슬러 올라가서 문자열이 있는 버퍼까지 간다. 그 다음에는 %s가 있기 때문에 스택에서 4바이트를 읽고 주소로 인식한다. 마지막으로 printf함수는 0x0a0b0c0d번지에서 NULL값이 나올 때까지 읽는다. 이처럼 해커는 원하는 부분을 볼 수 있다.

각주

  1. myPPT, 〈포맷 스티링〉, 《티스토리》, 2017-07-28
  2. haks2198, 〈[Format String Attack 포맷 스트링 공격이란?]〉, 《네이버 블로그》, 2016-10-19
  3. pRoneeR, 〈[FS 포맷스트링(Format String) Attack]〉, 《티스토리》, 2007-02-02
  4. 4.0 4.1 ChocoPeanut, 〈버퍼 오버플로&포맷 스트링 공격〉, 《티스토리》, 2017-04-29

참고자료


같이 보기


  검수요청.png검수요청.png 이 포맷스트링 공격 문서는 보안에 관한 글로서 검토가 필요합니다. 위키 문서는 누구든지 자유롭게 편집할 수 있습니다. [편집]을 눌러 문서 내용을 검토·수정해 주세요.