ドメインコントローラーのデータベース(NTDS.DIT)への攻撃
目次:
さて、お約束しましたように、以前に説明した攻撃の個別のサイバーキルチェーン段階の分析を開始します。本日は、会社インフラの攻撃ベクトルの一つをレビューします。この攻撃は「目的の行動」および「偵察」という2つの段階と見なせます。私たちの目標は:
- 取得したアカウントを使用して足場を得ること;
- さらに攻撃を行うか、またはインフラストラクチャへのアクセスを販売するために、会社に関する最大限の情報(メール、部署名、電話番号、職務名など)を取得すること。
SYSTEMとNTDS.DIT
侵入者は、企業ネットワークに入った後、ActiveDirectoryの「最も魅力的な」情報であるSYSTEMとNTDS.DITファイルを狙います。これらのファイルはデータベースであり、ActiveDirectoryの全データを含みます。NTDS.DITには、ドメイン内のすべてのユーザー情報が含まれており、パスワードのハッシュも含まれます。ファイルSYSTEMは、NTDS.DITからデータを復号化するために必要です。
この記事では、ハッシュ取得の方法については説明しません。既に多くの記事がその件について書かれています。この記事では、インフラの脆弱性を示すパスワード漏洩法(LMハッシュが利用可能な場合)と、非常に速く検索プロセスを自動化する方法に注目します。
したがって、必要なのはLMとNTLMハッシュのみです:ハッシュを受け取った後、LMハッシュから始めます。
LMハッシュについて
LMハッシュに関する情報です。
これは DES ブロック暗号 に基づいており、その実装には2つの脆弱性があるため、パスワードのクラッキングが容易です。まず、7文字を超えるパスワードは2つの部分に分割され、それぞれ別々にハッシュ化されるため、各部分を個別に攻撃することができます。次に、すべての小文字の文字が大文字に変換されます。
したがって、以下のようになります:
前述のように、LMハッシュは2つの部分に分かれています。9196B21FEF3C8906AAD3B435B51404EE
AAD3B435B51404EE – このハッシュは空のパスワードに由来します(この数列は、ハッシュの半分が空のパスワードであることを意味します)。
ハッシュの後半が空の文字セットに一致しているので、パスワードが7文字未満であることがわかります。
ここから、最初の部分をブルートフォースします
その目的のために hashcat, を使用します 強力な並列化されたGPUブルートフォース。次のキーを使用しています:
-a => 攻撃モード
-m => ハッシュタイプ
?d = 数字全て(0–9)。
?l = 小文字の文字(a–z)。
?u = 大文字の文字(A–Z)。
?s = 記号。
24秒でパスワードを受け取ります。
ここでNTLMの番です
1BC10704ED604747D7D827FC7A43D555
これをブルートフォースするのがずっと簡単なのは、次の情報を知っているからです:
- パスワードに使用されている文字;
- これらの文字の順序。
したがって、LMハッシュを持っているため、すべての作業に若干32秒以上かかりました。
課題をさらに複雑にします:
14文字のパスワードがあり、クラシックな企業パスワードの第3レベルの難易度を持っていると仮定します。:29355BC0D45C341B51ACD8D3923F7C21
同じ手法を使用します:ブルートフォースにより、最初の近似で(大文字のパスワードを)31秒で得ました。
29355BC0D45C341B51ACD8D3923F7C21
今度は、
NTLM:E06E777CD11F495E9B9098B5576A4343
パスワードの複雑さを14文字まで増やしても、LMハッシュがあれば30秒でクラッキングできるので、私たちの作業を難しくすることはできません!1つのハッシュを処理しました。大量のハッシュを処理する必要があった場合、すべてのパスワードのためのマスクを作成するのは非常に労力を要するでしょう。そこで次のことを行うツールを開発しました。 ntlm_mask_gen.exe
(ここからダウンロードできます из, MD5: c2a9dac79c51e14c84f35febd72d05ba
またはPythonコードを 附録1) :
- 各パスワードごとに単語リストを作成します;
- 単語リストを含む各パスワードごとにマスクを形成します;
- すべての個別のハッシュ、マスクおよび単語リストの文字列を含む1つのファイルを形成します。
次に行うべきことは、このファイルを実行し、 hashcat 数秒でパスワードをクラッキングすることを見ます。
説明した方法のデモンストレーション
説明した技術を実証するために、もう1つ実験を行います:
10文字の10個のパスワードを使用し、4レベルの難易度で次の設定を持っています(企業パスワードポリシーの技術/行政/システムアカウントの伝統的な要求):
- ハッシュのファイルを作成します
csv
:
- 次に、単語リストとマスクを生成するためのツール用のファイルを作成します。LMをクラッキングするためのNTLMハッシュとパスワードを、たとえば以下に入力します
test_ntlm_input.txt
:
- このファイルを実行し、14秒でパスワードを取得します:
明確化
ご存知のとおり、2008バージョン以降、LMハッシュの保存はデフォルトで無効になっていますが、2003バージョン(またはそれ以前)からの移行の場合、移行後にパスワードを変更していないアカウントはデータベースにLMハッシュが残っています。通常、これらはいくつかの理由でパスワードが変更されない技術アカウントであり、たとえば、いくつかの重要なサービスがクラッシュする可能性があるためです。このようなアカウントは非常に大きな脅威であり、大抵の場合は「期限切れなし」、そのアクセスは監視されず、しばしば管理者アクセスまで高い特権を持っています。結果として、このようなアカウントを使用して侵入するのが最も目立たない方法となります。
結論
2003またはそれ以前に基づいたドメインを手放し、より新しいバージョンを使用することは極めて重要です。移行した場合、すべてのアカウントがパスワードを変更し、データベースにLMハッシュがないことを確認しなければなりません。ドメイン管理者および情報セキュリティの専門家に対する一つのアドバイス:積極的に作業し、少なくとも四半期に一度は自分のパスワードをブルートフォースするべきです!ADへのアクセスを制御し、特にSYSTEMおよびNTDS.DITファイルを含む操作を監視するべきです。
この記事が、情報セキュリティの専門家にとって、IT専門家とのコミュニケーションにおける議論として役立つことを願っています。また、ActiveDirectoryへの攻撃に関連するリスクの理解を広げる目的で、管理者にとって役立つことを願っています。
附録1
import math alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'exception = ' &%{}<>^|"'file_name = open('D:hashcatkd_input.new','r')main_len = len(file_name.readlines())file_name.close()file_name = open('D:hashcatkd_input.new','r')file_name_out = open('D:hashcatkd_input.cmd','w')hashes = []passwords = []masks = [[] for i in range(main_len)]dicts = []char_sets_count = 0char_sets = [[] for i in range(main_len)]ind_main = 0 ch1 = [] ch2 = [] ch3 = [] ch4 = []ch_sets = [ch1, ch2, ch3, ch4] def new_init(pass_letter): for ind_0 in range(4):ch_sets[ind_0] = [] return predict(pass_letter) def predict(new_password):pass_letter = '' for let in new_password: if let in alphabet and not (let in pass_letter):pass_letter += let return dict_generator(pass_letter) def dict_generator(pass_letter): global char_sets_count dict_new = ''pass_len = len(pass_letter) if pass_len < 4: for ind1 in range(pass_len):ch_sets[ind1].append(pass_letter[ind1])ch_sets[ind1].append(pass_letter[ind1].lower()) else:filler(pass_letter, spreader(pass_len)) for ind2 in range(4): if len(ch_sets[ind2]) > 0:dict_new += '-' + str(ind2+1) + ' ' + ''.join(ch_sets[ind2]) + ' 'char_sets[char_sets_count] = [ch_sets[0], ch_sets[1], ch_sets[2], ch_sets[3]]char_sets_count += 1 return dict_newdef spreader(letter_num):spread_counter = [0, 0, 0, 0] for ind3 in range(4):spread_counter[ind3] = int(math.ceil(letter_num/float(4-ind3)))letter_num -= spread_counter[ind3] return spread_counterdef filler(pass_letter, counter_letter):count_l = 0 for ind_ch in range(4): for ind_set in range(counter_letter[ind_ch]):ch_sets[ind_ch].append(pass_letter[count_l])ch_sets[ind_ch].append(pass_letter[count_l].lower())count_l += 1 def mask_generator(password,pwd_idx):mask_new = '' for pw_let in password: if pw_let in alphabet: for ind_array in range(4): if pw_let in char_sets[pwd_idx][ind_array]:mask_new += '?'+str(ind_array+1) elif pw_let in exception:mask_new += '?s' else:mask_new += pw_let masks[pwd_idx] = mask_new# ---------------------- Main -------------------------------- for line in file_name:hashes.append(line[:32])passwords.append(line[33:len(line)-1])dicts.append(new_init(passwords[ind_main]))ind_main += 1 for pwd_idx in range(len(passwords)):mask_generator(passwords[pwd_idx],pwd_idx) for index in range(len(hashes)):text = 'cudaHashcat64.exe -m 1000 -a 3 -o ntlm_tesult.hash ' + str(hashes[index]) + ' ' + str(dicts[index]) + ' ' + str(masks[index]) + 'n'file_name_out.write(text)file_name.close()file_name_out.close()