On piping Curl to apt-key

21.08.2017 16:52

Piping Curl to Bash is dangerous. Hopefully, this is well known at this point and many projects no longer advertise this installation method. Several popular posts by security researchers demonstrated all kinds of covert ways such instructions could be subverted by an attacker who managed to gain access to the server hosting the install script. Passing HTTP responses uninspected to other sensitive system commands is no safer, however.

This is how Docker documentation currently advertises adding their GPG key to the trusted keyring for Debian's APT:

Screenshot of instructions for adding Docker GPG key.

The APT keyring is used to authenticate packages that are installed by Debian's package manager. If an attacker can put their public key into this keyring they can for example provide compromised package updates to your system. Obviously you want to be extra sure no keys from questionable origins get added.

Docker documentation correctly prompts you to verify that the added key matches the correct fingerprint (in contrast to some other projects). However, the suggested method is flawed since the fingerprint is only checked after an arbitrary number of keys has already been imported. Even more, the command shown specifically checks only the fingerprint of the Docker signing key, not of the key (or keys) that were imported.

Consider the case where download.docker.com starts serving an evil key file that contains both the official Docker signing key and the attacker's own key. In this case, the instructions above will not reveal anything suspicious. The displayed fingerprint matches the one shown in the documentation:

$ curl -fsSL https://www.tablix.org/~avian/docker/evil_gpg | sudo apt-key add -
$ sudo apt-key fingerprint 0EBFCD88
pub   4096R/0EBFCD88 2017-02-22
      Key fingerprint = 9DC8 5822 9FC7 DD38 854A  E2D8 8D81 803C 0EBF CD88
uid                  Docker Release (CE deb) <docker@docker.com>
sub   4096R/F273FCD8 2017-02-22

However, importing our key file also silently added another key to the keyring. Considering that apt-key list outputs several screen-fulls of text on a typical system, this is not easy to spot after the fact without specifically looking for it:

$ sudo apt-key list|grep -C1 evil
pub   1024R/6537017F 2017-08-21
uid                  Dr. Evil <evil@example.com>
sub   1024R/F2F8E843 2017-08-21

The solution is obviously not to pipe the key file directly into apt-key without inspecting it first. Interestingly, this is not straightforward. pgpdump tool is recommended by the first answer that comes up when asking Google about inspecting PGP key files. The version in Debian Jessie fails to find anything suspicious about our evil file:

$ pgpdump -v
pgpdump version 0.28, Copyright (C) 1998-2013 Kazu Yamamoto
$ curl -so evil_gpg https://www.tablix.org/~avian/docker/evil_gpg
$ pgpdump evil_gpg|grep -i evil

Debian developers suggest importing the key into a personal GnuPG keyring, inspecting the integrity and then exporting it into apt-key. That is more of a hassle, and not that useful for Docker's key that doesn't use the web of trust. In our case, inspecting the file with GnuPG directly is enough to show that it in fact contains two keys with different fingerprints:

$ gpg --version|head -n1
gpg (GnuPG) 1.4.18
$ gpg --with-fingerprint evil_gpg
pub  4096R/0EBFCD88 2017-02-22 Docker Release (CE deb) <docker@docker.com>
      Key fingerprint = 9DC8 5822 9FC7 DD38 854A  E2D8 8D81 803C 0EBF CD88
sub  4096R/F273FCD8 2017-02-22
pub  1024R/6537017F 2017-08-21 Dr. Evil <evil@example.com>
      Key fingerprint = 3097 2749 79E6 C35F A009  E25E 6CD6 1308 6537 017F
sub  1024R/F2F8E843 2017-08-21

The key file used in the example above was created by simply concatenating the two ASCII-armored public key blocks. It looks pretty suspicious in a text editor because it contains two ASCII headers (which is probably why pgpdump stops processing it before the end). However, two public key blocks could easily have also been exported into a single ASCII-armor block.

Posted by Tomaž | Categories: Code | Comments »

BeagleCore Module eMMC and SD card benchmarks

12.08.2017 13:03

In my experience, slow filesystem I/O is one of the biggest disadvantages of cheap ARM-based single-board computers. It contributes a lot to the general feeling of sluggishness when you work interactively with such systems. Of course, for many applications you might not care much about the filesystem after booting. It all depends on what you want to use the computer for. But it's very rare to see one of these small ARMs that would not be several times slower than a 10-year old Intel x86 box as far as I/O is concerned.

A while ago I did some benchmarking of the eMMC flash on the old Raspberry Pi Compute Module. I compared it with the SD card performance on Raspberry Pi Zero and the SATA drive on a CubieTruck. In the mean time, the project that brought the Computer Module on my desk back then has pivoted to a BeagleCore module. Since I now have a small working system with the BCM1 I thought I might do the same thing and compare its I/O performance with the other systems I tested earlier.

BeagleCore Module mounted on the SNA-LGTC board.

