오래 못 할 짓 하지 않기
[ 컴퓨터 보안 ] 5. Buffer Overflow 본문
Buffer Overflow
: 문자열에 할당된 메모리 용량보다 더 큰 공간을 할당했을 때
데이터 길이에 대한 불명확한 정의를 악용한 덮어쓰기로 인해 발생
ex) 비유하자면 학회원들한테 학회실 자리 쓰라고 함 ➡️ 인당 자리 1개 쓰리라 생각했는데 한 명이 자리 3,4개 씀
이러다간 교수님 오피스까지 쳐들어가서 앉아 있을 수도 있다.
위 그림으로 보면, 한 문자열에 4칸이 할당되었는데
들어오는 길이에 대한 제약을 두지 않아, 다른 역할을 해야할 E 위치에 까지 문자열이 덮어쓰기 된 상태이다.
정확한 원리로는
Return Address가 들어가야 할 자리에 악의적인 코드가 시작되는 주소를 적어서
프로그램이 그 쪽으로 흘러들어가게 만드는 방법이다.
📌 BOF 3단계
1. Buffer와 Return Address가 들어갈 곳의 거리(Offset)를 구한다.
2. 악의적인 코드가 있는 주소를 찾는다.
1. Offset
2. 악성 코드가 시작되는 메모리 주소를 구한다.
NOP로 채우는 이유는 다른 값들을 넣었다가는
그냥 실행되어버릴 수도 있기 때문에
아무 Instruction이 없는 명령어로 꽉 채워서
Return 주소를 Malicious Code가 있는 메모리 주소로 덮는다.
90 = NOP 명령어임.
NOP로 채워뒀기 때문에 계속 909090...으로 가다가 변하는 부분이 Shell code
BOF 카운터 치는 법
1. Using Safe Function
strncpy() , strncat() 같이 안전한 함수를 사용한다.
➡️ 입력받은 Data의 길이를 먼저 확인하고 들여보낸다. (개발자의 역할)
2. Address Space Layout Randomization ( ASLR )
메모리 공간 배치를 Randomize한다.
➡️ 어느 공간이 Return Address (Stack) 위치인지 알 수 없기 때문에 효과적 ( 예제에선 이거 끄고 함)
( OS 역할 )
이런 식으로 문제 나올 것 같음⬇️
3. Stack- Guard
➡️ 데이터가 stack에 가지 않도록 미리 막아주는 역할
Buffer 에서 Guard에 닿으면 조치를 취한다.
( Compiler 역할 )
📌작동 방식
- [ 스택이 아닌 메모리 ] and [ Buffer 와 Return Address ] 사이에 위치
> Stack에 랜덤으로 변수 하나 만들어 둠. (Flag 역할)
> 복사 함수를 실행한 후, 해당 변수 값이 변경됐다면 (=BOF로 스택에 접근)
BOF로 간주하고 프로그램 종료
4. Non-Executable Stack
➡️ Stack에서는 코드 실행 불가
= 지금껏 Stack에서 우리가 만들어 놓은 Shell code가 돌아가게 했는데
위와 같이 설정을 하면 Stack에 넣은 코드들은 실행이 되지 않는다.
즉, Stack에는 정말 값(데이터)만 넣어둔다.
➡️ 우회법 : Library로 접근한다.(Return-to-Libc)
Return to Libc
위에서 했던 예시들은 우리가 새로운 코드 주소를 넣었다기보단
스택에 Shell 코드를 넣고, 그 주소를 Return Address에 넣어서 공격을 했다.
하지만 이제 스택에 Shell 코드를 넣어도 의미가 없다.
Command로서 의미가 있는 게 아니라 정말 'Data Value'으로만 들어감
그럼 Stack에 있는 코드를 Return Address로 하지 않고
다른 곳에 있는 것들을 모아서 Return Addrss로 뿌리면 되겠다.
= 스택에 코드를 실행하는 것이 아니라
이미 존재하는 라이브러리 함수를 호출 ( 주로 libc = C 표준 라이브러리 )
📌 Return to libc 공격 흐름
시스템 공격의 대부분 목적은 root 쉘 권한을 따는 거다.
우린 System("/bin/sh") 을 실행시키는 게 목표임.
1. Task A : System() 함수의 주소 찾기
목적 ) Return Address = [ System 함수 주소 ] 로 하려고
gdb로 실행시켜서 찾을 수 있다.
2. Task B : "/bin/sh" 의 주소 찾기
목적 ) [ System 함수의 인자 ] 로 사용하려고
So, 해당 문자열의 메모리 주소를 확인해야한다.
3. Task C : System() 함수 인자 구성
목적 ) System() 함수가 "/bin/sh" 인자를 가지고 실행할 수 있도록
따라서, 그 주소들을 가지고 있는 Register들이 필요하다.
✚ Return-Oriented Programming (ROP)
: 기존 코드 조각(Gadget)을 활용하여 원하는 값이나 명령어를 수행하는 방법
이 코드 조각들은 기존 프로그램에 있는 코드나 Lib에서 가져올 수 있다.
- 레지스터에 값을 할당
- 연산
- System call
와 같은 기능들을 담당할 수 있다.
우리가 하려고 하는 건 레지스터에 값을 할당하는 것이다.
간단한 예를 들자면
우리가 System(뭐) 하고싶으면
String a = "exit" ;
System(a) ;
로 할 수 있듯이
그 문자열을 레지스터에 저장해두고
System 호출할 때 그 레지스터를 불러오는 것이다.
- Gadget 명령어 : 다음 Return Address의 값을 rdi 레지스터로 가져온 뒤에 return
pop = 가장 위에 올라온 걸 꺼내서 레지스터에 담는다. 정도로 생각하면 됨
rdi 레지스터에 /bin/sh의 주소가 담긴다.
return 해서 System()으로 간다.
이 때 rdi 상태 = /bin/sh 의 주소
System(rdi의 값);
= System("/bin/sh");
Class Activity⬇️
(출처)
한동대학교 고윤민교수님 - 컴퓨터보안
(참고)
https://olive-su.tistory.com/31
'3학년 2학기 > 컴퓨터 보안(Computer Security)' 카테고리의 다른 글
[ 컴퓨터 보안 ] 7. Cryptography - 치환 / 재배열 / 암.복호화 (0) | 2024.10.15 |
---|---|
[ 컴퓨터 보안 ] 6. Race Condition (0) | 2024.10.06 |
[ 컴퓨터 보안 ] 4. Capability Leaking / Environment Variable (0) | 2024.09.10 |
[ 컴퓨터 보안 ] 3. Authentication (인증) + Set-UID 특수권한 (0) | 2024.09.06 |
[ 컴퓨터 보안 ] 2. Linux 권한 관련 (0) | 2024.09.03 |