Tachyon-Networks TNA-30X Research

Introduction

Tachyon Logo

Kicking off a series of blog posts, we will take a nose dive into the research on the Tachyon-Networks TNA-30X series devices.

In the coming weeks further posts will provide details on the various vulnerabilities discovered during this research, these vulnerabilities were disclosed in early June and were quickly patched by the vendor.

Tear Down

With physical access to a device, a great starting point is to open it up and see how the device is built, it may also lead to options to dump firmwares or obtain a terminal interface to the device.

Removing the 12x hex screws on the front cover quickly reveals the internals of the device, with a main PCB attached to a Peraso PRM2143-01 antenna PCB via ribbon cable, reviewing the data sheet suggests this ribbon cable is a USB3 interface.

The remaining components on this side appear to be mostly ethernet and support systems for power, there does appear to be what looks like an unpopulated M.2 PCIe interface (unconfirmed).

Tachyon TNA-302 Open Front

Removing the screws from the main PCB and flipping over, reveals the core components

Tachyon TNA-302 PCB Back

Finding UART

During the tear down we noted 4 unpopulated pin headers, UART is a common console port which consists of 3 or 4 pins, matching what we appear to have found.

With the device off, using a multimeter in continuity mode we are able to identify the GND (ground) pin by checking for continunity between a known ground point on the device and going through each pin until continunity is found.

Next we power the device up and using the multimeter in voltage mode, we check the remaining pins for a constant voltage of usually 3.3V or 5V, this is likely to be the VCC pin.

The two remaining pins are likely to be RX and TX, with the multimeter in voltage mode, monitor each of the pins during startup, the pin with a voltage that flucates is likely to be TX and the pin with a stable or no voltage is likely to be the RX pin.

UART Pins

Getting console

Using a USB TTL/UART adapter and some pogo pins, attach to the device

Device Adapter
TX RX
RX TX
GND GND
VCC N/C

Using a serial terminal (eg. PuTTY) connect at 11500 baud, this is a common baud rate and is a great starting point.

With this serial connection we discover that the device boots direct into a root shell! Allowing for quick extraction of the firmware and exploration of the device.

U-Boot 2020.04-g1ee45d52 (Jul 18 2022 - 13:48:03 +0000)Venus-SoC

CPU:   Cortina Venus
DRAM:  496 MiB
WDT:   Started with servicing (60s timeout)
NAND:  Configure DMA ordering
ONFI
P-NAND    : MT29F1G08ABAEAWP
Chip  Size: 128MB
Block Size: 128KB
Page  Size: 2048B
OOB   Size: 64B
128 MiB
Loading Environment from NAND... OK
...
Hit ESC key to stop autoboot:  0 
Booting from partition: 2
...
Starting kernel ...

[    0.000000] Booting Linux on physical CPU 0x0
[    0.000000] Linux version 4.14.172 (builder@hoss2) (gcc version 8.3.0 (OpenWrt GCC 8.3.0 r12467-ac9fc6b983)) #0 SMP Tue Dec 13 16:16:47 2022
[    0.000000] Boot CPU: AArch64 Processor [411fd050]
[    0.000000] Machine model: Tachyon Networks TNA-110
...
[    0.000000] Kernel command line: console=earlycon=serial,0xf4329148 console=ttyS0,115200 mtdparts=ca_nand_flash:4M@0x0(u-boot),2M(u-boot-env),2M(hwinfo),8M(config),16M(persist),48M(ubi1),48M(ubi2) ubi.mtd=6
...
[    4.219858] 7 cmdlinepart partitions found on MTD device ca_nand_flash
[    4.226317] Creating 7 MTD partitions on "ca_nand_flash":
[    4.231666] 0x000000000000-0x000000400000 : "u-boot"
[    4.237129] 0x000000400000-0x000000600000 : "u-boot-env"
[    4.242712] 0x000000600000-0x000000800000 : "hwinfo"
[    4.247991] 0x000000800000-0x000001000000 : "config"
[    4.253248] 0x000001000000-0x000002000000 : "persist"
[    4.258598] 0x000002000000-0x000005000000 : "ubi1"
[    4.263799] 0x000005000000-0x000008000000 : "ubi2"
...
Press any key to enter console..
..

BusyBox v1.31.1 () built-in shell (ash)


___ ____ ____ _  _ _   _ ____ _  _    _  _ ____ ___ _ _ _ ____ ____ _  _ ____ ®
 |  |__| |    |__|  \_/  |  | |\ |    |\ | |___  |  | | | |  | |__/ |_/  [__  
 |  |  | |___ |  |   |   |__| | \|    | \| |___  |  |_|_| |__| |  \ | \_ ___]

