Hacking the Actiontec GT701-WG

omin0us and Sub

The Actiontec GT701-WG is the Wireless DSL Modem/Router that Qwest is now giving out to its subscribers to either rent for $3 a month or to buy. My friend Sub got one after get DSL setup at his house and found that it runs Linux on it. This paper will serve as documentation for the project as we attempt to hack this device, document as much as we can about it, and achieve our end goal of creating a custom firmware to flash to it, and possibly try and run BitchX IRC client off of it, just for the shear reason of "doing it because we can".

Update: Last night for the first time we accomplished our goal of hacking the firmware and flashing our own custom version of the firmware to the unit. read the bottom for more details.


MIPS 4KEc V4.8 32-bit
160 mhz

# cat /proc/cpuinfo 
processor               : 0 
cpu model               : MIPS 4KEc V4.8 
BogoMIPS                : 149.91 
wait instruction        : no 
microsecond timers      : yes 
extra interrupt vector  : yes 
hardware watchpoint     : yes 
VCED exceptions         : not available 
VCEI exceptions         : not available 

16 MB Samsung Ram.

# cat /proc/meminfo 
        total:    used:    free:  shared: buffers:  cached: 
Mem:  14983168 14032896   950272        0  1564672  5165056 
Swap:        0        0        0 
MemTotal:        14632 kB 
MemFree:           928 kB 
MemShared:           0 kB 
Buffers:          1528 kB 
Cached:           5044 kB 
SwapCached:          0 kB 
Active:           3100 kB 
Inactive:         5288 kB 
HighTotal:           0 kB 
HighFree:            0 kB 
LowTotal:        14632 kB 
LowFree:           928 kB 
SwapTotal:           0 kB 
SwapFree:            0 kB 

- Power AC Adapter
- 1 Ethernet (RJ-45)
- 1 Phone (RJ-11)
- 1 Line (RJ-11)
- 1 Mini USB

How to Log in:

Telnet to the Router's Gateway address. Default is
User: admin
Pass: admin

Operating System:

# cat /proc/version 
Linux version 2.4.17_mvl21-malta-mips_fp_le ([email protected])  
(gcc version 2.95.3 20010315 (release/MontaVista)) #1 Thu Jan 8 19:16:45 PST 2004 

So far we know the main root filesystem is mounted read-only from /dev/mtdblock/0 on a squashfs partition. The only place we are able to write to is anywhere in /var, as it is mounted with read/write permisions. /var is mounted in ram on a ramfs partition. We have made a few tests to

Running Processes:

These are the processes running from a fresh boot.

# ps aux 
  PID  Uid     VmSize Stat Command 
    1 admin      1272 S    init 
    2 admin           S    [keventd] 
    3 admin           R    [ksoftirqd_CPU0] 
    4 admin           S    [kswapd] 
    5 admin           S    [bdflush] 
    6 admin           S    [kupdated] 
    7 admin           S    [mtdblockd] 
   33 admin           D    [adsl] 
   38 admin      1664 S    /usr/bin/cm_pc 
   40 admin      1176 S    /usr/sbin/thttpd -d /usr/www -u root -p 80 -c /cgi-b 
   41 admin      2904 S    /usr/bin/cm_logic -m /dev/ticfg -c /etc/config.xml 
   42 admin       672 S    ipq_act 
   45 admin      1272 S    init 
   46 admin      1276 S    /usr/bin/cm_monitor 
   78 admin       632 S    /sbin/dproxy -c /etc/resolv.conf -d 
   95 admin      1276 S    /bin/sh -c /usr/sbin/user_drv 
   96 admin      4572 S    /usr/sbin/user_drv 
   97 admin      4572 S    /usr/sbin/user_drv 
   98 admin      4572 S    /usr/sbin/user_drv 
   99 admin      4572 S    /usr/sbin/user_drv 
  100 admin      4572 S    /usr/sbin/user_drv 
  105 admin      4572 S    /usr/sbin/user_drv 
  124 admin      2344 S    /usr/sbin/pppd plugin pppoa 0.32 user [email protected] 
  154 admin      1284 S    /usr/sbin/upnpd ppp0 br0 
  157 admin      1284 S    /usr/sbin/upnpd ppp0 br0 
  160 admin      1284 S    /usr/sbin/upnpd ppp0 br0 
  163 admin      1284 S    /usr/sbin/upnpd ppp0 br0 
  168 admin      1284 S    /usr/sbin/upnpd ppp0 br0 
  169 admin      1284 S    /usr/sbin/upnpd ppp0 br0 
  196 admin       616 S    /sbin/utelnetd 
  197 admin      1284 S    -sh 


