> For the complete documentation index, see [llms.txt](https://kabinet.gitbook.io/ctf-writeup/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://kabinet.gitbook.io/ctf-writeup/2024/greyctf-2024/fearless-concurrency.md).

# Fearless Concurrency

## Challenge Description

Rust is the most safest, fastest and bestest language to write web app! The code compiles, therefore it is impossible for bugs! PS: This is my first rust project (real) 🦀🦀🦀🦀🦀 Comment Suggest edit

Author: jro

<http://challs.nusgreyhats.org:33333>

<https://storage.googleapis.com/greyctf-challs/dist-fearless-concurrency.zip>

***

## Code Analysis

The web application has a registration function which generates a Random UID.

```rust
async fn register(State(state): State<AppState>) -> impl IntoResponse {
    let uid = rand::random::<u64>();
    let mut users = state.users.write().await;
    let user = User::new();
    users.insert(uid, user);
    uid.to_string()
}
```

\
We are also able to craft query

```rust
async fn query(State(state): State<AppState>, Json(body): Json<Query>) -> axum::response::Result<String> {
    let users = state.users.read().await;
    let user = users.get(&body.user_id).ok_or_else(|| "User not found! Register first!")?;
    let user = user.clone();

    // Prevent registrations from being blocked while query is running
    // Fearless concurrency :tm:
    drop(users);

    // Prevent concurrent access to the database!
    // Don't even try any race condition thingies
    // They don't exist in rust!
    let _lock = user.lock.lock().await;
    let mut conn = state.pool.get_conn().await.map_err(|_| "Failed to acquire connection")?;

    // Unguessable table name (requires knowledge of user id and random table id)
    let table_id = rand::random::<u32>();
    let mut hasher = Sha1::new();
    hasher.update(b"fearless_concurrency");
    hasher.update(body.user_id.to_le_bytes());
    let table_name = format!("tbl_{}_{}", hex::encode(hasher.finalize()), table_id);

    let table_name = dbg!(table_name);
    let qs = dbg!(body.query_string);

    // Create temporary, unguessable table to store user secret
    conn.exec_drop(
        format!("CREATE TABLE {} (secret int unsigned)", table_name), ()
    ).await.map_err(|_| "Failed to create table")?;

    conn.exec_drop(
        format!("INSERT INTO {} values ({})", table_name, user.secret), ()
    ).await.map_err(|_| "Failed to insert secret")?;


    // Secret can't be leaked here since table name is unguessable!
    let res = conn.exec_first::<String, _, _>(
        format!("SELECT * FROM info WHERE body LIKE '{}'", qs),
        ()
    ).await;

    // You'll never get the secret!
    conn.exec_drop(
        format!("DROP TABLE {}", table_name), ()
    ).await.map_err(|_| "Failed to drop table")?;

    let res = res.map_err(|_| "Failed to run query")?;

    // _lock is automatically dropped when function exits, releasing the user lock

    if let Some(result) = res {
        return Ok(result);
    }
    Ok(String::from("No results!"))
}
```

Looking at this function, we can see that it is vulerable to SQL Injection.

```rust
    let res = conn.exec_first::<String, _, _>(
        format!("SELECT * FROM info WHERE body LIKE '{}'", qs),
        ()
    ).await;

```

***

## Exploit

I used SQL Map, and was able to perform the sql injection

<figure><img src="/files/6y7cGtBO51yFvGQ61IV3" alt=""><figcaption></figcaption></figure>

Next, I attempted to dump the table.

<figure><img src="/files/mCrGHqgQEtCJimEAN9Rn" alt=""><figcaption></figcaption></figure>

Looking at the source code, I was able to identify how they created the random strings for the table.

```rust
    // Unguessable table name (requires knowledge of user id and random table id)
    let table_id = rand::random::<u32>();
    let mut hasher = Sha1::new();
    hasher.update(b"fearless_concurrency");
    hasher.update(body.user_id.to_le_bytes());
    let table_name = format!("tbl_{}_{}", hex::encode(hasher.finalize()), table_id);

```

I then copied the code, to create the hash of my current user with a simple rust script.

```rust
use sha1::{Digest, Sha1};
use hex;

fn main() {
    let mut hasher = Sha1::new();
    hasher.update(b"fearless_concurrency");
    hasher.update(2629577642326695728u64.to_le_bytes());
    let table_name = format!("tbl_{}", hex::encode(hasher.finalize()));
    println!("{}", table_name);
}

```

<figure><img src="/files/gqdmTIVp6nfMjCK1pV78" alt=""><figcaption></figcaption></figure>

Upon inspecting the dumped table, I noticed that my current user's table is not being deleted for some reason. To gain a better understanding, I proxied my SQL Map to Burp Suite.

<figure><img src="/files/MU1cu500ykj59t0Hoj46" alt=""><figcaption><p>SQL Injection payload generated by SQLMap</p></figcaption></figure>

It appears that there were multiple tables containing my user hashes. I attempted to retrieve the secret from the last table on the list.

<figure><img src="/files/DkKr5Sw0vNBvxrhdCM0p" alt=""><figcaption><p>SQL Injection payload generated by SQLMap</p></figcaption></figure>

With the retrieved secret, I was able to get the flag from the `/flag` endpoint.

<figure><img src="/files/bb9MzjfdXze5enVch2i4" alt=""><figcaption></figcaption></figure>

Flag: grey{ru57\_c4n7\_pr3v3n7\_l061c\_3rr0r5}

***

## Further Discussion

However, this is definitely not the intended solution. Looking at the source code, it will attempt to drop the table after the query ran.

```rust
    // You'll never get the secret!
    conn.exec_drop(
        format!("DROP TABLE {}", table_name), ()
    ).await.map_err(|_| "Failed to drop table")?;
```

The challenge name Fearless Concurrency also hinted at it being a Race Condition or something similar, where you have 2 payload running concurrently.

After the CTF, while discussing solution in the discord server, the author revealed that the intended solution was to inject a sleep, and use a seperate user account to perform the SQLI to leak the secret.

<figure><img src="/files/xtVhRRtLH2uSMxcq5Pt1" alt=""><figcaption><p>#web in the Grey Cat The Flag discord server</p></figcaption></figure>

Lorenz has also kindly shared his proof of concept script in the message linked below.

<https://discord.com/channels/969232688521281606/969232688911360035/1231460020412223589>