Tachyon Networks® (c) 2020-2023
https://tachyon-networks.com

1.11.1 rev 53981
root@(none):~# 

Running Processes and listening ports

Using the usual netstat -tunelp and ps -w we can see the device has a minimal network exposure by only listening on TCP 80 & 443, along with UDP 514.

Looking at the program for TCP 80 & 443, we can see its LUA based, and when referencing the process list we can observe the full command {wsxavante} /usr/bin/lua /usr/sbin/wsxavante /var/etc/http revealing the webserver is based on a deriative of xavante

root@tachyon-ptmp:/tmp# netstat -tunelp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      500/lua
tcp        0      0 0.0.0.0:443             0.0.0.0:*               LISTEN      500/lua
tcp        0      0 :::80                   :::*                    LISTEN      500/lua
tcp        0      0 :::443                  :::*                    LISTEN      500/lua
udp        0      0 0.0.0.0:514             0.0.0.0:*                           260/rsyslogd
udp        0      0 :::514                  :::*                                260/rsyslogd
root@tachyonp:/tmp# ps -w
  PID USER       VSZ STAT COMMAND
    1 root      4076 S    /sbin/init
    2 root         0 SW   [kthreadd]
    4 root         0 IW<  [kworker/0:0H]
    6 root         0 IW<  [mm_percpu_wq]
    7 root         0 SW   [ksoftirqd/0]
    8 root         0 IW   [rcu_sched]
    9 root         0 IW   [rcu_bh]
   10 root         0 SW   [migration/0]
   11 root         0 SW   [cpuhp/0]
   12 root         0 SW   [cpuhp/1]
   13 root         0 SW   [migration/1]
   14 root         0 SW   [ksoftirqd/1]
   16 root         0 IW<  [kworker/1:0H]
   17 root         0 SW   [cpuhp/2]
   18 root         0 SW   [migration/2]
   19 root         0 SW   [ksoftirqd/2]
   20 root         0 IW   [kworker/2:0]
   21 root         0 IW<  [kworker/2:0H]
   22 root         0 SW   [cpuhp/3]
   23 root         0 SW   [migration/3]
   24 root         0 SW   [ksoftirqd/3]
   25 root         0 IW   [kworker/3:0]
   26 root         0 IW<  [kworker/3:0H]
   27 root         0 SW   [kdevtmpfs]
   28 root         0 IW<  [netns]
   29 root         0 IW   [kworker/0:1]
   30 root         0 SW   [oom_reaper]
   31 root         0 IW<  [writeback]
   32 root         0 SW   [kcompactd0]
   33 root         0 IW<  [crypto]
   34 root         0 IW<  [kblockd]
   35 root         0 IW<  [cfg80211]
   36 root         0 IW<  [watchdogd]
   37 root         0 SW   [kswapd0]
   43 root         0 IW   [kworker/2:1]
   57 root         0 IW   [kworker/3:1]
   72 root         0 IW   [kworker/1:1]
   73 root         0 IW<  [nvme-wq]
   74 root         0 SW   [spi0]
   75 root         0 SW   [kpktgend_0]
   76 root         0 SW   [kpktgend_1]
   77 root         0 SW   [kpktgend_2]
   78 root         0 SW   [kpktgend_3]
   79 root         0 IW   [kworker/0:2]
   80 root         0 IW<  [ipv6_addrconf]
   81 root         0 SW   [ubi_bgt0d]
   83 root         0 IW<  [kworker/0:1H]
   84 root         0 IW<  [kworker/1:1H]
   85 root         0 SW   [ubifs_bgt0_2]
   88 root      1292 S    /bin/ash --login
   89 root      4092 S    launchd
   90 root         0 IW<  [kworker/3:1H]
   91 root         0 IW<  [kworker/2:1H]
  104 root      1248 S    watchdog -T 30 -t 10 /dev/watchdog
  105 root      1160 S    ubusd
  108 root         0 DW   [IROS-Timer]
  109 root         0 IW<  [PERI_TMR1_WQ]
  234 root         0 SW   [ubi_bgt1d]
  240 root      1664 S    /usr/sbin/haveged -w 1024 -d 32 -i 32 -v 1 -F
  259 root      2140 S    logd -S 1024
  260 root     16108 S    rsyslogd -n -i/var/run/rsyslogd.pid
  261 root      5304 S    {ledd} /usr/bin/lua /usr/share/config/ledd
  262 root      1944 S    {signal_updater} /usr/bin/lua /usr/share/config/signal_updater
  277 root         0 DW   [unsync_reset_th]
  278 root         0 IW<  [PLOAM_WQ]
  279 root         0 IW<  [PLOAM_KEY_CTRL_]
  280 root         0 IW<  [PLOAM_KEY_TK4_W]
  281 root         0 IW<  [PLOAM_KEY_TK5_W]
  282 root         0 IW<  [PLOAM_RGST_WQ]
  283 root         0 IW<  [ACTIVATE_WQ]
  284 root         0 IW<  [PON_MISC_INTR_W]
  288 root         0 SW   [sw_aging]
  289 root         0 DW   [port_scan]
  290 root         0 SW   [NETLINK RX]
  291 root         0 DW   [ca_ni_netlink]
  292 root         0 IW<  [CA_NI_FILL_EQ_P]
  339 root         0 IW   [kworker/1:2]
  359 root         0 IW<  [CA_NI_GPHY0_WQ]
  360 root         0 IW<  [CA_NI_GPHY1_WQ]
  361 root         0 IW<  [CA_NI_GPHY2_WQ]
  362 root         0 IW<  [CA_NI_GPHY3_WQ]
  415 root      8992 S    /usr/sbin/lldp-server
  467 root      5668 S    /usr/sbin/wpa_supplicant -P /var/run/wpa_supplicant-wlan0.pid -D nl80211 -i wlan0 -c /var/run/wpa_supplic
  479 root      1248 S    udhcpc -p /var/run/udhcpc-br-wan.pid -s /usr/share/config/dhcpscript.lua -f -t 4 -i br-wan -x hostname:ta
  482 root      2168 S    {snmp_traps.lua} /usr/bin/lua /usr/share/lua/snmp_traps.lua /var/run/snmp-traps.json
  500 root     12708 S    {wsxavante} /usr/bin/lua /usr/sbin/wsxavante /var/etc/http
  501 root      1248 S<   ntpd -n -N -p time.google.com -p time.cloudflare.com
  502 root      4076 S    /usr/sbin/stsd -i prs0
  508 root      4092 S    /usr/sbin/bwmonitor -d -p 5
  513 root      1248 S    /bin/sh /usr/sbin/prs-events-processor
  516 root      1124 S    prs-events-parser --structured-mode
  517 root      2008 S    lua /usr/share/lua/prs-events/prs-events-processor.lua
