24 September, 2024

Automating UART Command Injection with the Flipper Zero and JavaScript

Automating UART Command Injection with the Flipper Zero and JavaScript
Travis Phillips
Author: Travis Phillips
Share:

Overview

The Flipper Zero recently came out with a JavaScript system built off of MJS that exposes some of the functionality of the Flipper Zero.  Using this new JavaScript system, users can now create small JavaScript programs in .js files, as opposed to writing them and compiling them in C.  These new scripts can then be run from the SD card under the /ext/apps/Scripts/ folder. This lowers the barrier to entry for some simpler applications and can allow you to quickly throw together a proof of concept.  Having recently seen that this was released, I wanted to take a stab at creating a small program I’ve had in mind for a while.

Firmware Differences

While the JavaScript system exists in the factory firmware, there are many different firmware options out there.  The factory firmware is probably one of the most limited for exposed functionality in JavaScript.  I personally have been using the Momentum Firmware as of late, and this one has a lot more functionality in the JavaScript API.

Derek Jamison put together an amazing write-up on Flipper Zero JavaScript over on his GitHub page which highlights several of the differences between firmware choices and what is supported with each one.He also included a nice table to summarize a bunch of info and several examples to fill the void in the documentation for the JavaScript API.  Derek Jamison also has an amazing YouTube channel where he covers a lot of topics about the Flipper Zero including C development.  If you are interested in learning more about the Flipper Zero, I would highly recommend watching his videos.

The Project: UART Injector

So I wanted to try my hand at the JavaScript functionality on the Flipper Zero and decided, ”Why not make a project I’ve had in mind for a while.”  The project was to make it so the Flipper Zero could connect to a UART interface on a PCB and inject command payloads that I would want to run without the use of a computer.  While the Flipper Zero does have a USB-to-UART Bridge, it would be nice if common tasks could be performed in a standalone manner without needing the computer.

I figured this scope would give me enough of a project to dive into, see how viable it is, and if there were limitations that would still cause a fallback to C programming or if they could be worked around.

So What Can it Do?

When designing this project, I had a few goals in mind:

    1.  Simple to use:
         a.  I want to just wire it, select the baud rate and the payload and fire away
    2.  The ability to inject a command
    3.  Sometimes, the ability to inject a command and capture the output

That last requirement was the more difficult one.  Since the Flipper Zero has a small screen, it’s not ideal to read on.  However, the Flipper Zero does have a SD card, and the mobile application can allow you to read files from the Flipper Zero in the field.  This would be a lot more ergonomic.

Optionally, I wanted the following:

    •  Sanity check that this is a Linux root shell
    •  Ability to easily add payloads in the future without technical debt

Turns out, these were both fairly easy to add to the JavaScript application thanks to the serial.expect() function.

With the application built, I decided to wire it up to the UART port of an old PogoPlug Mobile device I had laying around and give the application its maiden flight.

 

Flipper Zero wired to UART port

Command Injection without Capture:

The first payload I tested was the Bindsh 4444 - Telnetd payload.  This creates a persistent (until reboot) passwordless bind shell on TCP port 4444 on the device using BusyBox’s telnetd applet.  Busybox’s version of telnetd has a -l switch which is shown in the help menu as:

-1 LOGIN        Exec LOGIN on connect

This is to allow a custom binary to handle the login process.  If you set this to /bin/sh, then the authentication check is bypassed and you are just dropped into a shell.  Effectively, we just want to run:

telnetd -1 /bin/sh -p 4444

I added this as a payload in the script.  Since this one does not return an output, there is nothing to capture and the script is finished once it sends the payload.  The graphic below shows an attempt to connect to the PogoPlug Mobile on port 4444 before running the script, at which point the script is executed and the port is now open for as many connections as we want.



attempt to connect to the PogoPlug Mobile on port 4444

This payload can make life a lot simpler if telnetd is present on the device since it allows you shell access to a device on the network without having to be tethered to the device’s UART interface.  This also makes it easier to extract flash from the system by reading the MTD devices over the network.

Other examples of these types of payloads are rebooting the device or even triggering a kernel panic, which put the device in an unusable state and it didn’t automatically reboot to self heal.

Run Commands and Extract Output to SD Card:

The other requirement I had was being able to execute a command or series of commands and save the output to the SD card on the Flipper Zero.  I figured this could make initial reconnaissance efforts easier if the device has an exposed UART port with a linux shell on it.  I had created a few payloads to test out on this device.  The graphic below shows the payloads to gather MTD information and general system information, which was then saved to a file.

The payloads to gather MTD information and general system information

Once this was completed, we had the files.  A sample of the Get MTD file is shown below:

MTD.txt contents

/ # > ---===[ /dev/mtd0 - u-boot ]===---
  [*] Type: nand
  [*] Size: 2097152

---===[ /dev/mtd1 - uImage ]===---
  [*] Type: nand
  [*] Size: 3145728

