Notice
Recent Posts
Recent Comments
Link
«   2025/03   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
Archives
Today
Total
관리 메뉴

ENFJ_Blog

개발자의 실수 - Type Error 본문

Pwn

개발자의 실수 - Type Error

ENFJ_T 2023. 9. 29. 23:54

 

https://dreamhack.io/lecture/courses/118

 

Logical Bug: Type Error

타입을 잘못 사용하여 발생할 수 있는 버그를 학습합니다.

dreamhack.io

Type Error란?

Type Error는 자료형 선언할 때 크기, 용도, 부호 여부를 고려하지 않고 사용할 때 발생하는 버그이다.

 

자료형-크기/범위/용도

(signed) char 1 바이트 [−128, +127] 정수, 문자
unsigned char [0, 255]
(signed) short (int) 2 바이트 [−32,768, +32,767] 정수
unsigned short (int) [0, 65,535]
(signed) int 4 바이트 [−2,147,483,648, +2,147,483,647] 정수
unsigned int [0, 4,294,967,295]
size_t 32bit: 4 바이트
64bit: 8 바이트
생략 부호 없는 정수
(signed) long 32bit: 4 바이트
64bit: 8 바이트
정수
unsigned long
(signed) long long 32bit: 8 바이트
64bit: 8 바이트
정수
unsigned long long
float 4 바이트 실수
double 8 바이트 실수
Type * 32bit: 4 바이트
64bit: 8 바이트
주소

Out of Range : 데이터 유실

// Name: out_of_range.c
// Compile: gcc -o out_of_range out_of_range.c
#include <stdio.h>
unsigned long long factorial(unsigned int n) {
  unsigned long long res = 1;
  
  for (int i = 1; i <= n; i++) {
    res *= i;
  }
  
  return res;
}

int main() {
  unsigned int n;
  unsigned int res;
  
  printf("Input integer n: ");
  scanf("%d", &n);
  
  if (n >= 50) {
    fprintf(stderr, "Input is too large");
    return -1;
  }
  
  res = factorial(n);
  printf("Factorial of N: %u\n", res);
}

위와 같은 예제에서 16, 17을 입력하다가 갑자기 18에서 17보다 적어지는 것을 확인할 수 있다. 여기서 왜 작아지는 지를 생각해보면 unsigned int 형의 크기를 생각 할 수 있는데, unsigned int의 크기는 4바이트이다. 그런데 !18 = 0x16BEEC CA730000 이다 그런데 4바이트 이므로 상위만 옮겨지고 하위 4바이트를 살리면, 0xCA730000이 된다. 이는 출력에서 본 3,396,534,272와 똑같다. 이처럼 그 변수를 저장할 수 있는 범위를 넘어가게 되면 저장할 수 있는 만큼만 남고 나머지는 다 버려지게 된다. 

// Name: oor_signflip.c
// Compile: gcc -o oor_signflip oor_signflip.c
#include <stdio.h>

unsigned long long factorial(unsigned int n) {
  unsigned long long res = 1;
  
  for (int i = 1; i <= n; i++) {
    res *= i;
  }
  return res;
}

int main() {
  int n;
  unsigned int res;
  
  printf("Input integer n: ");
  scanf("%d", &n);
  
  if (n >= 50) {
    fprintf(stderr, "Input is too large");
    return -1;
  }

  res = factorial(n);
  printf("Factorial of N: %u\n", res);
}

이 코드는 앞에 코드와는 다르게 변수 n의 자료형이 int 형으로 변환 된것을 확인할 수 있다. 이는 입력 값이 음수가 될 수 있다는 뜻이다. 그런데 -1을 입력하면 n을 저장하는 메모리 공간에서는 0xfffffffff 이렇게 저장된다. 왜 그런지는 2의 보수를 공부하면 알 수 있다. (난 앎) 

그런데 factorial 함수는 unsigned int형으로 받으므로, 이 값은 부호 없는 정수인 4,294,967,295로 전달되고, 결국 4,294,967,295번 반복문을 실행하게 된다. 당연히 시간이 오래 걸릴 뿐만 아니라 전과 같이 쟤대로 된 값이 나오지도 않는다.

이러한 문제를 예방하려면 자료형을 알맞게 써야된다.

// Name: oor_bof.c
// Compile: gcc -o oor_bof oor_bof.c -m32
#include <stdio.h>
#define BUF_SIZE 32

int main() {
  char buf[BUF_SIZE];
  int size;
  
  printf("Input length: ");
  scanf("%d", &size);
  
  if (size > BUF_SIZE) {
    fprintf(stderr, "Buffer Overflow Detected");
    return -1;
  }
  
  read(0, buf, size);
  return 0;
}

위와 같은 방법을 이용해 이러한 코드가 있을 때 -1을 입력해주면 if문에 걸리지 않고, read에서 오버플로우를 이르킬 수 있다.

타입 오버플로우랑 타입 언더 플로우는 그냥 int형 최대에서 1을 더해줄 때는 오버플로우가 나서 음수의 최대치로 가는 것과 같은 것을 말한다

예시

unsigned int a = 4,294,967,295 + 1

printf("%d", a)

-> 0

// Name: integer_overflow.c
// Compile: gcc -o integer_overflow integer_overflow.c -m32
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

int main() {
  unsigned int size;
  scanf("%u", &size);
  
  char *buf = (char *)malloc(size + 1);
  unsigned int read_size = read(0, buf, size);
  
  buf[read_size] = 0;
  return 0;
}

위와 같은 상황에서 unsigned int의 최댓값인 4,294,967,295을 입력하면 overflow로 인해 size + 1의 값은 0이 된다. 한마디로 char buf[0] 이 되는 것이다. 그렇지만 size는 그대로 4,294,967,295이기 때문에 read로 읽게 될 경우 4,294,967,295의 값 만큼 쓸 수 있는 heap overflow가 발생하게 된다.

'Pwn' 카테고리의 다른 글

ptmalloc - linux 메모리 할당 ( Memory Allocator )  (0) 2023.10.19
Command Injection  (2) 2023.10.11
Format String Bug  (0) 2023.09.01
Out of Bounds(OOB)  (0) 2023.08.26
전체적 노션 링크(지금까지 공부한거)  (0) 2023.08.16