The BCM1 is a small Linux-running computer that comes in the form of a surface-mount hybrid module. It is built around a Texas Instruments AM335x Sitara system-on-chip with a single-core 1 GHz ARM Cortex-A8 CPU. The module comes with 512 MB RAM and 4 GB eMMC chip. It is supported by the software from the BeagleBoard ecosystem and in my case runs Debian Jessie with the 4.4.30-ti-r64 Linux kernel. Our board has a micro SD card socket, so I was also able to benchmark the SD card as well as the eMMC flash. I was using the Samsung EVO+ 32 GB card.

To perform the benchmark I used the same script on BCM1 as I used in my previous tests. I used hdparm and dd to estimate uncached and cached read and write throughputs. I ran each test 5 times and used the best result.

Comparison of write performance for ARM systems.

The write performance is better with the SD card than the eMMC flash on BCM1. SD card on BCM1 is also faster than the SD card on Raspberry Pi Zero, although this is probably not relevant. It's likely that the Zero performance was limited by the no-name SD card that came with it. eMMC on the BCM1 is slower than on Raspberry Pi CM.

The 16.2 MB/s result for the SD card here is somewhat suspect however. After several repeats, the first run of five was always the fastest, with the later runs only yielding around 12 MB/s. It is as if some caching was involved (even though fdatasync was specified with dd).

Comparison of read performance for ARM systems.

Interestingly, things turn around with read performance. BCM1's eMMC flash is better at reading data than the SD card. In fact, BCM1 eMMC flash reads faster than both Raspberry Pi setups I tested. It is still at least 3 times slower than a SATA drive on the CubieTruck.

Comparison of cached read performance for ARM systems.

Cached read performance is the least interesting of these tests. It's more or less the benchmark of the CPU memory access rather than anything related to the storage devices. Hence both BCM1 results are more or less identical. Interestingly, BCM1 with the 1 GHz CPU does not seem to be significantly better than the Compute Module with the 700 MHz CPU.

My results for the BCM1 eMMC flash are similar to those published here for the BeagleBone Black. This is expected, since BeagleBone Black has the same hardware as BCM1, and gives me some confidence that my results are at least somewhat correct.

Posted by Tomaž | Categories: Digital | Comments »

z80dasm 1.1.5

06.08.2017 12:26

This is becoming a summer of retro computing blog posts, isn't it? In any case, today I'm pleased to announce the release of z80dasm 1.1.5, the latest version of my disassembler for the Z80 CPU machine code.

Ast Moore recently noticed that z80dasm doesn't recognize several well known undocumented instructions. When I made z80dasm on the base of Jan Panteltje's dz80 several years ago my goal was to make a disassembler that would correctly produce an assembly listing for Galaksija's ROM. While I did add support for the sli instruction back then, it seems that Galaksija didn't use any of the other undocumented instructions.

z80dasm 1.1.4 did correctly include unknown instructions in the disassembly as raw hex values with defb directives, however it guessed the length of some of them wrong, which desynced the instruction decoder and produced garbage output. The output would still assemble back into the same binary, but the point of a disassembler is to produce human-readable output, so that was not very useful.

Fixing this problem was a bit more involved than I expected, but not for the usual reasons. One of the guidelines I had with z80dasm was to keep z80dasm and z80asm a matched pair. Being able to reproduce back the original binary from disassembled source is very useful when hacking on old software as well as in testing z80dasm. Unfortunately, it turned out that z80asm is quite bad at supporting these undocumented instructions as well. For instance, inc and dec with ixl and ixh register operands are not recognized at all. Some other instructions, like add with these undocumented operands produce wrong opcodes.

I guess this is not surprising. In contrast to the official instruction set there is no authoritative documentation for these instructions, so even the names differ in different sources. For example, both sli a and sll a mnemonics are commonly used for the cb 37 opcode.

I tried to fix some of the problems with z80asm I found, but it turned out to be quite time consuming. Both z80asm and z80dasm are written in, by today's standards, quite an archaic C style, with lots of pointer arithmetic and packing various unrelated things into a single int variable to save space. While I'm still fairly familiar with z80dasm code, the modifications I did to z80asm took a several sheets of paper to figure out.

$ hd
00000000  ed 70 dd 2c dd 23 23 3d  ed 71 dd 09              |.p.,.##=.q..|
$ z80dasm example.bin
; z80dasm 1.1.5
; command line: z80dasm example.bin

	org	00100h

	defb 0edh,070h	;in f,(c)
	defb 0ddh,02ch	;inc ixl
	inc ix
	inc hl	
	dec a	
	defb 0edh,071h	;out (c),0
	add ix,bc

In the end, I gave up and solved the problem of z80asm compatibility by leaving z80dasm to decode undocumented instructions as defb directives. However, the instruction lengths should now be decoded correctly and also the mnemonic is included in a comment for better readability. There is now also a new --undoc option in 1.1.5. If it's specified on the command-line, z80dasm will place all instructions directly into the disassembly.

As before, you can get the source code for z80dasm in a release tarball or from the git repository. See the README file for install instructions and the included man page for explanations of all command-line options. z80dasm is also included in Debian, however 1.1.5 has not been uploaded yet.

Posted by Tomaž | Categories: Code | Comments »