Cisco Type 7パスワードをPython3で復号する

はじめに

Cisco Type 7パスワードが簡単に復号できると記述しているサイトが沢山あったのでやってみます。

結論から言うと自力での解読は無理だったので諦めました。復号のためのアプローチすら組み立てられませんでした。完全に力不足。

ズルをしてJavascriptからPythonのコードに書き直しただけになってしまいました。情けない・・・

環境

  • IOSv(Cisco Virl 1.6.65)
  • Python 3.7.3

目次

  1. 調査
  2. Python3でコードを書く
  3. 考察

調査

Netmikoを使用して1文字ずつパスワードを設定してはsh runの結果から暗号化文字を取得するところから始めます。調査時に使用したコードを以下に載せておきます。こういう作業は自動化しないと正直厳しい・・・

Catalyst2960L + Netmiko + TextFSMを使ってみるの時に使用したコードを流用します。

dev_opts/devicve_conf/cisco_device.json

{
    "virl_ios_telnet" : {
        "ip" : "10.1.254.110",
        "port" : "17001",
        "device_type" : "cisco_ios_telnet",
        "username" : "guest",
        "password" : "guest",
        "secret" : "cisco"
    }
}

dev_opts/test_netmiko.py

表示可能なASCII文字を1文字ずつenable passwordコマンドに投入し、sh runで取得したType 7 暗号化パスワードを正規表現で取得しています。結果の表示形式は、10進数、16進数、ASCII文字、暗号化文字列としています。

from netmiko import ConnectHandler
import json
from pathlib import Path
from pprint import pprint
import re

def connect(device):
    with ConnectHandler(**device) as conn:
        conn.enable()
        print("{:6}{:6}{:6}{}".format("dec", "hex", "str", "enc"))
        for i in range(32, 127):
            str = chr(i)
            conn.send_config_set(['enable password ' + str])
            sh_run = conn.send_command('sh run')

            m = re.search(r'enable password 7 (?P<PASSWD>.*)', sh_run, re.MULTILINE)
            print("{:<6}{:6}{:6}{}".format(i, hex(i), str, m.group('PASSWD')))


if __name__ == "__main__":
    dir = Path(__file__).resolve().parent / 'device_conf' / 'cisco_device.json'
    devices = json.load(open(dir, 'r'))

    connect(devices['virl_ios_telnet'])

以下が実行結果になります。暗号化文字列は実行するたびに変化します。0~9の数値では暗号化文字列が同一になっていますが、これは先頭文字に数値を受け付けないためです。実際に試すと、% Incomplete command.が帰ってきます。なので、"/"の暗号化文字列が数値(0〜9)の間は返却されてきていました。

ちょっと本筋とはズレますが、やたら実行に時間がかかります。Netmikoが遅いだけなのか?pyATSだと速かったりするんでしょうか?

