Skip to content

SECURITY: Ghost Bits Vulnerability - Type conversion from char to u8 silently truncates high bits #4252

@To-be-w1th0ut

Description

@To-be-w1th0ut

Ghost Bits Vulnerability in sqlx

Summary

A security vulnerability has been identified in Rust's type conversion mechanism that affects applications using sqlx. The vulnerability, dubbed "Ghost Bits," enables attackers to bypass WAF detection and execute SQL injection attacks by exploiting high-bit truncation during type conversions from char (32-bit) to u8 (8-bit).

Note: Rust's compiler emits warnings for such conversions, requiring developers to explicitly ignore warnings to be vulnerable. This significantly reduces the risk compared to other languages.

Severity

Medium - CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H (7.5)

Note: Severity is reduced from Critical to Medium due to Rust's compiler warnings and strong type system.

Affected Components

  • sqlx query parameter handling
  • sqlx raw SQL execution
  • sqlx where clause processing

Affected Versions

All versions (requires developers to explicitly ignore compiler warnings)

Technical Details

Vulnerability Mechanism

When Rust code converts char (32-bit) to u8 (8-bit), the high 24 bits are silently discarded:

let ch: char = '\u{2F58}';  // 爻 (U+2F58) = 0x00002F58
let b: u8 = ch as u8;       // Only low 8 bits: 0x58 = 'X'
// Warning: unused result: `char` to `u8` conversion truncates

Critical Difference: Rust's compiler emits an explicit warning for this conversion.

Attack Example

SQL Injection Bypass:

// Original payload: ' OR '1'='1
// Ghost Bits payload: ħ OR ħ1ħ=ħ1

// WAF sees: "ħ OR ħ1ħ=ħ1" (no match)
// Backend converts: ħ (U+0127) → 0x0127 as u8 → 0x27 = '''
// Result: "' OR '1'='1" (SQL injection executed)

Proof of Concept

use sqlx::postgres::PgPool;

async fn handler(pool: &PgPool, id: String) -> Result<(), sqlx::Error> {
    let waf_pattern = "' OR '1'='1";
    
    // WAF detection
    if !id.contains(waf_pattern) {
        println!("✓ WAF bypass successful");
    }
    
    // Backend processing (vulnerable code - requires #[allow(trivial_casts)])
    let mut id_bytes = Vec::new();
    #[allow(trivial_casts)]
    for ch in id.chars() {
        id_bytes.push(ch as u8);
    }
    
    let restored = String::from_utf8_lossy(&id_bytes);
    
    // ❌ DANGEROUS - SQL concatenation
    let query = format!("SELECT * FROM users WHERE id = '{}'", restored);
    sqlx::query(&query).fetch_all(pool).await?;
    
    Ok(())
}

Impact

Attackers can bypass WAF/IDS protection and execute:

  • SQL injection (requires ignoring compiler warnings)
  • Data exfiltration
  • Database compromise

However, the impact is reduced because:

  1. Rust compiler emits explicit warnings
  2. Developers must add #[allow(trivial_casts)] to be vulnerable
  3. Code reviews typically catch these patterns

Mitigation

Immediate Mitigation

  1. Avoid dangerous type conversions:

    // ❌ DANGEROUS - Requires #[allow(trivial_casts)]
    #[allow(trivial_casts)]
    for ch in s.chars() {
        let b = ch as u8;
    }
    
    // ✅ SAFE - Use bytes() iterator
    for b in s.bytes() {
        // b is already a u8, no conversion needed
    }
    
    // ✅ SAFE - Use as_bytes()
    let bytes: &[u8] = s.as_bytes();
  2. Input validation:

    fn is_valid_ascii(s: &str) -> bool {
        s.chars().all(|ch| ch.is_ascii())
    }
  3. Use sqlx's safe query methods:

    // ❌ DANGEROUS
    let query = format!("SELECT * FROM users WHERE id = '{}'", id);
    sqlx::query(&query).fetch_all(pool).await?;
    
    // ✅ Safe - Use parameterized queries
    sqlx::query("SELECT * FROM users WHERE id = $1")
        .bind(&id)
        .fetch_all(pool)
        .await?;
    
    // ✅ Safe - Use macros
    sqlx::query!("SELECT * FROM users WHERE id = $1", id)
        .fetch_all(pool)
        .await?;

References


This vulnerability has been disclosed following responsible disclosure practices. Due to Rust's strong type system and compiler warnings, the risk is significantly reduced compared to other languages.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions