docs: README.ko.md, README.ru.md #22
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
| # SPDX-License-Identifier: LGPL-2.1-or-later OR MIT | |
| name: build | |
| on: | |
| push: | |
| branches: [main] | |
| tags: ['v*'] | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| jobs: | |
| # GNU toolchain + glibc-static. Compile-check across architectures using | |
| # the same toolchain most distros ship. Artifacts are large (~800 KB) but | |
| # confirm the source builds cleanly under GCC for each target. | |
| build-glibc: | |
| name: glibc / ${{ matrix.target.arch }} | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| target: | |
| - { arch: x86_64, prefix: '', apt: '' } | |
| - { arch: aarch64, prefix: aarch64-linux-gnu, apt: 'gcc-aarch64-linux-gnu linux-libc-dev-arm64-cross libc6-dev-arm64-cross' } | |
| - { arch: armv7, prefix: arm-linux-gnueabihf, apt: 'gcc-arm-linux-gnueabihf linux-libc-dev-armhf-cross libc6-dev-armhf-cross' } | |
| - { arch: riscv64, prefix: riscv64-linux-gnu, apt: 'gcc-riscv64-linux-gnu linux-libc-dev-riscv64-cross libc6-dev-riscv64-cross' } | |
| - { arch: ppc64le, prefix: powerpc64le-linux-gnu, apt: 'gcc-powerpc64le-linux-gnu linux-libc-dev-ppc64el-cross libc6-dev-ppc64el-cross' } | |
| - { arch: s390x, prefix: s390x-linux-gnu, apt: 'gcc-s390x-linux-gnu linux-libc-dev-s390x-cross libc6-dev-s390x-cross' } | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| persist-credentials: false | |
| - name: install cross-toolchain | |
| if: matrix.target.apt != '' | |
| run: | | |
| sudo apt-get update -qq | |
| sudo apt-get install -y --no-install-recommends ${{ matrix.target.apt }} | |
| - name: build | |
| run: | | |
| if [ -z "${{ matrix.target.prefix }}" ]; then | |
| make | |
| else | |
| make CC=${{ matrix.target.prefix }}-gcc LD=${{ matrix.target.prefix }}-ld | |
| fi | |
| - name: verify | |
| run: | | |
| file payload exploit exploit-passwd | |
| echo "payload: $(stat -c%s payload) bytes" | |
| echo "exploit: $(stat -c%s exploit) bytes" | |
| echo "exploit-passwd: $(stat -c%s exploit-passwd) bytes" | |
| - name: upload binaries | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: copy-fail-c-${{ matrix.target.arch }}-glibc | |
| path: | | |
| exploit | |
| exploit-passwd | |
| retention-days: 90 | |
| # zig cc + musl-static. Produces deploy-anywhere binaries with a fully | |
| # static link and no runtime libc version dependency. zig ships musl libc | |
| # for every target; we still need GNU binutils-<arch>-cross for the | |
| # `ld -r -b binary` step that wraps the payload as a relocatable .o, | |
| # plus kernel UAPI headers for nolibc and linux/if_alg.h. | |
| build-musl: | |
| name: musl / ${{ matrix.target.arch }} | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| target: | |
| - { arch: x86_64, zig_target: x86_64-linux-musl, apt: '', ld_prefix: '', uapi_base: /usr/include, uapi_asm: /usr/include/x86_64-linux-gnu/asm } | |
| - { arch: aarch64, zig_target: aarch64-linux-musl, apt: 'binutils-aarch64-linux-gnu linux-libc-dev-arm64-cross', ld_prefix: aarch64-linux-gnu, uapi_base: /usr/aarch64-linux-gnu/include, uapi_asm: /usr/aarch64-linux-gnu/include/asm } | |
| - { arch: armv7, zig_target: arm-linux-musleabihf, apt: 'binutils-arm-linux-gnueabihf linux-libc-dev-armhf-cross', ld_prefix: arm-linux-gnueabihf, uapi_base: /usr/arm-linux-gnueabihf/include, uapi_asm: /usr/arm-linux-gnueabihf/include/asm } | |
| - { arch: riscv64, zig_target: riscv64-linux-musl, apt: 'binutils-riscv64-linux-gnu linux-libc-dev-riscv64-cross', ld_prefix: riscv64-linux-gnu, uapi_base: /usr/riscv64-linux-gnu/include, uapi_asm: /usr/riscv64-linux-gnu/include/asm } | |
| - { arch: ppc64le, zig_target: powerpc64le-linux-musl, apt: 'binutils-powerpc64le-linux-gnu linux-libc-dev-ppc64el-cross', ld_prefix: powerpc64le-linux-gnu, uapi_base: /usr/powerpc64le-linux-gnu/include, uapi_asm: /usr/powerpc64le-linux-gnu/include/asm } | |
| - { arch: s390x, zig_target: s390x-linux-musl, apt: 'binutils-s390x-linux-gnu linux-libc-dev-s390x-cross', ld_prefix: s390x-linux-gnu, uapi_base: /usr/s390x-linux-gnu/include, uapi_asm: /usr/s390x-linux-gnu/include/asm } | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| persist-credentials: false | |
| - uses: mlugg/setup-zig@d1434d08867e3ee9daa34448df10607b98908d29 # v2.2.1 | |
| with: | |
| version: 0.14.0 | |
| use-cache: false | |
| - name: install cross-binutils and UAPI headers | |
| if: matrix.target.apt != '' | |
| run: | | |
| sudo apt-get update -qq | |
| sudo apt-get install -y --no-install-recommends ${{ matrix.target.apt }} | |
| - name: build | |
| run: | | |
| LD_BIN=ld | |
| if [ -n "${{ matrix.target.ld_prefix }}" ]; then | |
| LD_BIN=${{ matrix.target.ld_prefix }}-ld | |
| fi | |
| make zig-musl-static \ | |
| ZIG_TARGET=${{ matrix.target.zig_target }} \ | |
| LD="$LD_BIN" \ | |
| UAPI_LINUX_DIR=${{ matrix.target.uapi_base }}/linux \ | |
| UAPI_ASM_GENERIC_DIR=${{ matrix.target.uapi_base }}/asm-generic \ | |
| UAPI_ASM_DIR=${{ matrix.target.uapi_asm }} | |
| - name: strip binaries | |
| env: | |
| LD_PREFIX: ${{ matrix.target.ld_prefix }} | |
| run: | | |
| STRIP=strip | |
| if [ -n "$LD_PREFIX" ]; then | |
| STRIP="${LD_PREFIX}-strip" | |
| fi | |
| for bin in exploit exploit-passwd; do | |
| BEFORE=$(stat -c%s "$bin") | |
| "$STRIP" --strip-all "$bin" | |
| AFTER=$(stat -c%s "$bin") | |
| echo "$bin stripped: $BEFORE -> $AFTER bytes" | |
| done | |
| - name: verify | |
| run: | | |
| file payload exploit exploit-passwd | |
| echo "payload: $(stat -c%s payload) bytes" | |
| echo "exploit: $(stat -c%s exploit) bytes" | |
| echo "exploit-passwd: $(stat -c%s exploit-passwd) bytes" | |
| - name: upload binaries | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: copy-fail-c-${{ matrix.target.arch }}-musl | |
| path: | | |
| exploit | |
| exploit-passwd | |
| retention-days: 90 | |
| release: | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| needs: [build-glibc, build-musl] | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| with: | |
| persist-credentials: false | |
| - name: download all artifacts | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| path: artifacts | |
| - name: rename binaries for release upload | |
| run: | | |
| mkdir -p release | |
| for d in artifacts/copy-fail-c-*; do | |
| base=$(basename "$d") | |
| passwd_name="${base/copy-fail-c-/copy-fail-c-passwd-}" | |
| cp "$d/exploit" "release/$base" | |
| cp "$d/exploit-passwd" "release/$passwd_name" | |
| chmod +x "release/$base" "release/$passwd_name" | |
| done | |
| ls -la release/ | |
| - name: create release | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| TAG: ${{ github.ref_name }} | |
| REPO: ${{ github.repository }} | |
| run: | | |
| gh release create "$TAG" \ | |
| --title "$TAG" \ | |
| --notes-file - release/* <<EOF | |
| Cross-platform C port of CVE-2026-31431 (Copy Fail). | |
| Discovery and disclosure: Theori / Xint, https://copy.fail/ | |
| ## Binaries | |
| Each binary is statically linked. Download the one matching the | |
| target's architecture, libc, and exploit variant. | |
| Exploit variants: | |
| * \`copy-fail-c-<arch>-<libc>\`: binary-mutation variant. Mutates | |
| a setuid binary's page cache, then execs it. | |
| * \`copy-fail-c-passwd-<arch>-<libc>\`: /etc/passwd UID-flip variant. | |
| Mutates four bytes of /etc/passwd's page cache, then execs `su`. | |
| Works where the binary-mutation route is blocked but has a | |
| narrower cashout surface; see README for details. | |
| Build modes: | |
| * \`-glibc\` builds: GNU cross-toolchain, glibc-static. Larger (~800 KB) | |
| but functionally identical to source-built binaries on a glibc system. | |
| * \`-musl\` builds: zig cc + musl-static. Smaller (~30-60 KB) and | |
| forward-compatible with any glibc version on the target. | |
| Architectures: x86_64, aarch64, armv7, riscv64, ppc64le, s390x. | |
| See [README](https://github.com/$REPO/blob/main/README.md) | |
| for the kernel-version window of applicability and a description of | |
| the exploit mechanism. | |
| EOF |