<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel>
  <title>alpinelinux.org</title>
  <link>https://aggr.aymericbeaumet.com/sources/alpinelinux.org/</link>
  <description>Browse aymeric&#39;s, an independent, searchable archive of readable snapshots from followed feeds, preserved in Git with aggr.</description>
  <language>en</language>
  <atom:link href="https://aggr.aymericbeaumet.com/sources/alpinelinux.org/rss.xml" rel="self" type="application/rss+xml"/>
  <lastBuildDate>Thu, 5 Nov 2026 14:44:20 +0000</lastBuildDate>
  <generator>aggr</generator>
  <item>
    <title>Alpine Linux Security Advisory: OpenSSH-server password authentication bypass</title>
    <link>https://aggr.aymericbeaumet.com/items/hnrss-org/2026-11-05-alpine-linux-security-advisory-openssh-server-password/</link>
    <guid isPermaLink="false">urn:aggr:item:faa3b3b6eb5cb0c3b37b7c1ea366b0f24f1aada2</guid>
    <atom:link href="https://alpinelinux.org/posts/2026-11-05-openssh-password-authentication-bypass/" rel="via" type="text/html"/>
    <pubDate>Thu, 5 Nov 2026 14:44:20 +0000</pubDate>
    <dc:creator>pantalaimon</dc:creator>
    <description>If you run sshd on Alpine Linux, update openssh now and restart sshd. Upgrading the package alone does not replace the running daemon. If you cannot update yet, at least disable password logins: set PasswordAuthentication no in your sshd…</description>
    <content:encoded>&lt;p&gt;&lt;strong&gt;If you run &lt;code&gt;sshd&lt;/code&gt; on Alpine Linux, update &lt;code&gt;openssh&lt;/code&gt; now and restart &lt;code&gt;sshd&lt;/code&gt;.&lt;/strong&gt; Upgrading the package alone does not replace the running daemon.&lt;/p&gt;
