xilun / cbwin

Launch Windows programs from "Bash on Ubuntu on Windows" (WSL)
Other
327 stars 25 forks source link

cover safe elevated usage (disallow trivial escalation in that case) #14

Closed xilun closed 8 years ago

xilun commented 8 years ago

Even if, for now, outbash should only be used on single user computers, it would be nice to at least disallow trivial escalation when outbash is launched elevated. For now it does nothing special, so if it has been launched elevated all the Win32 processes it launches will also be elevated. This is not desirable, because it does not control access.

It is desirable to allow outbash to be launched elevated, because bash.exe allows it and the resulting WSL processes get Win32 administrator privilege (mostly for FS access on drive FS). For now I think that WSL has a security hole that allows escalation (Microsoft/BashOnWindows#626) though existing elevated WSL processes but I have no doubt they will fix it for Redstone.

So, back to outbash: if it runs elevated, it should "simply" run commands on behalf of the caller process. I think this is doable with a very cautious usage of Iphlpapi to retrieve the Win PID of the peer and then open the process handle (to get the token) in such a way that we know it is effectively from the process which emitted the command. We must really be very very cautious: I've constructed a scenario where the Win PID potentially points to another process even though the socket is still open and fully alive... The safe approach would be somehow similar to what I've done to suspend the processes of the Windows Job: a second control while the Handle is open.

NOTE: remember that outbash.exe intrinsically allows escalation from WSL user to WSL root. This is due to the WSL design, and unrelated to this issue. The WSL VolFs is physically hosted in AppData of the user account with Win32 user permissions, Win32 processes are not subject to WSL access control, so they are effectively equivalent to WSL root. There is no way to change that without modifying Windows.

xilun commented 8 years ago

Although the problem seems not easy to fix in WSL (see the linked BashOnWindows issue for a discussion about that), I'm still thinking about implementing the approach described above on my side. Given the implication on the underlying layers, and the fact I can do nothing about that, I'm likely to add a flag to explicitly allow elevated execution and conveying the fact that this still imply a security risk in regard to UAC effectiveness. (I will probably even add that flag first.)

xilun commented 8 years ago

Win PID of processes doing a TCP connection are not updated after the connect in the lists returned by iphlpapi, but it seems that they are also never recycled (scenario I try to cover: the original process that did the connect does not exist anymore). I asked for confirmation on Microsoft/BashOnWindows#652, because I have no proof that this is reliable, only I never got any recycling for Win PID listed by iphlpapi while leveraging the birthday paradox with a few thousands of processes in my tests. The highest PID was around 70000, with 500 died-after-connect processes and 2000 "attacking" ones, and with the simplifying hypothesis of random allocations, the proba of no collision by "chance" is negligible -- and I did similar tests several times -- so the question that remains is: does this happen to be like that right now because of implementation details, or is this an actual architectural guarantee of Windows. If this is, this actually simplifies the code I need to write (compared to what I thought at first), because we can simply try to open the process by its Win PID (while we keep our end of the connection open): if it is still there (and we have enough perms) we will be able to open it AND know that it is actually the process that did the connection, otherwise we can drop it.

Note: I also found that blog entry from an MS employee: https://blogs.msdn.microsoft.com/michael_howard/2005/10/23/acls-on-sockets/ At first I thought this would secure loopback TCP connections with an ACL (obviously, this would be impossible for non-loopback TCP connections), but I tested it and this is not the case. So I don't really understand when this ACL is applied and against what kind of threat this is useful.

I also took a look at the Windows Firewall which seems impossible to use for this application for at least three reasons: configuration must be done by an admin (whereas I need dynamic conf by non-admins), it is not guaranteed to be activated, and it is bypassed for localhost connections.

So I think, for a first level of security, GetExtendedTcpTable(... TCP_TABLE_OWNER_PID_CONNECTIONS ...)+OpenProcess+OpenProcessToken+AccessCheck is the way to go. This let me avoid to read even a single byte from unauthorized clients. In a second time I will be able to try to unelevate when needed. Unfortunately, a call of GetExtendedTcpTable(... TCP_TABLE_OWNER_PID_CONNECTIONS ...) for each process launch will technically yield a total of O(n**2) entries read for n processes launches -- but I suspect this will not be too much of an actual issue with n < a few thousands.

xilun commented 8 years ago

Commit c4748ff implements access control using the method described in the previous comment. For now only the user is checked, so UAC bypass using an elevated outbash.exe is not prevented yet. This will be implemented in a future commit.

xilun commented 8 years ago

Commit 6e77697 adds integrity level to the access control check.