バッファオーバーフローの脆弱性

とても初歩的なんだろうけれど、バッファオーバフローの脆弱性というのを確認した。

以下のコードがあるとき、bufの範囲を超えた入力をうけつけると、その配列より前に宣言しているnの値を書き換えることができてしまう。

#include <stdio.h>
#include <stdlib.h>

int main() {
  int n = 0;
  char buf[8];
  scanf("%s", buf);

  if (n == 0) {
    printf("OK");
  } else {
    printf("NG");
  }
  return 0;
}

標準入力に8文字以上を入力した場合、nの値は書き換わってしまう。 *1

こういうので、関数フレームのリターンアドレスとか書き換えられたら確かにやばい。 けど、実際にはgccなどのコンパイラはスタックを保護してくれるので、そんな簡単には書き換わらない。

これ確認しようとすると、明示的にスタック保護を無効化する-fno-stack-protectorを付けてコンパイルする必要がある。

gcc -fno-stack-protector -o main main.c

自作Cコンパイラだとそんな保護機能は作ってないので簡単に試せるけれど。

参考:はじめて学ぶバイナリ解析

脆弱性をついてシェルを起動するところくらいまで解説があって、とっかかりとしてはわかりやすかった。*2

バイナリ解析

*1:ちょうど8文字入力した場合は、OKが返ってくるが、終端文字(=0)でnの値が上書きはされている。

*2:コンパイラの保護は無効化するけど

Reactの開発環境の前にnginxのリバースプロキシを置くときの設定

Reactの開発環境の前にnginxのリバースプロキシを適当に置くと、以下のようなエラーが出る。*1

WebSocket connection to 'wss://127.0.0.1/sockjs-node' failed: Error during WebSocket handshake: Unexpected response code: 404

これは、Reactの開発環境がWebSocketを使っているためである。 nginxでWebSocketをリバースプロキシするときは、ヘッダをWebSocket側に送ってやらないといけない。 (ブラウザはヘッダを送っているが、nginxが勝手に中継してくれないということ。)

        # For React Debug
        location = /sockjs-node {
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";

            proxy_pass http://host.docker.internal:3000;
        }

*1:本当はそれぞれで閉じてテストできるようにすべきなんだけど。。。

1024未満のportでmoshする(ntpの123とか)

moshで使うUDPのportには通常1024以上を使う。

なぜなら、moshのサーバはユーザ権限で動作するので、root権限が必要な1024未満のportは使えないためである。

1024未満のportしか使えないときはどうするかというと、moshの場合は、権限(Capability)を付与するのが楽そう。 具体的には以下のようにする。

sudo setcap CAP_NET_BIND_SERVICE+ep /usr/bin/mosh
sudo setcap CAP_NET_BIND_SERVICE+ep /usr/bin/mosh-server
sudo setcap CAP_NET_BIND_SERVICE+ep /usr/bin/mosh-client

全部はいらない気がするけど。

これで123とかでもmoshできるようになる。 以下は、tcpにport80を使って、udpにport123を使った場合。

mosh --ssh="ssh -p 80" -p 123 user@xxx.xxx.xxx.xxx

manifest.jsonにはcookieが送信されない!

最近クッキーネタ多いな。。。

なんかエラーが起きてると思ったら、manifest.json(Web App Manifest)にCookieが送られていないことが判明。

調べてみると、manifest.jsonファイルについては、オリジンによらず資格情報、 つまりCookieを送らない仕様なのだそう。(確認していないがAuthenticationヘッダも送られないような反応をネットではみかける。)

対策としては、MDNに書いてある通り、crossorigin="use-credentials"を付ければCookieが送られるようになった。

<link rel="manifest" href="/app.webmanifest" crossorigin="use-credentials">

ただ、manifest.jsonファイルの性質を考えれば、認証の対象外にしておくことにした。

なお、普通のmanifest.json以外のJSONにアクセスする範囲ではCookieは送られる。manifest.jsonファイルだけ特別の様子。

参考:https://developer.mozilla.org/ja/docs/Web/HTML/Attributes/crossorigin

nginxのproxy_passの設定の違い

違うのはわかっていても、なかなか覚えらないのでメモしておく。

nginxでリバースプロキシの設定をするとき、 Request URLによって、転送したいURLを指定するが、末尾のスラッシュの有無で振る舞いが異なる。

以下の場合は、指定したサーバに転送はされるが、Request URLそのまま転送される。

location /path {
    proxy_pass http://localhost:3000;
}

つまり以下のURLに転送されることになる。

https://localhost:3000/path

次に末尾にスラッシュを付けたURLを指定した場合は、Request URLから/pathが除かれて転送される。

location /path {
    proxy_pass http://localhost:3000/;
}

つまり以下のURLに転送されることになる。

http://localhost:3000/

Dockerコンテナからlocalhostへのアクセス

WindowsでDockerコンテナからホストのポートにアクセスしたい場合は、localhostではアクセスできない。 WSL1でもWSL2でも変わらないみたい。

以下に置き換えればよい。

host.docker.internal

Macでも同じらしい。

Tsunami実行のためのDockerfile

Googleの?セキュリティスキャナのTsunamiを試すにあたり、Dockerfileを書いたのでおいておく。

公式のgithubに書いてある通りやっていけば実行することは難しくない。 https://github.com/google/tsunami-security-scanner

ただ、取り回しをよくするためにコンテナ化しておいた。 探せばdockerhubにも落ちているだろうけれど。

FROM ubuntu:latest

ENV DEBIAN_FRONTEND=noninteractive
ENV TZ=Asia/Tokyo

RUN apt-get update && apt-get install -y nmap ncrack openjdk-8-jdk curl git

RUN bash -c "$(curl -sfL https://raw.githubusercontent.com/google/tsunami-security-scanner/master/quick_start.sh)"

WORKDIR /root/tsunami

ENV IP=127.0.0.1

CMD java -cp "tsunami-main-0.0.2-SNAPSHOT-cli.jar:/root/tsunami/plugins/*" \
  -Dtsunami-config.location=/root/tsunami/tsunami.yaml \
  com.google.tsunami.main.cli.TsunamiCli \
  --ip-v4-target=${IP} \
  --scan-results-local-output-format=JSON \
  --scan-results-local-output-filename=/tmp/tsunami-output.json && cat /tmp/tsunami-output.json

Dockerfileのビルドの前に、ホスト環境でのインストールを済ませておく必要がある。 nmapなどのインストールが必要らしい。

sudo apt-get update && sudo apt-get install -y nmap ncrack openjdk-8-jd k curl git

これをビルドする。

docker build -t tsunami .

対象とするホストのIPは環境変数で設定するようにしたので、引数感覚で実行できる。

docker run -rm -t -e IP=127.0.0.1 tsunami

脆弱性がなければ以下のような結果が返ってくる。

{
  "scanStatus": "SUCCEEDED",
  "scanStartTimestamp": "2020-07-02T14:09:13.389Z",
  "scanDuration": "46.875s",
  "fullDetectionReports": {
  }
}

脆弱性がみつからなかったからといって安心できるかというと、 今のところそこまで信用はできないかなぁ、と思っている。