パスワードを取得するには、レジストリを使用する方法と、レジストリファイルファイルに直接アクセスする方法の2つの方法があります。 いずれの場合でも、SYSTEMユーザー特権または大切なファイルの盗難(たとえば、別のOSからの起動)が必要になります。 ここでは、アクセスを取得する可能性については説明しませんが、研究の目的上、最初のオプションを選択する方が明確になります。これにより、レジストリブッシュの構造に集中することができなくなります。 また、 sysinternalsのpsExecユーティリティは、システムから起動するのに役立ちます。 もちろん、これらの目的のために、ウィンドウの脆弱性を使用できますが、記事はそれについてではありません。
Vブロック
Vistaより前のWindowsでは、デフォルトで、LMとNTの2つの異なるハッシュにパスワードが保存されていました。 whist以上では、LMハッシュは保存されません。 まず、これらのハッシュを探す場所を見てみましょう。次に、それらが何であるかを理解します。
ユーザパスワードだけでなく、他の有用な情報がHKLM \ SAM \ SAM \ドメイン\でレジストリに格納されているアカウント\ユーザー\ [RID] \ V
Vブロックとして知られています。 SAMセクションは、対応するファイルc:\ Windows \ System32 \ config \ SAMにあります。 RID-一意のユーザー識別子。たとえば、 HKLM \ SAM \ SAM \ Domains \ Account \ users \ names \ <ユーザー名>ブランチを調べることで見つけることができます(デフォルトのパラメーター、フィールドはパラメータータイプです)。 たとえば、「管理者」アカウントのRIDは常に500(0x1F4)であり、「ゲスト」ユーザーのRIDは501(0x1f5)です。 デフォルトではSAMセクションへのアクセスはSYSTEMユーザーのみが可能ですが、本当に見たい場合は、システム権限でregeditを実行してください。
PsExec.exe -s -i -d regedit.
Vブロックを便利な形式で観察するには、たとえば、テキストファイルにエクスポートすることができます(Regeditのファイルエクスポート)。
表示される内容は次のとおりです。
0x0から0xCCは、Vブロック内のすべてのデータのアドレス、サイズ、およびデータに関する追加情報です。 実際の住所を取得するには、見つかった住所に0xCCを追加する必要があります。 アドレスとサイズは、ビッグエンディアンの原則に従って保存されます。つまり、バイトを反転する必要があります。 各パラメーターには4バイトが割り当てられますが、実際にはすべてのパラメーターが1バイトまたは2バイトに収まります。 参照先は次のとおりです。
ユーザー名-0x
ユーザー名の長さ-0x10
LMハッシュアドレス-0x9s
LMハッシュ長-0xa0
NTハッシュアドレス-0xa8
NTハッシュ長-0xac
この場合、ユーザー名はオフセット0xd4 + 0xccにあり、その長さは0xcバイトになります。
NTハッシュはオフセット0x12c + 0xccに配置され、そのサイズ(常に同じ)= 0x14になります。
パスワードの保存に関するもう1つの詳細は、NTハッシュとLMハッシュの両方に常に4バイトが追加されることです。その目的は私には謎です。 さらに、パスワードが無効になっていても4バイトが存在します。 この場合、LMハッシュの長さ= 4であることは明らかです。アドレスを見ると、LMハッシュがないにもかかわらず、これらの4バイトを見ることができます。
したがって、ハッシュオフセットを検索するときは、アドレスに4バイトを安全に追加し、サイズを考慮するときは、それを減算します。 コードを読む方が便利な場合、アドレス検索は、反転、余分な4バイト、開始オフセット0xcc(C#コード)を考慮して、次のようになります。
int lmhashOffset = userVblock[0x9c] + userVblock[0x9d] * 0x100 + 4 + 0xcc;
int nthashOffset = userVblock[0xa8] + userVblock[0xa9] * 0x100 + 4 + 0xcc;
int lmhashSize = userVblock[0xa0] + userVblock[0xa1] * 0x100 - 4;
int nthashSize = userVblock[0xac] + userVblock[0xad] * 0x100 - 4;
int usernameOffset = userVblock[0xc] + userVblock[0xd] * 0x100 + 0xcc;
int usernameLen = userVblock[0x10] + userVblock[0x1a] * 0x100;
userVblock-バイトの配列としての値HKLM \ SAM \ SAM \ Domains \ Account \ users \\V。
Vブロックの詳細については、 こちらをご覧ください 。
アルゴリズム
これで暗号化アルゴリズムを理解できます。
NTハッシュ生成 :
1.ユーザーのパスワードはUnicode文字列に変換されます。
2.この文字列に基づいてMD4ハッシュが生成されます。
3.結果のハッシュはDESアルゴリズムで暗号化され、キーはユーザーのRIDに基づいてコンパイルされます。
LMハッシュ生成 :
1.ユーザーパスワードは大文字に変換され、14バイトの長さになるまでゼロが埋め込まれます。
2.結果の文字列は7バイトの2つの半分に分割され、それぞれがDESアルゴリズムで暗号化されます。 その結果、長さ16バイトのハッシュ(長さ8バイトの2つの独立した半分で構成される)を取得します。
3.結果のハッシュはDESアルゴリズムで暗号化され、キーはユーザーのRIDに基づいてコンパイルされます。
4. Windows 2000以降では、受信した両方のハッシュは、syskeyユーティリティによって生成される「システムキー」またはブートキーと呼ばれるキーを使用してRC4アルゴリズムでさらに暗号化され、かなり巧妙な方法で暗号化されます。
元のパスワードと各ステップを個別に取得するための一般的な手順を検討してください
1.ブートキーを取得し、それに基づいてRC4のキーを生成し、RC4を使用してハッシュを復号化します
2.ユーザーのRID'ovからDESのキーを受け取り、ハッシュDES'omを復号化します
3.結果のハッシュは、総当たり攻撃を受けます。
ブートキー
システムキー(ブートキー)は4つの部分に分割され、次のレジストリキーにあります。
HKLM \システム\ CurrentControlSet \コントロール\ Lsa \ JD
HKLM \システム\ CurrentControlSet \コントロール\ Lsa \ Skew1
HKLM \システム\ CurrentControlSet \コントロール\ Lsa \ GBG
HKLM \システム\ CurrentControlSet \コントロール\ Lsa \データ
セクションシステムは、ファイルcである:\ WINDOWS \ System32に\のconfig \システム
CurrentControlSetセクションは、controlsetセクションの1つへのリンクであり、システムの起動時に作成されることに注意してください。 これは、システムが非アクティブの場合、システムファイルで見つからないことを意味します。 ファイル内のキーを検索する場合は、HKLM \ SYSTEM \ Select \ defaultでデフォルトのContolSet値を見つける必要があります。
代わりに、HKLM \システム\ CURRENTCONTROLSET \のHKLM \システム\ CONTROLSET001 \を見て- HKLM \ SYSTEM \選択\デフォルト= 1であれば例えば
各レジストリキーには、「クラス」と呼ばれる隠し属性があります。 Regeditでは簡単に表示されませんが、たとえば、これらのレジストリキーをテキストファイルにエクスポートすると表示されます。 winapiには、この属性を取得するRegQueryInfoKey関数があります。
フラグメントは、16進数の文字列表現で保存され、さらにBIG ENDIANの原則(つまり、文字列ではなく数字)に従って格納されます。
たとえば、次のレコードが見つかりました。
Key Name: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\JD
Class Name: 46003cdb = {0xdb,0x3c,0x00,0x46}
Key Name: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\Skew1
Class Name: e0387d24 = {0x24,0x7d,0x38,0xe0}
Key Name: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\GBG
Class Name: 4d183449 = {0x49,0x34,0x18,0x4d}
Key Name: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa\Data
Class Name: 0419ed03 = {0x03,0xed,0x19,0x04}
4つの部分から収集されたキーは、バイトの配列になります。
scrambled_key = {0xdb,0x3c,0x00,0x46,0x24,0x7d,0x38,0xe0,0x49,0x34,0x18,0x4d,0x03,0xed,0x19,0x04};
配列のさらなる要素は、一定の配列Pに基づいて再配置されます
int[] p = { 0xb, 0x6, 0x7, 0x1, 0x8, 0xa, 0xe, 0x0, 0x3, 0x5, 0x2, 0xf, 0xd, 0x9, 0xc, 0x4 };
この配列の要素は、順列の位置を定義します。
key[i] = scrambled_key[p[i]];
この例では、配列を取得します。
key[] = {0x4d,0x38,0xe0,0x3c,0x49,0x18,0x19,0xdb,0x46,0x7d,0x00,0x04,0xed,0x34,0x03,0x24 };
この配列は、いわゆるbootkeyです。 パスワードの暗号化は行わず、ブートキー、fブロックのフラグメント、およびいくつかの定数に基づく特定のハッシュを取得します。 ハッシュブートキーと呼びましょう。
ハッシュ化されたブートキー
ハッシュブートキーを取得するには、2つの文字列定数(ASCII)が必要です。
string aqwerty = "!@#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%\0";
string anum = "0123456789012345678901234567890123456789\0";
また、F-ブロックユーザー(HKLM \ SAM \ SAM \ドメインが必要になります\アカウント\ユーザーがFを\\)、 F:すなわち、その16 バイト[0x70を:0x80を]
これらの値を1つの大きな配列に接着して、MD5ハッシュを形成します。これはRC4暗号化のキーになります
rc4_key = MD5(F[0x70:0x80] + aqwerty + bootkey + anum).
ハッシュされたブートキーを取得する最後のステップは、rc4暗号化(または復号化-rc4では1つの同じ関数)で、FブロックフラグメントF [0x80:0xA0]のキーを取得します。
hashedBootkey = RC4(rc4_key,F[0x80:0xA0])
ハッシュ化されたブートキーを手に入れ、それを正しく処理する方法を学ぶことは残っています。
ハッシュ化されたブートキーでパスワードを解読する
LMおよびNTパスワードの場合、さらに2つの文字列定数が必要です-
string almpassword = "LMPASSWORD";
string antpassword = "NTPASSWORD";
同様に4バイト(ゼロパディング)の形態と前半ハッシュBootkey(hashedBootkey [0x0の:が0x10でRIDユーザー])。
これはすべて単一バイト配列に接着され、ルールに従ってMD5と見なされます。
rc4_key_lm = MD5(hbootkey[0x0:0x10] +RID + almpassword);
rc4_key_nt = MD5(hbootkey[0x0:0x10] +RID + antpassword);
受信したmd5ハッシュは、ユーザーのVブロック内のLMおよびNTハッシュを暗号化したrc4のキーです
userLMpass = RC4(rc4_key_lm,userSyskeyLMpass);
userNTpass = RC4(rc4_key_lm,userSyskeyNTpass);
この段階で、syskey暗号化なしで保存される形式でユーザーパスワードを受け取りました。最も難しいのは背後にあると言えます。 次のステップに進みます。
DES
ユーザーのRIDの4バイトに基づいて、いくつかの順列とビット演算を使用して、2つのDESキーを作成します。 難読化を行う関数は次のとおりです(C#):
private byte[] str_to_key(byte[] str) {
byte[] key = new byte[8];
key[0] = (byte)(str[0] >> 1);
key[1] = (byte)(((str[0] & 0x01) << 6) | (str[1] >> 2));
key[2] = (byte)(((str[1] & 0x03) << 5) | (str[2] >> 3));
key[3] = (byte)(((str[2] & 0x07) << 4) | (str[3] >> 4));
key[4] = (byte)(((str[3] & 0x0F) << 3) | (str[4] >> 5));
key[5] = (byte)(((str[4] & 0x1F) << 2) | (str[5] >> 6));
key[6] = (byte)(((str[5] & 0x3F) << 1) | (str[6] >> 7));
key[7] = (byte)(str[6] & 0x7F);
for (int i = 0; i < 8; i++) {
key[i] = (byte)(key[i] << 1);
}
des_set_odd_parity(ref key);
return key;
}
private byte[] sid_to_key1(byte[] rid) {
byte[] s = new byte[7];
s[0] = (byte)(rid[0] & 0xFF);
s[1] = (byte)(rid[1] & 0xFF);
s[2] = (byte)(rid[2] & 0xFF);
s[3] = (byte)(rid[3] & 0xFF);
s[4] = s[0];
s[5] = s[1];
s[6] = s[2];
return str_to_key(s);
}
private byte[] sid_to_key2(byte[] rid) {
byte[] s = new byte[7];
s[0] = (byte)((rid[3]) & 0xFF);
s[1] = (byte)(rid[0] & 0xFF);
s[2] = (byte)((rid[1]) & 0xFF);
s[3] = (byte)((rid[2]) & 0xFF);
s[4] = s[0];
s[5] = s[1];
s[6] = s[2];
return str_to_key(s);
}
さて、 des_set_odd_parity(ref key)関数以外にコメントするものはありません。これはopensslライブラリの関数の1つであり、そのタスクは「奇数ビット」を追加することです。攻撃に対するキーの耐性を高めるために使用されます。
次に、NT(またはLM)ハッシュを8バイトの2つの部分に分割し、DESで復号化します。半分はsid_to_key1関数で生成されたキーで暗号化され、2番目はsid_to_key2です。
obfskey_l = userNTpass[0x0:0x7]
obfskey_r = userNTpass[0x8:0xF]
byte[] deskey1 = sid_to_key1(RID);
byte[] deskey2 = sid_to_key2(RID);
byte[] md4hash_l = DES(obfskey_l, deskey1);
byte[] md4hash_r = DES(obfskey_r, deskey2);
2つの半分を接着した後、NTの場合はmd4ハッシュ、LMの場合はLanMan(DES)を取得します。 結果のハッシュは、ブルートフォース攻撃に完全に対応できます。
ところで、空のパスワードからのmd4ハッシュは31d6cfe0d16ae931b73c59d7e0c089c0です
この調査は、ソースコードophcrack-3.3.1に加えて、記事「赤いボタンをプッシュ:SysKeyとSAM」に基づいて実施されました。