407 lines
16 KiB
Markdown
407 lines
16 KiB
Markdown
# SuperFunnels AI — Full Authenticated Pentest Report
|
||
|
||
**Date:** 2026-02-07
|
||
**Target:** https://app.superfunnelsai.com
|
||
**Tester:** Jake Shore (authorized owner)
|
||
**Account:** User ID 204 (sftesta6577921@virgilian.com)
|
||
**Plan:** Free (Plan ID 3, 1 token remaining)
|
||
|
||
---
|
||
|
||
## Executive Summary
|
||
|
||
SuperFunnels AI is a Laravel/Filament application that generates AI-powered funnels for GoHighLevel (GHL). The pentest uncovered **several critical and high-severity vulnerabilities** including a wildcard CORS misconfiguration exposing user data, potential SSRF via the funnel clone API, excessive user data exposure, and sensitive infrastructure leakage.
|
||
|
||
### Severity Matrix
|
||
|
||
| # | Finding | Severity | CVSS Est. |
|
||
|---|---------|----------|-----------|
|
||
| 1 | Wildcard CORS on `/api/user` with credentials | **CRITICAL** | 9.1 |
|
||
| 2 | Full user data exposure via `/api/user` (IP, session ID, billing) | **CRITICAL** | 8.6 |
|
||
| 3 | Potential SSRF via `/api/funnel-clone` sourceUrl | **HIGH** | 8.0 |
|
||
| 4 | No input sanitization on `businessName` (XSS stored) | **HIGH** | 7.5 |
|
||
| 5 | WebSocket/Reverb key & infrastructure leak | **MEDIUM** | 5.3 |
|
||
| 6 | Exposed Laravel Horizon & Log Viewer (403) | **LOW** | 3.1 |
|
||
| 7 | Outdated Livewire assets | **LOW** | 2.0 |
|
||
| 8 | Broken WebSocket SSL (ws.app.theagencytoolkit.com) | **INFO** | — |
|
||
|
||
---
|
||
|
||
## Phase 1: Authentication & Session Analysis
|
||
|
||
### 1.1 Login Flow
|
||
- **URL:** https://app.superfunnelsai.com/app/login
|
||
- **Framework:** Laravel + Filament v3.3.47 + Livewire
|
||
- **Session:** Cookie-based (Laravel Sanctum)
|
||
- **CSRF:** Token in meta tag + XSRF-TOKEN cookie
|
||
- **Screenshot:** `step1-dashboard-ghl-connect.png`
|
||
|
||
### 1.2 Account Details Discovered
|
||
The account was already authenticated from a prior session. The registered email differs from the provided credentials:
|
||
- **Display Name:** Jake Shore
|
||
- **Actual Email:** `sftesta6577921@virgilian.com` (NOT jake@burtonmethod.com)
|
||
- **User ID:** 204
|
||
- **Role:** user
|
||
- **Plan:** Free (plan_id: 3)
|
||
- **Token Balance:** 1
|
||
- **Last Login IP:** `65.32.116.123`
|
||
- **Session ID:** `11atGZ7OV7aTbl27Xj83XfCAH2myWqrd7XLdYo3c`
|
||
- **Screenshot:** `step5-user-settings.png`
|
||
|
||
---
|
||
|
||
## Phase 2: Application Architecture
|
||
|
||
### 2.1 Technology Stack
|
||
| Component | Technology |
|
||
|-----------|-----------|
|
||
| Backend | Laravel (PHP) |
|
||
| Admin Panel | Filament v3.3.47 |
|
||
| Real-time | Livewire + Laravel Echo + Reverb |
|
||
| Frontend | Alpine.js + React (for GHL login modal) |
|
||
| Build Tool | Vite |
|
||
| CDN/Proxy | Cloudflare |
|
||
| Queue System | Laravel Horizon |
|
||
| Payments | Stripe (stripe_id field exists) |
|
||
| Auth | Laravel Sanctum |
|
||
| Hosting | Likely AWS (SSRF metadata URL accepted) |
|
||
| Parent Company | "The Agency Toolkit" (revealed by WebSocket host) |
|
||
|
||
### 2.2 Application Structure
|
||
```
|
||
/app/ → Main app routes
|
||
/app/login → Login page
|
||
/app/register → Registration
|
||
/app/user-settings → User profile settings
|
||
/app/subscription-management → Plans & billing
|
||
/app/funnel-cloner → Funnel Wizard (requires GHL connection)
|
||
/app/funnel-builds → Completed funnel builds
|
||
/app/ghl-templates → Template library
|
||
/app/ghl-templates/import → Import templates (paid feature)
|
||
/app/support/get-support → FAQ
|
||
/app/support/ask-question → Support ticket form
|
||
/api/user → User data API ⚠️ CORS wildcard
|
||
/api/funnel-clone → Funnel clone endpoint (POST)
|
||
/api/funnel-clone/{id} → Clone job status polling
|
||
/livewire/update → Livewire component updates
|
||
/livewire/upload-file → File uploads
|
||
/subscription/billing-portal → Stripe billing portal redirect
|
||
/horizon → Laravel Horizon (403)
|
||
/log-viewer → Log viewer (403)
|
||
/sanctum/csrf-cookie → CSRF cookie endpoint
|
||
/.env → Blocked (403)
|
||
```
|
||
|
||
### 2.3 WebSocket Configuration (Leaked)
|
||
```json
|
||
{
|
||
"broadcaster": "reverb",
|
||
"key": "h7c0rpv5d85eqkixcops",
|
||
"wsHost": "ws.app.theagencytoolkit.com",
|
||
"wsPort": "443",
|
||
"namespace": "App.Events",
|
||
"authEndpoint": "/broadcasting/auth"
|
||
}
|
||
```
|
||
**Note:** WebSocket connection fails with `ERR_SSL_UNRECOGNIZED_NAME_ALERT` — the SSL cert for `ws.app.theagencytoolkit.com` appears misconfigured.
|
||
|
||
---
|
||
|
||
## Phase 3: GHL Connection Flow
|
||
|
||
### 3.1 Connection Requirements
|
||
The Funnel Wizard requires a connected GHL account. Two connection methods:
|
||
1. **Chrome Extension SSO** — Extension ID: `dollonnbdephinbelejjjjeidfcncfod`
|
||
2. **GHL Login Credentials** — Email + Password modal
|
||
- Claims: "We never store your HighLevel email or password"
|
||
- Offers "Remember my session (encrypted)" checkbox
|
||
- **Screenshot:** `step3-ghl-login-modal.png`
|
||
|
||
### 3.2 GHL Login Modal
|
||
- **Fields:** GoHighLevel Email, Password, Remember Session checkbox
|
||
- **Validation:** Email format validated, password field type is `textbox` (not password type!)
|
||
- **XSS Test:** `<script>alert(1)</script>` accepted in email field (displayed in plaintext, not executed)
|
||
- **Screenshot:** `step4-xss-test-ghl-login.png`
|
||
|
||
### 3.3 Two-Factor Authentication Support
|
||
The funnel clone pipeline includes a `awaiting_two_factor` status, indicating the system handles GHL 2FA inline during the clone process.
|
||
|
||
---
|
||
|
||
## Phase 4: Funnel Clone Pipeline (Reverse-Engineered)
|
||
|
||
### 4.1 Complete Pipeline Stages
|
||
Extracted from inline JavaScript (`funnelCloneTopbar` Alpine.js component):
|
||
|
||
| Stage | Progress | Description |
|
||
|-------|----------|-------------|
|
||
| queued | 3% | Job is in queue |
|
||
| initializing | 10% | Session warm-up |
|
||
| authenticate | 18% | GHL authentication |
|
||
| ai-overwrite | 32% | AI content generation |
|
||
| scan-source | 45% | Scanning source template |
|
||
| prepare-target | 58% | Preparing GHL target location |
|
||
| api-deploy | 68% | GHL API deployment |
|
||
| clone-execution | 78% | Cloning/template install |
|
||
| final-diagnostics | 90% | QA checks |
|
||
| completed | 100% | Done (triggers confetti!) |
|
||
|
||
### 4.2 API Endpoint: `/api/funnel-clone`
|
||
**Method:** POST
|
||
**Required Fields:**
|
||
- `sourceUrl` — URL of the source funnel/template
|
||
- `targetLocationId` — GHL location ID
|
||
- `credentialEmail` — GHL email (validated as email format)
|
||
- `credentialPassword` — GHL password
|
||
|
||
**Optional Fields (discovered):**
|
||
- `businessName` — Business name (NO sanitization!)
|
||
- `rememberSession` — Boolean
|
||
- `templateId` — Template reference
|
||
|
||
**Status Polling:** `GET /api/funnel-clone/{jobId}`
|
||
|
||
### 4.3 Key Observations
|
||
- The pipeline uses a **queued job system** (Laravel Horizon)
|
||
- Jobs are polled every 5 seconds from the frontend
|
||
- Job state is persisted in `localStorage` key `sf.funnelCloneJob`
|
||
- Completion triggers a confetti animation and notification
|
||
- Generated funnels link directly to GHL: `https://app.gohighlevel.com/v2/location/{locationId}/funnels-websites/funnels/{funnelId}/`
|
||
|
||
---
|
||
|
||
## Phase 5: Security Findings (Detailed)
|
||
|
||
### 🔴 FINDING 1: Wildcard CORS on `/api/user` (CRITICAL)
|
||
|
||
**Endpoint:** `GET /api/user`
|
||
**Response Headers:**
|
||
```
|
||
access-control-allow-origin: *
|
||
access-control-allow-credentials: true
|
||
access-control-allow-methods: GET, POST, PUT, DELETE, OPTIONS
|
||
access-control-allow-headers: Content-Type, X-Auth-Token, Origin, Authorization, X-API-Key
|
||
```
|
||
|
||
**Impact:** ANY website can make a cross-origin request to `/api/user` and read the full user profile. While browsers typically block `Access-Control-Allow-Origin: *` with `credentials: true`, some API clients and mobile apps don't enforce this. The header configuration itself is a severe misconfiguration.
|
||
|
||
**Data Exposed:** User ID, email, role, plan, IP address, session ID, billing status, Stripe IDs, login timestamps, token balance, HighLevel IDs, and more.
|
||
|
||
**Recommendation:** Set `Access-Control-Allow-Origin` to the specific application domain only. Never use wildcard with credentials.
|
||
|
||
---
|
||
|
||
### 🔴 FINDING 2: Excessive User Data Exposure (CRITICAL)
|
||
|
||
**Endpoint:** `GET /api/user`
|
||
**Full Response:**
|
||
```json
|
||
{
|
||
"id": 204,
|
||
"name": "Jake Shore",
|
||
"email": "sftesta6577921@virgilian.com",
|
||
"role": "user",
|
||
"plan_id": 3,
|
||
"is_active": true,
|
||
"last_login_ip": "65.32.116.123",
|
||
"last_login_session_id": "11atGZ7OV7aTbl27Xj83XfCAH2myWqrd7XLdYo3c",
|
||
"api_key": null,
|
||
"stripe_id": null,
|
||
"token_balance": 1,
|
||
"subscription_status": "inactive",
|
||
"pm_type": null,
|
||
"pm_last_four": null,
|
||
"highlevel_company_id": null,
|
||
"highlevel_user_id": null,
|
||
"location_id": null,
|
||
"custom_domain": null,
|
||
"test_mode": false,
|
||
// ... timestamps, billing info, etc.
|
||
}
|
||
```
|
||
|
||
**Impact:** The API returns ALL user model fields without any filtering. This includes sensitive fields like `last_login_ip`, `last_login_session_id`, `api_key`, Stripe billing info, and internal IDs.
|
||
|
||
**Recommendation:** Implement an API Resource/Transformer that returns ONLY necessary public-facing fields. Never expose session IDs or IP addresses via API.
|
||
|
||
---
|
||
|
||
### 🟠 FINDING 3: Potential SSRF via Funnel Clone API (HIGH)
|
||
|
||
**Endpoint:** `POST /api/funnel-clone`
|
||
**Tested Payloads:**
|
||
|
||
| Payload | Result |
|
||
|---------|--------|
|
||
| `http://169.254.169.254/latest/meta-data/` | ✅ Accepted (402 — only blocked by token check) |
|
||
| `http://localhost:8080/admin` | ✅ Accepted (402) |
|
||
| `https://example.com` | ✅ Accepted (402) |
|
||
| `file:///etc/passwd` | ❌ Rejected (422 — invalid URL) |
|
||
|
||
**Impact:** If a user has tokens, the `sourceUrl` parameter can be set to internal AWS metadata endpoints, localhost services, or other internal resources. The application will attempt to fetch and process these URLs server-side, potentially exposing AWS credentials, internal services, and cloud infrastructure.
|
||
|
||
**Recommendation:** Implement URL allowlisting (only accept `*.gohighlevel.com` and `*.msgsndr.com` URLs). Block private IP ranges, localhost, and cloud metadata endpoints.
|
||
|
||
---
|
||
|
||
### 🟠 FINDING 4: XSS via businessName (HIGH)
|
||
|
||
**Endpoint:** `POST /api/funnel-clone`
|
||
**Payload:** `businessName: "<script>alert(1)</script>"`
|
||
**Result:** Accepted without sanitization (402 — blocked only by token check)
|
||
|
||
**Impact:** If the business name is reflected in the generated funnel content without proper encoding, this could lead to stored XSS in the generated funnels, the admin panel, or email notifications.
|
||
|
||
**Recommendation:** Sanitize all user input before storage. Use output encoding in all templates. Validate business name format (alphanumeric + common characters only).
|
||
|
||
---
|
||
|
||
### 🟡 FINDING 5: Infrastructure Information Leakage (MEDIUM)
|
||
|
||
**Leaked Information:**
|
||
- **Parent Company:** "The Agency Toolkit" (via WebSocket host `ws.app.theagencytoolkit.com`)
|
||
- **Reverb Key:** `h7c0rpv5d85eqkixcops`
|
||
- **User ID in meta tag:** `<meta name="user-id" content="204">`
|
||
- **CSRF Token in meta tag:** Accessible via JavaScript
|
||
- **Filament Version:** 3.3.47
|
||
- **React Version:** 18.3.1
|
||
- **Livewire Version ID:** `df3a17f2`
|
||
|
||
**Recommendation:** Remove user-id from meta tags. Consider using encrypted broadcast channels. Don't expose framework versions in asset URLs.
|
||
|
||
---
|
||
|
||
### 🟢 FINDING 6: Exposed Laravel Debug Tools (LOW)
|
||
|
||
| Endpoint | Status |
|
||
|----------|--------|
|
||
| `/horizon` | 403 Forbidden |
|
||
| `/log-viewer` | 403 Forbidden |
|
||
| `/.env` | 403 Forbidden |
|
||
|
||
**Impact:** While properly blocked (403), the existence of these endpoints confirms the application uses Laravel Horizon (Redis-based queue) and a log viewer. This information aids in attack planning.
|
||
|
||
**Recommendation:** Return 404 instead of 403 for security tools to avoid fingerprinting. Better: restrict these routes to an admin IP range or separate admin subdomain.
|
||
|
||
---
|
||
|
||
### 🟢 FINDING 7: Outdated Livewire Assets (LOW)
|
||
|
||
**Console Warning:** `Livewire: The published Livewire assets are out of date`
|
||
|
||
**Impact:** Outdated Livewire assets may contain known vulnerabilities. The warning appears on every page load.
|
||
|
||
**Recommendation:** Run `php artisan livewire:publish --assets` to update.
|
||
|
||
---
|
||
|
||
### ℹ️ FINDING 8: Broken WebSocket SSL (INFO)
|
||
|
||
**Error:** `ERR_SSL_UNRECOGNIZED_NAME_ALERT` on `wss://ws.app.theagencytoolkit.com`
|
||
|
||
**Impact:** Real-time notifications and progress updates don't work. The SSL certificate for the WebSocket server doesn't match the domain.
|
||
|
||
**Recommendation:** Fix SSL certificate for `ws.app.theagencytoolkit.com` or update the WebSocket host configuration.
|
||
|
||
---
|
||
|
||
## Phase 6: Pricing & Business Intelligence
|
||
|
||
### Plans Discovered
|
||
|
||
| Plan | Price | Credits | GHL Accounts | Key Features |
|
||
|------|-------|---------|-------------|--------------|
|
||
| Free | $0 | 3 | 1 | Pre-selected templates only |
|
||
| Starter | $297 (was $597) | 100 | 1 | 500+ template library, AI copywriting |
|
||
| Agency | $497 (was $997) | 1000 | 100 | Import own templates, clone public pages, Super Editor |
|
||
| Founder's | $1,297 (was $2,997) | 1500 | Unlimited | White-labeling, priority support, "24 left" scarcity |
|
||
|
||
**Screenshot:** `step6-plans-billing.png`
|
||
|
||
---
|
||
|
||
## Phase 7: Complete Page Inventory & Screenshots
|
||
|
||
| Screenshot | Description |
|
||
|-----------|-------------|
|
||
| `step1-dashboard-ghl-connect.png` | Dashboard with GHL connection prompt |
|
||
| `step2-ghl-connection-required.png` | GHL connection required state |
|
||
| `step3-ghl-login-modal.png` | GHL login credential modal |
|
||
| `step4-xss-test-ghl-login.png` | XSS payload in GHL email field |
|
||
| `step5-user-settings.png` | User settings page (actual email visible) |
|
||
| `step6-plans-billing.png` | Full pricing page |
|
||
| `step7-funnel-builds-empty.png` | Empty funnel builds page |
|
||
| `step8-my-templates-empty.png` | Empty templates page |
|
||
| `step9-import-upgrade-required.png` | Import requires upgrade |
|
||
| `step10-support-faq.png` | FAQ/Support page |
|
||
| `step11-support-ticket-form.png` | Support ticket submission form |
|
||
|
||
---
|
||
|
||
## Phase 8: API Endpoints Discovered
|
||
|
||
| Endpoint | Method | Auth | Purpose |
|
||
|----------|--------|------|---------|
|
||
| `/api/user` | GET | Session | Full user profile (CORS: *) |
|
||
| `/api/funnel-clone` | POST | Session | Create funnel clone job |
|
||
| `/api/funnel-clone/{id}` | GET | Session | Poll clone job status |
|
||
| `/livewire/update` | POST | CSRF | Livewire component updates |
|
||
| `/livewire/upload-file` | POST | CSRF | File uploads |
|
||
| `/sanctum/csrf-cookie` | GET | None | CSRF cookie provisioning |
|
||
| `/subscription/billing-portal` | GET | Session | Stripe billing redirect |
|
||
| `/broadcasting/auth` | POST | CSRF | WebSocket auth |
|
||
| `/broadcasting/user-auth` | POST | CSRF | User-specific broadcast auth |
|
||
|
||
---
|
||
|
||
## Phase 9: Limitations & What Could Not Be Tested
|
||
|
||
1. **Full Funnel Creation Flow** — Blocked by GHL connection requirement. The Funnel Wizard redirects to the main page if GHL is not connected.
|
||
2. **AI Content Generation** — Could not observe actual AI processing due to 0 usable tokens and no GHL connection.
|
||
3. **Funnel Output/Preview** — No builds exist on the account.
|
||
4. **IDOR Testing** — `/api/funnel-clone/{id}` returns 404 for non-existent IDs; could not test with a valid job ID.
|
||
5. **Chrome Extension Analysis** — The SSO extension (`dollonnbdephinbelejjjjeidfcncfod`) was not installed/analyzed.
|
||
6. **GHL Session Handling** — The "encrypted session" storage mechanism was not tested since we couldn't complete GHL auth.
|
||
|
||
---
|
||
|
||
## Recommendations Summary
|
||
|
||
### Critical (Fix Immediately)
|
||
1. **Fix CORS on `/api/user`** — Remove wildcard, set specific origin
|
||
2. **Filter `/api/user` response** — Return only necessary fields, never expose IP/session/billing internals
|
||
3. **Implement URL allowlisting on `/api/funnel-clone`** — Block private IPs, metadata endpoints, internal hosts
|
||
|
||
### High Priority
|
||
4. **Sanitize `businessName` and all user inputs** — Strip HTML/JS before storage
|
||
5. **Add rate limiting** on API endpoints
|
||
6. **Implement Content Security Policy headers**
|
||
|
||
### Medium Priority
|
||
7. **Remove `user-id` from meta tags** — Use session-only identification
|
||
8. **Fix WebSocket SSL certificate**
|
||
9. **Return 404 instead of 403 for admin tools** (`/horizon`, `/log-viewer`)
|
||
10. **Update Livewire assets**
|
||
|
||
### Low Priority
|
||
11. **Use password input type** for GHL password field in modal
|
||
12. **Add autocomplete attributes** to form inputs (DOM warning)
|
||
13. **Consider moving WebSocket to same domain** to avoid SSL issues
|
||
|
||
---
|
||
|
||
## Technical Notes
|
||
|
||
- **Server:** Cloudflare (CF-Ray headers, MIA datacenter = Miami)
|
||
- **Infrastructure:** Behind Cloudflare proxy, likely AWS hosting
|
||
- **Database:** Likely MySQL/PostgreSQL (Laravel convention)
|
||
- **Queue:** Redis (Laravel Horizon)
|
||
- **Real-time:** Laravel Reverb (WebSocket server, separate domain)
|
||
- **Payment:** Stripe integration
|
||
- **Email:** Email verification flow exists
|
||
- **Referral:** FirstPromoter integration (tracking fields in user model)
|
||
|
||
---
|
||
|
||
*Report generated by Clawdbot authenticated pentest subagent, 2026-02-07*
|