# lsmod 
Module                  Size  Used by 
tiwlan                 66544   2 
ip_nat_talk             3128   0 (unused) 
ip_conntrack_talk       2924   2 
ip_nat_tftp             2344   0 (unused) 
ip_conntrack_tftp       2236   1 
ip_nat_irc              3288   0 (unused) 
ip_conntrack_irc        3900   1 
ip_nat_h323             3408   0 (unused) 
ip_conntrack_h323       3116   1 
ip_nat_ftp              4088   0 (unused) 
ip_conntrack_ftp        5052   1 
ipt_multiport           1020   0 (unused) 
ipt_REDIRECT            1092   1 
ipt_iprange             1196   0 (unused) 
ipt_limit               1404   0 (unused) 
ipt_TCPMSS              3020   0 (unused) 
ipt_sLog                2884   1 
ipt_state                968   3 
ipt_MASQUERADE          1732   1 
iptable_nat            23192   6 [ip_nat_talk ip_nat_tftp ip_nat_irc ip_nat_h323  
                                  \ip_nat_ftp ipt_REDIRECT ipt_MASQUERADE] 
iptable_filter          2124   0 (unused) 
ip_conntrack           29920   8 [ip_nat_talk ip_conntrack_talk ip_nat_tftp  
                                  \ip_conntrack_tftp ip_nat_irc ip_conntrack_irc  
                                  \ip_nat_h323 ip_conntrack_h323 ip_nat_ftp  
                                  \ip_conntrack_ftp ipt_REDIRECT ipt_state  
                                  \ipt_MASQUERADE iptable_nat] 
ip_tables              14688  12 [ipt_multiport ipt_REDIRECT ipt_iprange ipt_limit  
                                  \ipt_TCPMSS ipt_sLog ipt_state ipt_MASQUERADE  
                                  \iptable_nat iptable_filter] 
ip_queue                7760   0 (unused) 
tiatm                 113704   1 
avalanche_usb          48720   1 

Here is the startup script in /etc/init.d/rcS

#! /bin/sh
# rcS           Call all S??* scripts in /etc/rcS.d in
#               numerical/alphabetical order.
# Version:      @(#)/etc/init.d/rcS  2.76  19-Apr-1999  [email protected]
trap "" SIGHUP

umask 022
export PATH tart modules needed for website block, I think following add by Steven
insmod ip_queue

#       Trap CTRL-C &c only in this shell so we can interrupt subprocesses.
trap ":" INT QUIT TSTP

mount -n /proc
#mount -n -o remount,rw /
mount /var

