Our Blog

P4wnP1 LTE updates

Reading time ~11 min

After publishing my blog post about running P4wnP1 on an LTE modem, where I explained how to install Linux and P4wnP1 on an actual LTE modem for sneaky USB attacks, and then trying and failing to do an internal presentation to show it off to folks, I realised that I had not completely documented the process. In fact, I had left it rather incomplete as it turned out! As I was intending to give a public demonstration of P4wnP1-LTE, I had some work to do.

Unusable LTE

Firstly, I was unable to get the LTE interface to connect reliably, and pass packets. Having revisited it since that embarrassing internal presentation failure, I now suspect that the problem was strongly linked to the temperature of the LTE modem. To address this, I made use of the commands mentioned at the end of the previous post (chcpu -d 1,2,3) to disable 3 of the 4 cores. This was able to stop the temperatures rising above around 65C, and since doing so, I have had no issues maintaining the LTE connection. cat /sys/class/thermal/*/temp will output the temperatures of the various sensors that the kernel knows about, if you want to check easily.

Which is not to say that I have had no issues connecting over LTE (using a Vodacom SIM)! In the previous post, I also described setting up a Wireguard VPN to an Internet-accessible host, and then using SSH to access the modem over that VPN. I started noticing that trying to monitor the P4wnP1 logs, or grepping the P4wnP1 code base for certain strings would also result in my SSH connection dying instantly. However, I could immediately reconnect using SSH, so the modem was staying up, and the LTE connection and Wireguard VPN were all “healthy”. Furthermore, when the WiFi link was up, and Wireguard was routing over WiFi, I had no problems running the same commands. What could possibly be the problem?

I started writing a description of the problem in our internal chat to try and solicit debugging suggestions, and while doing so, it struck me that the SSH connection was fine with small amounts of data. I could connect via SSH, authenticate, navigate around the file system, etc. It was not time based, as whether I ran the grep immediately, or after other commands, the lockup behaviour was identical. Large amounts of terminal output would reliably lock up the SSH connection in exactly the same way, every time.

This didn’t seem to be a problem with downloading files over the LTE connection. I could apt update && apt install with no problems. I recalled a conversation that I had seen a few weeks prior in Discord, where someone described a connection hanging due to an incorrect MTU.

What is MTU, though? According to Wikipedia, the maximum transmission unit (MTU) is the size of the largest protocol data unit (PDU) that can be communicated in a single network layer transaction. You can figure out the MTU for a particular interface using ping.

ping 8.8.8.8 -c 2 -M do -s 1392

This sends two ICMP packets to 8.8.8.8, with a data size of 1392 octets (-s 1392), and sets the Path MTU Discovery strategy to prohibit fragmentation (-M do). Basically, this tells any routers not to break up any packets that are too large for the next hop, but rather to return an ICMP error. This allows you to know what the MTU of a particular network path is.

I used this with an ever decreasing packet size to try and figure out what the MTU of my LTE interface was. It was defaulted to something like 1500, but to my surprise, I got all the way down to an ICMP data size of 996 before I was able to successfully get an ICMP response back. Due to ICMP packet framing overhead, you need to add 8 bytes of ICMP and 20 bytes of IPv4 to the data size to calculate the actual MTU. After setting the LTE MTU to 1024, I was able to successfully establish the Wireguard VPN, and SSH connection over LTE, and execute all the commands that were failing previously.

A possible alternative approach is to run tcpdump while downloading something large from the Internet, and seeing what size packets are sent from upstream. One presumes that the Mobile Provider also knows what MTU the link should have, and will only send packets of that maximum size. This is because the MTU is only effective in a single direction, for packets SENT from the system, and doesn’t affect the size of packets received.

A surprising detail though, is that while you normally have to have layered interfaces use a smaller MTU than the underlying interface MTU, in order to account for VPN framing, this does not seem to be the case for Wireguard. Wireguard works just fine over LTE with an MTU of 1420. I’m honestly not sure why or how this works. Maybe Wireguard just fragments the packets anyway?

The lesson for me there was that taking the time to frame the symptoms I was seeing in a way that someone else could make sense of helped me to solve the problem on my own. The classic Rubber Duck debugging technique strikes again.

Rebuilding P4wnP1

So with the network interfaces now reliable, was I ready for a public presentation? Unfortunately not! It turns out that P4wnP1 has some unmet expectations with regards to the kernel modules that should be available, that I had overlooked in the previous blog post. It was looking for libcomposite in lsmod output, and trying to modprobe libcomposite if it was not found. This works fine on the Raspberry Pi, but fails on the OpenStick distribution, because libcomposite is built in, and not a module. I discovered this by running rmmod libcomposite, and saw that information in the resulting error message. I’m honestly not sure if there is a better way of knowing what is built in, and what modules are available, other than looking for the kernel .config file.

Knowing that P4wnP1 is very old code, and that I had trouble trying to build it in the past, on top of having near zero Golang experience, I contemplated papering over it by making shims for lsmod that would return the expected output even if libcomposite was a builtin and not a module. But I decided to bite the bullet, and at least try to build the codebase with my changes.

The P4wnP1 code base has a Makefile in the root, so I started there, and tried to make compile. To my dismay, this ended up downloading so many go dependencies, and complaining about things like deprecation, and being unable to find dependencies, even though going to the URL it printed out showed that the dependency was there. Fortunately, Leon, our resident Golang enthusiast, was able to explain that I probably didn’t want the -u option that Mame82 had included when invoking go get. This tells go to update dependencies to newer minor or patch releases. Skipping the updates had everything compile with almost no problems, other than needing to change go get gopherjs to go install gopherjs due to syntax changes.

With the Makefile updated, and commenting out a bunch of entries designed to install Debian/Kali packages, and made Kali configuration changes for P4wnP1_ALOA, I had P4wnP1 compiled. Except it was compiled for the 64-bit AArch64 Ubuntu installation I was using as a build server, rather than the armv6 architecture needed for the Raspberry Pi (that also runs successfully on the NanoPi R1S and the OpenStick!). Grepping the source for GOARCH, I found a build script at ./build_support/build.sh that did the necessary. After a few iterations trying to figure out why my changes didn’t solve the problem, I realised that the output of rmmod libcomposite was on stderr, not stdout, and corrected my code. Success!

P4wnP1 wants to control the WiFi

With all of that done, I still faced the problem that P4wnP1 wanted to take over control of the wireless interface from NetworkManager, and implemented the wireless configuration in the same way that it did (successfully) on the Raspberry Pi Zero. I had worked around this on the NanoPi R1S by renaming the real wifi interface to something other than wlan0, and creating a dummy wlan0 with the mac80211_hwsim module. Unfortunately, mac80211_hwsim is not currently compiled for the OpenStick, so I can’t do this. Granted, this is more of an issue for the NanoPi, because the WiFi interface is the only way to access the hardware. For the OpenStick, the LTE interface is available. My solution was to instruct NetworkManager not to try and manage the WiFi interface when P4wnP1 is running, using a systemd service configuration option:

ExecStartPre=/usr/bin/nmcli device set wlan0 managed no
ExecStart=/usr/local/bin/P4wnP1_service
ExecStopPost=/usr/bin/nmcli device set wlan0 managed yes

This tells systemd to run the listed commands before starting and after stopping P4wnP1 respectively, resulting in NetworkManager ignoring the wlan0 interface while P4wnP1 is running. Ideally P4wnP1 should be updated to have an option to ignore the WiFi interface completely, and let the OS manage it instead, as opposed to the current “disable it or manage it” option.

Shell over Raw HID

The last thing to test was the Raw HID channel. I ran the following commands to download my HID to TCP proxy, install golang on the OpenStick, and build and run it:

git clone https://github.com/RoganDawes/CovertChannel
cd CovertChannel
cp Client/PowerShell/helper.js /usr/local/P4wnP1/HIDScripts/covertchannel.js
apt install -y golang socat
cd Server/Go
go run server.go

You could also cross compile it and copy it across if you prefer, using the same GOARCH, etc flags from building P4wnP1 in the previous section.

With the CovertChannel server running, I could open another terminal and run socat TCP-L:4444,fork,reuseaddr - to wait for my shell, then go to the P4wnP1 web interface to launch the keystroke injection. Selecting the HIDScript tab, and the “Load and Replace” button, I selected the “covertchannel.js” file that I had just copied. It’s important to include the correct USB VID and PID in the script, so that the PowerShell running on the client knows which device to connect to. Check that they are the same between the HID tab and the last couple of lines of the covertchannel.js script. Then hit Run, and wait for the magic! After a few moments, you should see a lot of activity in the go server terminal, and a DOS Shell prompt appear in your socat terminal. If it doesn’t show up after about 10-15 seconds, try press Enter a couple of times. Enjoy!

If anyone with Golang skills wants to try and update P4wnP1 to integrate the CovertChannel proxy, and bring the dependencies into the mid twenties, that would be awesome. Integrating the terminal (socat) into P4wnP1 using something like term.js would also be cool, but perhaps quite a lot more work. I do have the start of that work for anyone who wants to to continue, though. Get in touch if this sounds like fun!