현재 네트워크 및 분산 시스템 보안에서 널리 사용되는 50 개 이상이 버퍼 오버플로입니다. 가장 유명한 예는 1988 년 fingerd 취약점을 이용한 웜입니다. 버퍼 오버플로에서 가장 위험한 것은 스택 오버플로입니다. 침입자가 스택 오버플로를 이용할 수 있고 함수가 반환될 때 반환 프로그램의 주소를 변경하여 임의의 주소로 이동할 수 있기 때문입니다. 이로 인해 발생하는 위험 중 하나는 프로그램 충돌로 인해 서비스가 거부되는 것이고, 다른 하나는 셸을 얻는 것과 같은 악성 코드를 점프하고 실행하는 것입니다.
먼저 스택과 관련된 몇 가지 개념을 소개하겠습니다. 동적 메모리에는 스택 (stack), 힙 (heap) 의 두 가지 유형이 있습니다. 스택은 메모리 상단, 메모리 하단, 프로그램 실행 시 스택 아래로, 스택 위로 스택 위로 증가합니다. 일반적으로 로컬 변수, 반환 주소, 함수의 인수는 스택 안에 있습니다.
낮은 주소
로컬 변수
오래된 기본 포인터
반환 주소
함수의 인수 (왼쪽)
함수의 인수 (. 。 。 )
함수의 매개 변수 (오른쪽)
높은 주소
우리는 애플릿 테스트를 쓸 수 있습니다:
#include "string.h"
Void 테스트 (char * a);
Int main(int argc, char* argv[])
{
Char a [] = "헬로";
테스트 (a);
Return 0;;
}
Void 테스트 (char * a)
{
Char* j;;
Char buf [6];
Strcpy (부, a);
Printf ("amp; 메인 = p \ n ",amp;; 메인);
Printf ("amp; Buf=p\n ",amp;; Buf);
Printf ("amp; A=p\n ",amp;; A);
Printf ("amp; 테스트 = p \ n ",amp;; 테스트);
For (j = buf-8; Jlt;; ((char *) amp; A)+8; J++)
Printf ("p: 0xx \ n", j, * (unsigned char *) j);
}
Main 은 문자열 hello 를 정의한 다음 test 함수를 호출합니다. test 함수에는 길이가 6 인 로컬 문자열 변수 buf 가 있습니다. 그런 다음 복사 매개변수 a 를 buf 에 복사합니다. 여기서 a 의 길이가 buf 의 길이보다 작지 않기 때문에 buf 가 넘치지 않습니다.
그런 다음 각 함수, 매개변수, 로컬 변수의 주소, 로컬 문자열 변수 buf 와 매개변수 a 사이의 주소를 표시합니다.
Amp;; 메인 = 0040100a
Amp;; Buf=0012FF14
Amp;; A=0012FF28
Amp;; 테스트 = 00401005
0012ff0c: 0xcc
0012ff0d: 0xcc
0012ff0e: 0xcc
0012ff0f: 0xcc
0012ff10: 0xcc
0012ff11: 0xcc
0012ff12: 0xcc
0012 ff13: 0xcc
0012FF14: 0x68H 여기가 바로 buf 입니다!
0012ff15: 0x65e
0012 ff16: 0x6c l
0012ff17: 0x6c l
0012ff18: 0x6f o
0012ff19: 0x0 \ 0
0012ff1a: 0xcc
0012ff1b: 0xcc
0012FF1C: 0x1c 여기는
입니다0012FF1D: 0xff 두 개
0012FF1E: 0x12 이전
0012FF1F: 0x0 기본 포인터, 그 사람
0012ff20: 0x80
0012 ff21: 0xff
0012 ff22: 0x12
0012f23: 0x0
0012FF24: 0x34 이것이 바로
입니다0012FF25: 0xb8 이 주소를 반환했습니다
0012FF26: 0x40 과 main 의 주소는
입니다0012FF27: 0x0 가까이 오세요! <
/p >
0012FF28: 0x78 이것은
입니다0012FF29: 0xff 매개변수 a, 즉
0012ff2a: 0x12a 문자열의
0012FF2B: 0x0 주소
0012ff2c: 0xe
0012ff2d: 0x0
0012ff2e: 0x0
0012ff2f: 0x0
C 컴파일러는 자체 경계 검사를 하지 않기 때문에 buf 의 내용이 hello 가 아니라 충분히 길면 원래 반환 주소를 덮어쓸 가능성이 높기 때문에 프로그램은 다른 곳으로 점프합니다. 실험을 위해 간단한 함수인 echo 를 정의하여 test 가 반환될 때 echo 로 건너뛰도록 합니다.
Printf ("amp; Echo=p\n ",amp;; 에코); Echo 의 주소가 0x0040100f 라는 것을 이미 알고 있습니다. 위의 예시에서 0012ff14 부터 0012ff27 까지 얼마나 많은 데이터를 덮어야 하는지 알고 있습니다. 한번 세어 보면 알 수 있을까요? 8? 1 대체는 다음과 같습니다.
#include "string.h"
Void 테스트 (char * a);
Voidecho ();
Int main(int argc, char* argv[])
{
차 a [16];
Int i;;
For (I = 0; Ilt;; 16; I++) a =' x'; //반환 주소
를 달성하기 위해 중요하지 않은 부분을 덮습니다A [16] = 0xf; //여기에 반환 주소 다시 쓰기
A [17] = 0x10;
A [18] = 0x40;
A [19] = 0x00; //한편 하이 바이트는 정확히 00 이고 00 은 문자열의 끝입니다
테스트 (a);
Return 0;;
}
Void 테스트 (char * a)
{
Char* j;;
Char buf [6];
Strcpy (부, a); //할당된 버퍼는 5 에 불과하지만 결과는 19 가 넘칩니다!
Printf ("amp; 메인 = p \ n ",amp;; 메인);
Printf ("amp; Buf=p\n ",amp;; Buf);
Printf ("amp; A=p\n ",amp;; A);
Printf ("amp; Echo=p\n ",amp;; 에코);
Printf ("amp; 테스트 = p \ n ",amp;; 테스트);
For (j = buf-8; Jlt;; ((char *) amp; A)+8; J++)
Printf ("p: 0xx \ n", j, * (unsigned char *) j);
}
Void echo()
{
Printf("haha! \ n ");
Printf("haha! \ n ");