diff --git a/.github/workflows/kernelctf-auto-releaser.yaml b/.github/workflows/kernelctf-auto-releaser.yaml new file mode 100644 index 00000000..75646ec4 --- /dev/null +++ b/.github/workflows/kernelctf-auto-releaser.yaml @@ -0,0 +1,38 @@ +name: kernelCTF auto releaser +on: + workflow_dispatch: + schedule: + - cron: '0 12 * * *' # every day at 12:00 UTC +permissions: + contents: read +defaults: + run: + shell: bash + working-directory: kernelctf +jobs: + get_new_builds: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v3 + + - name: Install prerequisites + run: sudo apt install -yq --no-install-recommends python3-lxml + + - id: check + name: Check latest kernel versions + run: ./get_latest_kernel_versions.py + outputs: + releases: ${{ steps.check.outputs.releases }} + + build_release: + needs: get_new_builds + strategy: + matrix: + release: ${{ fromJSON(needs.get_new_builds.outputs.releases) }} + fail-fast: false # do not cancel other builds + uses: ./.github/workflows/kernelctf-release-build.yaml + secrets: inherit + with: + releaseId: ${{ matrix.release.releaseId }} + branch: ${{ matrix.release.branch }} diff --git a/.github/workflows/kernelctf-release-build.yaml b/.github/workflows/kernelctf-release-build.yaml new file mode 100644 index 00000000..dfd4aa87 --- /dev/null +++ b/.github/workflows/kernelctf-release-build.yaml @@ -0,0 +1,73 @@ +name: kernelCTF release build +on: + workflow_dispatch: + inputs: + releaseId: + description: 'Release ID' + type: string + required: true + branch: + description: 'Branch, tag or commit' + type: string + required: false + workflow_call: + inputs: + releaseId: + type: string + branch: + type: string +run-name: 'kernelCTF release: ${{inputs.releaseId}}' +permissions: + contents: read +defaults: + run: + shell: bash + working-directory: kernelctf +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v3 + + - name: Check release does not exist yet + run: curl --fail -I https://storage.googleapis.com/kernelctf-build/releases/${{inputs.releaseId}}/bzImage && exit 1 || true + + - name: Install prerequisites + run: sudo apt install -yq --no-install-recommends build-essential flex bison bc ca-certificates libelf-dev libssl-dev cpio pahole + + - name: Build + run: ./build_release.sh ${{inputs.releaseId}} ${{inputs.branch}} + + - name: Show releases + run: find releases -type f|xargs ls -al + + - name: Upload release artifact + uses: actions/upload-artifact@v3 + with: + name: ${{inputs.releaseId}} + path: kernelctf/releases/${{inputs.releaseId}} + if-no-files-found: error + + upload: + runs-on: ubuntu-latest + needs: build + steps: + - name: Download exploit + uses: actions/download-artifact@v3 + with: + name: ${{inputs.releaseId}} + path: ./kernelctf/releases/${{inputs.releaseId}} + + - name: Authenticate to Google Cloud + uses: google-github-actions/auth@v1 + with: + credentials_json: '${{secrets.GCS_SA_KEY}}' + + - name: Upload release + uses: 'google-github-actions/upload-cloud-storage@v1' + with: + path: kernelctf/releases/${{inputs.releaseId}} + destination: kernelctf-build/releases + predefinedAcl: publicRead + process_gcloudignore: false # removes warnings that .gcloudignore file does not exist diff --git a/kernelctf/build_release.sh b/kernelctf/build_release.sh new file mode 100755 index 00000000..2822922c --- /dev/null +++ b/kernelctf/build_release.sh @@ -0,0 +1,108 @@ +#!/bin/bash +set -ex + +usage() { + echo "Usage: $0 (lts|cos|mitigation)- []"; + exit 1; +} + +RELEASE_NAME="$1" +BRANCH="$2" + +if [[ ! "$RELEASE_NAME" =~ ^(lts|cos|mitigation)-(.*) ]]; then usage; fi +TARGET="${BASH_REMATCH[1]}" +VERSION="${BASH_REMATCH[2]}" + +case $TARGET in + lts) + REPO="https://github.com/gregkh/linux" + DEFAULT_BRANCH="v${VERSION}" + CONFIG_FN="lts.config" + ;; + cos) + REPO="https://cos.googlesource.com/third_party/kernel" + ;; + mitigation) + REPO="https://github.com/thejh/linux" + case $VERSION in + v3-6.1.55) + DEFAULT_BRANCH="mitigations-next" + CONFIG_FN="mitigation-v3.config" + CONFIG_FULL_FN="mitigation-v3-full.config" + ;; + 6.1 | 6.1-v2) + DEFAULT_BRANCH="slub-virtual-v6.1" + CONFIG_FN="mitigation-v1.config" + ;; + esac ;; + *) + usage ;; +esac + +BRANCH="${BRANCH:-$DEFAULT_BRANCH}" +if [ -z "$BRANCH" ]; then usage; fi + +echo "REPO=$REPO" +echo "BRANCH=$BRANCH" + +BASEDIR=`pwd` +BUILD_DIR="$BASEDIR/builds/$RELEASE_NAME" +RELEASE_DIR="$BASEDIR/releases/$RELEASE_NAME" +CONFIGS_DIR="$BASEDIR/kernel_configs" + +if [ -d "$RELEASE_DIR" ]; then echo "Release directory already exists. Stopping."; exit 1; fi + +mkdir -p $BUILD_DIR 2>/dev/null || true +cd $BUILD_DIR +if [ ! -d ".git" ]; then git init && git remote add origin $REPO; fi + +if ! git checkout $BRANCH; then + git fetch --depth 1 origin $BRANCH:$BRANCH || true # TODO: hack, solve it better + git checkout $BRANCH +fi + +if [ "$TARGET" == "cos" ]; then + rm lakitu_defconfig || true + make lakitu_defconfig + cp .config lakitu_defconfig +else + curl 'https://cos.googlesource.com/third_party/kernel/+/refs/heads/cos-6.1/arch/x86/configs/lakitu_defconfig?format=text'|base64 -d > lakitu_defconfig + cp lakitu_defconfig .config +fi + +# build everything into the kernel instead of modules +# note: this can increase the attack surface! +sed -i s/=m/=y/g .config + +if [ ! -z "$CONFIG_FN" ]; then + cp $CONFIGS_DIR/$CONFIG_FN kernel/configs/ + make $CONFIG_FN +fi + +make olddefconfig + +if [ ! -z "$CONFIG_FN" ]; then + if scripts/diffconfig $CONFIGS_DIR/$CONFIG_FN .config|grep "^[^+]"; then + echo "Config did not apply cleanly." + exit 1 + fi +fi + +if [ ! -z "$CONFIG_FULL_FN" ]; then + if scripts/diffconfig $CONFIGS_DIR/$CONFIG_FULL_FN .config|grep "^[^+]"; then + echo "The full config has differences compared to the applied config. Check if the base config changed since custom config was created." + exit 1 + fi +fi + +make -j`nproc` + +mkdir -p $RELEASE_DIR 2>/dev/null || true + +echo "REPOSITORY_URL=$REPO" > $RELEASE_DIR/COMMIT_INFO +(echo -n "COMMIT_HASH="; git rev-parse HEAD) >> $RELEASE_DIR/COMMIT_INFO + +cp $BUILD_DIR/arch/x86/boot/bzImage $RELEASE_DIR/ +cp $BUILD_DIR/lakitu_defconfig $RELEASE_DIR/ +cp $BUILD_DIR/.config $RELEASE_DIR/ +gzip -c $BUILD_DIR/vmlinux > $RELEASE_DIR/vmlinux.gz diff --git a/kernelctf/get_latest_kernel_versions.py b/kernelctf/get_latest_kernel_versions.py new file mode 100755 index 00000000..d694374e --- /dev/null +++ b/kernelctf/get_latest_kernel_versions.py @@ -0,0 +1,35 @@ +#!/usr/bin/env -S python3 -u +import json +from utils import * +from lxml import etree + +releases = [] + +def add_release(release_id, branch=None): + url = f"https://storage.googleapis.com/kernelctf-build/releases/{release_id}/bzImage" + status_code = requests.head(url).status_code + if status_code == 200: + print(" -> Release already exists, skipping...") + return + if status_code != 403: + fail(f"Unexpected HTTP status code for release check: {status_code} (url = {url})") + + global releases + releases.append({ "releaseId": release_id, "branch": branch }) + +latest_lts = run("git ls-remote --tags --sort='-v:refname' https://github.com/gregkh/linux 'v6.1.*[0-9]'")[0].split("refs/tags/")[1] +print(f"Latest LTS: {latest_lts}") +add_release(f"lts-{latest_lts[1:]}") + +for cos_milestone in [97, 105]: + release_notes = fetch(f"https://cloud.google.com/feeds/cos-{cos_milestone}-release-notes.xml") + tree = etree.XML(release_notes.encode('utf-8')) + entries = tree.xpath("//*[local-name() = 'content']/text()") + latest_entry = entries[0] + version_tuple = checkOnlyOne(list(set(re.findall(f"cos-{cos_milestone}-(\d+)-(\d+)-(\d+)", latest_entry))), "too many versions were found") + release_id = f"cos-{cos_milestone}-{'.'.join(version_tuple)}" + commit = checkOnlyOne(re.findall("https://cos.googlesource.com/third_party/kernel/\+/([0-9a-f]{40})", latest_entry), "multiple commits were found") + print(f"Latest COS {cos_milestone}: {release_id}, commit = {commit}") + add_release(release_id, commit) + +ghSet("OUTPUT", "releases=" + json.dumps(releases)) diff --git a/kernelctf/kernel_configs/lts.config b/kernelctf/kernel_configs/lts.config new file mode 100644 index 00000000..88360512 --- /dev/null +++ b/kernelctf/kernel_configs/lts.config @@ -0,0 +1,2 @@ +# CONFIG_IO_URING is not set +CONFIG_SYSTEM_TRUSTED_KEYS="" diff --git a/kernelctf/kernel_configs/mitigation-v1.config b/kernelctf/kernel_configs/mitigation-v1.config new file mode 100644 index 00000000..dc0e1158 --- /dev/null +++ b/kernelctf/kernel_configs/mitigation-v1.config @@ -0,0 +1,12 @@ +# CONFIG_IO_URING is not set +CONFIG_SYSTEM_TRUSTED_KEYS="" + +## required by CONFIG_SLAB_VIRTUAL +CONFIG_DEBUG_VIRTUAL=y + +## required by CONFIG_KMALLOC_SPLIT_VARSIZE +# CONFIG_SLAB_MERGE_DEFAULT is not set + +## turns on Jann's hardening +CONFIG_KMALLOC_SPLIT_VARSIZE=y +CONFIG_SLAB_VIRTUAL=y diff --git a/kernelctf/kernel_configs/mitigation-v3-full.config b/kernelctf/kernel_configs/mitigation-v3-full.config new file mode 100644 index 00000000..a916c33e --- /dev/null +++ b/kernelctf/kernel_configs/mitigation-v3-full.config @@ -0,0 +1,113 @@ +################### General hardening ########################################## + +# Panic instead of failing gracefully and printing a warning when detecting data +# corruption (e.g. in list debugging and SLAB_VIRTUAL) +CONFIG_BUG_ON_DATA_CORRUPTION=y +# Check linked lists for corruption. Must be enabled together with +# CONFIG_BUG_ON_DATA_CORRUPTION. +CONFIG_DEBUG_LIST=y +# Prevent overflows and other overwrites in copy_from/to_user +CONFIG_HARDENED_USERCOPY=y +# Detect some buffer overflows in strcpy/memcpy +CONFIG_FORTIFY_SOURCE=y +# Sets kernel.dmesg_restrict to 1 by default +CONFIG_SECURITY_DMESG_RESTRICT=y +# Prevent processes belonging to the same (unprivileged) user from ptracing each +# other except for parents ptracing their children +CONFIG_SECURITY_YAMA=y +# Zero stack frames on function entry, makes some uninitialized variable uses +# unexploitable +CONFIG_INIT_STACK_ALL_ZERO=y +# Print a warning if there are WX mappings at boot +CONFIG_DEBUG_WX=y +# Stack canaries +CONFIG_STACKPROTECTOR=y +CONFIG_STACKPROTECTOR_STRONG=y +# Guard pages for kernel stacks +CONFIG_VMAP_STACK=y +# Randomize the offset of data on the kernel stack in syscalls +CONFIG_RANDOMIZE_KSTACK_OFFSET=y +CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT=y +# Text KASLR +CONFIG_RANDOMIZE_BASE=y +# Other KASLR +CONFIG_RANDOMIZE_MEMORY=y +# Enforce W^X in the kernel +CONFIG_STRICT_KERNEL_RWX=y +CONFIG_STRICT_MODULE_RWX=y +# Enable UMIP on the CPU to prevent using sidt/sgdt in userspace to leak kernel +# pointers (if the CPU supports UMIP) +CONFIG_X86_UMIP=y + +################### CPU side channels ########################################## + +# Meltdown mitigation +CONFIG_PAGE_TABLE_ISOLATION=y +# Spectre mitigations +CONFIG_RETPOLINE=y +CONFIG_CPU_IBPB_ENTRY=y +CONFIG_CPU_IBRS_ENTRY=y + +################### Memory allocator ########################################### + +# SLUB because SLAB_VIRTUAL doesn't support SLAB or SLOB and those are +# deprecated anyway +CONFIG_SLUB=y +# Randomize the order of the freelist when a new slab is created +CONFIG_SLAB_FREELIST_RANDOM=y +# Prevent attacks on the SLUB freelists +CONFIG_SLAB_FREELIST_HARDENED=y +# Don't merge slab caches (makes random caches/varsize useless and cross-cache easier) +CONFIG_SLAB_MERGE_DEFAULT=n +# Allocate msg_msg and some other useful objects in separate -cg caches +CONFIG_CGROUPS=y +CONFIG_MEMCG=y + +################### BPF ######################################################## + +# Allow sandboxing with seccomp +CONFIG_SECCOMP=y +CONFIG_SECCOMP_FILTER=y +# This is required for jitting seccomp filters (probably) +CONFIG_BPF_SYSCALL=y +# Remove Spectre gadgets in the BPF interpreter +CONFIG_BPF_JIT=y +CONFIG_BPF_JIT_ALWAYS_ON=y +# Makes the kernel.unprivileged_bpf_disabled default to 2 +CONFIG_BPF_UNPRIV_DEFAULT_OFF=y + +################### Attack surface reduction ################################### + +# Disable io_uring +CONFIG_IO_URING=n +# Prevent attackers from stopping the kernel inside copy_from/to_user +CONFIG_USERFAULTFD=n +CONFIG_FUSE_FS=n +# Disable staging drivers, which may be more buggy +CONFIG_STAGING=n + +################## Extra mitigations/not upstreamed ############################ + +# Protects against cross-cache attacks. Must be enabled together with +# CONFIG_BUG_ON_DATA_CORRUPTION +CONFIG_SLAB_VIRTUAL=y +# Splits kmalloc caches in fixed-size and dynamic size to make UaF exploitation +# harder +CONFIG_KMALLOC_SPLIT_VARSIZE=y +# Create multiple copies of the normal and -cg kmalloc caches to make spraying +# harder +CONFIG_RANDOM_KMALLOC_CACHES=y + +################### Make the kernel less annyoing to debug ##################### + +# Compile the kernel with debug info +CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y +# Have all symbols in kallsyms +CONFIG_KALLSYMS=y +CONFIG_KALLSYMS_ALL=y +CONFIG_TRIM_UNUSED_KSYMS=n +# Include the kernel configuration in the bzImage/vmlinux +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +# SLUB stats in /sys/slab +SLUB_DEBUG=y diff --git a/kernelctf/kernel_configs/mitigation-v3.config b/kernelctf/kernel_configs/mitigation-v3.config new file mode 100644 index 00000000..11dd6752 --- /dev/null +++ b/kernelctf/kernel_configs/mitigation-v3.config @@ -0,0 +1,25 @@ +# CONFIG_IO_URING is not set +CONFIG_SYSTEM_TRUSTED_KEYS="" + +## required by CONFIG_KMALLOC_SPLIT_VARSIZE +# CONFIG_SLAB_MERGE_DEFAULT is not set + +## turns on our mitigations +CONFIG_KMALLOC_SPLIT_VARSIZE=y +CONFIG_SLAB_VIRTUAL=y + +## turns on CONFIG_RANDOM_KMALLOC_CACHES +CONFIG_RANDOM_KMALLOC_CACHES=y + +## turns on additional hardenings +CONFIG_BUG_ON_DATA_CORRUPTION=y +CONFIG_FORTIFY_SOURCE=y +CONFIG_DEBUG_WX=y +CONFIG_BPF_UNPRIV_DEFAULT_OFF=y +# CONFIG_FUSE_FS is not set + +### Make the kernel less annoying to debug +## Compile the kernel with debug info +CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y +# Have all symbols in kallsyms +CONFIG_KALLSYMS_ALL=y