Take control of your Buffalo Linkstation NAS

Take control of your Buffalo Linkstation NAS

I finally bought a NAS for all of my super-important stuff. It became a Buffalo Linkstation LS200, most because of the price ($300 for 4TB). It supports all of the standard protocols such as FTP, SAMBA, ATP and so on.

However, it would be really useful to use some sane protocols like sftp so you could use rsync for your backup scripts.

Bring a big coffee mug and let the hacking begin....

I knew that the NAS was based on the ARM architecture and supports a whole set of high level protocols, so one qualified guess is that there lives a little penguin in the box.

Lets start with download the latest firmware from the Buffalo webpage. When the firmware is unzipped we have these files:

marcus@tuxie:~/shared/buffalo\$ ls -al
total 780176
drwxr-xr-x  4 marcus marcus      4096 Jun 13 00:06 .
drwxr-xr-x 18 marcus marcus      4096 Jun 12 23:27 ..
-rw-r--r--  1 marcus marcus 190409979 Jun 13 00:06 hddrootfs.img
drwxr-xr-x  2 marcus marcus      4096 Apr  8 13:25 img
-rw-r--r--  1 marcus marcus  12602325 Apr  1 15:25 initrd.img
-rw-r--r--  1 marcus marcus       656 Apr  1 15:25 linkstation_version.ini
-rw-r--r--  1 marcus marcus       198 Apr  1 15:25 linkstation_version.txt
-rw-r--r--  1 marcus marcus 205568610 Jun 12 23:13 LS200_series_FW_1.44.zip
-rw-r--r--  1 marcus marcus    350104 Apr  1 15:25 LSUpdater.exe
-rw-r--r--  1 marcus marcus       327 Apr  1 15:25 LSUpdater.ini
-rw-r--r--  1 marcus marcus    674181 Apr  1 15:25 u-boot.img
-rw-r--r--  1 marcus marcus   2861933 Apr  1 15:25 uImage.img
-rw-r--r--  1 marcus marcus      4880 Apr  8 13:23 update.html

Ok, u-boot.img, uImage.img, initrd.img and hddrootfs.img tells us that I have a black little penguin cage in front of me. First of all, find out what kind of file these *.img files really are.:

marcus@tuxie:~/shared/buffalo$ file ./hddrootfs.img
./hddrootfs.img: Zip archive data, at least v2.0 to extract

Really? It is just an zip-file. Lets extract it then.:

marcus@tuxie:~/shared/buffalo$ unzip hddrootfs.img
Archive:  hddrootfs.img
[hddrootfs.img] hddrootfs.buffalo.updated password:

Of course, it is protected with a password. I will let my old friend John the Ripper take a look at it (I guess I had a great luck, the brute force attack only took 2.5 hours). The password for the file is: aAhvlM1Yp7_2VSm6BhgkmTOrCN1JyE0C5Q6cB3oBB

marcus@tuxie:~/shared/buffalo$ unzip hddrootfs.img
Archive:  hddrootfs.img
[hddrootfs.img] hddrootfs.buffalo.updated password:
  inflating: hddrootfs.buffalo.updated

Terrific. We got a hddrootfs.buffalo.updated file. What is it anyway?:

marcus@tuxie:~/shared/buffalo$ file hddrootfs.buffalo.updated
hddrootfs.buffalo.updated: gzip compressed data, was "rootfs.tar", from Unix, last modified: Tue Apr  1 08:24:05 2014, max compression

It is just a gzip compressed tar archive, couldn't be better! Extract it.:

marcus@tuxie:~/shared/buffalo$ mkdir rootfs
marcus@tuxie:~/shared/buffalo$ tar -xz --numeric-owner -f hddrootfs.buffalo.updated  -C ./rootfs/
marcus@tuxie:~/shared/buffalo$ ls -1l rootfs/
total 80
drwxr-xr-x  2 root root 4096 Apr  1 08:23 bin
-rwxr-xr-x  1 root root 1140 Feb  3 07:51 chroot.sh
drwxr-xr-x  2 root root 4096 Apr  1 08:23 debugtool
drwxr-xr-x  5 root root 4096 Apr  1 08:23 dev
drwxr-xr-x 33 root root 4096 Jun 12 23:17 etc
drwxr-xr-x  4 root root 4096 Apr  1 08:23 home
drwxr-xr-x  9 root root 4096 Apr  1 08:23 lib
drwxr-xr-x  3 root root 4096 Feb  3 07:51 mnt
drwxr-xr-x  2 root root 4096 Apr  1 07:35 opt
-rwxr-xr-x  1 root root 2741 Feb  3 07:51 prepare.sh
drwxr-xr-x  2 root root 4096 Apr  1 07:35 proc
drwxr-xr-x  3 root root 4096 Apr  1 08:23 root
drwxr-xr-x  3 root root 4096 Apr  1 08:22 run
drwxr-xr-x  2 root root 4096 Apr  1 08:23 sbin
drwxr-xr-x  2 root root 4096 Apr  1 07:35 sys
-rwxr-xr-x  1 root root 3751 Feb  3 07:51 test.sh
drwxrwxrwt  3 root root 4096 Apr  1 08:23 tmp
drwxr-xr-x 11 root root 4096 Apr  1 08:23 usr
drwxr-xr-x  9 root root 4096 Apr  1 08:22 var
drwxrwxrwx  6 root root 4096 Apr  1 07:53 www

