As I mentioned towards the end of my previous blog post, where I detailed running my blog on the PinePhone's GSM/WWAN/GPS modem, I suspected that the daemon responsible for parsing AT commands on the modem's side is susceptible to OS command injection, as it uses a lot of
system() calls. My hunch turned out to be true.
Communication with the PinePhone
Among other channels, the PinePhone communicates with the Quectel modem by sending AT commands to the modem over a serial line -
/dev/ttyUSB2 on the PinePhone's side and
/dev/ttyHSL0 on the modem's side.
The modem, which runs a full Linux install separate from the PinePhone's main OS, receives these commands, parses them, and executes them according to program logic. After this, the modem either returns
ERROR over the serial line back to the PinePhone. The daemon primarily responsible for this is
Getting the daemon is easy. It's possible to set up
adb access and extract it using
adb. It's also possible to simply extract it from the firmware's update packages, as it's not encrypted in any way.
atfwd_daemon in Ghidra reveals that the executable uses
system() in 233 different places across the file. That's… quite a lot.
system() with user input is never a good idea, most of the calls cannot be exploited due to being hardcoded or the fact that user input is converted to an integer using
However, there are a few places where user input is
%s and no checks or sanitization is performed on user input.
One of these places is in a routine called
Here we can see that
param1 is being formatted as
ipth_dme -dmacc %s &, which is then passed to
system(). What's interesting to note here is that
ipth_dme does not exist on the system at all, so this program would never run.
Traversing the program execution flow, we can see that the switch case in the previous screenshot is triggered when some part of user input begins with "dmacc". This is checked in a routine called
The rest of the input remains relatively untouched.
Going further up the program flow, we can see that the command in question which parses this input is
From this, we can deduce that arbitrary command execution is possible. We can, for example, use backticks to execute our commands in a subshell. As an example, to reboot the modem:
Due to the fact that the daemon runs as root, the code is also being executed as the root user on the modem.
As an example, in this Asciinema recording, I
cat /etc/passwd and run
id, and return the data to the PinePhone's OS over a serial line:
It's very possible that this vulnerability affects other Quectel products as well, as firmware is commonly reused, but I do not possess other hardware to test it on.
- 03/04/2021 - Attempted to contact vendor
- 13/04/2021 - Vendor confirmed vulnerability
- 23/04/2021 - Vendor issued $2,000 bounty
- 24/04/2021 - Assigned ID CVE-2021-31698
- 08/09/2021 - Write-up published