11328 root         0 IW   [kworker/u8:1]
12575 root      3020 S    ash
14420 root      2148 S    {phymond} /usr/bin/lua /usr/sbin/phymond
16126 root         0 IW   [kworker/u8:0]
16849 root      2348 S    {usbwatchd.lua} /usr/bin/lua /usr/share/config/usbwatchd.lua
17516 root         0 IW   [kworker/u8:2]
17827 root      4076 S    /usr/sbin/cpustat -l -c 7
17833 root      1248 R    ps -w

Web Server

Digging into the /var/etc/http folder from the wsxavante process we discovered, we can observe more details on the web server configuration.

In this configuration we can see static content being served from /tmp/www/active, but more interesting there is a CGI interface providing dynamic content for url paths under /cgi.lua/ which call upon the /usr/libexec/cgi/main.lua handler.

/var/etc/http/web-ssl.json

{
  "ssl": {
    "key": "/usr/lib/server.key",
    "certificate": "/usr/lib/server.crt"
  },
  "hosts": [
    {
      "host": "*",
      "port": 443
    },
    {
      "host": "::",
      "port": 443
    }
  ],
  "serve": {
    "files": {
      "root": "/tmp/www/active",
      "pattern": [
        "%.gif$",
        "%.ico$",
        "%.png$",
        "%.html$",
        "%.css$",
        "%.js$",
        "%.jsx$",
        "%.svg$",
        "%.ttf$",
        "%.otf$",
        "%.woff$",
        "%.woff2",
        "%.eot$",
        "%.bin$",
        "download/%$"
      ]
    },
    "websock": {
      "prefix": "/ws.lua/",
      "pattern": "^/ws.lua/.+$"
    },
    "redirect": [
      {
        "pattern": "^.*/$",
        "destination": "index.html?h=57fa0c61"
      },
      {
        "pattern": "^/(.+)$",
        "destination": "%s/index.html?h=57fa0c61"
      }
    ],
    "luacgi": {
      "pattern": "^/cgi%.lua/.+$",
      "prefix": "/cgi%.lua/",
      "handler": "/usr/libexec/cgi/main.lua"
    }
  }
}