Here we go!

Modify the root filesystem

First of all, I really would like to have SSH access to the box, and I found that there is a SSH daemon in here (/usr/bin/sshd), but why is it not activated? Take a look in one of the scripts that seems to be related to ssh:

marcus@tuxie:~/shared/buffalo/rootfs$ head -n 20 etc/init.d/sshd.sh
#!/bin/sh
[ -f /etc/nas_feature ] && . /etc/nas_feature
SSHD_DSA=/etc/ssh_host_dsa_key
SSHD_RSA=/etc/ssh_host_rsa_key
SSHD_KEY=/etc/ssh_host_key
SSHD=`which sshd`
if [ "${SSHD}" = "" -o ! -x ${SSHD} ] ; then
 echo "sshd is not supported on this platform!!!"
fi
if [ "${SUPPORT_SFTP}" = "0" ] ; then
        echo "Not support sftp on this model." > /dev/console
        exit 0
fi
umask 000

What about the second if-statement? SUPPORT_SFTP? And what is the /etc/nas_feature file? It does not exist in the package. Is it auto generated at boot? Anyway, I remove the second statement, it seems evil. So, if this starts up the ssh daemon, we would like to login as root, uncomment PermitRootLogin in sshd_config:

marcus@tuxie:~/shared/buffalo/rootfs$ sed -i 's/#PermitRootLogin/ PermitRootLogin/' etc/sshd_config

Then copy your public rsa-key to /root/.ssh/authorized_keys. If you don't have a key, generate it with:

marcus@tuxie:~/shared/buffalo/rootfs$ ssh-keygen

Copy the key to target:

marcus@tuxie:~/shared/buffalo/rootfs$ mkdir ./root/.ssh
marcus@tuxie:~/shared/buffalo/rootfs$ cat ~/.ssh/id_rsa.pub > ./root/.ssh/authorized_keys

This will let you to login without give any password.

Lets try to re-pack the whole thing.:

marcus@tuxie:~/shared/buffalo/rootfs$ mv ../hddrootfs.buffalo.updated{,.old}
marcus@tuxie:~/shared/buffalo/rootfs$ tar -czf ../hddrootfs.buffalo.updated *
marcus@tuxie:~/shared/buffalo/rootfs$ cd ..
marcus@tuxie:~/shared/buffalo$ zip -e hddrootfs.img hddrootfs.buffalo.updated

I encrypt the file with the same password as before, I dare not think about what happens if I don't.

Time to update firmware

I execute the LSUpdater.exe from a virtual Windows machine and hold my thumbs... The update process takes about 8 minutes and is a real pain, would it brick my NAS..?

After a while the power LED is indicating that the NAS is up and running. Wow. Quick! Do a portscan!:

