11
8

More than 5 years have passed since last update.

bashでncの代わりをやってみる

Posted at

シチュエーション

TCPでサーバにアクセスしたいが、ncなどがインストールされておらず、bashのみ使えるという状況。(UDP通信にも応用もできると思うが、確認はしていない)

経緯

自分の手元にPCが2台あり、1台はWindowsマシンでCygwinがインストールされていて、もう1台にはLinuxがインストールされている。

LinuxからWindowsへssh経由でファイルをコピーしたかったが、Linux側でsshdが起動しなかった。
そこでLinux側でsocatを使ってシェルとソケットをバインドし、そこにWindowsからncで接続、リダイレクトでファイルのコピーをしようとしたがWindowsにはncがインストールされていなかった。

bashを使ってソケット通信ができるということなので、実験したことをまとめる。
(手元にPCがあるんだからUSBとかでコピーすればええやないか、というつっこみは無しで)

使用したコマンド

  • bash: Linuxに標準インストールされているシェル。Cygwinにもインストールされている。
  • socat: ncの上位互換(?)なツール。接続を待ち受けて、コマンドを実行するのに使う。

以後、bashを使うホストを"ホストB"と、socatを使うホストを"ホストS"と呼ぶ。
自分の経緯においては、WindowsがホストB、LinuxがホストSに相当する。

bashを使ってソケット通信する方法

"/dev/tcp/{IPアドレス or ホスト名}/{ポート番号}"というファイルに対して読み書きすると、それがソケット通信として反映される。
/dev/tcpというファイルは存在しないが、該当ファイルへのアクセスをbashがいい感じに解釈してソケットを割り当ててくれる。

bashをクライアントとして使う

ホストBで入力したコマンドを、ホストSで実行し、結果をホストBに返す。

1. ホストSで接続を待ち受ける

ホストSはsocatで待ち受けておき、ホストBが接続してくるとホストSのシェルを開く。(telnetdの代わりのような使い方)

[S] $ socat tcp-listen:{待ち受けポート番号},reuseaddr,fork exec:/bin/bash

2. ホストBから接続する

hostnameコマンドを入力し、コマンドを実行したのがホストSであることを確認する。

[B] $ hostname
B
[B] $ exec 3<>/dev/tcp/{ホストSのIPアドレス}/{ホストSの待ち受けポート番号}; echo "hostname" >&3; cat <&3
S
C-cを入力して終了

[B] $

ホストSでコマンドを実行すると、以後入力を受け付けてくれなくなる。
ソケットが閉じてしまうのが原因だと思うが、詳しくは分からなかった。

hostnameコマンドに続けて、";"区切りでexitコマンドを入力するとC-cを入力しなくても自動的に終了してくれる。

[B] $ exec 3<>/dev/tcp/{ホストSのIPアドレス}/{ホストSの待ち受けポート番号}; echo "hostname; exit" >&3; cat <&3
S

[B] $

3. コマンドを連続して入力できるようにする

whileループを使うことにより、連続してコマンドを入力できた。

[B] $ while read cmd; do exec 3<>/dev/tcp/{ホストSのIPアドレス}/{ホストSの待ち受けポート番号}; echo "$cmd; exit" >&3; cat <&3; done
hostname # コマンドを入力
S        # 結果
uname    # 次のコマンドを入力
Linux    # 結果
C-cを入力して終了

[B] $

ホストSでコマンドを実行するたびに接続が切れるので、ループのたびにexecを実行する必要がある。
もう少し上手くできないかなぁという感じである。

bashをサーバとして使う

結論から言って、bash単体で接続を待ち受ける機能は無いらしいので、ncやsocatや各種スクリプト言語の力を借りるしかないと思う。
しかし、リバースシェルとしてなら実現bash単体で実現できるので、それを紹介する。

シチュエーションは、ホストSで入力したコマンドを、ホストBで実行し、結果をホストSに返すというものである。

1. ホストSで接続を待ち受ける

機能的にはホストSがクライアントでホストBがサーバであるが、リバースシェルなのであらかじめホストSが接続を待ち受けておく必要がある。
再び、socatを使う。

[S] $ socat tcp-listen:{待ち受けポート番号},reuseaddr,fork stdout

2. ホストBから接続する

ホストBはホストSからコマンドを読み込み、そのコマンドをホストB上で実行し、結果をホストSに返す。

[B] $ exec 3<>/dev/tcp/{ホストSのIPアドレス}/{ホストSの待ち受けポート番号}; while read cmd <&3; do $cmd 2>&3 >&3; done

3. ホストSからコマンドを入力する

hostname # コマンドを入力
B        # 結果

ホストB上でコマンドを実行できているのが確認でた。

参考サイト

bash - PIB(2015-04-19: ソケット通信)
ncコマンドとbashの/dev/tcpで通信 - suztomoの日記

11
8
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
11
8