Reverse Engineering the OBi200 Google Voice Appliance: Part 3

In part 1 of this series, I analyzed the firmware of the OBi200 and walked through exploiting some RCE vulns to pop a shell. In part 2, I covered the process of identifying and connecting to the board’s undocumented UART port to access the console. This post will cover cross-compiling 3rd party binaries from source for the target, debugging crashes in the OBi binaries, and some other interesting findings.


After getting console access to the device, the next steps in diving deeper required some additional tools. For example, running tcpdump right on the device would help in understanding more about the listening services:

The included busybox was old and a bit limited as well:

# busybox   
BusyBox v1.16.2 (2015-06-18 14:18:21 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.

Since the board didn’t have the resources to run gdb, I also needed to build gdbserver. In order to build these and other utilities, I needed to get my hands on a toolchain capable of compiling for the OBi.


Getting ahold of the right toolchain for cross compiling ARM was made a lot simpler with crosstool-NG. After installation, I chose the pre-configured arm-unknown-linux-uclibcgnueabi toolchain and built it by running ct-ng build. It took some time to run the build process, but ultimately produced binaries that would run on the OBi target.

Next, I cross-compiled tcpdump, gdbserver, busybox, strace, and some other tools from source and moved them over to the OBi with netcat. Most of the aforementioned projects have some documentation on cross compiling — see the wiki for gdbserver, for instance:

And of course, I couldn’t resist building a cryptocoin miner:

Obviously just for fun — it didn’t exactly run well despite its impressive hardware!


With the above-mentioned tools now copied over to the OBi, I was able to begin exploring more extensively. Recall from part 1 of this series the numerous vulnerabilities previously disclosed in some similar Obihai software components. I tested some of those same crash scenarios to see if the OBi200 (at its shipped firmware version) was similarly vulnerable. Here’s an example request attempting to overflow a static buffer used for preparing a command string sent to the OBi IPC socket:

Authorization: ***REMOVED***
User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36
Upgrade-Insecure-Requests: 1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9

As suspected, the OBi crashed and immediately rebooted. Let’s debug the crash.

The OBi’s watchdog daemon made it difficult to kill and relaunch the main binary with gdbserver, so instead I attached to the currently-running process using multi mode:

pgrep obiapp
gdbserver --multi localhost:3617

From another machine, I connected the gdb client:

Note the executed gdb was produced by crosstool-NG — not the one installed by the distro’s package manager. The host and target configuration must match your respective debugging environment.

Next, I attached to the running obiapp process and continued execution of the threads.

After running the example HTTP request above, the crash was caught — the dumped stack and registers are below:

Note the program generates a segmentation fault upon continuing to the invalid memory address 0x41414140 — just 0x00000001 away from the payload “AAAA” from the HTTP request above. This suggests that an attacker is possibly able to control the return address and interrupt program flow, ultimately leading to execution of malicious code. Unfortunately, before getting a chance to develop the exploit, I’d updated the OBi’s firmware which patched the overflow and I moved on to other areas.

Additional Findings

I wanted to touch on some other interesting areas of the OBi that weren’t fully explored. Let’s start with hard-coded keys — see the below pseudo code decompiled from the obiapp binary:

signed int __fastcall sub_8C964(const char *a1, signed int a2)
  signed int v2; // r10@1
  const char *v3; // r8@1
  signed int result; // r0@3
  int v5; // r5@5
  int v6; // r4@5
  void *v7; // r0@6
  void *v8; // r10@6
  int v9; // r0@6
  int v10; // r0@6
  size_t v11; // r4@6
  FILE *v12; // r5@6
  char v13; // [sp+10h] [bp-130h]@6
  char dest; // [sp+9Fh] [bp-A1h]@1
  char v15; // [sp+E0h] [bp-60h]@6
  int v16; // [sp+110h] [bp-30h]@6
  int v17; // [sp+114h] [bp-2Ch]@6

  v2 = a2;
  v3 = a1;
  memcpy(&dest, "Nd6o5nSwyGkjfxFTeTU7OrUbGySOldH+WATDn6/D5GfeU/zatF9EO4LAMELvnFq0", 0x41u);
  if ( v2 > 15 && !strncmp(v3, "Salted__", 8u) )
    v5 = ((int (*)(void))EVP_aes_256_cbc)();
    v6 = EVP_md5();
    if ( EVP_BytesToKey(v5, v6, v3 + 8, &dest) == 32 )
      v7 = malloc(v2 - 16);
      v17 = v2 - 16;
      v16 = 0;
      v8 = v7;
      v9 = EVP_CIPHER_CTX_init(&v13);
      v10 = EVP_aes_256_cbc(v9);
      EVP_DecryptInit_ex(&v13, v10, 0, &v15);
      EVP_DecryptInit_ex(&v13, 0, 0, 0);
      EVP_DecryptUpdate(&v13, v8, &v17, v3 + 16);
      EVP_DecryptFinal_ex(&v13, (char *)v8 + v17, &v16);
      v11 = v16 + v17;
      v12 = fopen("/tmp/module.tgz", "wb");
      if ( v11 == fwrite(v8, 1u, v11, v12) )
        syslog(150, "Wrote plaintext; len: %d\n", v11);
        result = 0;
        syslog(150, "Error writing plaintext to file.\n");
        result = -1;
      syslog(150, "Error initializing context.\n");
      result = -1;
    syslog(150, "Invalid module file.\n");
    result = -1;
  return result;

This function appears to decrypt Lua script modules sent from Obihai’s servers. Note the static string above copied into dest which is eventually passed as the data parameter in EVP_BytesToKey . The salt is passed into this function as the first parameter, though I didn’t confirm whether it was dynamic or NULL .

Another interesting finding from elsewhere in the decompiled code:

char byte_26D6B8 = 'U'; // weak
char aNknown[7] = "NKNOWN"; // weak
char aOb100UnitInfo[16] = "OB100 UNIT INFO"; // weak
char aThisisthesecre[27] = "thisisthesecretofobihaimfd"; // weak
char aThisisanothers[31] = "thisisanothersecretofobihaimfd"; // weak

Two very interesting “secrets” above. The mention of a different model OBi left me unsure as to whether these variables were used in my model. The only usages I found were here:

sub_A3440((int)&v32, (int)aThisisthesecre, 0x1Au);
sub_A351C((int)&v34, (int)&v32);
sub_A3440((int)&v32, (int)aThisisanothers, 0x1Eu);
sub_A3440((int)&v32, (int)(&v34 + 1), 0xFu);
sub_A3440((int)&v32, v15, v11);
sub_A351C((int)&v33, (int)&v32);
if ( !memcmp(&v33, &v35, 0x10u) )
  v16 = v11 - v10;
  v17 = malloc(v11 - v10);
  if ( v17 )
    RC4_set_key(&v31, 15, &v34);
    RC4(&v31, v16, (const char *)(v15 + v10), v17);
    memcpy((void *)(v15 + v10), v17, v16);
    free((void *)v17);
  if ( !memcmp((const void *)(v30 + v15), (const void *)(v30 + v15 + 9), 9u)
    && !memcmp((const void *)(v30 + v15), (const void *)(v30 + v15 + 18), 9u) )
    return 1;
  syslog(0, "p2p hn validation error %d!\n", v30);
  result = -1;
  syslog(0, "UNIT DATA checksum validation error:\n");
  result = -1;

Judging by the syslog messages, this seemed to be some kind of reporting functionality possibly sent back to Obihai.

There were a number of other areas I didn’t touch in the OBi — for example, the provisioning process through the cloud interface here (including Lua scripts) as well as potential crashes in the SIP/other UDP protocols. There are also a number of integrations (OBIEXTRAS) available for the device, making the attack surface quite vast.

Reported Issues

I identified several other vulnerabilities during the course of this research, including some of medium-risk as well as one critical issue. I’m still working with Obihai on these vulnerabilities and will likely disclose them after fixes are released.

Share this: Facebooktwitterlinkedin