Today I will share something that I have discovered some time ago. It is not something that will revolutionize your investigations, but sometimes can help you understand what happened on the system that you are investigating. What is more, it may be treated as a curiosity (personally I really like such things). So what am I talking about? As the title suggests, there is a way to run commands found under the RunOnce key, without any logons to the system.
This method only applies to the key located in HKEY_LOCAL_MACHINE, for some reasons it does not trigger RunOnce key from HKEY_CURRENT_USER.
For those who need to refresh their memory and recall what RunOnce key is, I pasted a small definition below:
RunOnce registry keys to make a program run when a user logs on. The
Run key makes the program run every time the user logs on, while the
RunOnce key makes the program run one time, and then the key is deleted. These keys can be set for the user or the machine.“
The Windows registry includes the following four
To run that key, we have to use a Windows native binary named RUNONCE.EXE. It can be found in C:\Windows\System32. There is not much information about this executable in the Internet, except few things that we can discover our-self. The file description says that it is a Run Once Wrapper.
- Date: empty
- Language: English-United States
- Code-page: Unicode UTF-16, little endian
- Company Name: Microsoft Corporation
- File Description: Run Once Wrapper
- File Version: 10.0.16299.15 (WinBuild.160101.0800)
- Internal Name: RunOnce
- LegalCopyright: © Microsoft Corporation. All rights reserved.
- OriginalFilename: RUNONCE.EXE
- ProductName: Microsoft® Windows® Operating System
- ProductVersion: 10.0.16299.15
To better understand that binary, I debugged it and found out that it can take some arguments and based on them run commands found under HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunOnce, or it just quits.
It took me some time (during debugging) to find the place in the binary, where the executable discovers arguments that were passed starting the process. Finally, I found out that it uses something that is called _wcmdln. Quick googling let me discover that it is an internal CRT global variable, which stores the complete command line. It may be worth to remember that, especially when you debug malicious software that uses arguments to build a malicious logic.
Moving further, I found all arguments that can be handle by the binary.
As you can see in the screenshot above, the executable handles five different arguments:
Having that information, I googled it again and found two interesting links:
The first link says that this binary “executes a Run Once Task that has been configured in the registry” and as the example it provides this command:
During my tests, I found out that if you want to start RunOnce’s commands, it’s enough to simply run RUNONCE.EXE with only one short argument “-r“. That was quite interesting, because I could not find this argument debugging the binary. And because I really wanted to find it out, I started from the beginning and first I made myself familiar with StrCmpICW. Doing that, I understood that this function returns zero if strings are identical and a nonzero value if strings are different. When I was using “-r” as the argument, StrCmpICW was always returning a non zero value – and that makes sense, right? The value was different than the arguments I found in the binary. But then I noticed, that after the last strings comparison the binary substracts some values from the number stored in ECX. And at the end of the function, makes a decision (run or not run commands) based on that value. In addition to that, before anything is substracted from ECX, the data located under [RBX] address is moved to ECX. Dumping the data located under RBX, I found an argument – which I passed during the execution.
Once the process has the argument in ECX, several mathematical operations impacting that registry are made. If ECX at the beginning equals 72, the function returns 1 (in the EAX registry) and later the executable takes the value from EAX and compares with “1”, if it is the same the RunOnce’s tasks are executed.
And now, there is one really “fun” thing. If you run that tool using any parameter that starts with “r”, the commands under RunOnce key will be executed. Let’s take a look at the example below, I run this command: C:\Users\REM\Desktop\runonce.exe -rhubarb
As you can see in the screenshot above, the standard checks (for RunOnceEx6432 and other arguments) did not give any positive results, and at the end the executable took the parameter which I provided running RUNONCE.EXE. But please notice that it only took the first character! I provided “rhubarb” not “r”.
The screenshot below even better illustrates that moment:
In the ECX, there is only 72 in HEX and nothing else. Going further the function return the value which triggers the enumeration of HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnce and run commands found under that key. In my case there was only one value for “notepad.exe”. Screenshots below demonstrate that execution.
Okay, now the most important part of that article… What does it mean for us (DFIR analysts)? Theoretically, adversaries can add the malicious command/payload to RunOnce key and then trigger the execution. There may be some reasons behind such approach. For example this can be a technique to bypass some detections. What is more, the executed payload in such way will be automatically deleted by the system, unless adversaries prefix a RunOnce value name with an exclamation point (!) to defer deletion of the value. But remember, to add and run the value under the RunOnce key in HKLM you need elevated rights.
As it was said, it can be a technique used by threat actors, but… I think that there is one more scenario in which this finding is important. I have observed that there are some legitimate installers and updaters, which use runonce.exe to run everything from RunOnce key in HKLM. I do not know why they do it, but I know it is possible. In such scenario, you can investigate the infected machine and find out that something was executed, but in your opinion it should never have taken place – because the machine was not rebooted and no one logged in to the system. How to deal with something like that?
Fortunately for us, there is Microsoft-Windows-Shell-Core.evtx. This event log provides several logs, which tell when RunOnce and Run key was enumerated and subsequently what commands were executed. Below you can find four most important event IDs, which are significant for RunOnce and Run key executions:
- 9705 – Started enumeration of commands for registry key…
- 9706 – Finished enumeration of commands for registry key…
- 9707 – Started execution of command…
- 9708 – Finished execution of command… (that log also gives a PID!).
So if you parse that logs along with PREFETCH files, you will get a timeline which can look like that:
Based on the information that you can see here, you can conclude that commands found under RunOnce key were triggered by the execution of RUNONCE.EXE. Without having that information, you could completely omit that part and wonder why the malicious payload was executed – “but I have analyzed the sample, and it should wait for a logon..!”.
I have never seen any malware which uses that technique on purpose, but it may be still a good idea to monitor such executions in your environment. You should be able to use your EDR telemetry, to understand what applications launch it on the regular basis and then exclude them from your triggers.
If you ever find a sample that uses RUNONCE.EXE to execute the malicious payload, please…. let me know!!!