ipTIME UART Magic (and Not So Useful?) Key

Published on Sunday, 05 February 2017 in Reverse Engineering ; tagged with iptime, n704, v3, router, uart, magic, key, reverse, ida, binwalk ; text version

I recently bought a new toy to play with: an ipTIME n704-v3. ipTIME is the most (or one of the most) common brand in Korea for devices like routers and I was curious to check one out.

ipTIME n704-v3 router

When connecting to the n704 UART pins, I could not interact with the device. First because I realized my BusPirate TX was dead (sigh); second, after switching the BusPirate, because n704 would not react to my input. There was no boot menu (e.g. U-Boot) and there was no configuration menu. There was one thing that picked my curiosity when reading the boot log messages though:

=================================================================
press magic key to change default setting ...

That sounds interesting! But what could be the magic key? Since Google couldn't answer my question, I decided to look for the answer myself. Be prepared to learn something not so useful today!

TL;DR: x d i a g

ipTIME n704-v3 UART Pins

When opening the device, we quickly find the UART and the JTAG pins. In our case, we will only look at the UART pins. Using a logic analyser (or a combination of multimeter and brute-force; it is only 4 pins), we identify the following pin configuration:

ipTIME n704-v3 UART pins

From left to right, we have:

  1. GND
  2. MOSI
  3. MISO
  4. VCC

Connecting a Bus Pirate, we use the following serial configuration:

$ screen /dev/tty.usbserial-A603PKII 115200

HiZ>m  (open menu)

1. HiZ
2. 1-WIRE
3. UART
4. I2C
5. SPI
6. 2WIRE
7. 3WIRE
8. LCD
x. exit(without change)

(1)>3  (choose UART)
Set serial port speed: (bps)
 1. 300
 2. 1200
 3. 2400
 4. 4800
 5. 9600
 6. 19200
 7. 38400
 8. 57600
 9. 115200
10. BRG raw value

(1)>9  (115200 baudrate)
Data bits and parity:
 1. 8, NONE *default 
 2. 8, EVEN 
 3. 8, ODD 
 4. 9, NONE

(1)>1  (default)
Stop bits:
 1. 1 *default
 2. 2

(1)>1  (default)
Receive polarity:
 1. Idle 1 *default
 2. Idle 0

(1)>1 (default)
Select output type:
 1. Open drain (H=Hi-Z, L=GND)
 2. Normal (H=3.3V, L=GND)

(1)>2  (normal output type)
Ready

UART>(3)  (bridge mode)
UART bridge
Reset to exit
Are you sure? y

Boot log messages

When booting the router, we have the following log mesages:

Decompressing...done


CFE version 5.100.138.9 based on BBP 1.0.37 for BCM947XX (32bit,SP,LE)
Build Date: Thu Jul 16 13:26:48 KST 2015 (bcm5357@ski)
Copyright (C) 2000-2008 Broadcom Corporation.

Init Arena
Init Devs.
Boot partition size = 131072(0x20000)
Found an ST compatible serial flash with 64 64KB blocks; total size 4MB
CPU type 0x19749: 300MHz
Tot mem: 32768 KBytes

RESTORE_DEFAULTS --> 0 commit -> 0
====================================================================================
Product ID: n704v3
Version   : 9.986
====================================================================================
Check Magic RTMG: [ OK ]
Check Product ID .. (boot:n704v3)---(run:n704v3) : [ OK ]
Check ICV .. 722f0f15:722f0f15 : [ OK ]
====================================================================================

Loader:raw Filesys:raw Dev:flash0.os File: Options:(null)
Loading: ....... 4378624 bytes read
Entry at 0x80001000
Starting program at 0x80001000
CPU ProcId is: 0x00019749, options: 0x000021cd
Primary instruction cache 32kb, linesize 32 bytes (4 ways)
Primary data cache 32kb, linesize 32 bytes (4 ways)
Linux version 2.4.20 (bcm5357@ski) (gcc version 3.2.3 with Broadcom modifications) #178 Thu Oct 13 16:13:14 KST 2016
 Found an ST compatible serial flash with 64 64KB blocks; total size 4MB
Determined physical RAM map:
 memory: 02000000 @ 00000000 (usable)
