Hacker News new | past | comments | ask | show | jobs | submit login
Named Pipes in .NET 6 with Tray Icon and Service (erikengberg.com)
146 points by entrep on Nov 23, 2021 | hide | past | favorite | 51 comments



Good article, but I wish the author would've addressed securing these named pipes.

Consider that if a user-mode application can send messages to a privileged process (like a Windows service).

What prevents any user-mode application from doing that? And if your Windows service is running as "NT_AUTHORITY/SYSTEM" and even executes privileged commands, well you might find you've got a simple privilege escalation vuln.

Remember, secure your named pipes...especially when the named pipe server runs as SYSTEM.

- https://stackoverflow.com/a/59983266

- https://versprite.com/blog/security-research/vulnerable-name...


I've worked on apps like this, and I didn't know or care which user was going to use the features requiring elevation. So I couldn't manage permissions per user. My approach to security was to simply limit the input (method parameters usually) from the unprivileged process. For example, not letting the client send arbitrary commands to execute, use filesystem path whitelists, only elevate when required, etc. If the privileged code uses a resource, and that resource can be changed/replaced by an unprivileged user, then the privileged code can be manipulated. Like a Registry key in HKCU for example, or a file in a user's AppData folder. Using enums as method parameters for privileged code helped me avoid some obvious vulns I might've otherwise created. I've definitely done it the wrong way before. It can be tough.


This is generally how I've approached this problem as well. I like using the `ServiceController.ExecuteCommand` method and just send some integer value from the client -> server...and the server maps the integer value to a pre-determined command.

https://docs.microsoft.com/en-us/dotnet/api/system.servicepr...

You've definitely outlined the risk clearly of allowing a client to specify anything arbitrarily.

I once wrote a sudo implementation for Windows Vista / Windows 7 and first attempt used named pipes communicating to a windows service that did some token manipulation to execute things as the user (but with elevated token attached as well). There be (security) dragons.

I like using named pipes and they are a great IPC mechanism for communicating amongst processes of the same privilege level. I would not use them for message passing between processes of different privilege levels.


Good to address the security aspects of named pipe. However none of those describes how to secure the server.

The server needs to call ImpersonateNamedPipeClient() on the incoming client connection to assume the client’s security token, that would lower the server’s privilege to the level of the client. That’s it!

A guest level client can connect to the server. The server’s privilege becomes guest, and cannot access any resources that guest has no permission to access.

[1] https://docs.microsoft.com/en-us/windows/win32/api/namedpipe...


FWIW, the project in the article is explicitly intended to allow privilege elevation: "You have an application which runs in user context, without any administrative rights, and you need to perform some tasks which requires higher privileges."


The server can do whatever it needs to do in its higher privilege. Just when it interacts with the client connection, it lowers its privilege to the client's level. It gets the incoming data, sanitizes it, and reverts back to higher privilege to do the work. This minimizes the attack surface to the area dealing with client interaction, not the whole server. The server might link in a 3rd party XML library to sanitizes the incoming data and you don't know what the library can do. Running that in the client privilege level ensures that whatever it does only under the client's privilege.


That thread still has higher privilege write access to it's process's state, including the stacks of other threads that haven't impersonated that client. ImpersonateNamedPipeClient is a very leaky security barrier, and far from the only thing you need to know about when it comes to named pipe security.


Thanks! That’s very valid feedback. Could be my next write up.


You're welcome. An alternative I've used to named pipes among processes of different privilege levels is to build the service to listen for custom commands sent to it. These are just integers, and the service maps those to pre-defined commands.

Then the only thing the user-mode application can send are just flags (integers) that the service has already pre-determined what it will do in response.

Here's an article: https://www.codeproject.com/Articles/24434/How-to-Write-Wind...

And here's a succinct example: https://stackoverflow.com/a/5805700


Interesting. Didn't know about it.


I think the package he used, also has some kind of pipe authorization access control.


That is correct.


I think this is the first NamedPipes tutorial in C# that I've ever seen that doesn't do things completely wrong by using a StreamWriter or StreamReader. Of course, that's because it uses another library that wraps all the tricky bits of NamedPipes that everybody always does wrong -> https://github.com/HavenDV/H.Pipes

NamedPipes are sweet for doing same-machine IPC on Windows, that is for sure, but the built-in API is full of footguns.


Can you elaborate what’s wrong with using StreamReader or StreamWriter with a np? I’ve used them before so wondering what I’m potentially doing wrong here and what’s the alternative.


It becomes an issue if the data you're reading or writing is larger than 1024 bytes and you are in PipeTransmissionMode.Message, because of some implementation details with buffers and how StreamReader/Writer handle Read/Write calls on the underlying NamedPipe(Client/Server)Stream

See https://stackoverflow.com/questions/31936100/namedpipeserver...


Thanks for the info, I personally avoid the message mode and just operate on bytes so that could be why I haven't had problems with it.


Just to clarify something at the start of the article... If you are using full Visual Studio to develop with .NET 6, you will need 2022. If not, (eg. VS Code), will work with the command line sdk.


Pretty sure there isn't anything you can't do in Jetbrains Rider EAP.

Edit: would like to know why I'm being downvoted.


Apologies - my comment was poorly worded and I think it caused confusion. The article sort of implied that Visual Studio was "The Way to Develop in .NET", while both you and I obviously know that's not true.

I've seen a lot of documentation (third party and Microsoft) that just start in on a "Visual Studio"-based solution while ignoring everything else, which kind of rubs me the wrong way.


My reply wasn’t trying to be negative to yours. Just also wanting to include Rider :) sometimes the features lag behind in rider. Hence I had to be specific and say EAP.

Yeah I know the feeling. I like to remind people there is a good development experience on linux also when using Rider. Sometimes feel like people are still stuck on “.net is windows only!”


