seanmonstar / reqwest

An easy and powerful Rust HTTP Client
https://docs.rs/reqwest
Apache License 2.0
9.87k stars 1.11k forks source link

Request for Equivalent of `--path-as-is` #2396

Open i5-650 opened 2 months ago

i5-650 commented 2 months ago

Hello,

I'm currently exploring ATAC as a potential replacement for curl.

However, for cybersecurity purposes, I frequently use curl with the --path-as-is option, which disables URL normalization.

At present, the main obstacle preventing @Julien-cpsn from implementing this feature in ATAC is the absence of support for it in the reqwest library.

Would it be possible to have this feature implemented?

Best regards

seanmonstar commented 2 months ago

Do you have some examples of paths that you wish weren't normalized, and what reqwest does with them?

i5-650 commented 2 months ago

Let's take a simple example:

I have a web server:

const STATIC_DIR: &str = "./static/";

#[get("/")]
async fn index() -> impl Responder {
    NamedFile::open(Path::new(STATIC_DIR).join("index.html"))
}

#[get("/static/{filename:.*}")]
async fn static_files(filename: web::Path<String>) -> impl Responder {
    let path = Path::new(STATIC_DIR).join(filename.into_inner());
    match NamedFile::open(path) {
        Ok(file) => file,
        Err(_) => NamedFile::open(Path::new(STATIC_DIR).join("404.html")).unwrap(),
    }
}

async fn not_found() -> impl Responder { NamedFile::open(Path::new(STATIC_DIR).join("404.html")) }

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    return HttpServer::new(|| {
        App::new()
            .service(index).service(static_files)
            .default_service(web::route().to(not_found))
    }).bind(("127.0.0.1", 8080))?.run().await;
}

As explained in the Actix documentation about static files, this server is vulnerable to a path traversal attack.

Let's say it's a CTF, and the file is located at the root of the server. You would need to access this URL: http://localhost:8080/static/../a_file. However, it will be normalized to http://localhost:8080/a_file, which prevents exploitation of this vulnerability.

To exploit such a vulnerability, you can use curl with the following command:

curl --path-as-is http://localhost:8080/statitc/../a_file

If you try to do this with reqwest:

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let resp = reqwest::get("http://127.0.0.1:8080/static/../a_file").await?;
    let content = resp.text().await?;
    println!("{}", content);
    Ok(())
}

It will be logged on the server as a request to /a_file directly, bypassing the intended path traversal.

seanmonstar commented 2 months ago

Ok, thanks for the example. So you're referring to path segments being flattened. Makes sense. I wasn't sure if you also meant percent encoding, etc.

reqwest currently makes use of the url crate for request targets. (That might change next version, undecided.) So, it'd either need to be supported in url, or a workaround added to reqwest directly. I'm not going to work on that myself, but discussing how to do so in this issue is welcome.