April 16th, 2024

Dubious security vulnerability: Program allows its output to be exfiltrated

A security vulnerability report came in for the reg.exe program.

There is an information disclosure vulnerability in the reg.exe program. It is possible to use this program to exfiltrate data, as demonstrated by the following proof of concept.

The proof of concept was a program that ran reg.exe, captured the program output, and then uploaded the results to a presumed malicious web server.

The finder explained that the system should not have allowed this to happen. The reg.exe program should not have allowed its presumed sensitive output to be captured and sent to another system, and the system should not have allowed the malicious proof of concept program to run.

The first thing to note is that reg.exe did nothing wrong. It had one job: reading the data from the registry and printing it to its standard output, and it did it. There was no security boundary crossed: The reg.exe program doesn’t grant the user access to data that the user doesn’t already have access to. And the reg.exe cannot control what happens to the output it generated. Not only is this not reg.exe‘s responsibility, it would require the power of clairvoyance!

var secret_data = read_output("reg.exe HKLM\Something\Valuable")
if (prompt("Should I send this to somebody?")) {
    send(secret_data);
}

Does this program disclose sensitive data? Well, it depends on how the user answers the question.

“Well, then reg.exe should refuse to generate the data if it could potentially be disclosed for any input.”

Great, let’s try this program:

var secret_data = read_output("reg.exe HKLM\Something\Valuable")
if (find_counterexample_to_collatz_conjecture()) {
    send(secret_data);
}

To determine whether this discloses information, we would have to solve a famous unsolved problem in mathematics.

“Well, then, reg.exe should play it safe and refuse to generate the data if it’s not sure.”

If you put it that way, then you’re basically saying that reg.exe should never produce any data.

From a security standpoint, what happened here is that the user ran a malicious program. The user did so voluntarily. There was nothing that forced the user to run the program, there was nothing that tricked the user into running the program, there was nothing that ran the program automatically. It was just assumed that the user ran the program.¹

If the program were downloaded, then tools like SmartScreen could warn the user “Hey, you’re downloading a program that has a low reputation. Do you really want to download it?” Running reg.exe is not inherently suspicious, and sending data to a Web server is something that users probably want to happen when they do things like, oh I dunno, visit a Web site. Even if the program manages to get onto the system, anti-malware tools like Windows Defender will also do their best to block malicious programs. But what this program does is not obviously malicious. It gathered information from the registry and sent it to a Web server. This sounds like just the sort of thing that a product support troubleshooting tool would do: Read some registry keys and send them to a Web site for product support purposes.

The finder suggested that the system should outright refuse to run untrusted programs. Well, you can do that if you want. There are a multitude of policies for locking down systems to different degrees. For example, you could use Windows S-Mode to block any programs that did not come through the Microsoft Store.

But requiring a program to refuse to allow its output to be used in undesirable ways is asking for the impossible. It’s like instructing someone, “You are not allowed to say anything that could possibly be used in a way I don’t like.” The only way to comply is simply never to say anything. A program that refuses to generate any output is certainly secure, but it’s also not very useful.

Bonus chatter: You might say, “Well, the program should produce encrypted output, so that even if it gets exfiltrated, only someone with the correct key can decode it.” I’m sure everybody that runs reg.exe will be thrilled that the output of the program is unreadable by humans. And besides, it won’t help you, because the malicious program would just decrypt the output before uploading!

¹ If you can find a way to force the user to run a malicious program, or trick the user into running a malicious program, or cause the malicious program to run automatically, then you may be onto something, but this report never made such a claim. It assumed that code execution had already been achieved. This is just another case of if I can run an arbitrary program, then I can do arbitrary things, or what we sometimes jokingly call MS07-052: Code execution results in code execution.

Topics
Other

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

2 comments

Discussion is closed. Login to edit/delete existing comments.

  • Markus G. Kuhn · Edited

    There were once operating systems, specifically written for government customers, that did control where classified confidential information goes. “Trusted Solaris” was an example, as was the “Compartmented Mode Workstation”. These were Unix derivatives with added “mandatory access control” mechanisms for “labeled multi-level security”. There was an “Orange Book Class B” standard defining this functionality, based on the Bell-LaPadula access-control model. You could label an information object as e.g. “TOP SECRET” and then any process, file...

    Read more
  • 紅樓鍮

    Nitpick: The program listed in the last article you link to is actually not guaranteed to consume 100% CPU (at least until P2809) because empty infinite loops in C++ are undefined behavior, meaning the compiler is allowed to optimize the loop away!