- Debugging across .NET and C++ on the same solution.

- Create a architecture diagram out of .NET and native compiled code.

- Integration with SharePoint and Dynamix SDKs

- SQL Server and Azure SDKs

- Using the Fakes mocking framework for MSIL rewriting

- Debugging the GPU shaders

Just a couple of examples, I can take plenty more out of VS enterprise.

I really don't get how people can think JetBrains does better than platform owners.

They will ever play catch-up with platform capabilities and only offer a subset of the package.


So, the parent comment I replied to was in relation to needing VS 2022 for .NET 6, to which I replied that you could use Jetbrains Rider EAP, obviously for .NET 6, to accomplish the task in the linked post.

> They will ever play catch-up with platform capabilities and only offer a subset of the package.

Obviously, they are competing with a 20+yo product.

But atleast I can get all my .NET work done on Linux without needing slow bloated VS with a ton of useless features like 'unit testing for poor code choices or legacy codebases', or 'integration with the 2nd worst collaborative tool after Jira'.


Sure when it all boils down to the tiny .NET Core subset of ASP.NET MVC applications, instead of those 20 years of history.

Looking forward to see how they deal with MAUI and Blazor integration.

What others call bloat I call productivity features.


> They will ever play catch-up with platform capabilities and only offer a subset of the package.

Having used both Visual Studio and Rider for many years now, one is definitely playing catch up but I’m not convinced it’s Rider.

There is an equally long list of things that Rider does and VS doesn’t. There’s a reason Resharper for Visual Studio is so popular.

I’m fond of Visual Studio but using Rider on macOS instead of VS on Windows is a much nicer experience for my .NET development (and I know it’s subjective).

Also worth bearing in mind Rider’s cost vs VS Enterprise’s eye-watering licence fees.


All well and good, if .NET was actually 100% 20 years of portable history without Microsoft/Windows specific tooling.

Borland taught me what it means to always being a 2nd class experience versus owning the platform tooling.

People complain about VS bloat and their answers is to put a yet another heavy weight plugin on top.

When my teammates ask me why my VS is so fast versus theirs, well I don't run Resharper.


Hot reload in Rider only works in debug mode on Windows (to be clear it also works in nondebug sessions on Windows). On Mac trying to use Hot Reload with Rider on a nondebug session errors out, so it’s not 100% yet.


But the article doesn't require hot reload to achieve the goal right?


I didn't use named pipes since 2010 but last time I checked named pipes are great for Inter Process Communication on the same machine. Great in terms of performance and access to a standard interface. WCF can be configured to use named pipes on local machines. Last time I checked named pipes were also available on the network and visible by other machines with some overhead.

Security can be achieved not at channel level but at message level: If cannot decrypt the message then it's not for you. At the expense of overhead you open the door for flexibility.

Ultimately it's a tool. What it matters is how you use it. Definitely better than using shared memory for IPC. Files are by default not secured either. Anyone can write into it.


Amazing read and insanely helpful. Thanks a ton both to the author and to who posted it, if different people.


We’re the same person. Thanks.


This uses the package H.Pipes which seems to use System.Runtime.Serialization.BinaryFormatter by default; isn't this insecure?


Why is BinaryFormatter insecure?

Edit: Never mind, see https://docs.microsoft.com/en-us/dotnet/standard/serializati...


It's possible to use different formatters for H.Pipes. I will probably update the post to not mislead.


I've recently done something close to this, and found Ceras[0] to be an excellent choice for the serialization layer.

[0] https://github.com/rikimaru0345/Ceras


I added a package for Ceras: https://www.nuget.org/packages/H.Formatters.Ceras/ I ran into some issues in testing where the data hash after serialization/deserialization does not match for Ceras. I added the package anyway, but this needs to be used with caution.


An alternative that comes with built-in dependency injection is using the .NET hosted process model. Much easier to test too.


Honest question - how relevant the skill like that nowadays with all the stuff going Web and Electron?

Are there any apps (except for creating stuff) that are being developed in a way that “server” part is running locally? It seems to me that everything goes web now and if you have a desktop it’s “just” a client for something remote?


Named pipes have been in Windows for many years https://docs.microsoft.com/en-us/windows/win32/api/winbase/n...


Been using them since 1992 but at this point I would naturally turn to sockets, even on the same machine. I can't even really say why though. Named pipes are nice because they are really more like a file but at this point sockets feel more natural because they exist and are supported everywhere.


You can apply permissions to named pipes and, well, they're named which is useful since you can use a unique and deterministic enough name that you don't need an extra band of communication for the client to know what port the server ended up starting on.


Yeah, good point, as opposed to anonymous pipes, which also still exist.


TCP server cannot assume the security context of the client, thus privilege elevation attack can easily happen.


More importantly in a lot of cases, you can ask the kernel for the client's SID as the server, and make decisions knowing that the client couldn't forge it.


First thing I do with SQL Server setups - turn off named pipes :)


Is there a reason you do this? Curious, I havent had to setup SQL Server myself in a while so I don't know what a reasonable reason would be.


Named pipes are fast when the database is on the same box, but slower than a normal tcp connection when the database is remote. Something to do with PeekNamedPipe calls having to prepend reads.


Yeah one of my favorite SE posters talks a little more about it, generally the benefits are only there if you have app/db/sql server all on the same box.

https://dba.stackexchange.com/a/24176/15642


I don't know why I expected something more technical for the tray icon part. I mean using a third party library is not really anything .NET 6 specific.


Seems to me, the focus of the article was more on named pipes than the tray icon, so maybe that's why?


I’ll guess that will be a separate post.


Wow the dotnet people are still doing this thing when they have screenshots of what to click in VS? So easy to reproduce!




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: