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.
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:
From left to right, we have:
- GND
- MOSI
- MISO
- 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:
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
:)