On node 0 totalpages: 8192
zone(0): 8192 pages.
zone(1): 0 pages.
zone(2): 0 pages.
Kernel command line: root=/dev/mtdblock2 noinitrd console=ttyS0,115200
CPU: BCM53572 rev 1 at 300 MHz
Calibrating delay loop... 149.91 BogoMIPS
Memory: 27808k/32768k available (3777k kernel code, 4960k reserved, 372k data, 108k init, 0k highmem)
 Dentry cache hash table entries: 4096 (order: 3, 32768 bytes)
Inode cache hash table entries: 2048 (order: 2, 16384 bytes)
Mount-cache hash table entries: 512 (order: 0, 4096 bytes)
Buffer-cache hash table entries: 1024 (order: 0, 4096 bytes)
Page-cache hash table entries: 8192 (order: 3, 32768 bytes)
Checking for 'wait' instruction...  unavailable.
POSIX conformance testing by UNIFIX
PCI: no core
PCI: no core
PCI: Fixing up bus 0
Linux NET4.0 for Linux 2.4
Based upon Swansea University Computer Society NET3.039
Initializing RT netlink socket
Starting kswapd
devfs: v1.12c (20020818) Richard Gooch (rgooch@atnf.csiro.au)
devfs: boot_options: 0x1
squashfs: version 3.2-r2 (2007/01/15) Phillip Lougher
pty: 256 Unix98 ptys configured
Serial driver version 5.05c (2001-07-08) with MANY_PORTS SHARE_IRQ SERIAL_PCI enabled
ttyS00 at 0xb8000300 (irq = 8) is a 16550A
loop: loaded (max 8 devices)
PPP generic driver version 2.4.2
MPPE/MPPC encryption/compression module registered
eth%d: 5.100.138.9 driver failed with code 22
sflash: cramfs filesystem found at block 1528
Creating 5 MTD partitions on "sflash":
0x00000000-0x00020000 : "boot"
0x00020000-0x003f0000 : "linux"
0x0017e3c0-0x003f0000 : "rootfs"
0x003f0000-0x00400000 : "nvram"
0x00000000-0x00400000 : "all"
NET4: Linux TCP/IP 1.0 for NET4.0
IP Protocols: ICMP, UDP, TCP, IGMP
IP: routing cache hash table of 512 buckets, 4Kbytes
TCP: Hash tables configured (established 2048 bind 4096)
Linux IP multicast router 0.06 plus PIM-SM
==> create_proc_ipclone() form DEV.c 
--> Init Smart QoS Monitor
ip_conntrack version 2.1 (512 buckets, 4096 max) - 400 bytes per conntrack
NETDETECT target register
ip_nat_bnet: Trying to register for port 6112
IPSEC netfilter connection tracking: registered
FTP proc OK....
ip_ct_ftp: registering helper for port 21
ip_ct_ftp: # of port 1
IPSEC netfilter NAT helper: ip_tables: (C) 2000-2002 Netfilter core team
ipt_time loading
NET4: Unix domain sockets 1.0/SMP for Linux NET4.0.
NET4: Ethernet Bridge 008 for NET4.0
802.1Q VLAN Support v1.7 Ben Greear <greearb@candelatech.com>
All bugs added by David S. Miller <davem@redhat.com>
VFS: Mounted root (cramfs filesystem) readonly.
Mounted devfs on /dev
Freeing unused kernel memory: 108k freed
Algorithmics/MIPS FPU Emulator v1.5

Checking System Requirements *******************************************

DRAM SIZE  : Requirement->32 Mbytes / Mounted->32 Mbytes -----------> [OK]
FLASH SIZE : Requirement->4 Mbytes / Mounted->4 Mbytes -----------> [OK]


