- Return address 영역에 공유 라이브러리 함수의 주소로 변경해, 해당 함수를 호출하는 방식이다.
Cdecl(C declaration)
인자 전달 방법 | Stack |
인자 전달 순서 | 오른쪽에서 왼쪽 순서로 스택에 쌓인다 |
함수의 반환 값 | EAX |
Stack 정리 | 호출한 함수가 호출된 함수의 stack 공간을 정리함 |
[EXAMPLE]
- 만약 ret했을 때, main함수로 복귀가 아닌 system("/bin/sh")함수를 호출 하고싶다면 다음과 같이 생각할 수 있다.
> SFP[ebp] + ret("system")[ebp+0x4] + argv1("/bin/sh")[ebp+0x8]
※ 주의 : ret주소에 라이브러리 주소를 넣어서 특정 함수를 call하는 건 일반적인 함수의 Call과는 다른 차이가 있다.
- 일반적인 함수의 Call은 실행후 복귀할 주소를 stack에 PUSH 하고 함수를 호출한다. [SFP + RET + ARGV1...]
> 그리고 함수 실행이 끝나면 에필로그시 stack에 저장된 복귀 주소를 POP RIP하여 RIP로 JMP 하게 된다.
- 하지만 ret주소에 라이브러리 주소를 넣어서 특정 함수를 call 하게 되면 복귀할 주소를 stack에 PUSH하는 과정이 없다. [SFP + ARGV1...]
> 따라서 함수 실행이 끝나면 에필로그시 RET주소가 아닌 ARGV1 값을 POP하게 된다.
즉, ret2libc 기법 사용시 인자 값을 전달하기 위해서는 Return Address의 4byte 뒤에 인자 값을 전달해야 한다.
stack | 평상 시 | ret2libc |
ebp-0x? | BUF | BUF |
ebp | SFP | SFP |
ebp+0x4 | RET | system() 함수 주소 |
ebp+0x8 | Argv1 | system() 함수가 끝내고 실행될 주소 (system()'s ret) |
ebp+0xc | Argv2 | Argv1 |
- 따라서 SFP[ebp] + ret("system")[ebp+0x4] + argv1("/bin/sh")[ebp+0x8] 식은 다음과 같이 바꿔야 한다.
> SFP[ebp] + (system) [ebp+0x4] + dummy [ebp+0x8] + (/bin/sh) [ebp+0xc]
※ 여러개의 함수를 ret을 통해 호출하고 싶을 경우, Gadget을 사용한다. (Gadget : ret로 끝나는 연속된 명령어)
- 우리가 입력한 next return address를 가리키기 위해 사용했던 인자들을 pop하고, esp를 next return address가 있는 스택으로 맞춰 리턴하기 위해 사용한다. (인자의 개수에 따라 pop의 개수를 정한다.)
[EXAMPLE] ppr Gadget
buf | sfp | setreuid | ppr | 3092(esp) | 3092 | system() | dummy | /bin/sh |
1. setreuid 함수 호출 후 다음 실행할 명령어는 pop pop ret이므로 esp가 가르키고 있는 있는 첫 번째 인자를 pop 한다.
2. 그 다음 인자를 pop 한다.
3. 두번의 pop 완료 후 esp는 system()함수의 주소를 가리키고 있을 것이다. 여기서 ret으로 system 함수를 호출한다.
- 0x56555659 : vuln 함수가 끝나고 복귀할 주소
- 0xffffd50c : vuln 함수의 ret 주소
- 0xffffd4ba : buf의 시작 주소
- 0xf7e292d0 : printf 주소
- printf offset = (printf addr - libc_start_addr) = 0x512d0이다.
- system함수와 /bin/sh의 offset을 구했다.
REF: https://www.lazenca.net/display/TEC/01.RTL%28Return+to+Libc%29+-+x86
02.RTL(Return to Libc) - x64 (0) | 2020.05.05 |
---|
댓글 영역