リアル脱出ゲーム ～デフォルトシェルがvimになってしまったら～

\#学生LT [Part1] Advent Calendar 2017 初日は、いるやんが学生LTで喋りきれなかったネタをお送りします。

**12/10 その他の脱出方法について追記しました。**

---

皆さんはどんなシェルをお使いでしょうか。
自分好みのシェルがある方はさぞかし `chsh` コマンドにも造詣が深いことでしょう。
ところで、最近のエディタはどんどん進化していて、bashやzshのようなシェルなんて起動しなくとも、エディタさえあれば十分という方も多いのではないかと思います。(いいえ)
そこで、例としてvimをデフォルトシェルにした後に、元に戻す方法を紹介します。
実験環境は Xubuntu 16.04 です。

~~これで安心していたずらできるね！~~

# アジェンダ
1. `# chsh -s /usr/bin/vim`
2. `# chsh <ユーザ名> -s /usr/bin/vim`
3. 1, 2の合わせ技

-- 追記分 --
4. vim8の :terminal を用いると簡単に脱出できる
5. vim から起動する処理系について
6. そもそも存在しない/機能しないシェルを選択してしまった場合

# `# chsh -s /usr/bin/vim`
`chsh` でシェルを指定するためにはそのコマンドが `/etc/shells` に書かれていないといけません。
ところがどっこい、スーパーユーザならなんでも指定できちゃうんです！

```
# chsh -s /usr/bin/vim
```

このコマンドを実行後、rootユーザのデフォルトシェルがvimになります。

## 脱出
vimには `:!` からシェルコマンドが実行する機能があるのですが、基本的には使えません。
なぜなら、デフォルトシェルを書き換えると、vimがコマンドを投げる先もvimになってしまうからです。

ただ、root権限でエディタが立ち上がっているので脱出は容易です。

まずは `chsh` の[man](https://linuxjm.osdn.jp/html/shadow/man1/chsh.1.html)を読みましょう。
すると、ユーザアカウント情報が `/etc/passwd` に記されているとあるので、root権限のvim上で

```
:e /etc/passwd
```

を実行します。
`/etc/passwd` は以下のようなファイルになっているので、1行目をサクッと書き換えちゃいましょう。

```
root:!!:0:0:root:/root:/usr/bin/vim
(略)
```

の `/usr/bin/vim` をフルパスのシェルのコマンド(例えば `/bin/bash`)に書き換えて、 `:wq` で保存。
再ログインすれば脱出完了、のはずです。

# `# chsh <ユーザ名> -s /usr/bin/vim`
今度はroot以外のユーザのデフォルトシェルをvimにしてみます。
デフォルトシェルの反映は一旦ログアウトしてから、です。

## 脱出
例によって `:!` は使えません。
また、root権限のvimではないので `/etc/passwd` を直接編集することもできません。

なので、vimからなんとかroot権限を取る方法を考えます。
幸いにして、vimには対応している言語の処理系を呼び出す方法があります。
今回はpython3コマンドを使って、 `sudo` コマンドを実行します。

```
:python3 import os; os.system("sudo chsh <ユーザ名> -s /bin/bash")
```

パスワードを要求されるので、タイプします。
再ログインすれば脱出完了、のはずです。

### 別解
- Shougo氏のプラグイン VimShell を使ってシェルを立ち上げる

# 1, 2の合わせ技
だんだん脱出が楽しくなってきましたね。
rootと一般ユーザの両方のデフォルトシェルがvimになってもやることは変わりません。

## 脱出
一般ユーザの時の脱出と同様にroot権限を獲得することを目指します。
ですが、 `sudo chsh <ユーザ名> -s /bin/bash` は失敗します。
どうやらPAMを通すタイプの認証はvimがデフォルトシェルになっているとダメらしいです。

こうしましょう。

```
:python3 import os; os.system("sudo su")
:e /etc/passwd
```

一旦rootにログインして、root権限のvimを立ち上げてから `/etc/passwd` を前述の方法で書き換えます。
一般ユーザのデフォルトシェルも一緒に書き換えておくと完全に脱出できると思います！

**追記分**
-

思いの外いいねが頂けていたので、もう少し色々調べてみました。

# vim8の `:terminal` を用いると簡単に脱出できる
vim8から `:terminal` というコマンドが使えるようになりました。

```
:term bash
```

これでデフォルトシェルにかかわらずvim内でウインドウ分割されてbashが立ち上がるので、適当に `chsh` コマンドを投げつけると脱出できます。
脱出以外にも普段使いにも便利ですね。

# vim から起動する処理系について
`:python3` はシェルコマンドとしてpython3を起動しているわけではなく、python3をサポートしているvimの内部コマンドとしてpython3の処理系が起動されています。
そのため、python3をサポートしていないvimでは `:python` としてPython2系の処理系を使うなどの対策をしてください。
`:!/bin/sh` を使えば、というはてぶコメントを確認していますが、手元ではどのように使えばよいのかがわかりませんでした。(結局vimに `/bin/sh` というコマンドが投げつけられてエラーになる)

# そもそも存在しない/機能しないシェルを選択してしまった場合
例えば `/bin/cat` などをデフォルトシェルにすると、標準入力を反芻するだけのただの案山子になってしまいます。
また、 `chsh` でtypoすると存在しないシェルがデフォルトシェルに設定されてしまいます。
上記のvimからの脱出より汎用的な内容です。

## 脱出
###  rootのデフォルトシェルのみが変更されている場合
簡単なケースです。
rootのデフォルトシェルにかかわらず、PAM認証しないコマンドであれば `sudo` コマンドから実行できるので、 `/etc/passwd` を変更するために

```
% sudo vipw
```

を実行して上記の方法でデフォルトシェルの修正を行います。
あるいは、単に

```
% sudo -e /etc/passwd
```

として管理者権限でファイルの編集を行うことも可能です。
`/etc/passwd` であれば `vipw` コマンドで良いかと思いますが、その他のファイルであればこの手法が有効です。(先にデフォルトシェルを修正してからでも遅くないと思います)

### ユーザがウインドウマネージャ下にログインできる場合
ターミナルエミュレータの設定などを活かして、直接有効なシェルの起動を試みる方法です。
Guake TerminalとGNOME Terminalにおいては端末起動時に起動するシェルコマンドを設定から指定できることを確認しています。
その後、適当な方法でデフォルトシェルを修復します。

### 管理者権限を取れるすべてのユーザのデフォルトシェルが変更され、かつ、ターミナルエミュレータの設定が利用できない場合
sshではない、直接ログイン可能なCLI環境とかのケースです。
(sshログインの場合のデフォルトシェル変更はアクセス不許可のための `/bin/false` や `/sbin/nologin` が有名で、これを突破しようとするのは攻撃になるのでここでは触れません)

端的に言うと、シングルユーザモードでログインしてchshで復旧を行います。
Ubuntuでの復旧はこちらの記事が参考になるかと思います。

[ubuntu 14.04 のパスワードを忘れたのでシングルユーザーモードでパスワードを変更する備忘録](https://qiita.com/esesesesescup/items/73cbadca52cf8c306b7e)

# おわりに
Linuxは壊して直してが楽しいし勉強になるし、積極的に壊していこう！