alias:n704v3 version:9.986
/proc/sys/net/netfilter/nf_conntrack_tcp_be_liberal: cannot create
Default Configuration
Bridge Init
/sys/devices/virtual/net/br0/bridge/multicast_snooping: cannot create
device eth1 is not a slave of br0
interface wl0.1 does not exist!
interface wl0.2 does not exist!
interface wl0.3 does not exist!
interface wl0.4 does not exist!
eth1: Invalid argument
eth1: Operation not supported
device eth1 is already a member of a bridge; can't enslave it to bridge br0.
eth1: Invalid argument
eth1: Argument list too long
interface wl0.3 does not exist!
interface wl0.4 does not exist!
eth1: Invalid argument
eth1: Operation not supported
device eth1 is already a member of a bridge; can't enslave it to bridge br0.
Vendor ID: 000014e4
Device ID: 00004329
RadioRev ID: d2057000
Chip Rev : 00000001
CoreRev ID: 0000001c
Board ID : 0000058e
BoardVendor : 000014e4
Board Rev: 00001153
UCode Rev: 02a30002
DRIVER Rev: 05648a09
ChipNum : 0000d144
PhyRev : 00000004
PhyType : 00000011
AnaRev : 00000000
eth1:LED GPIO6 1
LED eth1 - lednum:6 , action:1 , polarity:1
killall: dhcpd.helper: no process killed

=================================================================
press magic key to change default setting ...

Firmware reverse engineering

I usually prefer reversing the latest firmware whenever possible, which can be downloaded from ipTIME's website. The bonus here is that the latest firmware provides an English menu \o/ (compared to the firmware that was installed when buying the router, which was in Korean only).

Binwalk can successfully extract the firmware for us:

$ binwalk -e n704v3_kr_9_986.bin

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
46692         0xB664          LZMA compressed data, properties: 0x5D, dictionary size: 16777216 bytes, uncompressed size: 209044 bytes
131072        0x20000         TRX firmware header, little endian, image size: 3989504 bytes, CRC32: 0x60CDD3EF, flags: 0x0, version: 1, header size: 28 bytes, loader offset: 0x1C, linux kernel offset: 0x15E3C0, rootfs offset: 0x0
131100        0x2001C         LZMA compressed data, properties: 0x5D, dictionary size: 65536 bytes, uncompressed size: 4378624 bytes
1565632       0x17E3C0        CramFS filesystem, little endian, size: 2551808 version 2 sorted_dirs CRC 0x38930636, edition 0, 2187 blocks, 521 files

The last entry shown by binwalk is what we are interested in, since it contains the router's filesystem. Looking around, we undestand that the binary inittime is run first when the user space starts:

$ cat cramfs-root/default/etc/init.d/rcS
#!/bin/sh
/sbin/inittime

Interestingly, it contains a reference to the magic key:

inittime IDA String References

The string is referenced in the function sub_404304, along with 'magic key' not typed and magic key detected. Looking at the disassembly, the magic key is very easy to extract:

.text:00404568                 li      $t9, 0x400000
.text:0040456C                 nop
.text:00404570                 addiu   $t9, (get_char - 0x400000)
.text:00404574                 nop
.text:00404578                 jalr    $t9 ; get_char (renamed manually)
.text:0040457C                 nop
.text:00404580                 nop
.text:00404584                 lw      $gp, 0xC0+var_B0($sp)
.text:00404588                 move    $s2, $v0
.text:0040458C                 addiu   $v0, -0x77
.text:00404590                 sltiu   $v0, 2
.text:00404594                 li      $at, 0xFFFFFF7F
.text:00404598                 beqz    $v0, loc_404BCC
.text:0040459C                 move    $v0, $at
.text:004045A0                 nop
.text:004045A4                 li      $t9, 0x400000
.text:004045A8                 nop
.text:004045AC                 addiu   $t9, (get_char - 0x400000)
.text:004045B0                 nop
.text:004045B4                 jalr    $t9 ; get_char
.text:004045B8                 nop
.text:004045BC                 nop
.text:004045C0                 lw      $gp, 0xC0+var_B0($sp)
.text:004045C4                 move    $s0, $v0
.text:004045C8                 li      $v0, "d"
.text:004045CC                 beq     $s0, $v0, loc_404604
.text:004045D0                 nop
; [ . . . ]
.text:00404604 loc_404604:                              # CODE XREF: sub_404304+2C8j
.text:00404604                 li      $t9, 0x400000
.text:00404608                 nop
.text:0040460C                 addiu   $t9, (get_char - 0x400000)
.text:00404610                 nop
.text:00404614                 jalr    $t9 ; get_char
.text:00404618                 nop
.text:0040461C                 nop
.text:00404620                 lw      $gp, 0xC0+var_B0($sp)
.text:00404624                 move    $s0, $v0
.text:00404628                 li      $v0, "i"
.text:0040462C                 bne     $s0, $v0, loc_4045D8
.text:00404630                 nop
.text:00404634                 nop
.text:00404638                 li      $t9, 0x400000
.text:0040463C                 nop
.text:00404640                 addiu   $t9, (get_char - 0x400000)
.text:00404644                 nop
.text:00404648                 jalr    $t9 ; get_char
.text:0040464C                 nop
.text:00404650                 nop
.text:00404654                 lw      $gp, 0xC0+var_B0($sp)
.text:00404658                 move    $s0, $v0
.text:0040465C                 li      $v0, "a"
.text:00404660                 bne     $s0, $v0, loc_4045D8
.text:00404664                 nop
.text:00404668                 nop
.text:0040466C                 li      $t9, 0x400000
.text:00404670                 nop
.text:00404674                 addiu   $t9, (get_char - 0x400000)
.text:00404678                 nop
.text:0040467C                 jalr    $t9 ; get_char
.text:00404680                 nop
.text:00404684                 nop
.text:00404688                 lw      $gp, 0xC0+var_B0($sp)
.text:0040468C                 move    $s0, $v0
.text:00404690                 li      $v0, "g"
.text:00404694                 bne     $s0, $v0, loc_4045D8
.text:00404698                 nop
.text:0040469C                 nop
.text:004046A0                 li      $a0, 0x400000
.text:004046A4                 nop
.text:004046A8                 addiu   $a0, (aMagicKeyDetect - 0x400000)  # "magic key detected.."
.text:004046AC                 la      $t9, puts
.text:004046B0                 nop
.text:004046B4                 jalr    $t9 ; puts
.text:004046B8                 nop

