bashスクリプトに直接記載している平文のパスワードをCプログラムで難読化する。


# 概要
bashスクリプトにパスワードを平文で書くことを回避するために、bashスクリプトファイルをバイナリ実行ファイルに変換するCプログラムを作成したというお話です。

# 動作環境
動作を確認した環境は以下の通り。

項目|内容
---|---
OS|Mac OS X El Capitan 10.11.1
gcc|Apple LLVM version 7.0.0 (clang-700.0.72)
bash|GNU bash, バージョン 4.3.42(1)-release (x86_64-apple-darwin15.0.0)


# 内容
以下のパスワードを平文で記載しているtest.shを

``` bash:test.sh
…（省略）…

password='Password111'
sqlplus user/$password

…（省略）…
```

"getpasswd"というパスワードを返す実行ファイルを作成し、以下のようにしたい。

``` bash:test.sh
…（省略）…

password=$(getpasswd)   # getpasswdを呼び出すように変更
sqlplus user/$password

…（省略）…
```

まず、以下のパスワードを返すbashスクリプト"getpasswd.sh"を作成。

``` bash:getpasswd.sh
[ ! "$(users)" == 'user01' ] && exit 1
printf 'Password111'
```

次に、bashスクリプトファイルをバイナリ実行ファイルに変換するCプログラムを作成。
処理の流れは以下のとおり。

----

1. bashスクリプトファイルの読み込み。
2. bashスクリプトの難読化。
    printf('Password111');のように直接パスワードを記述すると、バイナリファイルをテキストエディタで開いた場合に見えてしまう。
    そのため、以下の難読化処理を行う。
    対象文字列から1バイトごと取り出し、以下の処理を行う。
    - 1ビット右にローテートシフト  
        例）  10001011 → 11000101
    - 0xffでXOR  
        例）  11000101 → 00111010

3. C言語でbashスクリプトを実行するプログラムのソースコードを作成。
    処理の流れは以下のとおり。
    1. 難読化したbashスクリプトを復元。
    1. system関数を使用して、復元したbashスクリプトを実行。

4. 3.のソースコードをコンパイルし、実行ファイルを作成。

----

ソースコードは以下のとおり。

``` c:bb8.c
/*
 * Name        : bb8
 * Description : bashスクリプト→バイナリ変換プログラム
 * Usage       : bb8 <bashスクリプトファイル名> <出力ファイル名>
 *               例)
 *                 bb8 getpasswd.sh getpasswd
 * Version     : 8.0.0 
 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(int argc, char *argv[]) {

  // 引数情報取得
  if(argc!=3) return (1);
  char ifile[strlen(argv[1])+1];
  char ofile[strlen(argv[2])+1];
  strcpy(ifile,argv[1]);
  strcpy(ofile,argv[2]);

  // bashスクリプトファイル読み込み
  unsigned char src[65535]={};
  FILE *fp = fopen(ifile, "r");
  int c=0x0;
  for (int i=0; (c=fgetc(fp))!=EOF; i++){
    src[i]=c;
  }
  fclose(fp);

  // 難読化
  for (int i=0; i<strlen((char *)src); i++) {
    src[i] = (src[i] << 1) | (src[i] >> 7);
    src[i] ^= 0xff;
  }

  // 難読化したbashスクリプトを復元し、system関数で実行する
  // Cプログラムのソースコード作成
  char src1[]=
    "#include <stdio.h>\n"
    "#include <string.h>\n"
    "#include <stdlib.h>\n"
    "int main() {\n"
    "  unsigned char src[] = {";
  char src2[65535]={};        // サイズは適当
  for (int i=0; i<=strlen((char *)src); i++) {
    if (i!=0) sprintf(src2, "%s,", src2);
    sprintf(src2, "%s0x%x", src2, src[i]);
  }
  char src3[]=
    "};\n"
    "  for (int i=0; i < strlen((char *)src); i++) {\n"
    "    src[i] ^= 0xff;\n"
    "    src[i]  = (src[i] >>1) | (src[i] << 7);\n"
    "  }\n"
    "  int ret=-1;\n"
    "  int status = system((char *)src);\n"
    "  if (WIFEXITED(status)) {\n"
    "    ret=(int)WEXITSTATUS(status);\n"
    "  }\n"
    "  return ret;\n"
    "}\n";

  // 上記で作成したソースコードをコンパイルし、実行ファイルを作成
  sprintf((char *)src, "gcc -xc - -o %s << EOF\n%s%s%s\nEOF\n", ofile, src1, src2, src3);
  int ret=-1;
  int status = system((char *)src);
  if (WIFEXITED(status)) {
    ret=(int)WEXITSTATUS(status);
  } 
  return ret;
}
```

コンパイル。

```console
$ gcc bb8.c  -o bb8
```

getpasswd.shをバイナリファイル"getpasswd"に変換。

```console
$ ./bb8 getpasswd.sh getpasswd
```

getpasswdの動作確認。

```console
$ ./getpasswd

Password111
```



以上。