GitLabにCVSS 10.0の脆弱性、.orgファイルの表示から未認証でコード実行
目次
TL;DR
影響 セルフマネージド(自分のサーバーで動かす)GitLab CE/EE(コミュニティ版・エンタープライズ版)の7.0以降すべて。.org ファイルの表示処理から、ログインなしでGitLabサーバー上のコードを実行される(CVE-2026-110264、CVSS 10.0)
対応 19.5.1、19.4.3、19.3.6、19.2.9、19.1.9、19.0.10、18.11.13のどれかへ更新。18.10以前はアップグレードパスに沿って18.11.13以降へ上げる。10月12日の修正版(19.4.2など)を入れていても、もう一度更新が必要
SaaS GitLab.comは修正済み。GitLab Dedicatedは19.3.5-internal0か19.4.2-internal0以降なら対応不要
2026年10月21日、GitLabがCritical Patch Release(定期リリースとは別に出す緊急の修正版)として19.5.1、19.4.3、19.3.6、19.2.9、19.1.9、19.0.10、18.11.13を公開した(GitLab Critical Patch Release: 19.5.1, 19.4.3, 19.3.6, 19.2.9, 19.1.9, 19.0.10, 18.11.13)。
セキュリティ修正は12件で、深刻度の4段階(Critical、High、Medium、Low)で最も高いCriticalはCVE-2026-110264の1件で、次のHighが2件ある。
CVE-2026-110264は、正規表現をコンパイルするときの整数オーバーフロー(数値が入れ物の上限を超えて回り込むこと)で、条件がそろうと認証なしのユーザーがGitLabサーバー上で任意のコードを実行できる。
CVSSは10.0(AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H)で、ネットワーク越しに、ログインも利用者の操作もなしに攻撃でき、影響はGitLab本体の外にも及ぶと評価されている。
影響範囲は7.0以降の全バージョンで、通常のサポート対象(19.3〜19.5)に加えて、19.2、19.1、19.0、18.11にも修正がバックポート(古い系列へ修正を移植すること)された。
1か月で3件目の正規表現まわりの脆弱性
GitLabでは9月から、Rubyの正規表現エンジンOnigmoに攻撃者の入力を流し込む脆弱性が続けて直っている。
3件ともHackerOneのバグ報奨金プログラム経由で、同じjoaxcar氏が報告した。
| 公開日 | CVE | CVSS | 入口 | 修正版 |
|---|---|---|---|---|
| 2026年9月23日 | CVE-2026-93577 | 9.9(要ログイン) | CI/CD設定の正規表現 | 19.4.1、19.3.3、19.2.7 |
| 2026年10月12日 | CVE-2026-97229 | 10.0(ログイン不要) | CI/CD設定の正規表現 | 19.4.2、19.3.5、19.2.8 |
| 2026年10月21日 | CVE-2026-110264 | 10.0(ログイン不要) | .org ファイルの表示 | 19.5.1、19.4.3、19.3.6、19.2.9、19.1.9、19.0.10、18.11.13 |
出典は各リリースの告知(19.4.1、19.4.2、19.5.1)。
GitLabのセキュリティチームが公開した検知ルールの説明では、CVE-2026-97229をCVE-2026-93577のバイパス(修正をすり抜ける別の攻撃方法)と書いている(Onigmo native worker crash)。
CI/CDの2件は、rules: の regexp に書いたパターンのコンパイル結果が2GiBを超え、符号付き32ビットのジャンプ先がマイナスに回り込んでいた。
影響範囲も19.2以降に限られていた。
CVE-2026-110264の入口は、リポジトリ内の .org ファイルを画面に表示する処理で、影響範囲も7.0以降まで広い。
10月12日の修正版(19.4.2など)も影響範囲に入っているので、前回の更新を済ませた環境も、もう一度更新の対象になる。
.orgファイルからコード実行に至るまで
.org は、EmacsのOrg-modeで使われるテキスト形式で、GitLabはリポジトリ内の .org ファイルを org-ruby というgem(Rubyのライブラリ)でHTMLに変換して表示する。
Org-modeには、見出しの先頭に TODO や DONE のような状態を表すキーワードを置く書き方があり、使うキーワードはファイル内の #+TODO: 行で自由に定義できる。
GitLabの検知ルールの説明(Org mode render error burst)によると、org-ruby 0.9.12はキーワードの一覧から正規表現を作るときはエスケープしている。
ところが、見出しからキーワードを取り除く2回目の処理では、キーワードをエスケープせずに Regexp.new へ渡していた。
gemの中身を見ると、lib/org-ruby/headline.rb の99行目はこうなっている。
@headline_text.sub!(Regexp.new("^#{@keyword}\s*"), "")
#+TODO: 行に書いた文字列がそのまま正規表現のパターンになるので、.org ファイルを置ける人なら、GitLabにコンパイルさせる正規表現を自由に決められる。
Onigmoは、{0,100} のような回数指定の繰り返しに番号(繰り返しID)を振り、コンパイル結果には符号付き16ビットで書き込む。
繰り返しが32,767個を超えると、番号がマイナスに回り込む。
照合のときはその番号でスタックの位置を決めるので、確保した領域の外へ書き込んでしまう(範囲外書き込み)。
検知ルールの説明では、リクエストの内容でメモリの配置を整えると、これを system() の呼び出しにつなげられ、GitLabの git ユーザーの権限でコマンドを実行できるという。
ログインなしで攻撃でき、ペイロード(攻撃用の .org ファイル)を置いた公開プロジェクトが1つあればよい、とも書いている。
graph TD
A[".orgファイルを表示する"] --> B["org-rubyが #+TODO: の<br/>キーワードを読む"]
B --> C["キーワードをエスケープせず<br/>Regexp.new に渡す"]
C --> D["Onigmoが繰り返しIDを<br/>16ビットで記録する"]
D --> E["32,767個を超えると<br/>IDがマイナスになる"]
E --> F["照合時に範囲外へ書き込む"]
F --> G["gitユーザーで<br/>コマンド実行"]
.org を表示する画面だけでなく、Web上のエディターのプレビューも入口になる。
プレビューは渡された file_path のファイル名で表示方法を選ぶので、.md を編集していても file_path に .org を指定すると、Org-modeの表示処理が走る。
検知ルールのサンプルでは、こちらはログイン済みのユーザー(user/42)として記録されている。
修正はGitLabに同梱するRubyへのパッチ
19.5.1のバグ修正の一覧には、Add Ruby regexp repeat ID overflow patch for 3.3, 3.4 and 4.0 という項目があり、19.4.3、19.3.6、19.2.9の告知にも同じ項目が載っている。
GitLabは、Omnibus(GitLabの公式パッケージ)に同梱するRubyにパッチを当てて直した。
omnibus-gitlabのコミット(Add Ruby regexp repeat ID overflow patch for 3.3, 3.4 and 4.0)によると、パッチの内容はRuby本体のPR #16801「Reject overflowed repeat/null-check IDs in huge regexps」をそのまま移植したものだ。
繰り返しIDか空チェックID(空文字列に一致する繰り返しの無限ループを防ぐための番号)が上限を超えたら、コンパイルの時点で RegexpError にする。
このPRは2026年4月29日にRubyのmasterへマージされたが、コミットメッセージには、修正はmaster(Ruby 4.1)にしかなく、ruby_3_3、ruby_3_4、ruby_4_0 のブランチには入っていないと書かれている。
修正済みのRubyなら、PR #16801の回帰テスト(同じバグの再発を確かめるテスト)のパターンはコンパイルの時点で too many range repeat、too many null check の RegexpError になる。
手元のM4 Mac miniでは、GitLabとは関係なくHomebrewで入れたRuby 4.0.6に同じパターンを Regexp.new で渡し、コンパイルが通るかどうかを試した。
{ "range repeat" => '(?:foobar){0,100}' * 100000,
"null check" => '(?:(?:foo)?|(?:bar)?)*' * 100000 }.each do |name, src|
begin
Regexp.new(src)
puts "#{name}: compiled (no error)"
rescue RegexpError => e
puts "#{name}: RegexpError: #{e.message}"
end
end
ruby 4.0.6 (2026-07-14 revision 03b6d3f889) +PRISM [arm64-darwin25]
range repeat: compiled (no error)
null check: compiled (no error)
2つともエラーにならずにコンパイルできた。
修正がRuby本体のmasterにしかない以上、外から受け取った文字列から Regexp.new でパターンを組み立てるRubyのアプリは、GitLab以外でも同じ影響を受けうる。手元で確かめたのは4.0.6だけだ。
ソースからGitLabをインストールしてシステムのRubyを使っている場合、GitLabを更新しても、そのRubyにはこのパッチが当たらない。
告知には、ソースインストール向けの手順は載っていない。
GitLabのバージョンと更新先
Omnibusでインストールした場合とDockerで動かしている場合は、次のどちらかでバージョンが分かる。
ブラウザで管理者としてログインし、/help を開いても表示される。
# Omnibus
sudo gitlab-rake gitlab:env:info | grep -A1 '^GitLab information'
cat /opt/gitlab/embedded/service/gitlab-rails/VERSION
# Docker(コンテナー名がgitlabの場合)
docker exec gitlab cat /opt/gitlab/embedded/service/gitlab-rails/VERSION
| 現在のバージョン | 更新先 |
|---|---|
| 19.5.0 | 19.5.1 |
| 19.4.0〜19.4.2 | 19.4.3 |
| 19.3.0〜19.3.5 | 19.3.6 |
| 19.2.0〜19.2.8 | 19.2.9 |
| 19.1.0〜19.1.8 | 19.1.9 |
| 19.0.0〜19.0.9 | 19.0.10 |
| 18.11.0〜18.11.12 | 18.11.13 |
| 18.10以前(7.0以降) | 修正版なし。アップグレードパスに沿って18.11.13以降へ |
18.10以前には修正版がないので、メジャー・マイナーバージョンを上げるしかない。
GitLabには、途中で飛ばせない「必須の停止点」があり、18系では18.2、18.5、18.8、18.11、19系では19.2、19.5がそれにあたる(Upgrade paths)。
停止点ごとに、バックグラウンドで走るマイグレーション(データベースの構造やデータを新しい版に合わせる処理)が終わるのを待ってから次へ進むので、古いバージョンからだと時間がかかる。
現在のバージョンと更新先を入れると経由する版を出してくれるUpgrade Path toolがある。
19.4.3と19.3.6には、更新中に走るデータベースのマイグレーションが含まれている。
1台構成のGitLabでは、マイグレーションが終わるまで起動しないので、更新中は止まる。
複数台構成なら、ゼロダウンタイムの手順で止めずに更新できる。
ログに残る攻撃の痕跡
GitLabは今回の告知で、セルフマネージド向けに2つの検知ルールをSigma形式(ログ検知ルールの共通の書式)で公開した。
SIEM(ログを集めて分析する仕組み)を使っていなくても、Omnibusなら同じ条件でログを grep できる。
| 検知ルール | 見るログ(Omnibus) | 条件 |
|---|---|---|
| Org mode render error burst | /var/log/gitlab/gitlab-rails/exceptions_json.log | .org ファイルの表示で Timeout::Error が出て、同じ接続元から30分以内に5回以上 |
| Onigmo native worker crash | /var/log/gitlab/puma/puma_stderr.log | Rubyのクラッシュ報告 [BUG] に、org-ruby か match_at、onig_compile などOnigmoの関数名が含まれる |
検知ルールの説明によると、攻撃は失敗することが多く、試すたびに表示処理が時間切れになるか、ワーカー(リクエストを処理するプロセス)ごと落ちる。
時間切れのエラーは exceptions_json.log に記録される。
検知ルールは、Markdown以外の書式を表示する処理(OtherMarkup)で起きた、.org ファイルの時間切れに絞っている。
sudo grep '"exception.class":"Timeout::Error"' /var/log/gitlab/gitlab-rails/exceptions_json.log \
| grep '"extra.class_name":"OtherMarkup"' \
| grep '\.org"'
当てはまる行があれば、meta.client_id(ログインしていなければ ip/<IPアドレス>)と meta.project を見て、どこからどのプロジェクトの .org ファイルが表示されたかを確かめる。
ワーカーが落ちた場合は、Rubyが [BUG] から始まるクラッシュ報告を書き出す。
sudo grep -n -A30 '\[BUG\]' /var/log/gitlab/puma/puma_stderr.log \
| grep -E 'org-ruby|headline\.rb|match_at|onig_search_gpos|onig_compile|compile_tree'
9月と10月12日に直ったCI/CDの2件は、パイプライン経由で攻撃されるとSidekiq(バックグラウンドジョブを処理するプロセス)のワーカーが落ちる。
こちらは /var/log/gitlab/sidekiq/current に記録される。
検知ルールの説明には、成功した攻撃ではログに何も出ないこともあり、クラッシュするとも限らない、と書いている。
ログに何も出なかったからといって、攻撃されていないとは言えない。
puma_stderr.log は既定ではSIEMへ送られていないことが多いとも書いていて、ログ収集の設定によってはクラッシュ報告がSIEMに届かない。
同時に直ったほかのセキュリティ修正11件
今回のリリースに含まれるセキュリティ修正12件のうち、CVE-2026-110264以外の11件にもHighが2件ある。
| CVE | 内容 | CVSS | 影響範囲 |
|---|---|---|---|
| CVE-2026-15723 | OmniAuthのSAMLとLDAPの自動リンクを両方有効にしていると、ログイン済みのユーザーが既存のLDAPアカウントを乗っ取れる | 8.2 | 10.1以降 |
| CVE-2026-112212 | OAuth/OIDCのディスカバリー(認証サーバーの設定情報を返すURL)の応答を共有キャッシュに毒入れし、外部のサーバーへ誘導できる | 7.5 | 18.3以降 |
この2件と残りのMedium以下9件は、19.3.6、19.4.3、19.5.1で直っている。
19.2.9以前のバックポート版は、告知の書き方ではCVE-2026-110264だけが対象で、ほかの11件の修正は入っていない。
回避策と悪用状況
告知には、更新できない場合の回避策は載っていない。
Org-modeの表示だけを無効にする設定も、GitLabの資料には見当たらなかった。
実際の攻撃に使われたかどうかについては、告知に記載がない。
米国CISAの、悪用が確認された脆弱性の一覧(KEV)にも、2026年10月21日(米国時間)更新の版の時点では載っていない。
検知ルールの説明は、GitLabの検証環境で再現したときのログをもとに書かれていて、そのうち1件は、手元で試したのと同じarm64のmacOS(arm64-darwin)でのクラッシュ(Bus Error)だった。