---===[ /dev/mtd2 - uImage2 ]===---
  [*] Type: nand
  [*] Size: 3145728

---===[ /dev/mtd3 - failsafe ]===---
  [*] Type: nand
  [*] Size: 8388608

---===[ /dev/mtd4 - root ]===---
  [*] Type: nand
  [*] Size: 117440512

/ #

 

And the Sysinfo.txt is shown in the table below.  One of my favorite things with this payload was executing /bin/\busybox so it provided a list of applets the developer provided in it.  This can help you see what is available to you for exfiltration and help with crafting command injection exploit payloads later if any are discovered on the device.

Sysinfo.txt contents

/ # > > > > > > > > > > > > > > > > >
    ---===[ System Info ]===---

 [*] Hostname: PogoplugMobile
 [*] Uname: Linux PogoplugMobile 2.6.31.8 #4 Tue Aug 23 13:00:50 PDT 2011 armv5tel GNU/Linux
 [*] Cmdline: console=ttyS0,115200 root=ubi0:rootfs ubi.mtd=4,2048 rootfstype=ubifs
 [*] Boardtype: RD-88F6192A-NAS


    ---===[ Network Info ]===---

eth0      Link encap:Ethernet  HWaddr 00:25:31:06:37:7A  
          inet addr:192.168.1.166  Bcast:192.168.1.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:601 errors:0 dropped:0 overruns:0 frame:0
          TX packets:26 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:532
          RX bytes:45267 (44.2 KiB)  TX bytes:4927 (4.8 KiB)
          Interrupt:11

eth0:0    Link encap:Ethernet  HWaddr 00:25:31:06:37:7A  
          inet addr:169.254.55.122  Bcast:169.254.255.255  Mask:255.255.0.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          Interrupt:11

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:541 errors:0 dropped:0 overruns:0 frame:0
          TX packets:541 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:42433 (41.4 KiB)  TX bytes:42433 (41.4 KiB)

xce0      Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  
          inet addr:10.67.101.1  P-t-P:10.67.101.1  Mask:255.255.255.255
          UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1350  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:500
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)


Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       
netstat: /proc/net/tcp6: No such file or directory
netstat: /proc/net/udp6: No such file or directory
netstat: /proc/net/raw6: No such file or directory
Active UNIX domain sockets (w/o servers)
Proto RefCnt Flags       Type       State         I-Node Path


    ---===[ CPU and Memory ]===---

Processor    : Feroceon 88FR131 rev 1 (v5l)
BogoMIPS    : 799.53
Features    : swp half thumb fastmult edsp
CPU implementer    : 0x56
CPU architecture: 5TE
CPU variant    : 0x2
CPU part    : 0x131
CPU revision    : 1

Hardware    : Feroceon-KW
Revision    : 0000
Serial        : 0000000000000000

MemTotal:         118628 kB
MemFree:          102400 kB
Buffers:               0 kB
Cached:             3996 kB
SwapCached:            0 kB
Active:             2508 kB
Inactive:           2880 kB
Active(anon):       1400 kB
Inactive(anon):        0 kB
Active(file):       1108 kB
Inactive(file):     2880 kB
Unevictable:           0 kB
Mlocked:               0 kB
SwapTotal:             0 kB
SwapFree:              0 kB
Dirty:                 0 kB
Writeback:             0 kB
AnonPages:          1424 kB
Mapped:             2236 kB
Slab:               8140 kB
SReclaimable:        120 kB
SUnreclaim:         8020 kB
PageTables:           96 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:       59312 kB
Committed_AS:       3916 kB
VmallocTotal:     516096 kB
VmallocUsed:         788 kB
VmallocChunk:     514724 kB


    ---===[ Mounts ]===---

rootfs on / type rootfs (rw)
ubi0:rootfs on / type ubifs (ro,relatime)
none on /proc type proc (rw,relatime)
none on /sys type sysfs (rw,relatime)
none on /dev/pts type devpts (rw,relatime,mode=600)
none on /tmp type tmpfs (rw,relatime)
none on /proc/bus/usb type usbfs (rw,relatime)


    ---===[ Busybox Applets ]===---

BusyBox v1.16.1 (2011-08-23 13:02:26 PDT) multi-call binary.
Copyright (C) 1998-2009 Erik Andersen, Rob Landley, Denys Vlasenko
and others. Licensed under GPLv2.
See source distribution for full notice.

Usage: busybox [function] [arguments]...
   or: function [arguments]...

    BusyBox is a multi-call binary that combines many common Unix
    utilities into a single executable.  Most people will create      a link to busybox for each function they wish to use and          BusyBox will act like whatever it was invoked as.