mkdir /var/etc
mkdir /var/etc/ppp
echo "/bin/cp  /etc/ppp/* /var/etc/ppp"
/bin/cp /etc/ppp/* /var/etc/ppp
/bin/cp /etc/* /var/etc

# unreserve for unp systems
echo "0 0" > /proc/sys/vm/pagetable_cache
# router
echo 1 > /proc/sys/net/ipv4/ip_forward
# pppox
echo 1 > /proc/sys/net/ipv4/ip_dynaddr
# ignore_all not yet used: this should be satisfactory
echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts
# drop spoofed addr: turn this off on non-loop-free networks
echo 1 > /proc/sys/net/ipv4/conf/default/rp_filter
echo 1 > /proc/sys/net/ipv4/conf/all/rp_filter
# do not honor source route flags
echo 0 > /proc/sys/net/ipv4/conf/default/accept_source_route
echo 0 > /proc/sys/net/ipv4/conf/all/accept_source_route
# protect against syn flood attacks
echo 1 >/proc/sys/net/ipv4/tcp_syncookies
# this needs proper sampling on av_blog to determine optimal value
# for now just observe softnet_stats to see # time was throttled
# historical value was 300
echo 100 > /proc/sys/net/core/netdev_max_backlog

(cd /; tar xf var.tar)

sleep 1

/sbin/insmod avalanche_usb
sleep 1

/sbin/insmod tiatm
sleep 1

# UPnP requires loopback
ifconfig lo

/usr/sbin/thttpd -d /usr/www -u root -p 80 -c '/cgi-bin/*'

/usr/bin/cm_pc > /dev/tts/0 &

#start modules needed for website block, I think following add by Steven
insmod ip_queue

#turn power led to green after 10s

we have been able to run the recovery app for the router, and pull the firmware images from that, and mount them. And have sniffed the network while running the recovery utility and found that it is connecting to what appears to be a custom ftp daemon with

User: adam2
Pass: adam2

Here is a dump from the sniffed session while running the recovery utility.We have written a small program to emulate the sending of this packet, as we don't really know yet what it is doing this for.

UDP broadcast port 5035: (16 bytes) 0x00 0x00 0x16 0x02 0x01 0x00 0x00 0x00 0xc0 0xa8 0x00 0x01 0x00 0x00 0x00 0x00
UDP response from modem to port 5035: (16 bytes) 0x00 0x00 0x16 0x02 0x02 0x00 0x00 0x00 0x01 0x00 0xa8 0xc0 0x00 0x00 0x00 0x00
220 ADAM2 FTP Server ready.
USER adam2
331 Password required for adam2.
PASS adam2
230 User adam2 successfully logged in.
200 Type set to I.
200 Media set to FLSH.
PORT 192,168,0,102,130,11
200 Port command successful.
STOR nsp.ar7wrd.squashfs.img mtd0
150 Opening BINARY mode data connection for file transfer.
226 Transfer complete.
200 Type set to I.
200 Media set to FLSH.
PORT 192,168,0,102,130,12
200 Port command successful.
STOR ram_zimage_pad.ar7wrd.nsp.squashfs.bin mtd1
150 Opening BINARY mode data connection for file transfer.
226 Transfer complete.
200 Type set to I.
200 Media set to FLSH.
PORT 192,168,0,102,130,13
200 Port command successful.
STOR config.xml mtd3
150 Opening BINARY mode data connection for file transfer.
226 Transfer complete.
221-Thank you for using the FTP service on ADAM2.
221 Goodbye.

The first thing the recovery app does is it appears to first broadcast a 16 byte UDP packet on port 5035, to which the router responds back with a 16 byte UDP packet.

We have successfully been able to code a small app to emulate the recovery app sending that UDP packet and were able the trick the router into responding to us with its own 16 byte UPD packet. Here is the source for our small little test app:

/* test prog for actiontec router hack 
 * by: omin0us
 * help from Sub.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <string.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define PORT 5035 
#define DEST_IP ""

int main()

	int sockfd;
	struct sockaddr_in actiontec_addr;
	int bytes_sent;
	char packet_data[] = 

	if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
		fprintf(stderr, "Error: could not establish socket\n");

	actiontec_addr.sin_family = AF_INET;
	actiontec_addr.sin_port = htons(PORT);
	actiontec_addr.sin_addr.s_addr = inet_addr(DEST_IP);
	memset(&(actiontec_addr.sin_zero), '\0', 8);

	if((bytes_sent = sendto(sockfd, packet_data, strlen(packet_data), 0,
		(struct sockaddr *)&actiontec_addr, sizeof(struct sockaddr))) == -1)
		fprintf(stderr,"error: could not send data\n");

	printf("sent %d bytes\n", bytes_sent);