marcus@tuxie:~/shared/buffalo$ sudo nmap -sS 10.0.0.4
[sudo] password for marcus:
Starting Nmap 5.21 ( http://nmap.org ) at 2014-06-13 11:59 CEST
Nmap scan report for nas (10.0.0.4)
Host is up (0.00049s latency).
Not shown: 990 closed ports
PORT      STATE SERVICE
21/tcp    open  ftp
22/tcp    open  ssh
80/tcp    open  http
139/tcp   open  netbios-ssn
443/tcp   open  https
445/tcp   open  microsoft-ds
548/tcp   open  afp
873/tcp   open  rsync
8873/tcp  open  unknown
22939/tcp open  unknown
MAC Address: DC:FB:02:EB:06:A8 (Unknown)
Nmap done: 1 IP address (1 host up) scanned in 0.27 seconds

And there it is! The SSH daemon is running on port 22.:

marcus@tuxie:~$ ssh admin@10.0.0.4
admin@10.0.0.4's password:
[admin@LS220D6A8 ~]$ ls /
bin/        debugtool/  home/       mnt/        proc/       sbin/       tmp/        www/
boot/       dev/        lib/        opt/        root/       sys/        usr/
chroot.sh*  etc/        lost+found/ prepare.sh* run/        test.sh*    var/
[admin@LS220D6A8 ~]$

It is just beautiful!

Wait, what about the /etc/nas_features file?

[admin@LS220D6A8 ~]$ cat /etc/nas_feature
DEFAULT_LANG=english
DEFAULT_CODEPAGE=CP437
REGION_CODE=EU
PRODUCT_CAPACITY="040"
PID=0x0000300D
SERIES_NAME="LinkStation"
PRODUCT_SERIES="LS200"
PRODUCT_NAME="LS220D(SANJO)"
SUPPORT_NTFS_WRITE=on
NTFS_DRIVER="tuxera"
SUPPORT_DIRECT_COPY=on
SUPPORT_RAID=on
SUPPORT_RAID_DEGRADE=off
SUPPORT_FAN=on
SUPPORT_AUTOIP=on
SUPPORT_NEW_DISK_AUTO_REBUILD=off
SUPPORT_2STEP_INSPECTION=off
SUPPORT_RESYNC_DELAY=off
SUPPORT_PRINTER_SERVER=on
SUPPORT_ITUNES_SERVER=on
SUPPORT_DLNA_SERVER=on
SUPPORT_NAS_FIREWALL=off
SUPPORT_IPV6=off
SUPPORT_DHCPS=off
SUPPORT_UPNP=off
SUPPORT_SLIDE_POWER_SWITCH=on
SUPPORT_BITTORRENT=on
BITTORRENT_CLIENT="transmission"
SUPPORT_USER_QUOTA=on
SUPPORT_GROUP_QUOTA=on
SUPPORT_ACL=on
SUPPORT_TIME_MACHINE=on
SUPPORT_SLEEP_TIMER=on
SUPPORT_AD_NT_DOMAIN=on
SUPPORT_RAID0=1
SUPPORT_RAID1=1
SUPPORT_RAID5=0
SUPPORT_RAID6=0
SUPPORT_RAID10=0
SUPPORT_RAID50=0
SUPPORT_RAID60=0
SUPPORT_RAID51=0
SUPPORT_RAID61=0
SUPPORT_NORAID=0
SUPPORT_RAID_REBUILD=1
SUPPORT_AUTH_EXTERNAL=1
SUPPORT_SAMBA_DFS=0
SUPPORT_LINKDEREC_ANALOG=0
SUPPORT_LINKDEREC_DIGITAL=0
SUPPORT_WEBAXS=1
SUPPORT_UPS_SERIAL=0
SUPPORT_UPS_USB=0
SUPPORT_UPS_RECOVER=0
SUPPORT_NUT=0
SUPPORT_SYSLOG_FORWARD=0
SUPPORT_SYSLOG_DOWNLOAD=0
SUPPORT_SHUTDOWN_FROMWEB=0
SUPPORT_REBOOT_FROMWEB=1
SUPPORT_IMHERE=0
SUPPORT_POWER_INTERLOCK=1
SUPPORT_SMTP_AUTH=1
NEED_MICONMON=off
ROOTFS_FS=ext3
USERLAND_FS=xfs
NASFEAT_VM_WRITEBACK=default
NASFEAT_VM_EXPIRE=default
MAX_DISK_NUM=2
MAX_USBDISK_NUM=1
MAX_ARRAY_NUM=1
DEV_BOOT=md0
DEV_ROOTFS1=md1
DEV_SWAP1=md2
SDK_VERSION=2
DEVICE_NETWORK_PRIMARY=eth1
DEVICE_NETWORK_SECONDARY=
DEVICE_NETWORK_NUM=1
DEVICE_HDD1_LINK=disk1_6
DEVICE_HDD2_LINK=disk2_6
DEVICE_HDD3_LINK=disk3_6
DEVICE_HDD4_LINK=disk4_6
DEVICE_HDD5_LINK=disk5_6
DEVICE_HDD6_LINK=disk6_6
DEVICE_HDD7_LINK=disk7_6
DEVICE_HDD8_LINK=disk8_6
DEVICE_HDD_BASE_EDP=md100
DEVICE_HDD1_EDP=md101
DEVICE_HDD2_EDP=md102
DEVICE_HDD3_EDP=md103
DEVICE_HDD4_EDP=md104
DEVICE_HDD5_EDP=md105
DEVICE_HDD6_EDP=md106
DEVICE_HDD7_EDP=md107
DEVICE_HDD8_EDP=md108
DEVICE_MD1_REAL=md10
DEVICE_MD2_REAL=md20
DEVICE_MD3_REAL=md30
DEVICE_MD4_REAL=md40
DEVICE_USB1_LINK=usbdisk1_1
DEVICE_USB2_LINK=usbdisk2_1
DEVICE_USB3_LINK=usbdisk1_5
DEVICE_USB4_LINK=usbdisk2_5
MOUNT_GLOBAL=/mnt
MOUNT_LVM_BASE=/mnt/lvm
MOUNT_HDD1=/mnt/disk1
MOUNT_HDD2=/mnt/disk2
MOUNT_HDD3=/mnt/disk3
MOUNT_HDD4=/mnt/disk4
MOUNT_HDD5=/mnt/disk5
MOUNT_HDD6=/mnt/disk6
MOUNT_HDD7=/mnt/disk7
MOUNT_HDD8=/mnt/disk8
MOUNT_ARRAY1=/mnt/array1
MOUNT_ARRAY2=/mnt/array2
MOUNT_ARRAY3=/mnt/array3
MOUNT_ARRAY4=/mnt/array4
MOUNT_USB1=/mnt/usbdisk1
MOUNT_USB2=/mnt/usbdisk2
MOUNT_USB3=/mnt/usbdisk3
MOUNT_USB4=/mnt/usbdisk4
MOUNT_USB5=/mnt/usbdisk5
MOUNT_MC_BASE=/mnt/mediacartridge
SUPPORT_MC_VER=1
SUPPORT_INTERNAL_DISK_APPEND=0
STORAGE_TYPE=HDD
BODY_COLOR=NORMAL
SUPPORT_MICON=0
SUPPORT_LCD=0
SUPPORT_USER_QUOTA_SOFT=0
SUPPORT_GROUP_QUOTA_SOFT=0
SUPPORT_NFS=0
SUPPORT_LVM=0
SUPPORT_OFFLINEFILE=0
SUPPORT_HIDDEN_SHARE=0
SUPPORT_HOT_SWAP=0
SUPPORT_LCD_LED=0
SUPPORT_ALERT=0
SUPPORT_PORT_TRUNKING=0
SUPPORT_REPLICATION=0
SUPPORT_USER_GROUP_CSV=0
SUPPORT_SFTP=0
SUPPORT_SERVICE_MAPPING=0
SUPPORT_SSLKEY_IMPORT=1
SUPPORT_SLEEPTIMER_DATE=0
SUPPORT_TERA_SEARCH=0
SUPPORT_SECURE_BOOT=0
SUPPORT_PACKAGE_UPDATE=0
SUPPORT_HDD_SPINDOWN=0
SUPPORT_DISK_ENCRYPT=0
SUPPORT_FTPS=1
SUPPORT_CLEANUP_ALL_TRASHBOX=0
SUPPORT_WAKEUP_BY_REBOOT=1
SUPPORT_DTCP_IP=0
SUPPORT_MYSQL=0
SUPPORT_APACHE=0
SUPPORT_PHP=0
SUPPORT_UPS_STANDBY=0
SUPPORT_HIDDEN_RAID_MENU=0
SUPPORT_ISCSI=0
SUPPORT_ISCSI_TYPE=
DEFAULT_WORKINGMODE=
MAX_LVM_VOLUME_NUM=0
MAX_ISCSI_VOLUME_NUM=0
INTERNAL_SCSI_TYPE=multi-host
SUPPORT_ELIMINATE_ADLIMIT=
USB_TREE_TYPE=
SUPPORT_WOL=0
WOL_TYPE=
SUPPORT_HARDLINK_BACKUP=0
SUPPORT_SNMP=0
SUPPORT_EDP=1
SUPPORT_POCKETU=0
SUPPORT_MC=0
SUPPORT_FOFB=0
SUPPORT_EDP_PLUS=0
SUPPORT_INIT_SW=1
SUPPORT_SQUEEZEBOX=0
SUPPORT_OL_UPDATE=1
SUPPORT_AMAZONS3=0
SUPPORT_SURVEILLANCE=0
SUPPORT_WAFS=0
SUPPORT_SUGARSYNC=0
SUPPORT_INTERNAL_DISK_REMOVE=1
SUPPORT_SETTING_RECOVERY_USB=0
SUPPORT_PASSWORD_RECOVERY_USB=0
SUPPORT_AV=
SUPPORT_TUNEUP_RAID=on
SUPPORT_FLICKRFS=0
SUPPORT_WOL_INT=1
TUNE=0
SUPPORT_EDP_PLUS=0
SUPPORT_SXUPTP=0
SUPPORT_SQUEEZEBOX=0
SUPPORT_EYEFI=0
SUPPORT_OL_UPDATE=1
SUPPORT_INIT_SW=1
SUPPORT_USB=1
SUPPORT_WAFS=0
SUPPORT_INFO_LED=1
SUPPORT_ALARM_LED=1
POWER_SWITCH_TYPE=none
SUPPORT_FUNC_SW=1
DLNA_SERVER="twonky"
SUPPORT_TRANSCODER=0
SUPPORT_LAYOUT_SWITCH=1
SUPPORT_UTILITY_DOWNLOAD=1
SUPPORT_BT_CLOUD=0
DEFAULT_VALUE_DLNA=1
DEFAULT_VALUE_BT_CLOUD=0
SUPPORT_EXCLUSION_LED_POWER_INFO_ERROR=1
DEFAULT_DLNA_SERVICE=off
SUPPORT_MOBILE_WEBUI=1
SUPPORT_SHUTDOWN_DEPEND_ON_SW=1
SUPPORT_SPARE_DISK=0

It seems that the files is generated. It also has the SUPPORT_SFTP config that we saw in sshd.sh.

What about the kernel

In the current vanilla kernel, there is devicetrees that seems to be for the Buffalo linkstation.:

marcus@tuxie:~/marcus/linux/linux$ grep -i buffalo arch/arm/boot/dts/*.dts
arch/arm/boot/dts/kirkwood-lschlv2.dts: model = "Buffalo Linkstation LS-CHLv2";
arch/arm/boot/dts/kirkwood-lschlv2.dts: compatible = "buffalo,lschlv2", "buffalo,lsxl", "marvell,kirkwood-88f6281", "marvell,kirkwood";
arch/arm/boot/dts/kirkwood-lsxhl.dts:   model = "Buffalo Linkstation LS-XHL";
arch/arm/boot/dts/kirkwood-lsxhl.dts:   compatible = "buffalo,lsxhl", "buffalo,lsxl", "marvell,kirkwood-88f6281", "marvell,kirkwood";

The device tree seems to match the current CPU:

[admin@LS220D6A8 ~]$ cat /proc/cpuinfo
Processor : Marvell PJ4Bv7 Processor rev 1 (v7l)
BogoMIPS : 795.44
Features : swp half thumb fastmult vfp edsp vfpv3 vfpv3d16 tls
CPU implementer : 0x56
CPU architecture: 7
CPU variant : 0x1
CPU part : 0x581
CPU revision : 1
Hardware : Marvell Armada-370
Revision : 0000
Serial  : 0000000000000000

Also, the kernel is not tainted indicating that there is no out-of-tree modules.:

[admin@LS220D6A8 ~]$ cat /proc/sys/kernel/tainted
0

It could therefor be possible to compile a custom kernel with support for more USB-devices that may be plugged into the NAS.

Other tips

The LSUpdater.exe will refuse to update if the same version of software is already on the target. This means that you cannot upload the same firmware again... unless you change the version!

Together with the uploader application, there is a linkstation_version.ini file that contains information about each of the *.img. The first thing I tried was just to increase the VERSION by one. This makes the LSUpdater.exe move a little forward, It stops complain about the same version, instead it complains about that this firmware is already on the target. However, I needed to change the timestamp of each binary (increased the day by one), then it updates the firmware without any problem.

marcus@tuxie:~/shared/buffalo$ cat linkstation_version.ini
[COMMON]
VERSION=1.44-0.36
BOOT=0.20
KERNEL=2014/04/01 14:33:46
INITRD=2014/04/01 14:35:10
ROOTFS=2014/04/01 15:23:41
FILE_BOOT = u-boot.img
FILE_KERNEL = uImage.img
FILE_INITRD = initrd.img
FILE_ROOTFS = hddrootfs.img

[TARGET_INFO1]
PID=0x0000001D
FILE_KERNEL=uImage.img
KERNEL=2014/04/01 14:33:46
FILE_BOOT_APPLY=u-boot.img
BOOT=0.20
BOOT_UP_CMD=""
[TARGET_INFO2]
PID=0x0000300D
FILE_KERNEL=uImage.img
KERNEL=2014/04/01 14:33:46
FILE_BOOT_APPLY=u-boot.img
BOOT=0.20
BOOT_UP_CMD=""
[TARGET_INFO3]
PID=0x0000300E
FILE_KERNEL=uImage.img
KERNEL=2014/04/01 14:33:46
FILE_BOOT_APPLY=u-boot.img
BOOT=0.20
BOOT_UP_CMD=""