Quantcast
Channel: C#タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9703

IE/Edgeに保存されているログインデータを出力してみる

$
0
0

はじめに

Chromeに保存されているパスワードを解読してみる
に引き続き、今回はInternet ExplorerとMicrosoft EdgeがどのようにWebサイトのログインデータを保存しているのか調べてみました。

IE/Edgeのログインデータの保存方法

調べてみたところ、ログインデータは「Windows Vaults」と呼ばれる特殊なフォルダに保存されていることがわかりました。
具体的には、C:\Windows\System32\vaultcli.dllに含まれているWindows VaultsのAPIを使用してアクセスする仕組みになっているようです。

ログインデータを読み込んでみる

ログインデータを読み込んで出力するプログラムは以下のようになります。

usingSystem;usingSystem.Collections.Generic;usingSystem.Reflection;usingSystem.Runtime.InteropServices;namespaceFetchVaultCredential{publicstaticclassProgram{publicenumVAULT_ELEMENT_TYPE:int{Undefined=-1,Boolean=0,Short=1,UnsignedShort=2,Int=3,UnsignedInt=4,Double=5,Guid=6,String=7,ByteArray=8,TimeStamp=9,ProtectedArray=10,Attribute=11,Sid=12,Last=13}publicenumVAULT_SCHEMA_ELEMENT_ID:int{Illegal=0,Resource=1,Identity=2,Authenticator=3,Tag=4,PackageSid=5,AppStart=100,AppEnd=10000}[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Ansi)]publicstructVAULT_ITEM_WIN8{publicGuidSchemaId;publicIntPtrpszCredentialFriendlyName;publicIntPtrpResourceElement;publicIntPtrpIdentityElement;publicIntPtrpAuthenticatorElement;publicIntPtrpPackageSid;publiculongLastModified;publicintdwFlags;publicintdwPropertiesCount;publicIntPtrpPropertyElements;}[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Ansi)]publicstructVAULT_ITEM_WIN7{publicGuidSchemaId;publicIntPtrpszCredentialFriendlyName;publicIntPtrpResourceElement;publicIntPtrpIdentityElement;publicIntPtrpAuthenticatorElement;publiculongLastModified;publicintdwFlags;publicintdwPropertiesCount;publicIntPtrpPropertyElements;}[StructLayout(LayoutKind.Explicit,CharSet=CharSet.Ansi)]publicstructVAULT_ITEM_ELEMENT{[FieldOffset(0)]publicVAULT_SCHEMA_ELEMENT_IDSchemaElementId;[FieldOffset(8)]publicVAULT_ELEMENT_TYPEType;}[DllImport("vaultcli.dll")]publicexternstaticintVaultOpenVault(refGuidvaultGuid,intoffset,refIntPtrvaultHandle);[DllImport("vaultcli.dll")]publicexternstaticintVaultCloseVault(refIntPtrvaultHandle);[DllImport("vaultcli.dll")]publicexternstaticintVaultFree(refIntPtrvaultHandle);[DllImport("vaultcli.dll")]publicexternstaticintVaultEnumerateVaults(intoffset,refintvaultCount,refIntPtrvaultGuid);[DllImport("vaultcli.dll")]publicexternstaticintVaultEnumerateItems(IntPtrvaultHandle,intchunkSize,refintvaultItemCount,refIntPtrvaultItem);[DllImport("vaultcli.dll",EntryPoint="VaultGetItem")]publicexternstaticintVaultGetItem_WIN8(IntPtrvaultHandle,refGuidschemaId,IntPtrpResourceElement,IntPtrpIdentityElement,IntPtrpPackageSid,IntPtrzero,intarg6,refIntPtrpasswordVaultPtr);[DllImport("vaultcli.dll",EntryPoint="VaultGetItem")]publicexternstaticintVaultGetItem_WIN7(IntPtrvaultHandle,refGuidschemaId,IntPtrpResourceElement,IntPtrpIdentityElement,IntPtrzero,intarg5,refIntPtrpasswordVaultPtr);publicstaticvoidGetLogins(){// Windows VaultsをチェックvarOSVersion=Environment.OSVersion.Version;varOSMajor=OSVersion.Major;varOSMinor=OSVersion.Minor;TypeVAULT_ITEM;if(OSMajor>=6&&OSMinor>=2){// Windows 8以降VAULT_ITEM=typeof(VAULT_ITEM_WIN8);}else{// Windows 7以前VAULT_ITEM=typeof(VAULT_ITEM_WIN7);}// VAULT_ITEM_ELEMENTからItemValueを取り出すobjectGetVaultElementValue(IntPtrvaultElementPtr){objectresults;objectpartialElement=Marshal.PtrToStructure(vaultElementPtr,typeof(VAULT_ITEM_ELEMENT));FieldInfopartialElementInfo=partialElement.GetType().GetField("Type");varpartialElementType=partialElementInfo.GetValue(partialElement);IntPtrelementPtr=(IntPtr)(vaultElementPtr.ToInt64()+16);switch((int)partialElementType){case7:// String(パスワード)IntPtrStringPtr=Marshal.ReadIntPtr(elementPtr);results=Marshal.PtrToStringUni(StringPtr);break;case0:// boolresults=Marshal.ReadByte(elementPtr);results=(bool)results;break;case1:// Shortresults=Marshal.ReadInt16(elementPtr);break;case2:// Unsigned Shortresults=Marshal.ReadInt16(elementPtr);break;case3:// Intresults=Marshal.ReadInt32(elementPtr);break;case4:// Unsigned Intresults=Marshal.ReadInt32(elementPtr);break;case5:// Doubleresults=Marshal.PtrToStructure(elementPtr,typeof(Double));break;case6:// GUIDresults=Marshal.PtrToStructure(elementPtr,typeof(Guid));break;case12:// セキュリティ識別子IntPtrsidPtr=Marshal.ReadIntPtr(elementPtr);varsidObject=newSystem.Security.Principal.SecurityIdentifier(sidPtr);results=sidObject.Value;break;default:// VAULT_ELEMENT_TYPEが実装されていないので無視results=null;break;}returnresults;}intvaultCount=0;// VaultのGUIDポインタIntPtrvaultGuidPtr=IntPtr.Zero;// 全てのVaultを取得varresult=VaultEnumerateVaults(0,refvaultCount,refvaultGuidPtr);if(result!=0){thrownewException("Vaultを取得できません。(0x"+result.ToString()+")");}// GUID対応表IntPtrguidAddress=vaultGuidPtr;Dictionary<Guid,string>vaultSchema=newDictionary<Guid,string>();vaultSchema.Add(newGuid("2F1A6504-0641-44CF-8BB5-3612D865F2E5"),"Windows Secure Note");vaultSchema.Add(newGuid("3CCD5499-87A8-4B10-A215-608888DD3B55"),"Windows Web Password Credential");vaultSchema.Add(newGuid("154E23D0-C644-4E6F-8CE6-5069272F999F"),"Windows Credential Picker Protector");vaultSchema.Add(newGuid("4BF4C442-9B8A-41A0-B380-DD4A704DDB28"),"Web Credentials");vaultSchema.Add(newGuid("77BC582B-F0A6-4E15-4E80-61736B6F3B29"),"Windows Credentials");vaultSchema.Add(newGuid("E69D7838-91B5-4FC9-89D5-230D4D4CC2BC"),"Windows Domain Certificate Credential");vaultSchema.Add(newGuid("3E0E35BE-1B77-43E7-B873-AED901B6275B"),"Windows Domain Password Credential");vaultSchema.Add(newGuid("3C886FF3-2669-4AA2-A8FB-3F6759A77548"),"Windows Extended Credential");vaultSchema.Add(newGuid("00000000-0000-0000-0000-000000000000"),null);for(inti=0;i<vaultCount;i++){// Vaultを開くobjectvaultGuidString=Marshal.PtrToStructure(guidAddress,typeof(Guid));GuidvaultGuid=newGuid(vaultGuidString.ToString());guidAddress=(IntPtr)(guidAddress.ToInt64()+Marshal.SizeOf(typeof(Guid)));IntPtrvaultHandle=IntPtr.Zero;stringvaultType;if(vaultSchema.ContainsKey(vaultGuid)){vaultType=vaultSchema[vaultGuid];}else{vaultType=vaultGuid.ToString();}result=VaultOpenVault(refvaultGuid,0,refvaultHandle);if(result!=0){thrownewException(vaultType+"からVaultを開けませんでした。"+"(0x"+result.ToString()+")");}// Vault内のアイテムを列挙intvaultItemCount=0;IntPtrvaultItemPtr=IntPtr.Zero;result=VaultEnumerateItems(vaultHandle,512,refvaultItemCount,refvaultItemPtr);if(result!=0){thrownewException(vaultType+"からのVaultの列挙に失敗しました。"+"(0x"+result.ToString()+")");}varstructAddress=vaultItemPtr;if(vaultItemCount>0){for(intj=1;j<=vaultItemCount;j++){// Vaultの列挙を開始varcurrentItem=Marshal.PtrToStructure(structAddress,VAULT_ITEM);structAddress=(IntPtr)(structAddress.ToInt64()+Marshal.SizeOf(VAULT_ITEM));IntPtrpasswordVaultItem=IntPtr.Zero;// フィールド情報の検索FieldInfoschemaIdInfo=currentItem.GetType().GetField("SchemaId");GuidschemaId=newGuid(schemaIdInfo.GetValue(currentItem).ToString());FieldInfopResourceElementInfo=currentItem.GetType().GetField("pResourceElement");IntPtrpResourceElement=(IntPtr)pResourceElementInfo.GetValue(currentItem);FieldInfopIdentityElementInfo=currentItem.GetType().GetField("pIdentityElement");IntPtrpIdentityElement=(IntPtr)pIdentityElementInfo.GetValue(currentItem);FieldInfodateTimeInfo=currentItem.GetType().GetField("LastModified");ulonglastModified=(ulong)dateTimeInfo.GetValue(currentItem);IntPtrpPackageSid=IntPtr.Zero;if(OSMajor>=6&&OSMinor>=2){// 新しいバージョンにはパッケージSIDがあるFieldInfopPackageSidInfo=currentItem.GetType().GetField("pPackageSid");pPackageSid=(IntPtr)pPackageSidInfo.GetValue(currentItem);result=VaultGetItem_WIN8(vaultHandle,refschemaId,pResourceElement,pIdentityElement,pPackageSid,IntPtr.Zero,0,refpasswordVaultItem);}else{result=VaultGetItem_WIN7(vaultHandle,refschemaId,pResourceElement,pIdentityElement,IntPtr.Zero,0,refpasswordVaultItem);}if(result!=0){thrownewException("Error occured while retrieving vault item. Error: 0x"+result.ToString());}objectpasswordItem=Marshal.PtrToStructure(passwordVaultItem,VAULT_ITEM);FieldInfopAuthenticatorElementInfo=passwordItem.GetType().GetField("pAuthenticatorElement");IntPtrpAuthenticatorElement=(IntPtr)pAuthenticatorElementInfo.GetValue(passwordItem);// 認証情報を取得objectcred=GetVaultElementValue(pAuthenticatorElement);objectpackageSid=null;if(pPackageSid!=IntPtr.Zero&&pPackageSid!=null){packageSid=GetVaultElementValue(pPackageSid);}if(cred!=null)// 取得に成功したデータを表示{Console.WriteLine("Vault Type   : {0}",vaultType);objectresource=GetVaultElementValue(pResourceElement);if(resource!=null){Console.WriteLine("Resource     : {0}",resource);}objectidentity=GetVaultElementValue(pIdentityElement);if(identity!=null){Console.WriteLine("Identity     : {0}",identity);}if(packageSid!=null){Console.WriteLine("PacakgeSid  : {0}",packageSid);}Console.WriteLine("Credential   : {0}",cred);Console.WriteLine("LastModified : {0}",System.DateTime.FromFileTimeUtc((long)lastModified));Console.WriteLine();}}}}}publicstaticvoidMain(string[]args){GetLogins();Console.ReadKey(true);}}}

実行する際は、ビルド設定の「32 ビットを選ぶ」のチェックを外してください。

仕組みとしては、Windows VaultのAPIをDllImportで読み込み、それを使ってVaultの構造体を取得してデータを取り出しています。
取得したデータ自体に暗号化などはされていないようです。

これを実行すると、以下のようにログインデータが出力されます。

Vault Type   : Web Credentials
Resource     : https://login.microsoftonline.com/
Identity     : foobar314159@yahoo.co.jp
Credential   : TekitounaPassword
LastModified : 2020/05/23 15:50:49

Vault Type   : Web Credentials
Resource     : https://accounts.google.com/
Identity     : unknown141421356@gmail.com
Credential   : SecurePass123456
LastModified : 2020/05/26 11:52:57

おわりに

という訳で、IE/Edgeに保存されているログインデータも取得できてしまいました。
実は、Windows Vaultsに関するAPIの仕様はMicrosoft公式からは公開されていません。
だから、Microsoftは特にログインデータに暗号化処理をしなかったのだと思います。
しかし、海外のハッカー達にとってWindows Vaultsの仕様を一から自力で解析することなど朝飯前だったようです。恐ろしいですね。

参考文献


Viewing all articles
Browse latest Browse all 9703

Trending Articles