Fearless Concurrency
Challenge Description
Code Analysis
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()
}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!"))
}Exploit






Further Discussion

Last updated