dec   hex   str   enc
32    0x20        0860
33    0x21  !     054A
34    0x22  "     074D
35    0x23  #     0150
36    0x24  $     0908
37    0x25  %     041E
38    0x26  &     1008
39    0x27  '     1450
40    0x28  (     024E
41    0x29  )     024F
42    0x2a  *     145D
43    0x2b  +     086A
44    0x2c  ,     0547
45    0x2d  -     015E
46    0x2e  .     015D
47    0x2f  /     1146
48    0x30  0     1146
49    0x31  1     1146
50    0x32  2     1146
51    0x33  3     1146
52    0x34  4     1146
53    0x35  5     1146
54    0x36  6     1146
55    0x37  7     1146
56    0x38  8     1146
57    0x39  9     1146
58    0x3a  :     0755
59    0x3b  ;     1152
60    0x3c  <     065A
61    0x3d  =     0752
62    0x3e  >     1247
63    0x3f  ?     1247
64    0x40  @     1129
65    0x41  A     0227
66    0x42  B     123B
67    0x43  C     0478
68    0x44  D     072B
69    0x45  E     0804
70    0x46  F     1068
71    0x47  G     052C
72    0x48  H     013B
73    0x49  I     1067
74    0x4a  J     132F
75    0x4b  K     1065
76    0x4c  L     1065
77    0x4d  M     1328
78    0x4e  N     0962
79    0x4f  O     0629
80    0x50  P     0236
81    0x51  Q     097D
82    0x52  R     0469
83    0x53  S     113A
84    0x54  T     046F
85    0x55  U     046E
86    0x56  V     1333
87    0x57  W     0033
88    0x58  X     063E
89    0x59  Y     1220
90    0x5a  Z     0735
91    0x5b  [     142C
92    0x5c  \     152E
93    0x5d  ]     0039
94    0x5e  ^     152C
95    0x5f  _     1226
96    0x60  `     1109
97    0x61  a     1416
98    0x62  b     0459
99    0x63  c     1511
100   0x64  d     045F
101   0x65  e     0949
102   0x66  f     1411
103   0x67  g     0303
104   0x68  h     060E
105   0x69  i     141E
106   0x6a  j     060C
107   0x6b  k     1212
108   0x6c  l     1212
109   0x6d  m     141A
110   0x6e  n     130B
111   0x6f  o     0700
112   0x70  p     0014
113   0x71  q     044A
114   0x72  r     111B
115   0x73  s     0518
116   0x74  t     1506
117   0x75  u     0834
118   0x76  v     0610
119   0x77  w     111E
120   0x78  x     131D
121   0x79  y     001D
122   0x7a  z     0109
123   0x7b  {     083A
124   0x7c  |     061A
125   0x7d  }     0319
126   0x7e  ~     010D

毎回実行結果が違いましたので、この時点で解読は諦めました。残された方法としてJavascriptで書かれたソースを眺めました。ソースはこちらのものを参照。https://www.ifm.net.nz/cookbooks/passwordcracker.html

Python3でコードを書く

今回はこれをベースにPython3に書き起こしてみました。そのまま掲載するとマズいかもしれないのでコピペだけでは動かないよう、xlatの一部をマスクします。

crack_cisco.py

import re

def crack_type7(crypttext):
    xlat = (
        0x6*, 0x7*, 0x6*, 0x6*, 0x3*, 0x6*, 0x6*, 0x6*,
        0x4*, 0x2*, 0x2*, 0x6*, 0x7*, 0x6*, 0x7*, 0x7*,
        0x6*, 0x6*, 0x6*, 0x4*, 0x4*, 0x4*, 0x4*, 0x5*,
        0x5*, 0x4*, 0x7*, 0x6*, 0x7*, 0x6*, 0x6*, 0x3*,
        0x3*, 0x3*, 0x3*, 0x3*, 0x6*, 0x6*, 0x7*, 0x7*,
        0x3*, 0x3*, 0x3*, 0x3*, 0x3*, 0x3*, 0x3*, 0x6*,
        0x3*, 0x6*, 0x6*, 0x3*, 0x3*
    )
    plaintext = ''

    if(len(crypttext) < 4 or len(crypttext) & 1):
        raise ValueError("Encrypted str len >= 4 and len must be even : '" + str(len(crypttext))+ "'")

    crypt_list = re.split(r'(..)', crypttext, re.ASCII)[1::2]
    seed = int(crypt_list.pop(0))
    if (seed > 15):
        raise ValueError("The upper 2 digits <= 15 : '" + str(seed) + "'")

    for crypt in crypt_list:
        plaintext += chr(int(crypt, 16) ^ xlat[seed])
        seed += 1
        seed %= len(xlat)
    return plaintext


if __name__ == "__main__":
    """
    crypt ex)
        "012A0911494B360E325F59060B01" : "Your Password"
        "13061E010803" : "cisco"
    """
    try :
        crypt = input("Encrypt Type7 Password : ")
        print("Decrypt : ", crack_type7(crypt))
    except ValueError as ve:
        print("[ERR]", ve)

実行結果

bash-3.2$ python3 crack_cisco.py 
Encrypt Type7 Password : 13061E010803
Decrypt :  cisco

考察

ソースから見えてきた事柄をまとめてみます。

  • Type 7パスワードの上位2桁は10進数seedを表す
  • seedの取りうる値の範囲は、0<=seed<=15
  • seedは1文字復号する毎にインクリメントされるため、xlatをどこからなめていくかを表すものでしかない
  • seedを除く部分がパスワード文字列となる
  • 暗号化された文字列は2桁毎に1文字のパスワードとなっているので、復号されたパスワード文字数は、(len(crypt_text) -2) / 2となる
  • xlat[seed]と2桁の暗号化文字列のXORで1文字復号可能
  • ということは、xlatは、2桁の暗号化文字列と暗号化前の文字のXORを取れば取得可能
  • xlatは循環している

ソースをみるとかなり簡単に復号できることがわかりました。また、肝であるxlatが容易に取得可能であることも見えてきました。

しかしながら、これを一から解読するというのは中々に難しいことであることもわかりました。

今後は、どういったアプローチでType 7パスワードを解読していけばいいかを考えていきたいと思います。ひとまず、暗号技術入門 第3版の再読から始めようと思います。

Type7パスワードを設定しているところはもう無いとは思いますが、ある場合は直ちにenable secretで設定しましょう。Cでの実装や上記のサイトでのJavascript実装が公開されていますし、cisco機器があればそもそもkey chainshow key chainコマンドで見えますし。

自分の管理外の機器のパスワード解読には使用しないで下さい。暗号への理解を深めるとか脆弱な暗号化方式であることを確認するために書いた記事なので・・・