&lt;p&gt;If you cannot update yet, at least disable password logins: set &lt;code&gt;PasswordAuthentication no&lt;/code&gt; in your sshd configuration and restart &lt;code&gt;sshd&lt;/code&gt;. Either one closes the hole.&lt;/p&gt;
&lt;h2&gt;What is the problem?&lt;/h2&gt;
&lt;p&gt;An attacker who can reach your SSH port can log in without knowing any password. It takes one connection, works every time, and needs no retries.&lt;/p&gt;
&lt;p&gt;The attacker needs the name of an account whose password field is &lt;code&gt;*&lt;/code&gt; (an account with no password set, which is normal for key-only accounts), and needs the machine to have at least one other account with a &lt;code&gt;$5$&lt;/code&gt; or &lt;code&gt;$6$&lt;/code&gt; password hash. The attacker then sends a very long password (257 to 1024 bytes). One user with a password is enough: it makes every account without one reachable.&lt;/p&gt;
&lt;p&gt;Details are in the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.openssh.com/releasenotes.html&quot;&gt;OpenSSH 10.6p2 release notes&lt;/a&gt;. No CVE identifier had been assigned at the time of writing.&lt;/p&gt;
&lt;h2&gt;Who is affected?&lt;/h2&gt;
&lt;p&gt;You are affected if all of the following are true:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You run the Alpine &lt;code&gt;openssh-server&lt;/code&gt; package, which is built without PAM, or you run &lt;code&gt;openssh-server-pam&lt;/code&gt; with &lt;code&gt;UsePAM no&lt;/code&gt;. (&lt;code&gt;openssh-server-pam&lt;/code&gt; with &lt;code&gt;UsePAM yes&lt;/code&gt; is not affected.)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PasswordAuthentication&lt;/code&gt; is &lt;code&gt;yes&lt;/code&gt; (the OpenSSH default, unless you changed it).&lt;/li&gt;
&lt;li&gt;The machine has an account whose password field is &lt;code&gt;*&lt;/code&gt;. This includes:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;root&lt;/code&gt;, which ships as &lt;code&gt;root:*&lt;/code&gt; in the base image since about Alpine 3.16. OpenSSH’s default &lt;code&gt;PermitRootLogin prohibit-password&lt;/code&gt; protects root, so root is only reachable if &lt;code&gt;PermitRootLogin yes&lt;/code&gt; is set. That does not help if another account that can log in is able to become root, for example through &lt;code&gt;doas&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Any key-only user created with &lt;code&gt;adduser&lt;/code&gt; without a password, or with &lt;code&gt;usermod -p &#39;*&#39;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Users coming from a directory service through musl-nscd (nslcd, winbind, sssd), since they have no shadow entry.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;alpine&lt;/code&gt; user in the official &lt;strong&gt;tiny-cloud&lt;/strong&gt; images (see below).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;At least one local account has a &lt;code&gt;$5$&lt;/code&gt; or &lt;code&gt;$6$&lt;/code&gt; password hash.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Affected releases: every Alpine release we checked, 3.21 through 3.24 and edge, and the older releases that no longer receive regular updates (3.16 to 3.20).&lt;/p&gt;
&lt;h3&gt;Who is not affected?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Docker and other container users&lt;/strong&gt; that do not run &lt;code&gt;sshd&lt;/code&gt; in the container.&lt;/li&gt;
&lt;li&gt;Systems with &lt;code&gt;PasswordAuthentication no&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Systems using &lt;code&gt;openssh-server-pam&lt;/code&gt; with &lt;code&gt;UsePAM yes&lt;/code&gt; (with &lt;code&gt;UsePAM no&lt;/code&gt; you are affected).&lt;/li&gt;
&lt;li&gt;Locked accounts: those with &lt;code&gt;!&lt;/code&gt; as the password field in &lt;code&gt;/etc/shadow&lt;/code&gt; (a line starting &lt;code&gt;user:!:&lt;/code&gt;). They cannot log in with a password or a key.&lt;/li&gt;
&lt;li&gt;Systems where no account has a &lt;code&gt;$&lt;/code&gt;-style password hash.&lt;/li&gt;
&lt;li&gt;Cloud images configured through cloud-init, which writes &lt;code&gt;PasswordAuthentication no&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;How can I fix it?&lt;/h2&gt;
&lt;h3&gt;1. Update&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot;&gt;apk upgrade -U -a
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alpine 3.21.10, 3.22.8, 3.23.8 and 3.24.4 contain the fixed package (see the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://alpinelinux.org/posts/alpine-3.21.10-3.22.8-3.23.8-3.24.4-released/&quot;&gt;release announcement&lt;/a&gt;). The fixed package versions are:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Branch&lt;/th&gt;
&lt;th&gt;Fixed &lt;code&gt;openssh&lt;/code&gt; version&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;edge&lt;/td&gt;
&lt;td&gt;10.6_p2-r0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3.24&lt;/td&gt;
&lt;td&gt;10.3_p1-r2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3.23&lt;/td&gt;
&lt;td&gt;10.2_p1-r1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3.22&lt;/td&gt;
&lt;td&gt;10.0_p1-r11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3.21&lt;/td&gt;
&lt;td&gt;9.9_p2-r1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3.20&lt;/td&gt;
&lt;td&gt;9.7_p1-r6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3.19&lt;/td&gt;
&lt;td&gt;9.6_p1-r3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3.18&lt;/td&gt;
&lt;td&gt;9.3_p2-r4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3.17&lt;/td&gt;
&lt;td&gt;9.1_p1-r7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3.16&lt;/td&gt;
&lt;td&gt;9.0_p1-r6&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Restart &lt;code&gt;sshd&lt;/code&gt; after upgrading&lt;/strong&gt; (&lt;code&gt;rc-service sshd restart&lt;/code&gt;). Upgrading the package does not restart a running &lt;code&gt;sshd&lt;/code&gt;, so you must do it after.&lt;/p&gt;
&lt;h3&gt;2. If you cannot update: disable password authentication&lt;/h3&gt;
&lt;p&gt;Before you start, make sure your SSH key login works, and keep your current session open until you have tested a new login.&lt;/p&gt;
&lt;p&gt;Run these as root. The first two commands create a drop-in file. Alpine’s default &lt;code&gt;sshd_config&lt;/code&gt; reads that directory from 3.19 onward; older releases ignore it. The &lt;code&gt;sed&lt;/code&gt; command changes &lt;code&gt;yes&lt;/code&gt; to &lt;code&gt;no&lt;/code&gt; on every &lt;code&gt;PasswordAuthentication&lt;/code&gt; line in &lt;code&gt;/etc/ssh/sshd_config&lt;/code&gt;, whatever the spacing, including lines inside a &lt;code&gt;Match&lt;/code&gt; block. It also adds &lt;code&gt;PasswordAuthentication no&lt;/code&gt; at the top of the file, for the case where there was no such line, and saves the original as &lt;code&gt;/etc/ssh/sshd_config.bak&lt;/code&gt;. The last two commands check the configuration, restart the daemon, and show the result:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;code-snippet&quot; data-language=&quot;Shell&quot;&gt;&lt;span class=&quot;syntax-source syntax-shell syntax-bash&quot;&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;mkdir&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt; -&lt;/span&gt;p&lt;/span&gt; /etc/ssh/sshd_config.d&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-support syntax-function syntax-echo syntax-shell&quot;&gt;echo&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; &lt;span class=&quot;syntax-string syntax-quoted syntax-single syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;#39;&lt;/span&gt;PasswordAuthentication no&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;#39;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-assignment syntax-redirection syntax-shell&quot;&gt;&amp;gt;&lt;/span&gt; /etc/ssh/sshd_config.d/00-no-passwords.conf&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;sed&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt; -&lt;/span&gt;i&lt;/span&gt;.bak&lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt; -&lt;/span&gt;e&lt;/span&gt; &lt;span class=&quot;syntax-string syntax-quoted syntax-single syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;#39;&lt;/span&gt;1i PasswordAuthentication no&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;#39;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-punctuation syntax-separator syntax-continuation syntax-line syntax-shell&quot;&gt;\
&lt;/span&gt;&lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt;  -&lt;/span&gt;e&lt;/span&gt; &lt;span class=&quot;syntax-string syntax-quoted syntax-single syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-begin syntax-shell&quot;&gt;&amp;#39;&lt;/span&gt;s/^\(\s*PasswordAuthentication[^y#]*\)yes/\1no/i&lt;span class=&quot;syntax-punctuation syntax-definition syntax-string syntax-end syntax-shell&quot;&gt;&amp;#39;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-punctuation syntax-separator syntax-continuation syntax-line syntax-shell&quot;&gt;\
&lt;/span&gt;  /etc/ssh/sshd_config&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;sshd&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt; -&lt;/span&gt;t&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-logical syntax-and syntax-shell&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;rc-service&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt; sshd restart&lt;/span&gt;
&lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;sshd&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt; -&lt;/span&gt;T&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;syntax-keyword syntax-operator syntax-logical syntax-pipe syntax-shell&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;syntax-meta syntax-function-call syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-function syntax-shell&quot;&gt;grep&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;syntax-meta syntax-function-call syntax-arguments syntax-shell&quot;&gt;&lt;span class=&quot;syntax-variable syntax-parameter syntax-option syntax-shell&quot;&gt;&lt;span class=&quot;syntax-punctuation syntax-definition syntax-parameter syntax-shell&quot;&gt; -&lt;/span&gt;i&lt;/span&gt; passwordauthentication&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The last command should print &lt;code&gt;passwordauthentication no&lt;/code&gt;. To undo, delete &lt;code&gt;/etc/ssh/sshd_config.d/00-no-passwords.conf&lt;/code&gt; and copy &lt;code&gt;/etc/ssh/sshd_config.bak&lt;/code&gt; back over &lt;code&gt;/etc/ssh/sshd_config&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Cloud images (tiny-cloud)&lt;/h2&gt;
&lt;p&gt;The official tiny-cloud images (the qcow2, VHD and AMI artifacts) from &lt;strong&gt;3.18.11 through 3.24.3&lt;/strong&gt; ship an &lt;code&gt;alpine&lt;/code&gt; user with a &lt;code&gt;*&lt;/code&gt; password field and passwordless &lt;code&gt;doas&lt;/code&gt;. They are safe on first boot. They become exploitable as soon as any &lt;code&gt;$6$&lt;/code&gt; hash exists on the machine, for example when an admin sets a console password, or user-data adds a user with &lt;code&gt;hashed_passwd&lt;/code&gt;. An attacker then gets a shell as &lt;code&gt;alpine&lt;/code&gt; and can use &lt;code&gt;doas&lt;/code&gt; to become root.&lt;/p&gt;
&lt;p&gt;Images built from 3.21.10, 3.22.8, 3.23.8, 3.24.4 and later include the fixed package. Images from earlier point releases do not, so on every running instance, update &lt;code&gt;openssh&lt;/code&gt; or disable password authentication.&lt;/p&gt;
&lt;h2&gt;Releases that are no longer supported (3.21 and older)&lt;/h2&gt;
&lt;p&gt;These releases have the same bug. 3.21 reached its end of support on 2026-11-01, but we have published fixed packages for each release all the way back to 3.16.&lt;/p&gt;
&lt;h2&gt;How do I know if I was attacked?&lt;/h2&gt;
&lt;p&gt;Look in &lt;code&gt;/var/log/messages&lt;/code&gt; for &lt;code&gt;sshd&lt;/code&gt; lines reading &lt;code&gt;Accepted password for&lt;/code&gt; an account that has no password (&lt;code&gt;alpine&lt;/code&gt;, &lt;code&gt;root&lt;/code&gt;, or any key-only user). Review &lt;code&gt;doas&lt;/code&gt; use and recently created users, files and authorized keys on exposed machines. If you cannot rule out a login, treat the machine as compromised.&lt;/p&gt;
&lt;h2&gt;How could this happen?&lt;/h2&gt;
&lt;p&gt;When sshd is built without PAM, it checks a password by hashing the attempt with &lt;code&gt;crypt()&lt;/code&gt; (the C library function that turns a password into its stored hash) and comparing the result to the stored password field. For an account whose stored field is too short to contain a salt (the random prefix of a hash), sshd borrows the salt of another account.&lt;/p&gt;
&lt;p&gt;musl’s &lt;code&gt;crypt()&lt;/code&gt; returns the string &lt;code&gt;*&lt;/code&gt; as an error indicator when the key is over 256 bytes long. &lt;code&gt;*&lt;/code&gt; is also exactly what the stored field contains for every account with no password, so the “hash” matches and the login succeeds.&lt;/p&gt;
&lt;p&gt;OpenSSH 10.6p2 avoids the problem in sshd. The stable-branch packages above carry a patch for the same problem.&lt;/p&gt;
&lt;h2&gt;Credits&lt;/h2&gt;
&lt;p&gt;Reported by Özgün Kültekin of &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://wakefield.co&quot;&gt;Wakefield&lt;/a&gt;. Thanks to Damien Miller and Darren Tucker of the OpenSSH project for the fix and the 10.6p2 release, to Timo Teräs for help analyzing the issue, and to Natanael Copa, who built and uploaded the updated packages.&lt;/p&gt;
</content:encoded>
    <category>news</category>
  </item>
  <item>
    <title>GitLab Security Incident: Attempted Exploitation of CVE-2026-97229</title>
    <link>https://aggr.aymericbeaumet.com/items/hnrss-org/2026-10-28-gitlab-security-incident-attempted-exploitation-of-cve-2026/</link>
    <guid isPermaLink="false">urn:aggr:item:ad17fb97ee54444544b4c23fe32313f0615c6d46</guid>
    <atom:link href="https://alpinelinux.org/posts/GitLab-security-incident-CVE-2026-97229.html" rel="via" type="text/html"/>
    <pubDate>Wed, 28 Oct 2026 23:45:46 +0000</pubDate>
    <dc:creator>sashk</dc:creator>
    <description>On October 20 and 21, an attacker tried to exploit a GitLab vulnerability (CVE-2026-97229) on our instance, a week after the fix was released. The attack came close to working. We have found no evidence that it succeeded, but we cannot…</description>
    <content:encoded>&lt;p&gt;On October 20 and 21, an attacker tried to exploit a GitLab vulnerability (CVE-2026-97229) on our instance, a week after the fix was released. The attack came close to working. We have found no evidence that it succeeded, but we cannot rule it out, so we treated the instance as compromised and rebuilt it from scratch. Our checks show that the aports repository, packages, and the October 21 releases were not tampered with.&lt;/p&gt;
&lt;p&gt;GitLab is being tested before we open it to the public again. All passwords have been reset, so when it is back you will need to set a new one with “Forgot your password?” on the sign-in page. We will post exactly what you need to do when it reopens. In the meantime: if you used the same password for your gitlab.alpinelinux.org account anywhere else, change it there. We have seen automated login attempts against our GitLab with passwords leaked from other sites.&lt;/p&gt;
&lt;p&gt;If you have questions or concerns please reach out to us at &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;mailto:infra@alpinelinux.org&quot;&gt;infra@alpinelinux.org&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;What happened&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;The vulnerability:&lt;/strong&gt; CVE-2026-97229 is a critical (CVSS 10.0) vulnerability in how GitLab evaluates regular expressions in CI configuration. The underlying bug is in Onigmo, the regular expression engine built into Ruby. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://docs.gitlab.com/releases/patches/patch-release-gitlab-19-4-2-released/&quot;&gt;GitLab published fixed releases on October 12&lt;/a&gt;, but the fix itself is a set of patches to the Ruby that GitLab bundles in its own packages and container images. Upstream Ruby &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.ruby-lang.org/en/news/2026/10/23/ruby-3-3-13-released/&quot;&gt;shipped the fix on October 23&lt;/a&gt;, in Ruby 3.3.13, 3.4.12 and 4.0.9.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The attack:&lt;/strong&gt; On October 20 and 21, a newly registered account sent crafted CI configurations to our GitLab instance, which was still running 19.3.4 and did not have the October 12 fix. This crashed GitLab worker processes 8 times. In 4 of those crashes the attacker got partway through the exploit and failed at the next step. The attacker also downloaded our GitLab container image, which suggests they were tuning the exploit to our exact build. We have found no evidence that the attacker ran code on the server, but we cannot rule it out.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Our upgrade didn’t fix the vulnerability:&lt;/strong&gt; Upgrading to GitLab 19.3.6 on October 22 didn’t fix the vulnerability. The fix isn’t in GitLab’s own code but in a patch to Ruby, which GitLab applies only in its official packages. Our image used standard Ruby, so it didn’t get the patch.&lt;/p&gt;
&lt;p&gt;If you run GitLab with your own Ruby (from source or in your own container image) rather than GitLab’s packages, upgrading GitLab is not enough: you also need Ruby 3.3.13, 3.4.12, 4.0.9 or later.&lt;/p&gt;
&lt;p&gt;If you use our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://hub.docker.com/r/alpinelinux/gitlab&quot;&gt;alpinelinux/gitlab&lt;/a&gt; container image, pull &lt;code&gt;alpinelinux/gitlab:19.3.6&lt;/code&gt; again. The image published on October 27 contains Ruby 3.3.13. Any 19.3.6 image you pulled before October 27, and all older tags, are vulnerable.&lt;/p&gt;
&lt;h3&gt;Incident Timeline&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Oct 28&lt;/td&gt;
&lt;td&gt;GitLab rebuild complete; all passwords reset and GitLab&#39;s secret keys replaced&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oct 27&lt;/td&gt;
&lt;td&gt;GitLab closed to everyone except developers and build servers; rebuild started&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oct 26&lt;/td&gt;
&lt;td&gt;Found the instance still vulnerable after the upgrade&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oct 25 to 26&lt;/td&gt;
&lt;td&gt;Attacker returns and tries to get back in; attempts fail&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oct 24&lt;/td&gt;
&lt;td&gt;aports, packages and Oct 21 releases verified against outside records&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oct 23&lt;/td&gt;
&lt;td&gt;Attacker&#39;s account blocked; Ruby releases the fix&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oct 22&lt;/td&gt;
&lt;td&gt;Upgraded to GitLab 19.3.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oct 21&lt;/td&gt;
&lt;td&gt;Main attack; GitLab crashes, 502 errors and failed CI jobs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oct 20&lt;/td&gt;
&lt;td&gt;Attacker registers an account and starts probing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oct 13&lt;/td&gt;
&lt;td&gt;CVE-2026-97229 published; we upgraded to GitLab 19.3.4 instead of 19.3.5 (which had the fix) by mistake&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Oct 12&lt;/td&gt;
&lt;td&gt;GitLab releases the fix&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;What we know and what we can’t rule out&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;What we found&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Alpine releases and packages were not modified.&lt;/strong&gt; The October 21 stable releases (3.21.9, 3.22.7, 3.23.7, 3.24.3), including their ISO images and the minirootfs tarballs the official &lt;code&gt;alpine&lt;/code&gt; Docker images are built from, and all packages built from October 20 to 24 came from aports commits we verified against records outside GitLab. See the FAQ below for details.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No signing keys were exposed.&lt;/strong&gt; The keys that sign Alpine packages and releases are not, and never have been, stored on the GitLab server.&lt;/li&gt;
&lt;li&gt;No evidence that the attacker’s code ran: timing probes showed no delays, and we found no unexpected files, keys, hooks or processes.&lt;/li&gt;
&lt;li&gt;No private data was returned to the attacker through any web or API request we can see.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Why we can’t be sure&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A successful exploit would leave no crash and no trace in the web logs.&lt;/li&gt;
&lt;li&gt;Some logs from the attack day are gone (background job logs, the old container’s filesystem).&lt;/li&gt;
&lt;li&gt;We had no logging of outbound network connections, and our hosting provider keeps none either.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;What could have been exposed if the attack succeeded&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Email addresses, names and IP addresses of all users&lt;/li&gt;
&lt;li&gt;Password hashes&lt;/li&gt;
&lt;li&gt;2FA secrets, CI/CD variables and runner tokens&lt;/li&gt;
&lt;li&gt;Webhook, integration and push mirror credentials&lt;/li&gt;
&lt;li&gt;Personal access tokens (stored as hashes)&lt;/li&gt;
&lt;li&gt;Private repositories, and confidential issues and merge requests&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;FAQ&lt;/h2&gt;
&lt;h3&gt;Why did we rebuild GitLab if we were able to mitigate the attack?&lt;/h3&gt;
&lt;p&gt;Because we cannot rule out code execution, and the evidence we would need to confirm it no longer exists, we took the harder route and rebuilt the GitLab instance from scratch. This removes any foothold a possibly successful exploit could have left behind.&lt;/p&gt;
&lt;p&gt;While GitLab is closed, our package mirrors, cgit, and mailing lists/IRC are available as normal.&lt;/p&gt;
&lt;h3&gt;Are Alpine packages and releases affected?&lt;/h3&gt;
&lt;p&gt;We have found no sign of tampering in aports or the releases.&lt;/p&gt;
&lt;p&gt;Every aports commit added October 20 to 22 arrived through a recorded push by a known developer. We checked this against records the attacker could not reach: a developer’s clone and all 45 builders. Every package built from October 20 to 24, on every architecture, came from commits as published on git.alpinelinux.org. The October 21 releases (3.21.9, 3.22.7, 3.23.7, 3.24.3) were built from those same verified commits.&lt;/p&gt;
&lt;p&gt;We ran these checks on October 24, covering commits from October 20 to 22 and packages built up to October 24. Our GitLab stayed vulnerable until we closed it on October 27, so we are extending the same checks to that date.&lt;/p&gt;
&lt;h3&gt;Is Alpine’s ruby package affected?&lt;/h3&gt;
&lt;p&gt;The bug is in Ruby’s regular expression engine, so any program that compiles regular expressions from untrusted input is exposed, not only GitLab. Alpine 3.22, 3.23, 3.24 and edge ship Ruby 3.4.12, and Alpine 3.21 ships Ruby 3.3.13; both include the fix. Run &lt;code&gt;apk upgrade&lt;/code&gt; to get it.&lt;/p&gt;
&lt;h3&gt;What are we changing in response to this event?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Fixed Ruby:&lt;/strong&gt; the rebuilt instance runs a Ruby with the fix (3.3.13 or later).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Faster patching:&lt;/strong&gt; apply critical GitLab security releases within a day, and check each release for Ruby or other component patches our own build must apply.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Better logging:&lt;/strong&gt; record outbound connections from the GitLab host and database connections, and keep logs longer than 14 days.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Signed release tags&lt;/strong&gt;, so releases can be verified independently of GitLab.&lt;/li&gt;
&lt;/ul&gt;
</content:encoded>
    <category>news</category>
  </item>
</channel>
</rss>
