Skip to content

kernelCTF: add CVE-2023-3610_cos #1308

kernelCTF: add CVE-2023-3610_cos

kernelCTF: add CVE-2023-3610_cos #1308

name: kernelCTF PR check
on:
pull_request_target:
types: [opened, synchronize, reopened, labeled]
paths: [pocs/linux/kernelctf/**]
workflow_call:
inputs:
prNumber:
description: 'PR number'
type: number
shaHash:
description: 'SHA hash'
type: string
skipRepro:
description: 'Skip reproduction'
type: boolean
required: false
default: false
workflow_dispatch:
inputs:
prNumber:
description: 'PR number'
type: number
shaHash:
description: 'SHA hash'
skipRepro:
description: 'Skip reproduction'
type: boolean
required: false
default: false
permissions: {}
env:
PR_REF: ${{ contains(github.event_name, 'workflow_') && (inputs.shaHash || format('refs/pull/{0}/merge', inputs.prNumber)) || github.event.pull_request.head.sha }}
jobs:
structure_check:
# if labeling triggered the job then only run in case of the "recheck" label
if: github.event.action != 'labeled' || github.event.label.name == 'recheck'
runs-on: ubuntu-latest
permissions: {}
outputs:
targets: ${{ steps.check_submission.outputs.targets }}
submission_dir: ${{ steps.check_submission.outputs.submission_dir }}
exploits_info: ${{ steps.check_submission.outputs.exploits_info }}
artifact_backup_dir: ${{ steps.check_submission.outputs.artifact_backup_dir }}
steps:
- run: pip install -U jsonschema
- name: Checkout repo content (sparse - only check-submission.py)
uses: actions/checkout@v4
with:
ref: master
sparse-checkout: |
kernelctf/check-submission.py
kernelctf/utils.py
sparse-checkout-cone-mode: false
- name: Checkout PR content (full)
uses: actions/checkout@v4
with:
path: pr-content
ref: ${{ env.PR_REF }}
fetch-depth: 0
- id: check_submission
name: Check submission
working-directory: pr-content
run: |
echo "::stop-commands::$(uuidgen)"
../kernelctf/check-submission.py ${{ github.event.pull_request.base.sha }}
exploit_build:
runs-on: ubuntu-latest
needs: structure_check
permissions: {}
strategy:
matrix:
target: ${{ fromJSON(needs.structure_check.outputs.targets) }}
fail-fast: false # do not cancel other targets
env:
RELEASE_ID: ${{ matrix.target }}
EXPLOIT_DIR: pocs/linux/kernelctf/${{ needs.structure_check.outputs.submission_dir }}/exploit/${{ matrix.target }}
SUBMISSION_DIR: ${{ needs.structure_check.outputs.submission_dir }}
steps:
- name: Checkout PR content (full)
uses: actions/checkout@v4
with:
ref: ${{ env.PR_REF }}
fetch-depth: 0
- name: Checkout Android scripts (sparse) - Android only
if: startsWith(matrix.target, 'android')
uses: actions/checkout@v4
with:
path: scripts-from-master
ref: master
sparse-checkout: |
kernelctf/server/get_android_dependencies.sh
sparse-checkout-cone-mode: false
- name: List files
run: find .
- name: Backup original exploit
run: mv $EXPLOIT_DIR/exploit ./
- name: Install Android dependencies (exploit-build-only mode)
if: startsWith(matrix.target, 'android')
run: |
bash scripts-from-master/kernelctf/server/get_android_dependencies.sh --exploit-build-only
- name: Install kernelXDK (Linux only)
if: startsWith(matrix.target, 'android') == false
run: |
sudo apt install libkeyutils-dev
wget https://github.com/google/kernel-research/releases/download/libxdk%2Fv0.1/libxdk-v0.1.tar.gz
tar -xzvf libxdk-v0.1.tar.gz
chmod -R o+rx include lib
sudo cp -R include/* /usr/local/include/
sudo cp -R lib/* /usr/lib/
- name: Build exploit
id: build_exploit
working-directory: ${{ env.EXPLOIT_DIR }}
run: |
if make -n prerequisites; then
make prerequisites
fi
make exploit
- name: Upload exploit (newly compiled)
if: success()
uses: actions/upload-artifact@v4
with:
name: ${{ needs.structure_check.outputs.artifact_backup_dir }}_exploit_${{ env.RELEASE_ID }}
path: ${{ env.EXPLOIT_DIR }}/exploit
if-no-files-found: error
- name: Upload exploit (original, build failed)
if: failure() && steps.build_exploit.outcome == 'failure'
uses: actions/upload-artifact@v4
with:
name: ${{ needs.structure_check.outputs.artifact_backup_dir }}_exploit_${{ env.RELEASE_ID }}
path: ./exploit
if-no-files-found: error
- name: Summarize result (success)
if: success()
run: printf '✅ Exploit was built successfully.\n\nIt can be found under the artifacts (`exploit_${{ env.RELEASE_ID }}`).\n' >> $GITHUB_STEP_SUMMARY
- name: Summarize result (failure)
if: failure() && steps.build_exploit.outcome == 'failure'
run: printf '❌ The exploit compilation failed.\n\nPlease fix it.\n\nYou can see the build logs by clicking on `...` here and then on "View job logs". Or by selecting `exploit_build (${{ env.RELEASE_ID }})` under Jobs in the left menubar.\n' >> $GITHUB_STEP_SUMMARY
exploit_build_debug:
runs-on: ubuntu-latest
needs: structure_check
permissions: {}
strategy:
matrix:
target: ${{ fromJSON(needs.structure_check.outputs.targets) }}
fail-fast: false # do not cancel other targets
env:
RELEASE_ID: ${{ matrix.target }}
EXPLOIT_DIR: pocs/linux/kernelctf/${{ needs.structure_check.outputs.submission_dir }}/exploit/${{ matrix.target }}
SUBMISSION_DIR: ${{ needs.structure_check.outputs.submission_dir }}
steps:
- name: Checkout PR content (full)
uses: actions/checkout@v4
with:
ref: ${{ env.PR_REF }}
fetch-depth: 0
- name: Checkout Android scripts (sparse) - Android only
if: startsWith(matrix.target, 'android')
uses: actions/checkout@v4
with:
path: scripts-from-master
ref: master
sparse-checkout: |
kernelctf/server/get_android_dependencies.sh
sparse-checkout-cone-mode: false
- name: Install Android dependencies (exploit-build-only mode)
if: startsWith(matrix.target, 'android')
run: |
bash scripts-from-master/kernelctf/server/get_android_dependencies.sh --exploit-build-only
- name: Install kernelXDK (Linux only)
if: startsWith(matrix.target, 'android') == false
run: |
sudo apt install libkeyutils-dev
wget https://github.com/google/kernel-research/releases/download/libxdk%2Fv0.1/libxdk-v0.1.tar.gz
tar -xzvf libxdk-v0.1.tar.gz
chmod -R o+rx include lib
sudo cp -R include/* /usr/local/include/
sudo cp -R lib/* /usr/lib/
- name: Convert exploit to debug build
working-directory: ${{ env.EXPLOIT_DIR }}
run: |
# Add -g flag to any gcc or clang variant (including x86_64-linux-android21-clang)
sed -i -E 's/([a-zA-Z0-9_-]*)(gcc|clang)(\s)/\1\2 -g\3/g' Makefile
sed -i '/configure --enable-debug/!s/configure/configure --enable-debug/g' Makefile
sed -i 's/-o exploit\b/-o exploit_debug/g' Makefile
sed -i 's/ -s\b//g' Makefile
sed -i 's/exploit:/exploit_debug:/g' Makefile
sed -i 's/all: exploit$/all: exploit_debug/g' Makefile
- name: Build exploit
working-directory: ${{ env.EXPLOIT_DIR }}
run: |
if make -n prerequisites; then
make prerequisites
fi
make exploit_debug
file exploit_debug | grep debug_info
- name: Upload debug build
uses: actions/upload-artifact@v4
with:
name: ${{ needs.structure_check.outputs.artifact_backup_dir }}_exploit_debug_${{ env.RELEASE_ID }}
path: ${{ env.EXPLOIT_DIR }}/exploit_debug
if-no-files-found: error
exploit_repro:
runs-on: ubuntu-latest
timeout-minutes: 300
permissions: {}
needs: [structure_check, exploit_build]
strategy:
matrix:
target: ${{ fromJSON(needs.structure_check.outputs.targets) }}
fail-fast: false
if: always() && needs.structure_check.result == 'success' && !inputs.skipRepro
env:
RELEASE_ID: ${{ matrix.target }}
SUBMISSION_DIR: ${{ needs.structure_check.outputs.submission_dir }}
EXPLOIT_INFO: ${{ toJSON(fromJSON(needs.structure_check.outputs.exploits_info)[matrix.target]) }}
defaults:
run:
shell: bash
working-directory: ./kernelctf/repro
steps:
- name: Checkout repo content (sparse - repro and server only)
uses: actions/checkout@v4
with:
ref: master
sparse-checkout: |
kernelctf/repro
kernelctf/server
sparse-checkout-cone-mode: true
- name: Download exploit
uses: actions/download-artifact@v4
with:
name: ${{ needs.structure_check.outputs.artifact_backup_dir }}_exploit_${{ env.RELEASE_ID }}
path: ./kernelctf/repro/exp/
- name: Make exploit executable
run: chmod +x ./exp/exploit
- name: Enable KVM group perms
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- name: Install tools (QEMU, inotify, expect) - Linux only
if: startsWith(matrix.target, 'android') == false
run: sudo apt-get update && sudo apt-get install -y qemu-system-x86 inotify-tools expect
- name: Fetch rootfs - Linux only
if: startsWith(matrix.target, 'android') == false
run: |
wget -O rootfs.img.gz https://storage.googleapis.com/kernelctf-build/files/rootfs_repro_v2.img.gz
gzip -d rootfs.img.gz
- name: Download bzImage - Linux only
if: startsWith(matrix.target, 'android') == false
run: |
if [ "$RELEASE_ID" == "mitigation-6.1" ]; then RELEASE_ID="mitigation-6.1-v2"; fi
wget https://storage.googleapis.com/kernelctf-build/releases/$RELEASE_ID/bzImage
- name: Load optional kernel modules for Cuttlefish - Android only
if: startsWith(matrix.target, 'android')
run: |
sudo modprobe vhost_net
sudo modprobe vhost_vsock
- name: Install Android dependencies and download system images (parallel) - Android only
if: startsWith(matrix.target, 'android')
run: |
# Start Android dependencies installation in background
(
bash ../../kernelctf/server/get_android_dependencies.sh > android_deps_install.log 2>&1
DEPS_EXIT=$?
if [ $DEPS_EXIT -eq 0 ]; then
echo "Dependencies installation completed"
# Clean up apt cache immediately after installation to free space
echo "Cleaning up apt cache..."
sudo apt-get clean
sudo rm -rf /var/lib/apt/lists/*
fi
exit $DEPS_EXIT
) &
DEPS_PID=$!
# Start system images download in background
(
mkdir -p android_release
cd android_release
# Extract build number from RELEASE_ID
BUILD_NUMBER=$(echo "$RELEASE_ID" | grep -oP '\d+$')
echo "Downloading and extracting Android system images for $RELEASE_ID (build: $BUILD_NUMBER)..."
# Download, extract, and delete phone image immediately
wget --progress=dot:giga https://storage.googleapis.com/kernelctf-build/releases/$RELEASE_ID/aosp_cf_x86_64_only_phone-img-${BUILD_NUMBER}.zip 2>&1 | tail -n 1
echo "Extracting phone image..."
unzip -q aosp_cf_x86_64_only_phone-img-${BUILD_NUMBER}.zip
echo "Removing phone image archive to save space..."
rm -f aosp_cf_x86_64_only_phone-img-${BUILD_NUMBER}.zip
# Download, extract, and delete host package immediately
wget --progress=dot:giga https://storage.googleapis.com/kernelctf-build/releases/$RELEASE_ID/cvd-host_package.tar.gz 2>&1 | tail -n 1
echo "Extracting host package..."
tar -xzf cvd-host_package.tar.gz
echo "Removing host package archive to save space..."
rm -f cvd-host_package.tar.gz
echo "System images ready"
) &
DOWNLOAD_PID=$!
# Wait for both to complete
echo "Running in parallel: dependencies installation (PID: $DEPS_PID) and image download (PID: $DOWNLOAD_PID)..."
wait $DEPS_PID
DEPS_EXIT=$?
wait $DOWNLOAD_PID
DOWNLOAD_EXIT=$?
# Check if either failed
if [ $DEPS_EXIT -ne 0 ]; then
echo "ERROR: Dependencies installation failed with exit code $DEPS_EXIT"
echo "Last 50 lines of android_deps_install.log:"
tail -n 50 android_deps_install.log || true
exit $DEPS_EXIT
fi
if [ $DOWNLOAD_EXIT -ne 0 ]; then
echo "ERROR: Image download failed with exit code $DOWNLOAD_EXIT"
exit $DOWNLOAD_EXIT
fi
echo "Both tasks completed successfully"
APK_SOURCE="../../kernelctf/server/android_shellserver/app/build/outputs/apk/release/app-release.apk"
if [ -f "$APK_SOURCE" ]; then
# Show space before cleanup
echo "Disk space before cleanup:"
df -h / | grep -E "Filesystem|/$"
# Copy APK to a known location
mkdir -p ./apk
cp "$APK_SOURCE" ./apk/app-release.apk
echo "APK saved to: $(pwd)/apk/app-release.apk"
# Remove android_shellserver directory (source + build artifacts ~200MB)
echo "Removing android_shellserver directory..."
rm -rf ../../kernelctf/server/android_shellserver
# Clean Gradle cache (~500MB)
if [ -d "$HOME/.gradle" ]; then
echo "Cleaning Gradle cache..."
rm -rf $HOME/.gradle/caches
rm -rf $HOME/.gradle/wrapper/dists
rm -rf $HOME/.gradle/daemon
fi
else
echo "ERROR: APK not found at $APK_SOURCE"
exit 1
fi
- name: Set paths - Android only
if: startsWith(matrix.target, 'android')
run: |
# Use the APK we saved during cleanup
APK_PATH="$(realpath ./apk/app-release.apk)"
echo "APK_PATH=$APK_PATH" >> $GITHUB_ENV
echo "[INFO] APK path set to: $APK_PATH"
echo "ANDROID_RELEASE_PATH=$(realpath android_release)" >> $GITHUB_ENV
- id: repro1
name: Reproduction (1 / 10)
continue-on-error: true
run: ./repro.sh 1
- id: repro2
name: Reproduction (2 / 10)
continue-on-error: true
run: ./repro.sh 2
- id: repro3
name: Reproduction (3 / 10)
continue-on-error: true
run: ./repro.sh 3
- id: repro4
name: Reproduction (4 / 10)
continue-on-error: true
run: ./repro.sh 4
- id: repro5
name: Reproduction (5 / 10)
continue-on-error: true
run: ./repro.sh 5
- id: repro6
name: Reproduction (6 / 10)
continue-on-error: true
run: ./repro.sh 6
- id: repro7
name: Reproduction (7 / 10)
continue-on-error: true
run: ./repro.sh 7
- id: repro8
name: Reproduction (8 / 10)
continue-on-error: true
run: ./repro.sh 8
- id: repro9
name: Reproduction (9 / 10)
continue-on-error: true
run: ./repro.sh 9
- id: repro10
name: Reproduction (10 / 10)
continue-on-error: true
run: ./repro.sh 10
- name: Upload repro logs as an artifact
uses: actions/upload-artifact@v4
with:
name: ${{ needs.structure_check.outputs.artifact_backup_dir }}_repro_logs_${{ env.RELEASE_ID }}
path: ./kernelctf/repro/repro_log_*.txt
- name: Reproduction // Summary
env:
STEPS: ${{ toJSON(steps) }}
run: |
echo $STEPS >> steps.json
../repro_summary.py ${{ github.run_id }}
- name: Upload repro summary as an artifact
uses: actions/upload-artifact@v4
with:
name: ${{ needs.structure_check.outputs.artifact_backup_dir }}_repro_summary_${{ env.RELEASE_ID }}
path: ./kernelctf/repro/repro_summary.md
backup_artifacts:
runs-on: ubuntu-latest
needs: [structure_check, exploit_build, exploit_build_debug, exploit_repro]
if: always() && needs.structure_check.result == 'success' && github.event_name != 'workflow_call'
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
with:
path: ./artifacts
- name: Authenticate to Google Cloud
uses: google-github-actions/auth@v2
with:
credentials_json: '${{secrets.KERNELCTF_GCS_SA_KEY}}'
- name: Upload artifacts to GCS
uses: 'google-github-actions/upload-cloud-storage@v2'
with:
path: ./artifacts
destination: kernelctf-build/artifacts/${{ needs.structure_check.outputs.artifact_backup_dir }}_${{ github.run_id }}
parent: false
predefinedAcl: publicRead
process_gcloudignore: false # removes warnings that .gcloudignore file does not exist