ROP(Return Oriented Programming)

‍김상빈·2023년 10월 7일

learning_pwnable

목록 보기
3/6

NX는 Non eXecutable로 불필요한 실행권한은 없애는 기법이다. 그래서 NX를 우회해야 개발자의 의도와 반하는 실행을 시킬수가 있다. 우회하는 방법 중에서 유명한 기법이 ROP이다. ROP 중에서 우선 RTL(Return To Libary) 기법을 우선 이야기해보려고 한다.

RTL(Return To Libc)

프로세스에서 실행 권한이 있는 영역은 일반적으로 바이너리 코드 영역과 libary 영역 이렇게 2개로 볼 수가 있다. 그 중 libc에는 system 함수와 execve 함수가 있는데 실행과 관련한 위험한 함수들이 존재한다. 이런 함수들을 이용해서 NX를 우회하는 것이 RTL이다.

자세히 이야기 전 PLT와 GOT에 대해서 이야기하고 넘어 가려고 한다.

PLT (Procedure Linkage Table)

우선 procedure이 무엇인지 알고 PLT에 대해서 설명하겠다.
procedure은 특정 task를 수행하기 위한 명령어 또는 코드블럭을 의미한다.
그렇다면 단어 뜻만 해석한다면 PLT는 특정 task들을 수행하기 위한 명령어들을 이용해서 link하는 명령어들의 table이라고 이해할 수가 있다.
그럼 실제로는 어떤 의미를 가지고 있는지 알아보겠다
우선 동적 link이기 때문에 바이너리를 실행시키면 동적 라이브러리를 메모리에 맵핑을 한다. 하지만 ASLR 때문에 무작위 메모리에 맵핑이 될 것이다. 그 후 함수를 호출할 때 PLT는 GOT에게 그 함수 주소를 물어서 알아본다. 하지만 처음 함수를 호출할 때는 GOT는 모른다. 그럼 PLT는 아까 뜻 풀이할 때 이야기 했다시피 특정 라이브러리를 호출하는 코드들을 모아놓았다. 이런 코드(동적 링커)들을 이용하여 해당 라이브러리의 함수 주소를 가져와서 GOT를 업데이트 한다. 그 다음 GOT를 통해 주소를 알아내서 함수를 실행시킨다.

그럼 GOT가 무엇인지 궁금한게 당연하다

GOT (Global Offset Table)

함수의 주소를 table에 저장해 놓은 것을 말한다. 하지만 런타임될 때 초기화 되며 업데이트되는 방식으로 table이 채워진다.

PLT를 통해 GOT의 table을 채우는 것을 runtime resolve라고 한다.

드림핵에서는 이렇게 설명했다.

바이너리가 실행되면 ASLR에 의해 라이브러리가 임의의 주소에 매핑됩니다. 이 상태에서 라이브러리 함수를 호출하면, 함수의 이름을 바탕으로 라이브러리에서 심볼들을 탐색하고, 해당 함수의 정의를 발견하면 그 주소로 실행 흐름을 옮기게 됩니다

바이너리에서는 GOT에 저장되어 있는 offset을 check하지 않기 때문에 이 offset을 변조 시킬 수 있으면 RTL이 된다.

쉽게 이야기하면 plt는 호출을 담당을 하는 table이고 got는 실제 주소를 담당하는 table로 이해하면 편하다.

이 내용들을 제대로 들었다면 한가지 궁금증이 또 생기는 것이 당연하다. offset이 뭘까라는 궁금증은 당연하다.

OFFSET

컴퓨터 과학에서의 offset은 기준점 또는 시작지점에서 얼마나 멀리 있는지 나타내는 거리로 설명을 한다. 즉 쉽게 이야기하면 놀이공원을 라이브러리라고 가정하면, 놀이공원 입구에서 회전목마까지의 거리를 10이라고 하면 이 10이 놀이공원 입구에서 회전목마의 offset은 10인 것이다. 그리고 자이로드롭까지의 거리가 15라고 가정한다. 그리고 내가 회전목마라는 함수를 실행하려고 놀이공원 입구 주소 + 10을 했다. 그럼 회전목마라는 함수가 실행이 되는 것이 정상이다. 하지만 바이너리에서는 이 offset을 확인하지 않기 때문에 놀이공원 주소 + 10인 것을 내가 임의로 15로 수정하여 자이로드롭으로 바꿀수가 있게 되는 것이다.

