Powershell スクリプト内での認証情報秘匿【難読化の超基本】

# どこで使うの？

[Windows資格情報](https://support.microsoft.com/ja-jp/windows/%E8%B3%87%E6%A0%BC%E6%83%85%E5%A0%B1%E3%83%9E%E3%83%8D%E3%83%BC%E3%82%B8%E3%83%A3%E3%83%BC%E3%81%AB%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%81%99%E3%82%8B-1b5c916a-6a16-889f-8581-fc16e8165ac0)を設定するバッチファイルを、どうしてもユーザーに配布して実行させる必要があるとします。この時、[cmdkey](https://docs.microsoft.com/ja-jp/windows-server/administration/windows-commands/cmdkey)コマンド実行部分にてパスワードを平文で書いたりするのはナンセンスです。そこで、内容を難読化しておきます。

# 免責

この方法は、一般ユーザーがうっかりバッチファイルの中身を見てしまってもパスワードなどの認証情報が読めないようにするためです。この程度ではプロ相手には通用しませんので、本格的なセキュリティ対策として利用するのはお控えください。

# スクリプトを用意

とりあえず、実行したいスクリプトを用意します。`cmdkey /add:Altair /user:Vega /pass:Bengbu`のようなものを実行するわけですが、今回は動作確認をしやすくするために、文字列をechoするだけの簡単なスクリプトにします。

```posh
write-host hello-obfuscation
```

# 暗号化

上記の文字列を何かしらの方法で暗号化します。

```posh
[Convert]::ToBase64String( ([System.Text.Encoding]::Default).GetBytes("write-host hello-obfuscation"))
```

今回はBase64暗号を用いて、このような文字列になりました。

```:output
d3JpdGUtaG9zdCBoZWxsby1vYmZ1c2NhdGlvbg==
```

# 復号化

それではデコードしてみます。

```posh
[System.Text.Encoding]::Default.GetString([System.Convert]::FromBase64String("d3JpdGUtaG9zdCBoZWxsby1vYmZ1c2NhdGlvbg=="))
```

デコード出来ました。

```:output
write-host hello-obfuscation
```

# Invoke-Expression

しかし、これでは文字列のままなので、コマンドとして実行できていません。
そこで、[Invoke-Expression](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/invoke-expression)コマンドを使用します。

先ほどのデコードした時のコマンドを`Invoke-Expression`の引数として渡します。

```posh
Invoke-Expression -Command $([System.Text.Encoding]::Default.GetString([System.Convert]::FromBase64String("d3JpdGUtaG9zdCBoZWxsby1vYmZ1c2NhdGlvbg==")))
```

出力結果を見ると、もともとの`Write-Host`コマンドが実行できていることが分かります。

```output
hello-obfuscation
```

# Aliasと省略

`Invoke-Expression`は長いので、省略します。また、`-Command`も無くてもいいので省略します。
`Invoke-Expression`と`-Command`の代わりに`iex`を使いましょう。

`iex`が`Invoke-Expression`と`-Command`の代わりになることは、`Get-Alias`コマンドで確認できます。

```
Get-Alias iex
```

```:output
CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Alias           iex -> Invoke-Expression
```

実際に省略したコマンドはこのようになっています。

```posh
iex $([System.Text.Encoding]::Default.GetString([System.Convert]::FromBase64String("d3JpdGUtaG9zdCBoZWxsby1vYmZ1c2NhdGlvbg==")))
```

出力結果から省略前と同様に`Write-Host`コマンドが実行できています。

```output
hello-obfuscation
```

# iexの秘匿

`iex`という文字列がそのまま入っているスクリプトはセキュリティ対策ソフトで検知されることが多いです。そこで、文字列`iex`を動的に生成し、これをコマンド化して使うことにしましょう。

まずは、`iex`文字列でコマンドが実行できることを確認します。

```posh
 & "iex" "write-host hello-obfuscation"
```

```:output
hello-obfuscation
```

このように、`&`の後ろに書いた最初の文字列がコマンドとして認識され、その引数に２番目の文字列が利用されていることが分かります。
後は、`iex`という文字列をどう生成するのかが分かれば良さそうです。

そこで利用するのは、確実に結果が分かっているコマンドです。そのコマンドの選定条件は下記です。

- 想定する環境での実行結果が常に同じであること
- 実行結果に同じ位置で`i`、`e`、`x`の３文字が出現すること

ここでは、こんなコマンドを利用してみましょう。

```posh
gcm cmd
```

```:output
CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Application     cmd.exe                                            10.0.1904… C:\WINDOWS\system32\cmd.exe
```

簡単にしたいので、`Source`部分だけ取り出しましょう。

```posh
$(gcm cmd).Source
```

```:output
C:\WINDOWS\system32\cmd.exe
```

まずは、ここから`i`を取り出してみます。

```posh
$(gcm cmd).Source[-23]
```

```:output
I
```

大文字ですね。Windowsでは大文字小文字は無視されるので、見なかったことにします。
次に、`e`も取り出しましょう。

```posh
$(gcm cmd).Source[-23,-1]
```

```:output
I
e
```

最後に、`x`です。

```posh
$(gcm cmd).Source[-23,-1,-2]
```

```:output
I
e
x
```

そして、配列に格納された３つの文字を連結して、文字列にします。

```posh
$(gcm cmd).Source[-23,-1,-2] -Join $Null
```

```:output
Iex
```

動的生成はできたようです。先ほどのコマンドと混ぜてみましょう。`& "iex" "write-host hello-obfuscation"`でしたね。

```posh
& $($(gcm cmd).Source[-23,-1,-2] -Join $Null) "write-host hello-obfuscation"
```

```:output
hello-obfuscation
```

完璧です。

# 完成したスクリプト

最後に、先ほどの暗号化と混ぜてみましょう。

```posh
 & $($(gcm cmd).Source[-23,-1,-2] -join $null) $([System.Text.Encoding]::Default.GetString([System.Convert]::FromBase64String("d3JpdGUtaG9zdCBoZWxsby1vYmZ1c2NhdGlvbg==")))
```

```:output
hello-obfuscation
```

Hello Obfuscation !

# まとめ

Base64は簡単な可逆暗号なので、セキュリティ対策としては甘いです。
ソースに平文で`Base64`って書いてあるので、
一般ユーザーの目は誤魔化せても、プロなら一瞬で解読されますね。
不可逆暗号を使ったほうが良いと思います。
ここでは、タイトルに銘打ったとおり基本なので割愛します。