1

I have a windows application that runs in 2 processes and communicates over localhost network port.

This application can hold secrets, such as a web cookie.

I'm trying to think through the security vulnerabilities.

My impressions:

  1. With modern virtual memory, applications have protected execution space (DEP) by default, so a developer doesn't need to do anything?
  2. Memory ACLs only protect you from malicious code running as OTHER users. If Bob installs my app and a malicious app, the malicious app running as Bob could access my apps memory.
  3. ASLR is for buffer-overflow exploits and doesn't apply to read-prevention.
  4. The localhost call is not secured against other users on the computer, anyone can listen to this. It would be more secure to use another ICP medium.
  5. If the localhost network port is decided at runtime, it is easier to MITM than if it was fixed.
  6. Windows provides a user key-store that might be a good place to hold secrets. But if the application holds the secret in RAM it is still potentially readable by other processes.

Please let me know what I've got wrong. Thanks!

0

TL;DR: You almost certainly should switch to more secure IPC. Beyond that, you probably don't have a great threat model.

I'll respond to your numbered points first, then write a general answer below:

  1. All recent Windows versions support DEP and all recent Microsoft compilers (and probably most or all third-party compilers too) will automatically mark their output as DEP-compatible (/NXCOMPAT). It is possible to configure Windows to override this flag (in either direction - using DEP even without the flag, or not using it even when present) but the default configuration will use the flag. Note that DEP doesn't really have anything to do with virtual memory per se - you could in theory implement it on a system that doesn't use virtual memory, although I don't think any such system has it - and has nothing at all to do with other processes accessing your memory, so you might want to make sure you understand what DEP (and virtual memory) are.
  2. Virtual memory prevents accidentally reading another process' address space. A process can request access to another process' memory (by calling OpenProcess with a read permission). This will be subject to an access check: the caller's security token (a kernel-mode data blob describing the process' identity) and requested permissions are checked against the target process' ACL. By default, this ACL forbids most permissions (including accessing memory) for other users, but allows code running as the same user (and not at different elevation levels, integrity levels, or using any other sandboxing) to take full control. So yes, if your user is running malware, your app is hosed.
  3. ASLR (like DEP) is a mitigation for a broad class of memory corruption vulnerabilities (including buffer overflows), and has nothing to do with inter-process memory access (except for stuff like "you can't hardcode the address you're reading, because the address space gets randomized"). Also, unless you have to use native code (C/C++) for some reason, instead of a managed language (C# or anything else on the .NET runtime, or Java, Python, etc.), you shouldn't even have to worry about this class of attack unless you're doing silly things with the memory management (or native code interop). Also (like DEP), ASLR is enabled via an optional (but enabled-by-default, in all modern versions) flag on the binary; all modern libraries should have it (and it's possible to force Windows to use it even if the binary isn't marked as compatible) but even a single library not using ASLR can be enough for an attacker to turn memory corruption into arbitrary code execution. Also, even with both ASLR and DEP, sometimes an attacker can still extract memory contents (or just execute arbitrary code); it depends on the vulnerability (actually usually on multiple vulnerabilities) in the program or the platform (runtime, OS, CPU, etc.).
  4. Loopback network connections have no inherent security, and are among the worst IPC options for sensitive code. Any malicious code on the same machine (as any user) can potentially compromise the communication by spoofing one or both ends. Additionally, various remote-initiated attacks (such as SSRF) could allow compromising such a system without even being local to the machine. Finally, using "localhost" (rather than a loopback IP address) is less safe, as "localhost" is merely resolved to the current machine by convention and could instead resolve to another machine (though this would be weird) or cause a DNS lookup that a network attacker could spoof (I've seen this one in the wild, though rarely). You should absolutely use a different IPC mechanism. Windows does not lack for them. Named pipes, local domain (Unix-style) sockets, ALPC / RPC, shared memory, mapped files, even things like window messages are more secure than network sockets. Named pipes are the most socket-like option that is compatible with old releases of Windows, but local domain sockets have been in Win10 for a while now and require minimal code changes (assuming you were programming against the sockets API directly; if using some library then the library may not yet support them).
  5. This depends on how you're communicating the port between the endpoints, but generally speaking, not even making the attacker need to figure out the port is just making things easy on them. Again, though, network sockets are a terrible choice for same-machine IPC of anything sensitive. Also, any program using fixed ports will just break if some other (entirely legitimate) process happens to be using that port, so you typically only use them when you must.
  6. The Windows Credential Store is indeed a good place to hold keys, credentials, and long-lived tokens. A stored value is still readable by any other process with the same or strictly-greater privileges as the one that stored it (any two normal processes under the same user, or if the storing process was a sandboxed app but the retrieving process is not). It also means committing the token to long-term storage, which you might be trying to avoid? The Credential Store encrypts its data using DPAPI, which uses a key protected by the user's password (and transparently decrypts the data for any process running under the same user).

What you're getting wrong:

Don't use network sockets for local IPC; seriously, that's almost always wrong unless one endpoint is a web app, and try to avoid it even then. Beyond that, though, you don't seem to have a very complete view of the threats against this system. Reading a secret out of another process is usually not the easiest way to get it, and any attacker who can do that can probably do far worse. You need a threat model, you need to decide on an acceptable level of risk, and you need to mitigate every threat whose risk you will not accept.


Threat Modeling advice below; not so much directly related to the question.

There's a few different approaches to threat modeling, but they all boil down to trying to enumerate the ways that somebody could do something you don't want. One option is to start by listing your potential attackers, all the things you're trying to prevent them from doing, and all the ways they could do them; alternatively, start by identifing all the places that any attacker could interact with the system ("attack vectors") and then for each interaction point, figure out what an attacker can do. There's a few mnemonics for enumerating threats, such as STRIDE (Spoofing, Tampering, [non-]Repudiation, Information disclosure, Denial of service, Elevation of privilege).

For this system, who are your potential attackers?

  • Admins (or malware running with admin privileges) on the same machine: go back to the drawing board; it can't be made safe.
  • Other processes running as the same (non-admin) user as the one that started the app: might be possible but will require admin privileges to install and update; if that's a blocker then back to the drawing board.
  • Apps running as a (non-admin) other user: you're probably fine so long as you switch to secure IPC.
  • Other machines on the same network, or on the Internet: you might be fine (so far as the "pass secret between local processes" aspect is concerned) as is but should switch to secure IPC anyhow.
  • An attacker who can access the disk directly while the machine is off (stolen laptop, etc.): if possible, use a full-volume encryption like BitLocker, but if you can't guarantee that (or can't configure it very securely), consider keeping the secret in memory that won't get written to the pagefile and either not writing it to persistent storage at all, or encrypting it with a key provided by the user in some way (password, hardware token, etc.) before doing so (DPAPI works here, although it depends on the security of the user's Windows password and cracking those is pretty easy).
  • An attacker who can access the RAM directly while the program is or recently was running (an advanced "stolen laptop" scenario): nothing you can do, short of beefing up physical security and/or embedding a hardware security module in the system (both of which are well out of scope for software).

