筆者在2016年11月發現並報告了HP Support Assistant(HPSA)的許可權提升漏洞,HP Product Security Response Team(HP PSRT)響應迅速,但卻以此漏洞可以通過軟件的自動更新功能自動修復為由拒絕為其發佈安全公告和CVE。4月份想起這件事後,筆者又分析了一遍修補後的HPSA,發現HP的開發人員在修補中犯了更為低級的錯誤,導致補丁可以被繞過重新實現許可權提升。在隨後與HP PSRT的溝通與合作中,再一次利用其它技巧繞過了其後續修補,最終筆者協助HP PSRT完成了漏洞的修補。
本文將分析此漏洞的成因及多次補丁繞過,希望能以此為案例提高開發人員對安全的認識和理解,以减少由於對所用科技理解不到位和安全程式設計意識匱乏而導致的安全性漏洞。
HPSA是惠普推出的系統管理軟體,被默認安裝在惠普的所有PC中。其用於維護系統及打印機,並提供自動更新等功能。HPSA使用.Net開發,其系統服務HPSupportSolutionsFrameworkService使用WCF與用戶端通信,完成系統更新、管理等高許可權敏感操作。雖然HPSA使用了較新的分佈式處理科技WCF,然而在Server與Client通信過程中,卻採用了不正確的認證管道。導致攻擊者可以繞過認證,最終利用其敏感服務介面的缺陷,實現everyone到system的許可權提升。
本文將從WCF科技背景、漏洞發現、漏洞利用、補丁實現和兩次繞過幾個方面進行分析。
WCF(Windows Communication Foundation)是用於面向服務應用程序的程式設計框架,基於WCF的服務可以有兩種形式:1).通過IIS寄宿的管道將服務寄宿於IIS中;2).通過自我寄宿(Self-Hosting)的管道將服務寄宿於普通應用程序、windows服務之中。
WCF使用Endpoint的概念,在服務Endpoint和客戶Endpoint之間傳輸非同步消息。Endpoint用來描述消息發往什麼地方,如何被發送等行為。一個服務端Endpoint主要由三部分構成:
1). Addrsss唯一標識endpoint,是描述服務介面的URI,可以是相對地址(相對於ServiceHost(Type,Uri[])的URI),也可以是絕對地址。
2). Binding指定綁定在endpoint上的介面類別型,描述endpoint間通信時使用的協定、消息編碼方式、安全設置等。WCF支持:HttpBindingBase,MsmqBindingBase,NetNamedPipeBinding,NetPeerTcpBinding,NetTcpBinding,UdpBinding,WebHttpBinding,WSDualHttpBinding,WSHttpBindingBase,CustomBinding多種綁定類型。
3). Contract契約指定並設定綁定到當前endpoint上的服務介面,即哪些方法被匯出給用戶端,方法的授權情况、消息格式等。
HPSA的系統服務HPSupportSolutionsFrameworkService具有SYSTEM許可權,並開啟了多個允許everyone帳戶讀寫的NamePipe。這一敏感行為引起了筆者的注意,囙此dump下安裝包進一步分析。
反混淆反編譯後進行程式碼稽核,發現HPSA的系統服務使用WCF與Client進行互動。它創建了一個綁定在NetNamedPipeBinding(URI:”net.pipe://localhost/HPSupportSolutionsFramework/HPSA”)上的Endpoint,並允許Client調用多個綁定在此Endpoint上的服務介面:HP.SupportFramework.ServiceManager.Interfaces::IServiceInterface。
HPSA在連接建立時對Client進行了認證,以封锁敏感介面被惡意程式調用。Server與Client的互動過程如下表所示:
在Server與Client的互動過程中,HPSupportSolutionsFrameworkService使用了多種途徑來確保安全:驗證Client是否為HP簽名、使用SecureString存儲GUID、使用RNGCryptoServiceProvider生成亂數、調用敏感介面時驗證Client的Token。
千里之堤毀於蟻穴,在看似縝密的認證邏輯中卻存在安全性漏洞:HPSupportSolutionsFrameworkService使用Process.MainModule.FileName獲取Client的檔案路徑,隨後驗證其檔案簽名。然而,在C#中Process.MainModule.FileName是通過調用GetModuleFileName()索引行程的PEB(Process Environment Block)來獲取模塊路徑的。PEB位於行程的用戶空間中,囙此可以被攻擊者修改替換。攻擊者只需在連接Server的Endpoint前修改PEB,使模塊路徑指向一個有效的HP簽名檔案即可繞過簽名檢測,最終通過認證。
繞過HPSA Server的認證後,就可以調用綁定在此Endpoint上的服務介面函數了。接下來的工作就是從可用的服務介面函數中尋找可以利用的方法,實現許可權提升。HPSupportSolutionsFrameworkService的服務介面函數實現在HP.SupportFramework.ServiceManager.ServiceTasks::ServiceTask中,大致瀏覽一遍介面函數發現UncompressCabFile服務介面可以用於任意檔案寫,DeleteFile服務介面可以用於任意檔案删除。
UncompressCabFile的實現邏輯如下:
public bool UncompressCabFile(string cabFilePath, string destDirectory, string token)
{
if (!\u0004.Instance.\u0001(SharedCommon.StringToSecureString(token)))
{
if (DebugLog.IsDebug)
{
DebugLog.LogDebugMessage("signature validation failure for UncompressCabFile", DebugLog.IndentType.None);
}
return false;
}
if (!File.Exists(cabFilePath))
{
return false;
}
if (!Validation.VerifyHPSignature(cabFilePath))
{
File.Delete(cabFilePath);
return false;
}
string text = "\"" + cabFilePath + "\"";
string text2 = "\"" + destDirectory + "\"";
ProcessStartInfo processStartInfo = new ProcessStartInfo();
processStartInfo.set_WindowStyle(1);
processStartInfo.set_Arguments("-qq " + text + " -d " + text2);
processStartInfo.set_FileName(SupportAssistantCommon.FrameworkPath + "Modules\\unzip.exe");
Process process = new Process();
process.set_StartInfo(processStartInfo);
process.Start();
process.WaitForExit();
if (File.Exists(cabFilePath))
{
File.Delete(cabFilePath);
}
return true;
}
UncompressCabFile利用unzip.exe將壓縮檔cabFilePath解壓至destDirectory,在解壓前首先驗證了cab檔案的簽名。由於在簽名驗證和解壓縮之間存在時間差,囙此這裡存在TOCTTOU(Time of Check To Time of Use)問題,可以利用條件競爭繞過簽名檢測將檔案寫入任意目錄,最終可以實現許可權提升。
DeleteFile的實現邏輯如下:
public void DeleteFile(string filePath, string token)
{
if (\u0007.Instance.\u0001(SharedCommon.StringToSecureString(token)))
{
try
{
File.Delete(filePath);
return;
}
catch (Exception ex)
{
if (DebugLog.IsDebug)
{
DebugLog.LogDebugMessage("exception in DeleteFile: " + ex.Message, DebugLog.IndentType.None);
}
return;
}
}
if (DebugLog.IsDebug)
{
DebugLog.LogDebugMessage("token not valid in DeleteFile", DebugLog.IndentType.None);
}
}
囙此利用過程如下所述:1.修改PEB,將行程路徑指向合法的HP簽名程式2.通過反射機制獲取HP.SupportFramework.ServiceManager.Interfaces命名空間中ServiceInterface類的get_Instance()方法3.實例化ServiceInterface4.調用ServiceInterface::UncompressCabFile服務介面,結合條件競爭實現許可權提升
漏洞報告後HP PSRT快速回應,並在半個月內通過郵件告知已經發佈了新版來解决此安全性漏洞。4月初,再次分析後發現新版本的HPSA依舊在使用everyone可寫的NamePipe,筆者决定針對HP的修復再次分析。
通過短暫的逆向分析,定位了補丁修復位置。補丁在HP.SupportFramework.ServiceManager.Interfaces::ServiceInterface::get_Instance()中添加了如下邏輯:
StackFrame stackFrame = new StackFrame(1);
MethodBase method = stackFrame.GetMethod();
Type declaringType = method.get_DeclaringType();
string name = method.get_Name();
if (name.ToLowerInvariant().Contains("invoke"))
{
string text2 = new \u0007().\u0001(Process.GetCurrentProcess());
text2 = Uri.UnescapeDataString(Path.GetFullPath(text2));
string text3 = Assembly.GetEntryAssembly().get_Location();
text3 = Uri.UnescapeDataString(Path.GetFullPath(text3));
if (text3.ToLowerInvariant() != text2.ToLowerInvariant())
{
if (DebugLog.IsDebug)
{
DebugLog.LogDebugMessage(string.Concat(new string[]
{
"Illegal operation. Calling process (",
text3,
") is not the same as process invoking method (",
text2,
")"
}), DebugLog.IndentType.None);
}
throw new Exception("Invoking methods is not allowed.");
}
}
namespace \u0007
{
// Token: 0x02000081 RID: 129
internal sealed class \u0007
{
internal string \u0001(Process \u0002)
{
try
{
string result = \u0002.get_MainModule().get_FileName();
return result;
}
…
}
…
}
}
以上程式碼在實例化時,首先通過Assembly.GetEntryAssembly().get_Location()獲取Client的檔案路徑,並與通過Process.MainModule.FileName方法獲取的Client模塊路徑進行對比,如果不一致則拋出异常。
.Net的運行時環境規定,擁有同樣標識的.Net程式集只能被加載一次。由於HP.SupportFramework.ServiceManager.dll已經被HPSupportSolutionsFrameworkService加載,所以HP的開發人員認為此舉可以有效封锁攻擊者通過修改PEB,並利用反射機制創建ServiceInterface來繞過認證。
然而,HP的.Net開發人員顯然是忽視了行程空間的安全邊界。此處所做的檢測仍然位於Client行程空間,如同修改PEB那樣,Client依舊擁有許可權修改行程空間內的數據和程式碼。Client可以採取多種方案繞過檢測:1.在實例化前,定位並修改HP.SupportFramework.ServiceManager.dll中的檢測邏輯;2.自己實現與Server的互動,認證,服務介面調用等;3.靜態Patch檢測邏輯,並修改程式集HP.SupportFramework.ServiceManager.dll的標識,使修改後的檔案可以被加載進Client行程空間。
其中方案3最為簡潔,這裡可以直接利用工具修改其判斷邏輯為if(text3.ToLowerInvariant()== text2.ToLowerInvariant()),並修改程式集的版本號(微軟官方檔案中描述了影響.Net可執行程式標識的屬性包括:AssemblyCultureAttribute,AssemblyFlagsAttribute,AssemblyVersionAttribute [3])。最終實現對補丁的繞過,重新實現許可權提升。
又一次,將漏洞和修補方案報告給HP PSRT後,HP的開發人員從兩個方面做了修補:1.對Client的認證管道做調整,Server不再使用Process.MainModule.FileName獲取Client的檔案路徑,而是通過GetProcessImageFileName()來獲取,避免從PEB獲取到被篡改的Client檔案路徑。2.在UncompressCabFile和DeleteFile中,檢查了參數裏的檔案/目錄路徑是否合法。
查看UncompressCabFile和DeleteFile裏的檔案/目錄路徑檢測邏輯,發現其僅僅使用了字串比較來檢測路徑是否合法,而不是對規範化後的路徑進行檢測。代碼如下:
internal static bool \u0001(string \u0002)
{
string[] array = new string[]
{
"AppData\\Local\\Hewlett-Packard\\HP Support Framework",
Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + "\\Hewlett-Packard\\HP Support Framework",
SupportAssistantCommon.MainAppPath,
SupportAssistantCommon.FrameworkPath
};
string[] array2 = array;
for (int i = 0; i < array2.Length; i++)
{
string text = array2[i];
if (\u0002.ToLowerInvariant().Contains(text.ToLowerInvariant()))
{
return true;
}
}
if (DebugLog.IsDebug)
{
DebugLog.LogDebugMessage("Invalid File detected: " + \u0002, DebugLog.IndentType.None);
}
return false;
}
囙此這裡使用目錄穿越即可繞過路徑檢查。對Client的認證也很容易繞過,使用Hewlett-Packard安裝目錄裏任意一個擁有有效簽名的程式,將漏洞利用程式碼注入其中即可繞過對Client的認證檢測。
最終,HP PSRT修正了路徑檢測的邏輯,新增了對目錄穿越行為的檢測,相關程式碼如下所示:
internal static bool \u0002(string \u0002)
{
if (!Path.IsPathRooted(\u0002) || \u0002.StartsWith("\\") || \u0002.Contains("..") || \u0002.Contains(".\\"))
{
if (DebugLog.IsDebug)
{
DebugLog.LogDebugMessage("Invalid File detected: " + \u0002, DebugLog.IndentType.None);
}
return false;
}
return true;
}
筆者在漏洞細節中建議HP PSRT徹查所有服務介面的安全性,對其參數進行正確的檢測,以免再次被攻擊者利用。
安全性漏洞會在軟件生命週期(需求分析、設計、實現、維護等過程)內的各個階段被引入,研發人員除了需要在設計和實現階段避免安全性漏洞外,還需要在出現漏洞後運用合理的修補方案。這裡HPSA出現的問題就是在設計、實現、維護階段共同引入的。
1).設計階段也許是為了保證未簽名程式也可以調用服務端的非敏感介面(例如DecryptFile,DeleteTempSession等未驗證Client身份的服務介面),又或許是為了讓Guest用戶也可以對系統進行更新等操作。最終導致HPSA沒有利用系統提供的存取權限檢查機制[2]來隔離許可權邊界,使得軟件從設計之初就引入安全風險。
2).實現階段HPSA的開發人員未意識到通過Process.MainModule.FileName獲取Client檔案路徑的不安全性,從而導致認證可以被繞過;也未意識到敏感服務介面的危險性,未對敏感服務介面的參數的合法性進行正確檢測,從而導致可以被攻擊者用於許可權提升。事實上,任何試圖通過行程對應的檔案來檢查行程安全性的做法都是存在安全隱患的。
3).維護階段在對一個漏洞的三次修補過程中,HPSA的開發人員更是忽視了行程的安全邊界,使用了多種錯誤的修補方案,導致補丁被多次繞過。
從這個漏洞的成因和多次修補可以看出,HP的開發人員存在對所用科技理解不到位,缺乏安全程式設計經驗的問題。希望這篇文章能給研發人員帶來安全程式設計的思考和經驗的提升,不在設計、實現、維護階段發生類似HPSA這樣的一系列錯誤。
- 11/30/2016 Provide vulnerability details and PoC to HP Inc. via [email protected]
- 12/02/2016 HP Inc. responded that they had validated and opened case PSR-2016-0118 for the issue
- 12/13/2016 HP Inc. released a fix for the reported issue
- 01/04/2017 HP Inc. responded that the vulnerability was fixed
- 01/05/2017 Ask for more information
- 01/14/2017 HP Inc. responded that they are still investigating
- 02/03/2017 HP Inc. responded that this issue can be automatically resolved,thus they don’t issue security bulletin and CVE numbers
- 04/20/2017 Report the patch can be bypass. Provide vulnerability details and PoC to HP Inc.
- 04/20/2017 HP Inc. responded that they had validated and opened case PSR-2017-0056 for the issue
- 05/29/2017 HP Inc. responded that the fixed version will be released in mid-June 2017
- 06/07/2017 HP Inc. published a new patch and asked me to confirm the vulnerability doesn’t exist
- 06/07/2017 Report the patch can be bypass again. Provide vulnerability details and PoC to HP Inc. Also,provide some repair advice.
- 06/15/2017 HP Inc. published a new patch and asked me to confirm the vulnerability doesn’t exist
- 06/15/2017 Confirm the patch is valid. And recommend HP Inc. make sure there no other vulnerable functions can be exploited now,nor will be in the future.
- 08/31/2017 HP Inc. published a security bulletin(https://support.hp.com/sk-en/document/c05648974)and issued a CVE(CVE-2017-2744).
- 09/11/2017 Report the patch can be bypass with junction point. Provide vulnerability details to HP Inc.
- 10/06/2017 HP Inc. responded that they restricted the directories within c:\program files\hewlett-packard\ to prevent the bypass.
1. Windows Communication Foundation Securityhttps://msdn.microsoft.com/en-us/library/ms732362(v=vs.110).aspx
2. Authentication and Authorization in WCF Services–Part 1https://msdn.microsoft.com/en-us/library/ff405740.aspx
3. Setting Assembly Attributeshttps://msdn.microsoft.com/en-us/library/4w8c1y2s(v=vs.110).aspx