-
Integer Overflow, Underflow / Signedness bugs / Widthness bugs보안개론 2024. 6. 4. 16:08
아리안 5
적재된 위성을 궤도에 전달하기 위한 용도의 로켓
64비트 숫자를 16비트에 채우면서 유발화성 기후 궤도 탐사선
록히드 마틴에서는 점화 데이터를 야드 단위로 만들었고,
나사는 미터법으로 생각했음 -> 지나치게 낮은 궤도 - 폭발
비트 크기를 제대로 맞추지 않으면 문제가 생길 수 있음
char: -128~127
unsigned char: 0~255
short: -32768~32767
unsigned short: 0~65535
Format Specifier
%d - 10진수 정수 (4바이트)
%u - 부호없는 정수
%i - 아무 정수 (10진수, 8진수, 16진수)
%hd - short (2바이트)
%hu - unsigned short
%hhd - char (1바이트)
%hhu - unsigned char
%ld - long (32비트 시스템이면 4바이트, 64비트 시스템이면 8바이트)
%lu - unsigned long
%o - 8진수 정수, 4바이트
%x - 16진수 정수, 4바이트
%p - 주소 출력, 32비트 시스템이면 4바이트, 64비트 시스템이면 8바이트 .. 인데 보통 48비트만 사용
Integer overflow / Wraparound
CWE 190
정수가 범위를 넘어선 너무 큰 값으로 증가하면 0이나 음수가 됨 (오버플로우)
CIA를 깨트릴 수 있음
가용성
- 정의되지 않을 작동으로 연결되어 크래시 발생 가능, 반복문의 인덱스에서 인티저 오버 플로우 발생 시 무한 루프에 빠져 정상 작동하지 않을 수 있음
무결성
- 문제가 되는 값들이 중요한 데이터이면 단순한 데이터 오염 발생
- 랩 어라운드가 발생하여 버퍼 오버플로우로 연결 시 메모리 오염 발생 가능
비밀성, 가용성, 접근 제어 등 ..
- 임의 코드 실행으로 연결될 수 있음
Signed Numbers
Binary Decimal Hexadecimal 0111 1111 127 0x7F 0111 1110 126 0x7E 1111 1111 -1 0xFF 1111 1110 -2 0xFE 1000 0001 -127 0x81 1000 0000 -128 0x80 양수 127
2진수 - 0111 1111
1의 보수 -> 1000 0000
2의 보수 -> 1000 0001
2 +(-3) = ?
3 : 이진수로 0011
1의 보수 -> 1100
2의 보수 -> 1101
0010 + 1101 = 1111 -> -1
4비트로 제한되어 있다고 치자
+5 + 4
0101 + 0100
1001 -> 오버플로우
-7 -6
1001 + 1010
1 / 0011 -> 오버플로우
32비트의 경우
int 최고 -> 0x7FFFFFFF (2147483647)
여기다 1 더하면 -> 0x80000000
=> 언더플로우
img_t table_ptr; int num_imgs; num_imgs = get_num_imgs(); table_ptr = (img_t*)malloc(sizeof(img_t) * num_imgs);
malloc 내부 인자 값이 엄청 커져서 오버플로우가 발생할 수도 있음
대신 xmalloc 권장
같은 이유로 memcpy도 문제가 있음
int catvars(char *buf1, char *buf2, unsigned int len1, unsigned int len2) { char mubuf[256]; if( (len1 + len2) > 256 ) { return -1; } memcpy(mybuf, buf1, len1); memcpy(mybuf + len1, buf2, len2); do_some_stuff(mybuf); return 0; }
len1과 len2를 더해서 오버플로우 발생 시 256보다 작을 수 있음
계산 결과이후 저장된 결과와 올바른 결과가 다를 수 있음
중요한 변수에 잘못된 값을 포함하게 하는 등, 문제가 될 수 있음
어떻게 예방할 수 있는가?
에러 핸들링, 익셉션 핸들링
int sum(int a, int b) { int c = a + b; if( a > 0 && b > 0 && c < 0 ) { throw new MyOverflowException(a, b); } return c; } int pord(int a, int b) { int c = a * b; if( a > 0 && b > 0 && c < 0 ) { throw new MyOverflowException(a, b); } return c; }
양수를 더했는데, 혹은 곱했는데 음수가 나오면 예외처리
static void update_value(char op) { if( op == '+' ) { if( value + 1 > value ) value++; else printf("too big!\n"); } else { if( value -1 < value ) value--; else printf("too small!\n"); } } int addOvf(int *result, int a, int b) { if( a > INT_MAX - b ) return -1; else { *result = a + b; return 0; } }
더했는데 원해 값보다 작아지거나,
원래 값이 너무 커서 남은 자리가 충분하지 않은지 검사하기
완화시키기(SDLC 단계에서 신경써야 함)
요구사항 단계
- 언어를 잘 선택해라: 파이썬같은 건 부족하면 알아서 늘림
설계 단계
- 라이브러리나 프레임워크를 잘 선택해라(SafeInt, IntegerLib)
구현 단계
- 입력 검증 하기
- 크기를 나타내는 건 unsigned 쓰기
Signedness bugs
- 부호가 없는 변수가 부호가 있는 것으로 해석되는 경우
- 부호가 있는 변수가 부호가 없는 경우로 해석되는 경우
// int.c #include <stdio.h> #include <string.h> #include <stdlib.h> int main(int argc, char *argv[]) { char buf[20]; int i = atoi(argv[1]); memcpy(buf, argv[2], i * sizeof(int)); } // void *memcpy(void *dest, const void *src, size_t num);
1. 버퍼 오버플로우
./ 111 AAAA
i * sizeof(int) => 444
444만큼 argv[2]의 내용을 카피? -- buf는 20바이트 크기 -> 넘침
스택의 구조가 망가지고, 리턴 어드레스나 saved ebp가 망가지고, 스택 카나리가 망가짐 (스택 스매싱 detected)
2. 부호화 버그로 인한 버퍼 오버플로우
memcpy의 size_t는 unsigned integer임
그런데 ./int -1 AAAA
이런식으로 사이즈에 음수값을 주면 -1은 16진수로 0xffffffff
0xffffffff를 memcpy에서 unsigned int로 받아들이면 굉장히 큰 숫자가 되어 버퍼 오버플로우 발생
맥에서는 테스트 결과 애초에 버퍼 크기를 넘기면 안된다
우분투에선 이렇게 된다
프로세스 구조 중 스택은 위와 같다.
여기서 버퍼 오버플로우가 발생하여 argc의 값까지 망가진 것을 볼 수 있다.
무결성이 깨지는 문제
CWE-195: Signed to Unsigned Conversion Error
unsigned int readdata() { int amount = 0; .... if( result == ERROR ) amount = -1; .... return amount; }
unsigned int로 -1을 리턴하면 엄청 큰 수가 리턴 될 것
int copy_something(char *buf, int len) { char kbuf[800]; if( len > sizeof(kbuf) ) { return -1; } return memcpy(kbuf, buf, len); }
len이 음수이면 if문은 통과하는데, memcpy에서 unsigned int로 컨버전되면서 엄청 큰 수로 바뀜
#include <stdio.h> int main(void) { int i; short s; char c; i = 0xdeadbeef; // 0xdeadbeef s = i; // 0xbeef c = i; // 0xef printf("i = 0x%x, (%d bits)\n", i, sizeof(i) * 8); // 0xdeadbeef (32bits) printf("s = 0x%x, (%d bits)\n", d, sizeof(s) * 8); // 0xffffbeef (16bits) printf("c = 0x%x, (%d bits)\n", c, sizeof(c) * 8); // 0xffffffef (8bits) return 0; }
d는 앞쪽 비트가 1 -> 사이즈 안맞추면 f로 차게 됨
%hx, %hhx를 사용하기
// width.c #include <stdio.h> #include <string.h> #incldue <stdlib.h> int main(int argc, char *argv[]) { unsigned short s; int i; char buf[80]; if( argc < 3 ) return -1; i = atoi(argv[1]); s = i; if( s >= 80 ) { printf("you don't"); return -1; } printf("s=%d\n", s); memcpy(buf, argv[2], i); buf[i] = '\0'; printf("%s\n", buf); return 0; } // ./width1 5 hello // s = 5 // hello // ./width1 80 hello // you don't // ./width1 65536 hello // s = 0 // Segmentation fault (core dumped)
int에 65536 받아놓고 unsigned short로 검사 .. > 너무 커서 버퍼가 넘침
'보안개론' 카테고리의 다른 글
Byte Ordering, Simple Buffer Overflow (0) 2024.06.04 Array Operations (0) 2024.06.04 Use After Free (0) 2024.06.03 bin, chunk, double free (0) 2024.06.03 ptr[-1], ptr[-2]가 가지는 의미 (0) 2024.05.19