ROP - Return Oriented Programing
Last updated
Last updated
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
**Return-Oriented Programming (ROP)**는 No-Execute (NX) 또는 **Data Execution Prevention (DEP)**와 같은 보안 조치를 우회하기 위해 사용되는 고급 익스플로잇 기법입니다. 공격자는 쉘코드를 주입하고 실행하는 대신, 바이너리 또는 로드된 라이브러리에 이미 존재하는 코드 조각을 활용합니다. 이를 **"가젯"**이라고 합니다. 각 가젯은 일반적으로 ret
명령어로 끝나며, 레지스터 간 데이터 이동이나 산술 연산과 같은 작은 작업을 수행합니다. 이러한 가젯을 연결하여 공격자는 임의의 작업을 수행하는 페이로드를 구성할 수 있으며, 효과적으로 NX/DEP 보호를 우회할 수 있습니다.
제어 흐름 탈취: 먼저, 공격자는 프로그램의 제어 흐름을 탈취해야 하며, 일반적으로 버퍼 오버플로우를 이용해 스택의 저장된 반환 주소를 덮어씁니다.
가젯 체이닝: 공격자는 원하는 작업을 수행하기 위해 가젯을 신중하게 선택하고 연결합니다. 여기에는 함수 호출을 위한 인수 설정, 함수 호출(예: system("/bin/sh")
), 필요한 정리 또는 추가 작업 처리 등이 포함될 수 있습니다.
페이로드 실행: 취약한 함수가 반환될 때, 합법적인 위치로 반환하는 대신 가젯 체인을 실행하기 시작합니다.
일반적으로 가젯은 ROPgadget, ropper 또는 pwntools(ROP)를 사용하여 찾을 수 있습니다.
cdecl: 호출자가 스택을 정리합니다. 함수 인수는 역순(오른쪽에서 왼쪽으로)으로 스택에 푸시됩니다. 인수는 오른쪽에서 왼쪽으로 스택에 푸시됩니다.
stdcall: cdecl과 유사하지만, 피호출자가 스택을 정리할 책임이 있습니다.
먼저, 바이너리 또는 로드된 라이브러리 내에서 필요한 가젯을 식별했다고 가정해 보겠습니다. 우리가 관심 있는 가젯은 다음과 같습니다:
pop eax; ret
: 이 가젯은 스택의 최상위 값을 EAX
레지스터로 팝하고 반환하여 EAX
를 제어할 수 있게 합니다.
pop ebx; ret
: 위와 유사하지만 EBX
레지스터에 대한 것으로, EBX
를 제어할 수 있게 합니다.
mov [ebx], eax; ret
: EAX
의 값을 EBX
가 가리키는 메모리 위치로 이동하고 반환합니다. 이는 종종 write-what-where gadget이라고 불립니다.
추가로, system()
함수의 주소를 사용할 수 있습니다.
pwntools를 사용하여 system('/bin/sh')
를 실행하기 위해 ROP 체인 실행을 위한 스택을 다음과 같이 준비합니다. 체인은 다음과 같이 시작됩니다:
정렬을 위한 ret
명령어 (선택 사항)
system
함수의 주소 (ASLR 비활성화 및 libc가 알려진 경우 가정, 더 많은 정보는 Ret2lib에서 확인)
system()
에서 반환 주소를 위한 자리 표시자
"/bin/sh"
문자열 주소 (system 함수의 매개변수)
Unix 유사 시스템에서는 System V AMD64 ABI 호출 규약을 사용하며, **첫 여섯 개의 정수 또는 포인터 인자는 레지스터 RDI
, RSI
, RDX
, RCX
, R8
, 및 R9
**에 전달됩니다. 추가 인자는 스택에 전달됩니다. 반환 값은 RAX
에 저장됩니다.
Windows x64 호출 규약은 첫 네 개의 정수 또는 포인터 인자에 대해 RCX
, RDX
, R8
, 및 R9
를 사용하며, 추가 인자는 스택에 전달됩니다. 반환 값은 RAX
에 저장됩니다.
레지스터: 64비트 레지스터에는 RAX
, RBX
, RCX
, RDX
, RSI
, RDI
, RBP
, RSP
, 및 R8
에서 R15
까지 포함됩니다.
우리의 목적을 위해, RDI 레지스터를 설정할 수 있는 가젯에 집중하겠습니다 ( "/bin/sh" 문자열을 **system()**의 인자로 전달하기 위해) 그리고 system() 함수를 호출합니다. 다음 가젯을 식별했다고 가정하겠습니다:
pop rdi; ret: 스택의 최상위 값을 RDI에 팝하고 반환합니다. **system()**의 인자를 설정하는 데 필수적입니다.
ret: 간단한 반환으로, 일부 시나리오에서 스택 정렬에 유용합니다.
그리고 우리는 system() 함수의 주소를 알고 있습니다.
아래는 pwntools를 사용하여 x64에서 **system('/bin/sh')**를 실행하기 위한 ROP 체인을 설정하고 실행하는 예입니다:
In this example:
우리는 pop rdi; ret
가젯을 사용하여 **RDI
**를 **"/bin/sh"
**의 주소로 설정합니다.
**RDI
**를 설정한 후, 체인에 **system()**의 주소가 있는 **system()
**으로 직접 점프합니다.
**ret_gadget
**은 대상 환경이 필요로 할 경우 정렬을 위해 사용되며, 이는 x64에서 함수 호출 전에 적절한 스택 정렬을 보장하기 위해 더 일반적입니다.
x86-64 ABI는 call instruction이 실행될 때 스택이 16바이트 정렬되도록 보장합니다. LIBC는 성능 최적화를 위해 SSE instructions(예: movaps)를 사용하며, 이 정렬이 필요합니다. 스택이 제대로 정렬되지 않으면(RSP가 16의 배수가 아닐 경우) system과 같은 함수 호출이 ROP chain에서 실패합니다. 이를 해결하려면 ROP 체인에서 system을 호출하기 전에 ret gadget을 추가하면 됩니다.
x64는 처음 몇 개의 인수에 레지스터를 사용하므로, 간단한 함수 호출을 위해 x86보다 더 적은 가젯을 필요로 하는 경우가 많지만, 레지스터 수가 증가하고 주소 공간이 커짐에 따라 올바른 가젯을 찾고 연결하는 것이 더 복잡할 수 있습니다. x64 아키텍처의 증가된 레지스터 수와 더 큰 주소 공간은 특히 Return-Oriented Programming (ROP) 맥락에서 익스플로잇 개발에 기회와 도전을 제공합니다.
이 정보는 다음 페이지를 확인하세요:
스택 카나리: BOF의 경우, ROP 체인을 남용하기 위해 반환 포인터를 덮어쓰려면 스택 카나리를 우회해야 합니다.
가젯 부족: 가젯이 충분하지 않으면 ROP 체인을 생성할 수 없습니다.
ROP는 임의의 코드를 실행하기 위한 기술일 뿐임을 유의하세요. ROP를 기반으로 많은 Ret2XXX 기술이 개발되었습니다:
Ret2lib: ROP를 사용하여 임의의 매개변수로 로드된 라이브러리에서 임의의 함수를 호출합니다(보통 system('/bin/sh')
와 같은 형태).
Ret2Syscall: ROP를 사용하여 시스템 호출을 준비하고, 예를 들어 execve
를 호출하여 임의의 명령을 실행합니다.
EBP2Ret & EBP 체이닝: 첫 번째는 흐름을 제어하기 위해 EIP 대신 EBP를 남용하고, 두 번째는 Ret2lib와 유사하지만 이 경우 흐름은 주로 EBP 주소로 제어됩니다(물론 EIP도 제어해야 합니다).
64비트, Pie 및 nx 활성화, 카나리 없음, vsyscall
주소로 RIP를 덮어쓰고 스택의 다음 주소로 돌아가며 플래그를 누출하는 함수의 일부를 얻기 위한 부분 덮어쓰기
arm64, ASLR 없음, 스택을 실행 가능하게 만들고 스택의 셸코드로 점프하기 위한 ROP 가젯
Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE) Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)