상세 컨텐츠

본문 제목

1. RTL (Return into Library) -x84

SYSTEM HACKING/Exploit Tech

by koharin 2019. 7. 25. 23:56

본문

728x90
반응형

Return into Libc(RTL)

 

Return address를 공유 라이브러리 함수의 주소로 바꿔 해당 함수를 호출하는 방법

-> RTL 공격 이용해서 NX bit(DEP) 우회 가능 (NX bit(DEP) : 메모리 상에 저장만할 수 있고 실행할 수 없게 하는 보호기법)

# calling convention (호출 규약)

인텔 x86 기반 시스템 -> C/C++ 에서 사용

Linux kernel : Cdecl ( C declaration) 호출 규약 사용

특징 :

-인자 전달 방법 : stack 이용

-인자 전달 순서 : 오른쪽에서 왼쪽 순서로 stack 에 저장

-함수 return /반환값 : EAX 레지스터에 저장

-사용된 stack 정리 : 해당 함수를 호출한 함수가

 

Ex. 함수 호출 규약 예

-----------------------

int a, b, c, d;

int ret;

 

ret = function(a, b, c, d);

------------------------

어셈블리 코드 간단히 나타내면,

------------------------

push d

push c

push b

push a

call function

mov ret, eax

----------------------

4개의 인자값을 stack에 오른쪽에서 왼쪽 순서로(d->a) stack에 저장

함수 호출 후 반환값은 EAX 레지스터에 저장되고 해당 값을 ret 변수에 저장

 

Cdelc 함수 호출 규약 예

 

 

 

vuln 함수 호출 부분에서 bp를 잡고 실행시켜 esp를 확인해보면, stack에 저장된 vuln 함수의 인자값 확인 가능

vuln 함수 gdb 분석

 

 

main 함수에서 전달된 인자값 사용

push ebp : main 함수에서 사용한 호출 프레임을 stack에 저장

(이전 함수에서 사용한 호출 프레임은 ebp 레지스터에 저장되어 있음)

mov esp, ebp : vuln 함수에서 사용할 새 호출 프레임이 ebp 레지스터에 초기화

                   ebp 레지스터 사용해 main 함수로부터 전달된 인자값 사용

“”DWORD PTR[ebp+*]”” 영역에서 4번째 인자값 확인 가능

 

 

 

“”DWORD PTR[ebp+8]”” 부터 “”DWORD PTR[ebp+16]”” 까지 vuln 함수에 전달된 인자값

 

“”DWORD PTR[ebp+4]”” 영역에 해당 함수 호출 후 돌아갈 return address가 저장되어 있음

“”DWORD PTR[ebp]”” 영역에는 이전 호출 프레임이 저장되어 있음

 

 

ebp+4의 값인 0x804844e vuln 함수를 호출한 main 함수에서 돌아갈 주소임을 알 수 있음

 

함수 실행 후 esp 레지스터를 확인해보면 인자값 4개가 들어가있고, esp-4(esp보다 4 byte 높은주소에)에는 return address가 들어있을 것이다.

따라서 다시 말하면, return address4 바이트 뒤에는 인자 값이 들어간다.

ret2libc 기법을 사용 시 인자값을 전달하려면 return address4 바이트 뒤에 인자 값을 전달해야 한다.

 

#Return to shellcode 확인

$ vi ret2libc.c

 

 

<dlfcn.h> 헤더파일 : https://okky.kr/article/405775 참고

Go 언어를 호출하는데, C 코드는 동적 링크 로더 라이브러리 (libdl.so)를 사용하여 내보낸 심볼을 동적으로 로드하고 바인딩한다.

이 헤더파일에 정의된 함수를 이용해 라이브러리 파일을 열고 dlsym을 사용해서 심볼을 찾는다. dlsym은 함수 심볼을 로드하고 함수 포인터에 할당한다.

여기선 “”printf”” 심볼을 찾아서 주소를 구하기 위해 사용했다. (go 언어라고 한다.)

컴파일 시, dl 라이브러리와 링크해 컴파일할 수 있는데, -ldl 인자를 사용한다.

main 함수 : vuln 함수 호출

vuln 함수 :

read 함수 이용해 사용자로부터 100개 문자 입력받음

-> *취약점 발생 : buf 변수의 크기는 50 바이트인데 100 바이트를 입력받아 stack overflow 발생

Libcbase address를 얻기 위해 libc 영역에서 printf 함수 주소 출력

 

$ gcc -o ret2libc re2libc.c -ldl

 

(원래는 -fno-stack-protector 인자도 있는데 오류 생겨서 뺐다.)

 

#overwriting the return address

-break point 설정

vuln 함수의 첫번째 명령어 0x80483fc

read 함수 호출 0x804844b

vuln 함수의 RET 명령어 0x8048457

 

 

#Return address 확인 가능

- esp 레지스터가 가리키고 있는 최상위 stack 주소0xfef017ac

- 0xfef017ac 영역에 return address(0x08048479)가 저장되어 있다.

 

 

확인해보면 main 함수에서 vuln 함수 호출 다음 주소임을 알 수 있다.

, vuln 함수 끝난 후 다시 vuln 함수를 호출한 곳으로 리턴할 때 갈 주소, return address임을 알 수 있다.

 

# buf 변수 주소 확인

 

Buf 변수의 위치0xfef01760 이며, return address 위치와 76 바이트 떨어져 있다.

사용자가 입력 값으로 문자를 76개 이상 입력하면, return address를 덮을 수 있다.

 

 

지금 안 되는데, 만약 됐으면

(gdb) x/2x 0xfef017ac

0xfef017ac: 0x42424242

이렇게 출력되고,

(gdb) x/s 0xfef017ac

0xfef017ac: “”BBBB\n\377””

(정확하진 않지만) 이렇게 출력될 것이다.

 

 

따라서 이렇게 해서 return address를 원하는 주소로 덮을 수 있다.

 

# find the Libc address of the system() function and “”/bin/sh””

 

System 함수 :

int system(const char *command)

인자값을 실행할 command의 경로를 문자열로 전달받음

-> RTL 공격 시 shell 실행 위해 “”/bin/sh”” 문자열을 system 함수의 인자로 전달해야 한다.

 

-Libc 영역에서 system 함수 주소 찾기

 

 

0xf7e49020(printf function address in libc) - 0xf7e00000(libc Start Address) = 0x49020(libc Base Address offset)

0xf7e3a940(system function address in libc) - 0xf7e00000(libc Start Address) = 0x3a940(system function Address offset)

 

-“”/bin/sh”” 문자열 찾기

(gdb) find “”/bin/sh””

 

 

 

 

0x75e660(printf function address in libc) - 0x71c000(libc start address)

= 0x42660 (libc base address offset)

0x7507c0(system function address in libc) - 0x71c000(libc start address)

= 0x347c0 (system function address offset)

 

“”/bin/sh”” 주소는 system 함수 내에서 찾아보자.

 

 

0x833604(“”/bin/sh”” string address in libc) - 0x71c000(libc start address)

= 0x117603 (“”/bin/sh”” string address offset)

 

# exploit 코드

 

 

Pwn 라이브러리를 import해서 exploit 하는데 사용한다.

Buf 변수 위치가 return address로부터 76 바이트 떨어진 것으로 76 바이트만큼은 dummy로 채우고, ret에는 system addr, 그리고 systemreturn addressdummy로 채우고 인자로 “”/bin/sh””를 줘서 “”/bin/sh”” 명령어를 실행시킴으로써 쉘을 획득하는 exploit 코드이다.

Sysaddrbinsh addr 구하는 것은 이전에 sysaddr - libcbase_addr으로 구한 offset을 다시 libcbase_addr에 더해서 구해서  똑같을 것 같은데

 

알고보니, ASLR이 적용되면 라이브러리 함수 주소가 변하지만, offset은 변하지 않는다.

따라서 libc start addr를 알면 offset을 더해 원하는 함수의 주소를 구할 수 있다.

 

 

 

 

Re2libc.c 코드를 다시 보면, ret2libc 파일이 실행될 때 Printf() address를 이제 exploit.py 코드에서 전달받으면,

stackAddr = p.recvuntil(“\n”)

newline을 전달받기 전까지 stackAddr에 저장한다. 따라서 printf addressstackAddr에 저장된다.

일전에 printf function address - libc start address = libc base address offset

을 구했으니까, stackAddr + offset을 하면 libc start address를 구할 수 있다.

그것을 libcBase에 저장하고, libcBase에 각각 sysAddr offset, binsh offset을 더하면 각각 sysAddr, binsh addr을 구할 수 있다.

 

 

 

 

위의 exploit 코드를 실행하면 shell을 얻을 수 있는데 프로그램을 종료하면 error가 발생한다.

 

 

-> error가 발생하는 이유는 system 함수 호출 후 0x42424242(BBBB) 영역으로 이동하려고 했기 때문이다.

System + ret + 인자의 구조를 갖기 때문에 “”BBBB””가 들어가는 곳은 원래 return address이다.

따라서 system 함수 종료 후 return address에 저장된 주소에 따라 이동을 해야 하는데, 존재하지 않는 주소를 넣었기 때문에 error가 발생한다.

따라서 0x42424242 영역(return address)system 함수 호출 후 이동할 영역의 주소를 넣으면 error가 발생하지 않는다.

대부분 exit() 함수의 주소를 저장한다.

728x90
반응형

관련글 더보기