htb Sandworm wp

nmap

有域名 ssa.htb加到hosts

看web
htb Sandworm wp_第1张图片
爆dns和域名
htb Sandworm wp_第2张图片

点到contact里面有个guide可以点
htb Sandworm wp_第3张图片
点进去之后web如下
htb Sandworm wp_第4张图片
htb Sandworm wp_第5张图片
从上往下依次是;
测试 解密密文
测试 公钥加密 生成密文
你的公钥 信息和签名

这边他会给你一个测试用的key在这里插入图片描述
htb Sandworm wp_第6张图片

尝试一下用他的公钥解一下他的签名
htb Sandworm wp_第7张图片
试了一会,发现这里是可以模板注入我这里修改了自己的签名用户id为{{7*7}},输出了一个48

htb Sandworm wp_第8张图片
通过修改自己的证书是可以执行注入的
但是我懒得修改…于是我直接丢到哈希下面让他执行
htb Sandworm wp_第9张图片
然后环境挺怪的…whoami执行之后没有返回,curl了下我的web也没收到请求htb Sandworm wp_第10张图片
而且弹shell没弹出来…没办法就开始读文件。
在家目录的config下面发现有firejail…那这个当前估计是跑在沙盒里了,怪不得执行不了whoami
htb Sandworm wp_第11张图片
最后找到了个这个文件,但是读的时候出乱码
htb Sandworm wp_第12张图片
于是base64带出来解一下
htb Sandworm wp_第13张图片

然后尝试登陆

get
在这里插入图片描述


Root

信息收集一波,firejail带个s位神奇,执行一哈
在这里插入图片描述

没权限…再看别的

还发现了一个奇怪的tipnet带s位
htb Sandworm wp_第14张图片
在这里插入图片描述
看下他的main

extern crate logger;
use sha2::{Digest, Sha256};
use chrono::prelude::*;
use mysql::*;
use mysql::prelude::*;
use std::fs;
use std::process::Command;
use std::io;

// We don't spy on you... much.

struct Entry {
    timestamp: String,
    target: String,
    source: String,
    data: String,
}

