Experiments to demonstrate AES-XTS's exploitable flaws and why StrongBox and SwitchCrypt do not suffer from them.
- python >= 3.x
- python3-cryptography
- libfuse (exp2 only)
- python3-fuse (exp2 only)
Experiment 1: Run ./exp1.sh
Experiment 2: Run ./exp2.py
+----------------+ +---------------------+ +-------------------+
| | | | | |
| [B] | | [A] | | [C] |
| logs & results <------+ python3-fuse/pyfuse +------> in-memory backend |
| | | driver | | & config files |
| | | | | |
+----------------+ +----------^----------+ +-------------------+
||
||
+-----------v---------+
| |
| [D] |
| FUSE kernel module |
| |
| |
+---------------------+
A client is using a backup service to "protect" an mounted encrypted filesystem "vault" or "hidden volume" or "hidden directory" where the backend is stored in a single file on the host filesystem. This single file is backed up by the backup service.
Our backup service could be something like Google Drive or Dropbox with an AES-XTS encrypted file being backed up vs SwitchCrypt encrypted file. Our fictional backup service is "evil" in that it is beholden to powerful government interests or is compromised by hostile actors.
The goal is to deny the client plausible deniability; i.e. does one of a set of files exist in the client's encrypted vault? This set of files are called the goal files.
A1: The backup service executable has a vulnerability where non-root files can be overwritten.
A2: Among other files, the backup service watches a single encrypted file representing the hidden volume or vault (called the "backend") currently mounted in the OS.
A3: The backup service keeps a history of all changes to backed up files. The backup service can query the filesystem structure at any time.
-
Get backup service (BS) executable to keep trying to write to suspected goal file locations (or all files)
-
Watch for an update from the backup service and compare the updated backend to the initial backend when it occurs.
-
If interesting sectors change compared to the original, then the goal file didn't exist there. If interesting sectors do not change, then the goal file must've existed there; plausible deniability broken!
-
We can use stored versions to rollback backup & mounted filesystem to a clean state after our attack, plausibly avoiding detection entirely.
Ensure a python environment is configured. Run ./exp1.sh
.
Or, to run it all manually (can set env DEBUG_MODE=1 for sb3 to enable ptvsd):
- Run python script
./sb3.py path/to/mount/point
- Run
tree path/to/mount/point
if you want to see what the fake filesystem looks like - Run
./exp1.py
- Run
umount `realpath path/to/mount/point`
to unmount the fake filesystem
- Use
sb3.py
to setup a fake filesystem with directory structure, a set of files with randomly generated contents, and a subset of goal files.- AES-XTS encrypted in-memory amalgum of files serves as the backend for this experiment
- Starts off with 10 autogenerated random files
- All reads and writes hit the backend immediately every time (not cached)
- A file is chosen at random to be the goal file; information is output by
sb3.py
- Use
exp1.py
to initiate experiment: for every file in the filesystem, overwrite it with a goal file. After each overwrite, check if current backend matches old backend.- If so, the file exists, exit with
success
- If not, restore the old backend as the new current backend and continue writing
- If we exhaust all possible files, the file probably doesn't exist, exit
with
fail
- If so, the file exists, exit with
The same attack doesn't work with StrongBox or SwitchCrypt because each write and overwrite occurs with a different keystream.
Chosen plaintext attack.
A user of some distributed online service offers users the ability to change their passwords. The service's various APIs use shared encrypted storage to communicate asynchronously. The new password must be "different enough" than the old password. To determine "difference," once a user inputs their new password, it is compared with their old password and the number of different characters is saved to the encrypted storage. If this number is high enough, the password is considered "different enough" and the change is accepted. Other APIs extract this information and act on it in various ways (e.g. sending the user an error message via email).
The password change is "evil" in that, through a compromised service, an attacker can observe this encrypted shared storage and can also communicate with the other services, including initiating password change attempts.
The goal is to steal a user's password by taking advantage of XTS revealing when a sector has the same contents written to it twice. This can be done by passively observing the drive while actively attacking the service.
Ensure a python environment is configured. Run python script ./exp2.py
.
P
is the real password of lengthN > 0
P'
is the password guessC
is the encrypted sector ciphertext containing the number of different charactersO1(P') == true
ifP == P'
, elseO1(P') == false
O2
counts the number of different characters betweenP
andP'
from left to right, returnsC
c
is our suspected "wrong character" ciphertext
alphabet
must be >= 4
characters. continue
sets i=0, c1="", c2="", c3=""
and returns to step #3. fail
ends the algorithm in failure (should
never be encountered under normal circumstances). succeed
prints (P, P') and
ends the algorithm in success.
- Intake password
P
of lengthN > 0
bytes - Loop: beginning with
n=1, i=0, p="", c1="", c2="", c3=""
, begin guessing passwords of lengthn
using characters from alphabetA
of lengtha
(fromA[0]
toA[a-1]
) - If
O1(P')
:succeed
- If
i >= a
orn > N
:fail
A[i] => p[i]
,O2(p) => c1
i + 1 => i
,A[i] => p[i]
,O2(p) => c2
- If
c1 == c2
:c1 => c
- If
c1 != c2
:
8.1.i + 1 => i
,A[i] => p[i]
,O2(p) => c3
8.2. Ifc1 == c3
:P' + A[i-1] => p => P'
,n + 1 => n
,continue
8.3. Ifc2 == c3
:P' + A[i-2] => p => P'
,n + 1 => n
,continue
8.4.fail
- for all
i
fromi + 1 ... a
:A[i] => p[i]
,O2(P') => c3
9.1. Whenc3 != c
,P' + A[i] => p => P'
,n + 1 => n
,continue
9.2. If everi >= a
thenfail
The problem with AES-XTS is that of "temporal penguins": since XTS is essentially AES in ECB mode (with a tweak and cipher stealing), an observer will notice when the same content has been written to the same sector more than once. We can take advantage of this to iterative guess the password given our scenario. Given the same scenario, StrongBox/SwitchCrypt will never reveal when the same data has been written to the same sector since a unique keystream is used for every write.
(work in progress)