본문 바로가기
pwnable

[Toddler's Bottle] asm

by 미스터 J 2024. 5. 11.
반응형

#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;
}


asm.c 코드이다. 모르는 것부터 정리해보자

 

void* mmap(void* start, size_t length, int prot, int flags, int fd, off_t offset);

파일이나 디바이스를 응용 프로그램의 주소 공간 메모리에 대응시킴

fd가 가리키는 객체를 파일에서 offset 바이트 지점을 기준으로 len 바이트만틈 메모리에 맵핑하도록 커널에 요청, addr을 넘길 경우, 메모리에서 해당 시작 주소를 선호한다고 커널에 알림. 접근 권한은 prot에 지정하고, 추가적인 동작 방식은 flags에 지정

성공하면 대응된 영역의 포인터를 반환하고, 실패하면 MAP_FAILED(-1)이 반환, errno는 다음 값으로 설정

          start : 요청한 물리 주소 공간을 매핑하고자 하는 주소, 보통 0

          length : 매핑하고자 하는 주소 공간의 크기, PAGE_SIZE의 배수

          prot : 메모리 보호 모드 설정

                    PROT_EXEC : 페이지가 실행될 수 있다

                   PROT_READ : 페이지를 읽을 수 있다.

                   PROT_WRITE : 페이지에 쓸 수 있다.

                   RPOT_NONE : 페이지에 접근할 수 없다

          flags : 대응된 객체 타입, 대응 옵션, 대응된 페이지 복사본에 대한 수정을 그 프로세스에만 보일 것이니지 참조하는 다른 프로세스와 공유할 것인지를 설정 (MAP_SHARED나 MAP_PRIVATE 중 하나는 반드시 명시해야 함)

                    MAP_FIXED : 지정된 주소 이외에는 선택하지 않음. 지정된 주소를 사용할 수 없으면 mmap은 실패 (start는 페이지 크기의 배수여야 한다. 가급적 MAP_FIXED 옵션은 사용하지 않는 편이 좋음)

                   MAP_SHARED : 이 객체를 대응시키는 다른 프로세스와 대응 영역을 공유

                   MAP_PRIVATE : 다른 프로세스와 대응 영역을 공유하지 않음

          fd : 디바이스 파일의 파일 디스크립터

          offset : 매핑시키고 싶은 물리 주소, 이 값은 드라이버에 전달되지만, 디바이스 드라이버가 다른 주소를 매핑할 경우 쓰이지 않음. 이 주소 역시 PAGE_SIZE 단위로 지정되어야 함.

 

int setvbuf(FILE* stream, char* buffer, int mode, size_t size); = 스트림 버퍼링 방식을 변경. 버퍼를 특정한 스트림의 입출력 연산에 사용될 수 있도록 변경. 버퍼링 방식과 버퍼의 크기를 설정할 수 있게 지원.

          stream : 작업을 수행할 열린 스트림의 FILE 객체를 가리키는 포인터

          buffer : 유저가 할당한 버퍼로 최소한 1바이트의 크기는 되야함. 만일 NULL로 설정하면 함수는 자동으로 지정한 크기의 버퍼를 할당

          mode : 파일 버퍼링의 형식을 지정

                    _IOFBF 완전한 버퍼링 (Full Buffering) : 앞에서 설명한 fully buffered 스트림

                    _IOLBF 행 버퍼링 (Line Buffering) : 출력시, 버퍼가 채워지거나 스트림에 개행 문자가 입력되었다면, 데이터가 버퍼에서 출력. 입력시에는 버퍼가 개행 문자를 만날 때 까지 버퍼를 채우게 됨

                    _IONBF 버퍼링 사용안함 (No Buffering) : 버퍼를 사용하지 않음. 각각의 입출력 작업은 버퍼를 지나지 않고 요청 즉시 진행되며 이 겨우 buffer 인자와 size 인자는 모두 무시.

          size : 지정할 버퍼의 크기로 단위는 바이트. 버퍼로 지정한 배열의 크기보다 반드시 작거나 같아야 함.

 

void * memset(void * ptr, int value, size_t num); = 어떤 메모리의 시작점부터 연속된 범위를 어떤 값으로 (바이트 단위) 모두 지정하고 싶을 때 사용.

          ptr : 채우고자 하는 메모리의 시작 포인터(시작 주소)

          value : 메모리에 채우고자 하는 값, int 형이지만 내부에서는 unsigned char (1byte) 로 변환되어서 저장된다.

          num : 채우고자 하는 바이트의 수. 즉, 채우고자 하는 메모리의 크기

 

void * memcpy(void* destinationm const void* source, size_t num); = 메모리의 일부분을 복사

          destination : 데이터가 복사될 곳의 주소, void* 형으로 형 변환 되어서 전달

          source : 복사할 데이터들이 위치한 주소로 역시 vodi* 형으로 형 변환 되어서 전달 됨

 

alarm() : second 초 후에 프로세스에 SIGALRM을 전달. 

 

int chroot(const char *path) = 특정 디렉토리를 루트 디렉토리 "/"로 지정. // 성공 0, 실패 -1

          char *path : 루트 디렉토리로 지정할 디렉토리


Q1. sandbox()함수에 명시된 함수, 변수에 대한 것은 찾지 못했다, 어떤 기능일까?

Q2. mmap()함수의 prot부분에 7은 어떤 의미일까?, flags 부분도 상세한 내용을 찾지 못했다.

 

반응형

'pwnable' 카테고리의 다른 글

[Toddler's Bottle] cmd1  (0) 2024.05.11
[Toddler's Bottle] lotto  (0) 2024.05.11
[Toddler's Bottle] blukat  (0) 2024.05.11
[Toddler's Bottle] leg  (0) 2024.05.11
[Toddler's Bottle] input  (1) 2024.05.11