fn main() {
    println!("                                                     
             ,,                                      
MMP\"\"MM\"\"YMM db          `7MN.   `7MF'         mm    
P'   MM   `7               MMN.    M           MM    
     MM    `7MM `7MMpdMAo. M YMb   M  .gP\"Ya mmMMmm  
     MM      MM   MM   `Wb M  `MN. M ,M'   Yb  MM    
     MM      MM   MM    M8 M   `MM.M 8M\"\"\"\"\"\"  MM    
     MM      MM   MM   ,AP M     YMM YM.    ,  MM    
   .JMML.  .JMML. MMbmmd'.JML.    YM  `Mbmmd'  `Mbmo 
                  MM                                 
                .JMML.                               

");


    let mode = get_mode();
    
    if mode == "" {
      return;
    }
    else if mode != "upstream" && mode != "pull" {
        println!("[-] Mode is still being ported to Rust; try again later.");
        return;
    }

    let mut conn = connect_to_db("Upstream").unwrap();


    if mode == "pull" {
        let source = "/var/www/html/SSA/SSA/submissions";
        pull_indeces(&mut conn, source);
        println!("[+] Pull complete.");
        return;
    }

    println!("Enter keywords to perform the query:");
    let mut keywords = String::new();
    io::stdin().read_line(&mut keywords).unwrap();

    if keywords.trim() == "" {
        println!("[-] No keywords selected.\n\n[-] Quitting...\n");
        return;
    }

    println!("Justification for the search:");
    let mut justification = String::new();
    io::stdin().read_line(&mut justification).unwrap();

    // Get Username 
    let output = Command::new("/usr/bin/whoami")
        .output()
        .expect("nobody");

    let username = String::from_utf8(output.stdout).unwrap();
    let username = username.trim();

    if justification.trim() == "" {
        println!("[-] No justification provided. TipNet is under 702 authority; queries don't need warrants, but need to be justified. This incident has been logged and will be reported.");
        logger::log(username, keywords.as_str().trim(), "Attempted to query TipNet without justification.");
        return;
    }

    logger::log(username, keywords.as_str().trim(), justification.as_str());

    search_sigint(&mut conn, keywords.as_str().trim());

}

fn get_mode() -> String {

  let valid = false;
  let mut mode = String::new();

  while ! valid {
    mode.clear();

    println!("Select mode of usage:");
    print!("a) Upstream \nb) Regular (WIP)\nc) Emperor (WIP)\nd) SQUARE (WIP)\ne) Refresh Indeces\n");

    io::stdin().read_line(&mut mode).unwrap();

    match mode.trim() {
     "a" => {
           println!("\n[+] Upstream selected");
           return "upstream".to_string();
     }
     "b" => {
           println!("\n[+] Muscular selected");
           return "regular".to_string();
     }
     "c" => {
           println!("\n[+] Tempora selected");
           return "emperor".to_string();
     }
     "d" => {
      println!("\n[+] PRISM selected");
      return "square".to_string();
     }
     "e" => {
      println!("\n[!] Refreshing indeces!");
      return "pull".to_string();
     }
     "q" | "Q" => {
      println!("\n[-] Quitting");
      return "".to_string();
     }
     _ => {
      println!("\n[!] Invalid mode: {}", mode);
     }
    }
  }
  return mode;
}

fn connect_to_db(db: &str) -> Result<mysql::PooledConn> {
    let url = "mysql://tipnet:4The_Greater_GoodJ4A@localhost:3306/Upstream";
    let pool = Pool::new(url).unwrap();
    let mut conn = pool.get_conn().unwrap();
    return Ok(conn);
}

fn search_sigint(conn: &mut mysql::PooledConn, keywords: &str) {
    let keywords: Vec<&str> = keywords.split(" ").collect();
    let mut query = String::from("SELECT timestamp, target, source, data FROM SIGINT WHERE ");

    for (i, keyword) in keywords.iter().enumerate() {
        if i > 0 {
            query.push_str("OR ");
        }
        query.push_str(&format!("data LIKE '%{}%' ", keyword));
    }
    let selected_entries = conn.query_map(
        query,
        |(timestamp, target, source, data)| {
            Entry { timestamp, target, source, data }
        },
        ).expect("Query failed.");
    for e in selected_entries {
        println!("[{}] {} ===> {} | {}",
                 e.timestamp, e.source, e.target, e.data);
    }
}

fn pull_indeces(conn: &mut mysql::PooledConn, directory: &str) {
    let paths = fs::read_dir(directory)
        .unwrap()
        .filter_map(|entry| entry.ok())
        .filter(|entry| entry.path().extension().unwrap_or_default() == "txt")
        .map(|entry| entry.path());

    let stmt_select = conn.prep("SELECT hash FROM tip_submissions WHERE hash = :hash")
        .unwrap();
    let stmt_insert = conn.prep("INSERT INTO tip_submissions (timestamp, data, hash) VALUES (:timestamp, :data, :hash)")
        .unwrap();

    let now = Utc::now();

    for path in paths {
        let contents = fs::read_to_string(path).unwrap();
        let hash = Sha256::digest(contents.as_bytes());
        let hash_hex = hex::encode(hash);

        let existing_entry: Option<String> = conn.exec_first(&stmt_select, params! { "hash" => &hash_hex }).unwrap();
        if existing_entry.is_none() {
            let date = now.format("%Y-%m-%d").to_string();
            println!("[+] {}\n", contents);
            conn.exec_drop(&stmt_insert, params! {
                "timestamp" => date,
                "data" => contents,
                "hash" => &hash_hex,
                },
                ).unwrap();
        }
    }
    logger::log("ROUTINE", " - ", "Pulling fresh submissions into database.");

}

可以看到他在开头引入本地的 extern crate logger

看下他的这个包内容在这里插入图片描述
然后看的时候这个文件突然没了…??
在这里插入图片描述
htb Sandworm wp_第15张图片
估计被脚本清了,看样思路是对的,类似之前ruby那个靶机我们要污染他的这个包。
于是直问一手gpt
htb Sandworm wp_第16张图片
然后狠狠的编译记得把log后面那里补一下
然后cargo build


到这里我突然想起来不对劲,我就算用执行了tipnet也是用的当前用户的环境…得看下是有没有什么方式让root触发这个程式。于是丢个pspy进去…

在这里插入图片描述
root执行没抓到,有个atlas执行了,之前拿到的atlas是个沙盒环境的,这个应该是正经shell,于是守株待兔一波。

gethtb Sandworm wp_第17张图片
发现有个1002的组,是jailer,用来看下版本firejail的版本
htb Sandworm wp_第18张图片

https://seclists.org/oss-sec/2022/q2/188

这洞之前在三头犬的user应该是用过…

exp直接用一下,需要开两个端。
htb Sandworm wp_第19张图片

结束
htb Sandworm wp_第20张图片

你可能感兴趣的:(linux,rust,安全)