SV.BFC.USING_STRUCT

安全でない方法でバインドされたソケット

ソケットが安全でない方法でバインドされると、悪意のあるユーザーに、接続を制御してユーザー情報を盗むサーバーソケットのハイジャックを許すことになりかねません。

ほとんどのシステムでは、SO_REUSEADDR ソケットオプションの設定と bind() の呼び出しの組み合わせにより、前のプロセスが INADDR_ANY にバインドしていたポートに任意のプロセスがバインド可能となります。INADDR_ANY を使用すると、サーバーはすべてのホストのネットワークインターフェイスでデータを受け取れます。bind() 関数は、特定のアドレスへのバインド時に同じポートに既に INADDR_ANY にバインドされたソケットはないことを確認するチェックは行いません。このような状況では、悪意のあるコードは、権限のないポートの INADDR_ANY にバインドされたサーバーの特定のアドレスにバインドし、その UDP パケットまたは TCP 接続にアクセスすることができます。

この脆弱性について、SV.BFC.USING_STRUCT チェッカーは、bind 関数への呼び出しに関連付けられた struct sockaddr の sin_addr.s_addr フィールドの INADDR_ANY のインスタンスにフラグを立てます。

脆弱性とリスク

ソケットが socket 関数の呼び出しで作成される場合、そのソケットは名前空間に存在しますが、名前は割り当てられていません。bind 関数は、名前のないソケットにローカル名を割り当てることにより、ソケットのローカル関連付けを確立します。

悪意のあるアプリケーションが setsockopt() を呼び出してソケットオプションを SO_REUSEADDR に設定した後、標準のネットワークプロトコルサービス用に使用中のポートに強制的にバインドすれば、これらのサービスへのアクセスを拒否することが可能です。

TCP 接続の場合、攻撃者が追加ソケットを既に使用中のポートにバインドすると、そのポートに入力する TCP 接続要求が正しいソケットで処理されるという保証ができなくなり、その動作は非確定的となります。非確定的動作の例外は、マルチキャストソケットに対する動作です。同じマルチキャストグループの 2 つのソケットメンバーが同じネットワークインタフェースおよびポートにバインドした場合、データは、無作為に選択されたソケットではなく、両方のソケットに配信されます。

こうした場合には、さまざまなネットワークサービスのパケットが盗まれたり、サービスのなりすましが起こります。また、悪意のあるユーザーは、サーバーに対してサービス拒否 (DoS) 攻撃を起動することがあります。

軽減と防止

正確なアドレスを指定するより INADDR_ANY を使用するほうが簡単ですが、この行為によって、別のプログラムがそのマシンの特定のアドレスの同じポートをリッスンすることができます。 また、悪意のあるプログラムは、クライアントからサーバーへのデータをインターセプトできます。このため、コードで特定のアドレスを使用するのが最適です。

脆弱コード例

コピー
  #include <sys/types.h>
  
  #include <sys/socket.h>
  #include <netinet/in.h>
  #include <unistd.h>
  #include <stdio.h>
  #include <arpa/inet.h>
  
  void bind_socket(void) {
 
      int server_sockfd;
      int server_len;
      struct sockaddr_in server_address;
 
      unlink("server_socket");
      server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
 
      server_address.sin_family = AF_INET;
      server_address.sin_port = 21;
      server_address.sin_addr.s_addr = htonl(INADDR_ANY);
 
      server_len = sizeof(struct sockaddr_in);
 
      bind(server_sockfd, (struct sockaddr *) &server_address, server_len);
 }

Klockwork は、関連付けられた INADDR_ANY キーワードのある struct sockaddr にコードがバインドしている 24 行目にフラグを立てます。server_address.sin_addr.s_addr フィールドの INADDR_ANY キーワードは、opportunistic コードが同じポートにバインドし、パケットが盗まれたり、サービスのなりすましが起こりかねないことを意味しています。

修正コード例

コピー
  #include <sys/types.h>
  
  #include <sys/socket.h>
  #include <netinet/in.h>
  #include <unistd.h>
  #include <stdio.h>
  #include <arpa/inet.h>
  
  void bind_socket(void) {
 
      int server_sockfd;
      int server_len;
      struct sockaddr_in server_address;
 
      unlink("server_socket");
      server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
 
      server_address.sin_family = AF_INET;
      server_address.sin_port = 21;
      server_address.sin_addr.s_addr = inet_addr("192.168.1.10");
 
      server_len = sizeof(struct sockaddr_in);
 
      bind(server_sockfd, (struct sockaddr *) &server_address, server_len);
 }

修正されたコード例では、INADDR_ANY キーワードが特定の IP アドレスに置換されました。