1
0

feat: finish p32

This commit is contained in:
2025-10-15 16:29:24 +08:00
parent 1f7c4e6571
commit 7b34736618
3 changed files with 171 additions and 0 deletions

14
Cargo.lock generated
View File

@@ -1052,6 +1052,20 @@ dependencies = [
"tokio", "tokio",
] ]
[[package]]
name = "p32"
version = "0.1.0"
dependencies = [
"anyhow",
"axum",
"common",
"hex",
"rand",
"reqwest",
"serde",
"tokio",
]
[[package]] [[package]]
name = "p33" name = "p33"
version = "0.1.0" version = "0.1.0"

14
problems/p32/Cargo.toml Normal file
View File

@@ -0,0 +1,14 @@
[package]
name = "p32"
version = "0.1.0"
edition = "2024"
[dependencies]
reqwest = { workspace = true }
tokio = { workspace = true }
axum = { workspace = true }
serde = { workspace = true }
common = { path = "../../common/" }
hex = { workspace = true }
rand = { workspace = true }
anyhow = { workspace = true }

143
problems/p32/src/main.rs Normal file
View File

@@ -0,0 +1,143 @@
use std::time::{Duration, Instant};
use common::sha1;
use anyhow::Result;
use axum::{Router, extract::Query, http::StatusCode, routing::get};
use rand::Rng;
use serde::Deserialize;
use std::sync::LazyLock;
use tokio::time::sleep;
static HMAC_KEY: LazyLock<[u8; 20]> = LazyLock::new(|| {
// 生成随机密钥
let mut rng = rand::rng();
let key: [u8; 20] = rng.random();
key
});
#[derive(Deserialize)]
struct Params {
file: String,
signature: String,
}
async fn verify_handler(Query(params): Query<Params>) -> StatusCode {
let my_signature = hmac_sha1(&*HMAC_KEY, params.file.as_bytes());
let provided_signature = match hex::decode(&params.signature) {
Ok(bytes) => bytes,
Err(e) => {
eprintln!("Hex decode error: {:?}", e);
return StatusCode::BAD_REQUEST;
}
};
match insecure_compare(&my_signature, &provided_signature).await {
true => StatusCode::OK,
false => StatusCode::INTERNAL_SERVER_ERROR,
}
}
async fn insecure_compare(a: &[u8], b: &[u8]) -> bool {
if a.len() != b.len() {
return false;
}
for i in 0..a.len() {
if a[i] != b[i] {
return false;
}
sleep(Duration::from_millis(5)).await;
}
true
}
fn hmac_sha1(key: &[u8], message: &[u8]) -> [u8; 20] {
let mut key = match key.len() {
0..=64 => key.to_vec(),
_ => sha1(key).to_vec(),
};
key.extend(vec![0; 64 - key.len()]);
let mut part_1 = key.iter().map(|x| x ^ 0x5c).collect::<Vec<u8>>();
let mut part_2_input = key.iter().map(|x| x ^ 0x36).collect::<Vec<u8>>();
part_2_input.extend(message);
let part_2 = sha1(&part_2_input);
part_1.extend(part_2);
sha1(&part_1)
}
#[test]
fn test_hmac_sha1() {
let output = hmac_sha1("123".as_bytes(), "helloworld".as_bytes());
let output = hex::encode(output);
let correct = "8849fb5557760d35f19e2100aa250bfb97152bd9";
assert_eq!(output, correct);
}
async fn start_attack() -> Result<()> {
let mut signature = vec![0u8; 20];
let mut pos = 0;
let mut last_max_time = Duration::from_millis(0);
let min_time_increase = Duration::from_millis(1); // 最小耗时增量阈值
while pos < 20 {
let mut max_time = Duration::from_millis(0);
let mut best_byte = 0u8;
for byte in 0x00..=0xff {
signature[pos] = byte;
let url = format!(
"http://localhost:3000/verify?file={}&signature={}",
"helloworld",
hex::encode(&signature)
);
let start = Instant::now();
let status = reqwest::get(&url).await?.status();
let elapsed = start.elapsed();
if status == StatusCode::OK {
println!("✓ 找到完整签名: {}", hex::encode(&signature));
return Ok(());
}
if elapsed > max_time {
max_time = elapsed;
best_byte = byte;
}
}
signature[pos] = best_byte;
println!(
"位置 {} 确定为: {:02x}, 耗时: {:?}",
pos, best_byte, max_time
);
// 检查耗时是否有足够增长
if pos > 0 && max_time.saturating_sub(last_max_time) < min_time_increase {
println!("⚠ 耗时增长不足回退1位重试",);
pos = pos.saturating_sub(1);
last_max_time = Duration::from_millis(0); // 重置基准
continue;
}
last_max_time = max_time;
pos += 1;
}
Ok(())
}
#[tokio::main]
async fn main() -> Result<()> {
let app = Router::new().route("/verify", get(verify_handler));
let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await?;
dbg!(hex::encode(*HMAC_KEY));
// 后台启动服务器
tokio::spawn(async move {
axum::serve(listener, app).await.unwrap();
});
start_attack().await?;
Ok(())
}