Now, mind you, if the secret isn't that valuable you can probably just accept the threats from some of those actors, because the mitigation is too expensive or inconvenient. Learning what risks to accept is an important part of security.

A few other things that might be somewhere in a complete threat model for a system like this:

  • Your secret is potentially used for something outside your processes (like communication with a web server); make sure that is secure too.
  • Your programs are presumably installed somewhere on the machine's disk; make sure the install location is secure (all the IPC and memory security in the world won't do you any good if the attacker can just edit the executable code).
  • Similarly, if you're storing any data of any sensitivity at all - even if it's less sensitive than the secret - make sure it's stored in a place the attackers cannot access it and, potentially, that it's encrypted.
  • If the secret is a security token and there's some way an attacker could try to guess it (making requests to a server, for example), make sure it has enough entropy (length of securely random data) to resist brute-forcing, and try to block side-channel attacks (such as timing attacks).
  • If the secret is used for cryptography (or indeed if you're writing any crypto code anywhere in there), get somebody who knows crypto to review it. Crypto is hard to get right, and sometimes a tiny mistake makes it trivial to totally break the protection.
  • ... and too many others to enumerate, depending on things like "does the program take user input", "does the program send or receive external network traffic", "does the program process passwords", "how does the program update" and so on.
  • Thanks for the great reply. What about using TLS with a self-signed cert? If the private-key is kept 'safe' would that significantly reduce the attack-ability of using the network? (The appeal of using network is the easy cross-OS use; most of the devs use Macs.) – Basil Nov 20 '19 at 17:26
  • Mutual TLS does allow adding security to network-based IPC, but it's a complicated, error-prone, and relatively low-performance option. Just use local sockets if everybody is running either Mac, Linux, or a version of win10 released later than Dec 2017, or use an #ifdef or similar that swaps the local socket cods for naped pipe code (which, again, is very similar) when compiling for Windows (this works on every supported Windows release today, and quite a few unsupported ones too).. – CBHacking Nov 21 '19 at 9:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.