Currently defined functions:
    [, [[, arp, ash, awk, basename, brctl, bunzip2, bzcat, bzip2, cat,
    chattr, chgrp, chmod, chown, chroot, clear, cmp, cp, crond, cut, date,
    dd, depmod, df, diff, dirname, dmesg, dnsdomainname, du, echo, egrep,
    eject, env, expr, false, fdflush, fdisk, fgrep, fsck, fuser, getty,
    grep, gunzip, gzip, halt, head, hexdump, hostname, hush, ifconfig,
    ifdown, ifplugd, ifup, init, insmod, ionice, ip, ipaddr, iplink,
    iproute, iprule, iptunnel, kill, killall, less, ln, login, losetup, ls,
    lsattr, lsmod, lspci, lsusb, lzop, lzopcat, md5sum, mkdir, mkdosfs,
    mke2fs, mkfifo, mkfs.ext2, mkfs.vfat, mknod, modprobe, more, mount, mv,
    nc, netstat, nice, nmeter, nohup, nslookup, od, passwd, pidof, ping,
    ping6, pivot_root, poweroff, printf, ps, pwd, rdev, readlink, realpath,
    reboot, renice, reset, rm, rmdir, rmmod, route, sed, seq, sh, sha1sum,
    sleep, sort, split, stat, strings, stty, su, sulogin, sum, swapoff,
    swapon, sync, sysctl, tail, tar, taskset, tee, telnet, telnetd, test,
    time, top, touch, tr, traceroute, traceroute6, true, tty, tune2fs,
    udhcpc, umount, uname, uniq, unlzop, uptime, usleep, vi, wc, wget,
    which, who, whoami, xargs, yes, zcat, zcip



    ---===[ Processes ]===---

  PID USER       VSZ STAT COMMAND
    1 root      2088 S    init
    2 root         0 SW<  [kthreadd]
    3 root         0 SW<  [ksoftirqd/0]
    4 root         0 SW<  [events/0]
    5 root         0 SW<  [khelper]
    8 root         0 SW<  [async/mgr]
  106 root         0 SW<  [kblockd/0]
  115 root         0 SW<  [khubd]
  118 root         0 SW<  [kseriod]
  121 root         0 SW<  [kmmcd]
  142 root         0 SW   [crypto]
  143 root         0 SW   [crypto_ret]
  148 root         0 SW   [pdflush]
  149 root         0 SW   [pdflush]
  150 root         0 SW<  [kswapd0]
  151 root         0 SW<  [aio/0]
  152 root         0 SW<  [nfsiod]
  153 root         0 SW<  [crypto/0]
  415 root         0 SW<  [scsi_eh_0]
  416 root         0 SW<  [scsi_eh_1]
  429 root         0 SW<  [mtdblockd]
  430 root         0 SW<  [nftld]
  451 root         0 SW<  [ubi_bgt0d]
  501 root         0 SW<  [usbhid_resumer]
  504 root         0 SW<  [rpciod/0]
  611 root         2088 S    udhcpc -b -i eth0 -H PogoplugMobile
  625 root         1672 S    /usr/local/cloudengines/bin/hbwd /usr/local/cloudeng
  626 root         7276 S    /usr/local/cloudengines/bin/hbplug
  627 root         2096 S    -/bin/sh
  715 root         2092 S    telnetd -p 4444 -l /bin/sh
  765 root         2092 R    ps
/ #

Final Thoughts

This was a fun project and shows how powerful the JavaScript system in the Flipper Zero can be.  While it took little time to slap this project together, it  is one I can use in my hardware projects.

Even though I managed to run through this project, there were some limitations I encountered that I had to work around.

No class Keyword

The keyword class is not recognized in MJS.  Originally, I wanted to make the Payload a data class.  However, while classes aren’t supported, objects are.  So instead of a class, it’s a function that returns an object the way I had in mind with the data class.

Keyword-class work around in MJS

Dialog Module has a File Picker, but no Save File:

Honestly, the Dialog module is really nice and allows any JavaScript you write to feel very close to a real application.  However, even though it has a function to pick a file, it does not have one for saving a new file.  As a result I needed to build my own.  Which brings us to my final limitation.

MJS Does Not Provide a Date Object:

Since I had to build my own Save File dialog, I figured a default name like uart_log_YYYYMMDD_HHMMSS would have been a nice default.  However, I was unable to find a workaround for this and don’t see any way in JavaScript on the Flipper Zero that you can get the current date and time.  Perhaps a future API version can provide something, even if it’s just a unix timestamp from the Flipper Zero.

Conclusion

I hope you’ve enjoyed this article on using JavaScript on Flipper Zero.  If you want to download my script and give it a go, you can find it in my Github repo.It’s a wonderful addition to the Flipper Zero that makes it quicker and easier to make simple applications or draft a quick proof of concept on the fly.  If you are interested in learning more about using the Flipper Zero as a hardware hacking tool, check out some of our past Professionally Evil Webcasts on YouTube where we’ve presented on that and several other exciting topics, or check out our upcoming Webcast.

Join the Professionally Evil newsletter