kernelCTF: add CVE-2023-3610_cos #1308
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |