ROP - Return Oriented Programing
Last updated
Last updated
AWS 해킹 학습 및 실습:HackTricks Training AWS Red Team Expert (ARTE) GCP 해킹 학습 및 실습: 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 가젯이라고 합니다.
추가로 system()
함수의 주소를 사용할 수 있습니다.
pwntools를 사용하여 ROP 체인 실행을 위해 스택을 준비하는 방법은 다음과 같습니다. system('/bin/sh')
을 실행하도록 목표로 하는데, 체인이 다음과 같이 시작하는 것에 주목하세요:
정렬 목적으로 ret
명령 (선택 사항)
system
함수의 주소 (ASLR 비활성화 및 알려진 libc를 가정, 자세한 정보는 Ret2lib 참조)
system()
에서의 반환 주소를 위한 자리 표시자
"/bin/sh"
문자열 주소 (system 함수의 매개변수)
유닉스류 시스템에서는 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 레지스터를 설정하고(system() 함수에 "/bin/sh" 문자열을 전달하기 위함) system() 함수를 호출할 수 있는 가젯에 집중해봅시다. 다음과 같은 가젯들을 식별했다고 가정해봅시다:
pop rdi; ret: 스택의 최상위 값을 RDI로 팝하고 반환합니다. **system()**에 대한 인자를 설정하는 데 필수적입니다.
ret: 간단한 반환으로, 특정 시나리오에서 스택 정렬에 유용합니다.
그리고 system() 함수의 주소를 알고 있다고 가정합니다.
아래는 pwntools를 사용하여 x64에서 **system('/bin/sh')**를 실행하기 위한 ROP 체인을 설정하고 실행하는 예시입니다:
x86-64 ABI는 call 명령어가 실행될 때 스택이 16바이트 정렬되도록 보장합니다. LIBC는 성능을 최적화하기 위해 movaps와 같은 SSE 명령어를 사용하는데, 이는 정렬이 필요합니다. 스택이 제대로 정렬되지 않으면 (RSP가 16의 배수가 아닌 경우), system과 같은 함수 호출이 ROP chain에서 실패할 수 있습니다. 이를 해결하기 위해 ROP chain에서 system을 호출하기 전에 ret gadget을 추가하면 됩니다.
x64는 처음 몇 개의 인자에 대해 레지스터를 사용하기 때문에 간단한 함수 호출에 대해 x86보다 적은 수의 가젯이 필요하지만, 올바른 가젯을 찾고 연결하는 것은 레지스터 수의 증가와 더 큰 주소 공간으로 인해 더 복잡할 수 있습니다. x64 아키텍처의 증가한 레지스터 수와 더 큰 주소 공간은 주로 Return-Oriented Programming (ROP)의 맥락에서 exploit 개발에 대한 기회와 도전을 제공합니다.
이 정보를 확인하려면 다음 페이지를 확인하십시오:
Introduction to ARM64v8스택 카나리: BOF에서는 ROP chain을 악용하기 위해 저장된 스택 카나리를 우회하여 반환 포인터를 덮어쓰는 것이 필요합니다.
가젯 부족: 충분한 가젯이 없으면 ROP chain을 생성할 수 없습니다.
ROP는 임의의 코드를 실행하기 위한 기술일 뿐임을 알아두세요. ROP를 기반으로 한 Ret2XXX 기술이 개발되었습니다:
Ret2lib: 로드된 라이브러리에서 임의의 매개변수로 임의의 함수를 호출하기 위해 ROP를 사용합니다 (일반적으로 system('/bin/sh')
와 같은 것).
Ret2Syscall: 시스템 호출 (예: execve
)을 준비하고 임의의 명령을 실행하기 위해 ROP를 사용합니다.
EBP2Ret 및 EBP Chaining: 첫 번째는 EIP 대신 EBP를 악용하여 흐름을 제어하고, 두 번째는 Ret2lib과 유사하지만 이 경우 흐름은 주로 EBP 주소로 제어됩니다 (물론 EIP를 제어해야 합니다).
64비트, Pie 및 nx 활성화, 카나리 없음, vsyscall
주소로 RIP를 덮어쓰고 스택에서 함수의 일부를 노출하는 주소의 일부를 얻기 위해 스택의 다음 주소로 반환하는 것이 목적
arm64, ASLR 없음, ROP 가젯을 사용하여 스택을 실행 가능하게 만들고 스택에서 셸코드로 이동