좀 전에 PLT GOT를 설명을 했다시피 바이너리에서 system 함수를 사용한 적이 없으면 got에 업데이트가 되지 않아 실제 주소가 없을 것이다. 하지만 libc에 system 함수가 있다면 plt에는 system 함수를 콜할 수 있는 명령어가 있을 것이다. 이를 활용하는 것이다. 예를 들어 read라는 함수를 바이너리에서 실행했다면 got에 실제 주소가 저장이 되어 있을것이다. 그리고 plt에는 그 명령어를 실행 시킬 수 있는 주소가 적혀있을 것이고 이 got.read와 libc.symbol['read']를 빼는데 symbol은 그 라이브러리에서 그 함수가 있는 offset을 말하는 것이다. 쉽게 설명해서 symbol은 책이 알렬로 쭉 있는데 시작점에서 원하는 책이 몇번 째에 있는지 그 거리를 알려주는 걸 말한다. 그러니 got로 그 함수의 실제 주소와 라이브러리에서 몇번째에 그 함수가 있는지를 빼면 라이브러리의 실제 주소를 알 수 있게 되는 것이다. 이제 이 주소를 알게 되었으면 그 라이브러리에 있는 함수의 symbol만 알고 있게 된다면 실제 그 함수의 주소를 쉽게 알아낼 수 있게 되는 것이다.

그래서 plt.read를 하면 got에 가서 got.read를 찾지만 got.read를 system 주소로 덮어버리면 system이 실행하게 되는 것이다.

여기서 한가지 주의할 점은, system 함수로 rip가 이동할 때, 스택은 반드시 0x10단위로 정렬되어 있어야 한다는 것입니다. 이는 system 함수 내부에 있는 movaps 명령어 때문인데, 이 명령어는 스택이 0x10단위로 정렬되어 있지 않으면 Segmentation Fault를 발생시킵니다.

위 내용을 이용해서 RTL을 하면 된다.

ROP(Return Oriented Programming)

우선 ROP를 이해하기 전에 각 x64 x86에 맞는 calling convention에 대해서 알아야 할 필요가 있다. 그 이유는 함수를 call해서 원하는 대로 바이너리 흐름을 장악하는 것이 핵심이니 그에 대한 규약을 이해해야 하는건 당연하다고 볼 수 있다.

x86 Calling Convention (cdecl)

인수 전달 방식: 스택을 사용하여 전달
호출자/피호출자: 호출한 함수(caller)가 스택을 정리
주요 특징: 함수의 인수는 오른쪽에서 왼쪽으로 스택에 푸시한다. 반환 값은 EAX 레지스터에 저장된다.

x64 Calling Convention (System V AMD64 ABI)

인수 전달 방식: 레지스터와 스택을 사용
호출자/피호출자: 호출된 함수(callee)가 스택을 정리
주요 특징: 처음 6개의 정수 인수는 RDI, RSI, RDX, RCX, R8, R9 레지스터에 전달된다. 처음 8개의 부동 소수점 인수는 XMM0~XMM7 레지스터에 전달된다. 추가 인수는 스택에 전달된다. 반환 값은 RAX 레지스터에 저장된다.

이제는 ROP에 대해서 이야기 해보려고 한다. 이미 RTL을 통해서 어떤 느낌인지는 이해 했을것으로 판단된다. ROP는 쉽게 말해서 gadget chain으로 볼수가 있다. 여기서 말하는 gadget은 작은 코드 조각 즉 조그만한 명령어들의 집합으로 보면 된다. 이런 gadget은 바이너리 내에 있는 코드 조각이여야하며 ret으로 코드가 끝난다.

참고
Gadget은 일반적으로 RET나 POP/RET 조합으로 끝나는 명령어 시퀀스를 말한다.
예시로는 아래와 같이 존재한다.x86에서는 인자들을 push하고 이 인자들을 이용해서 함수의 task를 수행하고 그 값을 eax에 넣다.

0x080484a3 : pop eax ; ret
0x080484a6 : pop ebx ; ret
0x080484d2 : add eax, ebx ; ret

이런 ropgadget chain을 이용하여 바이너리의 흐름을 원하는대로 조종하는 것을 ROP라고 한다.

x64로 에를 들면 pop rdi ; ret 가젯을 이용하여 rdi에 값을 넣고 어떤 함수를 plt를 이용하여 실행켜서 원하는 인자를 이미 변조한 rdi를 통해서 넣으면 원하는 방향으로 바이너리 흐름을 바꿀수가 있는 것이다.

x86로 예를 들면 인자들을 스텍에 넣어서 처리하다보니 그 인자를 EAX에 넣고 그 후 함수에 맞게 실행을 한 후 그 값을 리턴한다. 그러니 인자에 들어가고 싶은 값을 먼저 스택에 넣은 후 plt로 함수를 실행시키면 된다

이렇게 오늘은 ROP에 대해서 알아보았다.

profile
nickname: pwn_newbe

0개의 댓글