🔬 CPU 제어권 탈취의 3단계 원리
버퍼 오버플로우 공격은 CPU 레지스터와 스택 메모리를 이용해 프로그램의 실행 순서(Execution Flow)를 통제하는 과정입니다.
1. 프로그램 실행의 기본 원리
모든 프로그램은 EIP 레지스터가 가리키는 주소의 기계어 명령을 순서대로 실행합니다.
EIP→명령어 실행→EIP 주소 증가→다음 명령어 실행
2. 함수 호출과 스택 (Call and Return)
함수(Function)가 호출되면, CPU는 현재 위치로 다시 돌아오기 위해 복귀 주소(Return Address)를 스택에 저장합니다. 이 복귀 주소가 바로 EIP가 다음에 실행해야 할 주소입니다.
- 호출 (CALL): CPU는 현재 EIP 값을 스택에 PUSH하여 저장합니다. (이것이 반환 주소가 됩니다.)
- 실행 (Function Body): 함수 내부의 명령들을 실행합니다. 이 과정에서 버퍼와 EBP 등이 스택에 차례로 쌓입니다.
- 복귀 (RET): 함수 실행이 끝나면 CPU는 스택에서 저장했던 값을 POP하여 다시 EIP에 로드하고, 그 주소로 점프하여 원래 실행 위치로 돌아갑니다.
3. 버퍼 오버플로우의 원리적 개입
우리의 공격은 이 복귀 (RET) 단계를 가로챕니다.
| 단계 | 작동 원리 | 레지스터 변화 |
|---|
| Step 1: 데이터 주입 | 취약한 함수에 버퍼 크기를 초과하는 데이터(A, 패턴, JMP ESP 주소 등)를 주입합니다. | EIP가 스택의 높은 주소에 저장된 상태에서 데이터는 낮은 주소에서 높은 주소로 흘러 EIP가 저장된 메모리 공간을 덮어씁니다. |
| Step 2: EIP 조작 | EIP가 저장된 정확한 4바이트 위치에 우리가 원하는 JMP ESP 주소(0x9F105062 → \x9F\x10\x50\x62 리틀 엔디안)를 덮어씁니다. | EIP←JMP ESP 주소 |
| Step 3: 실행 흐름 전환 | 함수가 끝나고 RET 명령을 실행하면, CPU는 조작된 EIP 값을 읽고 그 주소로 점프합니다. | PC←조작된 EIP 주소 |
| Step 4: Shellcode 실행 | CPU는 JMP ESP 명령이 있는 주소에 도착하여 이 명령을 실행합니다. ESP가 가리키는 곳(NOP Sled 시작점)으로 점프하여 최종적으로 우리의 Shellcode가 실행됩니다. | PC←ESP (Shellcode 위치) |
💻 공격 성공의 기술적 순서 파헤치기
이 원리를 실현하기 위한 구체적인 기술적 순서는 다음과 같습니다.
1. 오프셋 찾기: EIP의 위치 특정 (Phase: Control)
- 목표: 스택 프레임 내에서 반환 주소(EIP)가 저장된 메모리 주소를 정확히 찾아내는 것입니다.
- 원리: 고유 패턴을 사용하여 크래시를 유발한 후, EIP 레지스터에 기록된 4바이트 값을 확인합니다. 이 값은 패턴 내에서 EIP의 시작 위치를 알려주는 GPS 좌표 역할을 합니다.
- EIP 오프셋까지는 패딩(A 문자)으로 채워 넣고, 그 다음 4바이트에 JMP ESP 주소를 삽입하여 EIP를 덮어쓰게 됩니다.
2. Bad Char 필터링: Shellcode 무결성 확보 (Phase: Prepare)
- 목표: 우리의 Shellcode가 중간에 잘리지 않고 온전하게 메모리에 복사되도록 방해 요소를 제거합니다.
- 원리: 프로그램이나 운영체제가 특정 바이트 값(예: \x00, \x0A)을 데이터의 끝이나 제어 문자로 인식하여 Shellcode 복사를 중단시키는 것을 막기 위해, 이들을 Shellcode 생성 시 제외하도록 설정합니다.
3. JMP ESP 찾기: 안전한 게이트웨이 확보 (Phase: Redirect)
- 목표: EIP에 덮어쓸, 실행이 절대 실패하지 않을 안전한 주소를 확보하는 것입니다.
- 원리: 우리가 보낸 Shellcode는 ESP 근처에 있습니다. JMP ESP 명령이 있는 주소는 프로그램 DLL 내부에서 찾습니다. 이 주소는 프로그램 실행 시 변하지 않으므로, 이 주소를 EIP에 넣으면 항상 ESP로 점프하여 Shellcode로 가는 길을 보장합니다.
4. 최종 페이로드: 공격 실행 (Phase: Execute)
모든 조각이 모여 다음과 같은 최종 페이로드를 완성하고 전송합니다.
Payload=[A∗Offset]+[JMP ESP Address (Little Endian)]+[NOP Sled]+[Shellcode]
NOP Sled는 JMP ESP 이후 Shellcode로 미끄러져 들어갈 안전 공간을 제공하여, 사소한 메모리 위치 오차에도 공격이 성공하도록 안정성을 극대화합니다.
네, 맞습니다! 버퍼 오버플로우 공격의 핵심 원리는 바로 원래의 복귀 주소(Return Address)가 저장된 메모리 공간을 찾아내서, 그 주소를 공격자가 원하는 주소로 완전히 덮어쓰거나 교체하는 것입니다.
이 과정을 기술적인 용어와 함께 다시 한번 명확하게 정리해 드릴게요.
🎯 EIP 덮어쓰기: 실행 흐름 탈취의 핵심
1. 원래 복귀 주소의 저장 (The Original Return Address)
함수가 호출될 때 (CALL 명령), 현재 EIP 레지스터에 저장된 다음 명령어의 주소는 스택 메모리에 "반환 주소(Return Address)"라는 이름으로 PUSH되어 저장됩니다.
- 이 반환 주소는 함수가 끝났을 때 (RET 명령), 프로그램이 실행을 원래의 정상적인 위치로 되돌리기 위해 사용하는 핵심 값입니다.
2. 덮어쓰기 (The Overwrite)
우리가 취약한 버퍼에 버퍼 크기를 초과하는 데이터를 낮은 주소에서 높은 주소 방향으로 밀어 넣으면, 이 데이터는 메모리상에서 버퍼 다음에 위치하는 반환 주소(EIP)의 저장 공간을 침범합니다.
- 공격자는 오프셋(Offset) 계산을 통해 이 반환 주소가 시작되는 정확한 4바이트 위치를 찾아냅니다.
- 이 4바이트 공간에, 원래의 정상적인 복귀 주소 대신 공격자가 지정한 새로운 주소를 삽입합니다.
교체되는 값: 우리가 삽입하는 새로운 주소는 JMP ESP 명령어가 들어있는 메모리 주소입니다.
3. 통제권 탈취 (Control Hijack)
함수가 종료될 때 (RET 명령), CPU는 스택에서 덮어씌워진 새로운 4바이트 값을 EIP 레지스터로 POP하여 로드합니다.
- 원래: EIP ← 정상적인 다음 명령어 주소
- 공격 후: EIP ← JMP ESP 주소 (공격자가 지정한 주소)
EIP가 공격자가 지정한 주소를 읽는 순간, 프로그램은 정상적인 실행 경로를 포기하고 JMP ESP 명령을 실행하는 공격자의 경로로 넘어가게 됩니다. 이것이 버퍼 오버플로우를 통한 실행 흐름 탈취의 원리입니다.