-
-
Notifications
You must be signed in to change notification settings - Fork 48
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Feature Request] Generalized automatic unidirectional data transmission API between VMs for endpoint secure communication #6102
Comments
It's worth mentioning that there are a few existing covert channel issues in Qubes that need to be addressed. In fact, they are severe enough to the extent of completely voiding the data-diode model (specifically the confidentality aspect, or the "Destination Computer" in TFC):
Both of these allow two cooperating VMs to communicate feasibly with each other. |
Redirecting stdout and stderr to
|
The thing you're looking for is qrexec. Make Source VM and Destination VM not connected to the network and use qrexec services for communication. Bonus point: you remove the whole TCP/IP stack from the TCB. In general it provides bi-directional transport layer with strict policy who can talk to who and on request what services (analogous to port numbers). Currently the dom0 policy cannot enforce uni-directional connection, but it can be implemented by the actual service receiving the connection (like - the first thing is to close stdin/stdout and don't read/write anything from there).
And finally, yes, @iamahuman is right - with the current state of things, it is unrealistic to assume a compromised VM will not be able to exfiltrate things. It is somehow harder than on traditional system (you may need another cooperating VM for that), but the general assumption is if a VM is compromised, it is not safe anymore the data inside stays private. |
Based on @marmarek's response, it sounds like there is no need for a new feature in Qubes, since |
Hi again! Thank you so much for your time and thoughtful replies!
Thanks! I managed to get it working, not sure if the new implementation (below) is correct and/or ideal.
I closed stdout and stderr on receiving side, but I'm puzzled what you mean I should close stdin, how can I pass a long ciphertext to the RPC service? I tried passing data as an argument but the input was limited to 47 chars.
Thanks! I'll have keep track of the ticket and see if (once implemented) it requires manual setup (like picking dedicated CPU cores) from the end user.
If the dom0 only has one policy file
--doesn't this lock the direction of qrexec requests and prevent any connection attempts from NetworkerVM? Or does it require additional blocking procedures from dom0/SourceVM side? I redirected stdout and stderr from Networker to /dev/null on SourceVM side below, just in case.
I agree these are the limitations. It's an improvement over the networked TCBs in that it forces the attacker to squeeze the malware through the TFC's data transport channel. I'm hoping Qrexec + TFC's authentication mechanisms make this hole too small for most adversaries. I'll do my best to expand the threat model to discuss the attacks you mentioned: side channels inside the CPU, as well as flow control timing and the issues with Xen shared memory. The details of these attacks are way beyond my comfort zone so it's going to take a while to digest all this.
I tried implementing this with shell script that calls the actual utility, while suppressing the stdout/stderr of said util from NetworkerVM side (see below)
IIUC all this happens way before anything that my code (below) on receiving side does, so if despite best efforts DestinationVM is compromised, malware could leak data without even touching TFC's files?
(That's correct, the serial transmissions are asynchronous so the first bit is the start bit that starts the clock on receiving side, the baudrate is pre-established and has so far stayed in sync well enough.) The new implementationI tried really hard to get this right: Relay Program on NetworkerVM calls
The dom0's
The Destination VM's (receiver side)
This should remove all output on NetworkerVM side the RPC call would produce so it should meet the lax requirements @iamahuman mentioned. At least, when I induce syntax errors into (On a side-note and since it's related to stdio, to do what can be done, the Transmitter Program on SourceVM also redirects stdout and stderr to
) The final utility is the
The Base64 encoding should ensure any raw binary data -- sent by the attacker who has taken over NetworkerVM -- that is stored into the temp file can not be accidentally executed. The TFC's main program can then read, decode, validate, and authenticate+decrypt the data before trusting it. The filenames with timestamps ensure data is processed in order. You're right, the feature I asked for already exists and works to the extent it can! |
I mean close stdin on the side where you want to prevent sending data and close stdout on the side you want to prevent reading data. Not both at the same time.
Yes it does. But once a connection is established, data can flow there in both directions.
Yes, that's exactly what you can do.
You can avoid echo and shell: You can also add
Right direction, but better avoid shell processing the message. You can simplify with:
(if you don't really require getting just one line, drop
You can simplify to avoid converting unicode<->bytes: #!/usr/bin/env python3
import base64, os, sys
with open('/home/user/.tfc_from_relay_<timestamp>', 'wb+') as f:
data = sys.stdin.buffer.read()[:-1] # Remove LF
f.write(base64.b64encode(data))
f.flush()
os.fsync(f.fileno()) |
To copy from the mailing list archive due to #6008:
|
The manual route is, in fact, already available: |
Yes, it could. Exfiltration can happen anytime after dom0 approves the qrexec connection. (That is, if we ignore other side channel issues that will allow data exfiltration even without dom0 intervention anyway.) |
This worked great! I set the executable path as absolute, and added an additional layer of encoding to output data to prevent the receiving application from misinterpreting line feed bytes present in the random data that is ciphertexts/public keys. I followed the rest of your advice too, and I'm so glad to have a more secure Qubes configuration in production now. @iamahuman I linked to to the message with Joanna's email in the threat model and listed the issues discussed in this thread.
I tried playing with the Again, a million thanks to both of you! |
I forgot to mention you should use |
Hi, this became quite a long post, I hope it's ok.
The current situation of secure comms on Qubes
The available solutions for secure communication on Qubes have so far been
TCB-on-networked AppVM: E.g. you can run Signal/Briar/Cwtch/Ricochet on a dedicated AppVM which offers security from some other compromised app (e.g. browser) on another AppVM, and it prevents someone who exploits the messaging app from also accessing your files since they're (hopefully) not mounted on the messaging app's VM. Since the Trusted Computing Base (TCB) of these messaging apps is by definition always connected to the network, Qubes doesn't contribute to confidentiality of the messaging apps' keys or plaintexts.
split GPG with protected keys: https://www.qubes-os.org/doc/split-gpg/ discusses using
work-gpg
VM to handle cryptographic operations with keys stored on the secure VM. The problem here is the email-client onwork-email
VM is still used to type in the plaintext. If the attacker has compromised thework-email
VM, they can observe the content of received and sent messages while the user accesses the incoming- or composes the outgoing plaintext.Split TCB with protected keys and plaintexts: I've been working on a solution to the limitations of the approaches listed above (on IM side) for the past eight years. The project I'm working on is a communications system called TFC that not only uses good crypto primitives (X448, XChaCha20-Poly1305, Blake2b hash ratchet, Argon2id for key derivation for persistent databases) but is also anonymous (v3 Onion Services), and most importantly, it utilizes strong isolation to provide endpoint security for the messaging app's keys and plaintexts. I'd love to generalize this split TCB approach but nobody else has worked on similar systems yet so I apologize for tooting my own horn that is, using my own SW as an example.
Quick overview of TFC's endpoint security
A TFC endpoint on Qubes consists of
Only the Networker VM is connected to the Internet. The Source VM talks to the Networker VM and the Networker VM talks to the Destination VM, unidirectionally. It's the method of unidirectionality that's of interest here, as the reasoning for the endpoint security is, malware needs bidirectional access to exfiltrate keys:
Since data transits to TCB halves unidirectionally
TFC was originally designed around three separate computers (your daily laptop + 2 cheap netbooks): this layout makes use of free-hardware-design data diode, and as long as you manage to avoid HW implants and eliminate covert exfiltration channels, it's pretty much safe from zero-days.
TFC's Qubes configuration is a relatively new addition: It's an intermediate solution that sits in-between the Qubes' split-gpg, and the HW data diode isolated setup. The security of the Qubes-isolated configuration depends on the robustness of the isolation mechanism.
What this ticket is about is, I'm not sure the current approach TFC AppVMs use to communicate unidirectionally is the best possible, and that additional feature(s) to change it to the best possible, might be needed.
TFC's current Qubes configuration
The current Qubes configuration uses iptables firewall to enforce the following rules:
Source VM's OS
The Source VM should be able to protect itself, it's hard to compromise OS the firewall of which drops all incoming data
Destination VM's OS
The problem here is, if the Destination VM's OS is compromised by the incoming UDP data, the firewall rules can be changed. After all there is no sudo password in Qubes so privilege escalation is trivial.
Networker VM's OS
The problem is again, if the OS of Networker VM is compromised, none of these settings are persistent.
To enforce the Networker and Destination VM's rules, the same rules are also applied on the
sys-firewall
VM thatSo the theory is, the
sys-firewall
is more inaccessible to the attacker, and that data that passes through it can't edit the firewall rules (it wouldn't be much of a firewall if it could).Qubes already has non-LAN based (unidirectional) data transfer
So to get back to the PGP-emails,
split-gpg
uses something calledqr-exec protocol (qubes.GPG)
. This protocol apparently has similar API to gpg that Thunderbird uses, but I suspect thequbes-gpg-client
andqubes-gpg-server
use something other than the internal LAN to exchange data, especially since the documentation statesqubes-gpg-server
runs in "more trusted, network-isolated, domain".It's also the case network-isolated VMs can still exchange files via nautilus with
right-click
->Move To Other AppVM...
so apparently there's benefit in having dom0 perform the unidirectional data transfer between AppVMs.In theory TFC's Transmitter and Relay programs could write the ciphertext into a file, and once the user has manually sent the ciphertext from Source to Networker VM, or from Networker to Destination VM, the Relay and Receiver programs could automatically poll the
QubesIncoming/<sourceVM>
directory for the ciphertext file, process the data in the file and remove the file (to allow the next file with identical file name to be sent). But this isn't what instant messaging is about.The feature request: Application for automated data transfer via dom0
So would it be possible to create some application that could then be called with some parameters, e.g. inside Python3.6+
subprocess.Popen(f'qubes-send-unidirectional --target-vm destination --port 1234 --data {ciphertext content}', shell=True).wait()
A server application running as a service, would be needed to listen on the target VM, and upon receiving data, it could then broadcast the data over e.g. UDP (to localhost:1234) that the Relay/Receiver program could then listen to.
This would require dom0 side AppVM management to have some simple configuration (see the next chapter below) that would make it harder to misconfigure (as there's no iptables).
Again, I'm not asking to implement this only for TFC. Generalized unidirectional transfer would make Qubes a lot more flexible. The server application could also have some configuration file that determines what commands (if any) get run when data is received to some port. E.g. the received data could also be piped to a gpg command for automatic decryption; This way there's no need to trust TFC (although its forward secrecy etc. are an improvement over PGP's stateless encryption), and the PGP-environment is more secure when compared to current split-GPG, as plaintext is also viewed and/or composed on a safe isolated VM. The configuration file parsing would of course require careful design to prevent shell injection.
Making misuse hard
I realize that automatic processing of incoming data is in a way, dangerous to the receiving AppVM, but that's something that's already possible (via UDP packets). If the unidirectionality can be enforced more robustly with dom0, I think it's definitely worth doing. Users who would use the feature would probably only use it for dedicated, isolated AppVMs anyway, and the point of Qubes is, an AppVM, even if dedicated for single purpose doesn't really cost anything.
(Also, it's a good thing to recap the current situation: I'd argue having always-online-when-running
work-email
VM show plaintexts is even more dangerous, as it can directly affect the security of the user.)To make misuse hard I think it's best to offer a dropdown menu called
Unidirectional AppVM type
belowTemplate
and aboveNetworking
when creating a new qube, that has five options:(none)
: This is the default, and it allows setting any network interface. Both checkboxes are hidden when(none)
is selected.This configuration must not allow unidirectional transfers to any AppVM, only
Relay
(explained below) must be able to do thatSource
: This will set networking to(none)
and prevent changing it. It will then expose☐ Allow automatic incoming data to this VM from following VMs: n/a(I'm using strike through to represent a greyed out option)☑ Allow automatic outgoing data from this VM to following VMs:
networker, destination
(The in-line code is what the user can type here, basically it's a comma-separated list that allows output to, or reception from one or more AppVMs)Relay
: This configuration will always be online, so selecting it should prevent setting networking to(none)
, but allow free selection of network interface. It will then expose both☑ Allow automatic incoming data to this VM from following VMs:
source
☑ Allow automatic outgoing data from this VM to following VMs:
destination
Selecting this configuration should display a warning message along the lines of
Destination
: Again, similar toSource
configuration, the networking interface should be locked to(none)
, but this time the other option should be unselectable:☑ Allow automatic incoming data to this VM from following VMs:
source, networker
☐ Allow automatic outgoing data from this VM to following VMs: n/aSelecting this configuration should display a warning message along the lines of
Guard
: A Guard is an optional AppVM. It is similar toRelay
in that it can relay unidirectional data from one AppVM to another. Where it differs is, the guard AppVM is always isolated from the network, so the networking interface must be locked to(none)
. The way this would work is one would setsource
AppVM with settings☐ Allow automatic incoming data to this VM from following VMs: n/a☑ Allow automatic outgoing data from this VM to following VMs:
source-networker-guard
source-networker-guard
AppVM with settings☑ Allow automatic incoming data to this VM from following VMs:
source
☑ Allow automatic outgoing data from this VM to following VMs:
networker
networker
AppVM with settings☑ Allow automatic incoming data to this VM from following VMs:
source-networker-guard
☑ Allow automatic outgoing data from this VM to following VMs:
networker-destination-guard
networker-destination-guard
AppVM with settings☑ Allow automatic incoming data to this VM from following VMs:
networker
☑ Allow automatic outgoing data from this VM to following VMs:
destination
destination
AppVM with settings☑ Allow automatic incoming data to this VM from following VMs:
networker-destination-guard
☐ Allow automatic outgoing data from this VM to following VMs: n/aFor the system to work as a whole, the
Guard
must relay the content of the data. However, inspecting/logging the data should be up to the user. The idea here is it can act as an audit/IDS/IPS system for transmitted plaintext data (especially plaintext data), and analyze metadata of encrypted transmissions, e.g. it could detect covert exfiltration of keys during hours no data was sent by the user. The guard benefits especially from using another type of OS (e.g. a BSD-variant when everything else is Linux), as it's unlikely to be compromisable by the same exploit that could then alter what's in the guard's logfile.To avoid the need for a complex network topology analysis to prevent the user from shooting their leg off by creating unintended exfiltration pathways, the Guard VMs should not contain duplicates of decryption keys for friendly MITM purposes, and they should always show a warning about this:
How complex is this for the end user?
I'd say not complex at all. The software vendor can make everything else automatic except
Also, naturally the user has to manually configure the AppVMs via dom0, i.e.
Unidirectional AppVM type
, andInstallation guides can walk them through these steps.
The text was updated successfully, but these errors were encountered: