RDP Cache
The RDP protocol is known to be related to many attacks, even if most of them are not related at all to the protocol itself. Regardless of the impact on the security, the RDP protocol is very interesting, and of course, it spreads across different layers of the communication stack. One of the interesting features RDP implements is client-side caching, leading to bandwidth saving. If the RDP client requests a tile from the RDP server, the server serves its hash first. If a client has already asked for the same tile (judging by the hash) and the client has it handy, there is no reason to transfer the same tile again.
This is how cache works in general, and of course, within the RDP protocol, it works in a very similar way. By default, the tile size is nowadays 64x64px, but it can be trimmed if the screen resolution does not divide by 64. The last tile in a row and the last row of files may simply not fill 64x64. From the caching perspective, it does not matter where the tile is located on the screen. If there is a tile with a blank desktop background, its hash will be the same (and useful, of course) regardless of its location.
Moreover, if the tile for the window content was caught when the window was in one part of the screen, it may be useful again even if the window was moved somewhere else (assuming the same 64px alignment). Yet another thing to be aware of is that a tile cached on server A may be potentially useful for communication with server B. If the hash is the same, there is a high chance that the previously cached tile content may be used.
Theoretically, some hash collisions may happen, but the tile content is not totally random – it contains parts of the desktop and visible windows, and not random collections of multicolor pixels. Wrapping everything up: we have cache tiles, their content matters as it contains parts of the screen, we must care about the size, but location or the server name are not important at all, which means we don’t have to care about storing them. Plus, we need to store a tile only once, even if it appears multiple times on the original screen.
To make it persistent, the cache is stored on a disk, and for performance reasons it is stored in 3 files, each containing roughly 100MB of tiles. Each file contains tiles stored as uncompressed 32bit bitmaps, each prepended with metadata, and the entire file has 12 bytes of a header describing the content. As mentioned above, the only part of a metadata meaningful for an analyst is the tile size, allowing to determine where one bitmap ends in a cache file and the next one begins.
And what can analyst (DFIR, attacker, or just a curious user) do with such data in practice? As bitmaps are stored in an obvious way clearly exposing R, G, and B values of each pixel, one can reconstruct each bitmap from the cache and get the overall idea of the screen content. On the other hand, it will be only an overall idea – there is no way of sticking tiles together to have the real look of the screen. Some tiles may be identical and stored only once, some tiles may be overwritten already, some may be stored multiple times (e.g. tile containing a clock), and some may come from different servers. An analyst can imagine what happened, sometimes reading really confidential information such as passwords or URLs but hoping for reconstructing the screen look is too much.
Does it pose a risk? In some cases, the answer may be yes. It’s why the cache is stored in a user profile, to separate it from other users’ cache, even if it’s not the most efficient approach from the caching perspective. And it’s exactly why mstsc.exe has a “/public” switch, meaning in practice “don’t store cache on the client machine”. It’s an end user choice if performance or privacy is more important in a particular case.
Now you can do one of two things: try to play with cache files on your own (you can find them in LocalAppData \Microsoft \Terminal Server folder) or take a ready-to-use PowerShell script called “DecodeRDPCache” located in my PSBits repo, splitting one huge cache blob into tiny 64x64px images.