본문 바로가기

Pwnable.kr

Pwnable.kr [asm]

#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;
}

mmap을 이용해 read, write, execute 권한을 전부 주고, 

거기에 아예 내 코드를 올릴 수 있게끔 해주고 있다. 

 

단, 간단히 sandbox() 함수에서 seccomp를 이용해 원하지 않는 system call을 막고 있어서,

open으로 flag 파일을 열고, read로 주어진 메모리 영역 중에다가 flag 내용을 읽은 뒤에

write로 stdout에다가 해당 내용을 쓰는 형태로 flag를 얻을 수 있다.

 

주어진 메모리 영역은 0x1000으로 넉넉하니, 쉘코드만 잘 작성하면 되는데,

처음에 x64 shellcode라는 걸 못봐서 32bit로 작성하는 삽질을 했다 ㅠ

 

쉘코드 작성은 inline asm을 이용하여 코드를 작성하고, byte 값을 추출하는 형태로 진행하였고,

인자값을 넣어주기 위해 mov를 이용할 경우, 4byte 미만의 값에 대해서 0x00이 포함되는 경우가 있어서

xor, inc, shl까지 열심히 이용해서 진행하였다.

 

아 그리고, 32bit에서는 int 0x80 (\xcd \x80)을 이용해서 syscall을 호출하는데, 

64bit에서는 그냥 syscall (\x0f \x05)어셈 명령어가 있다. 

각자 운영체제에 맞는 형식으로 어셈을 줘야지만 system call이 정상 호출된다. 

 

구글의 syscall number를 검색하면 아래 url이 나오고, 여기에 64bit에서 syscall을 하기 위해 

인자값을 어느 register에 넣어야하는지 자세히 알려주므로 보고 작성하면 된다.

https://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64

 

Linux System Call Table for x86 64 · Ryan A. Chapman

Linux 4.7 (pulled from github.com/torvalds/linux on Jul 20 2016), x86_64 Note: 64-bit x86 uses syscall instead of interrupt 0x80. The result value will be in %rax To find the implementation of a system call, grep the kernel tree for SYSCALL_DEFINE.\?(sysca

blog.rchapman.org

참고로 64bit의 경우 원래 인자 전달을 register를 통해서 하며

정수 / 포인터 인자의 순서는 rdi, rsi, rdx, rcx, r8, r9, 스택 순이고 

실수 인자는 xmm0, ... ,xmm7, 스택 순이다. 

 

flag파일의 이름이 길어서 설마... 하고 *를 이용해서 하려고 했으나 되지 않아 이름 전부를 전달해주어야 했다.

다만, 상대경로는 사용가능하니 그나만 10글자 정도라도 줄여서 진행하자...

 

아래 코드는 파일 이름에 대해서 \x61 형태로 출력해주는 코드이다.

text = "./this_is_pwnable.kr_flag_file_please_read_this_file.sorry_the_file_name_is_very_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo0000000000000000000000000ooooooooooooooooooooooo000000000000o0o0o0o0o0o0ong"
temp = ""
for i in range(0,len(text)):
        temp += "\\x"
        temp += text[i:i+1].encode('hex')
print temp

 

파일 이름 끝에 \x00까지 붙여야하는 기본도 놓치지 말자. 

'Pwnable.kr' 카테고리의 다른 글

Pwnable.kr [blukat]  (0) 2019.05.15
Pwnable.kr [unlink]  (0) 2019.05.14
Pwnable.kr [memcpy]  (0) 2019.05.08
Pwnable.kr [uaf]  (0) 2019.05.03
Pwnable.kr [cmd2]  (0) 2019.05.03