It remembers me the very first crackmes I tried to solve years ago. Simply put, when the string press magic key to change default setting ... is printed, inittime will check for inputs. When the sequence x d i a g (xdiag) is detected, a new menu is shown.

The first character, x, was the victim of some obscure compiler optimizations I suppose. Instead of checking that the character c == 0x78, it checks that the character c - 0x77 < 0x2, which is equivalent.

ipTIME hidden menu

Testing xdiag when connecting the UART:

# [ . . . ]
=================================================================
press magic key to change default setting ...  (pressed 'x' 'd' 'i' 'a' 'g')
magic key detected..


 'x' - setup system basic config


Diagnostics Setup : 1. ON  2. OFF  3. Don't set  4. ATE Enable [1/2/3/4]: 1  (tried menu 1)
New Length: 1712
Erase Boot Region
Write Boot Region
Checking Boot
[OK]
.
Setup hardware address [y/n]:n  (you can modify the MAC address)
  LAN MAC : 00:00:00:00:00:00
  WAN MAC : 00:00:00:00:00:01
************************************************************************
????????????????  (korean characters I suppose)
************************************************************************

Checking System Requirements *******************************************

DRAM SIZE  : Requirement->32 Mbytes / Mounted->32 Mbytes -----------> [OK]
FLASH SIZE : Requirement->4 Mbytes / Mounted->4 Mbytes -----------> [OK]

???????Flash **********************************************

Test Configuration Flash Region --------> [ OK ]

0xAA ????????? ---------------> .[OK]
0x55 ????????? ---------------> .[OK]
0xFF ????????? ---------------> .[OK]
eth1:LED GPIO6 0
LED eth1 - lednum:6 , action:21 , polarity:1
Led action failed


??????????*******************************************************

PORT1 ?????? ----------------------------> [2. ????: ??????]:[RAWIP:SIZE=60:COUNT=0]
PORT2 ?????? ----------------------------> [2. ????: ??????]:[RAWIP:SIZE=60:COUNT=0]
PORT3 ?????? ----------------------------> [2. ????: ??????]:[RAWIP:SIZE=60:COUNT=0]
PORT4 ?????? ----------------------------> [2. ????: ??????]:[RAWIP:SIZE=60:COUNT=0]
WAN1  ?????? ----------------------------> [1. ????: ??????]:[RAWIP:SIZE=60:COUNT=0]
NG (port->-1),(flash->0)
************************************************************************
????????????????????!!!!!!!
??????????????????????????????
???????????????????????????
?????????????????????
************************************************************************

Looking around, I didn't find anything interesting in that hidden menu. But hey, now we know! The magic key is to sequentially press x d i a g :)


contactdepier.re License WTFPL2