На главную | Содержание | Назад | Вперёд
Наши друзья

 

 

Установка заплат в ядро Windows NT для блокировки всей системы защиты

В большинстве случаев лучшие заплаты очень просты в реализации. Размер заплаты может составлять всего несколько байтов. В частотности, это справедливо для ядра Windows NT. С помощью буквально нескольких байтов можно установить в ядро заплату, которая устранит всю систему защиты. Эта хитрость была описана несколько нет назад одним из авторов этой книги (Хогланд). С тех пор появилось несколько сообщений об усовершенствовании этой заплаты ядра и уменьшении ее размера до одного байта. При установке одной из заплат разница между оригинальным байтом и байтом после установки заплаты составляет всего 2 бита! То есть можно создать удивительную двухбитовую программу атаки на операционную систему Windows NT. Идея случайного или умышленного изменения значения только одного бита, при котором последствия будут катастрофическими для всей системы безопасности, говорит сама за себя. Возможно, система безопасности Windows NT основана только на значении двух битов!
Каждый из нас, наверно, побоялся бы лететь на самолете, в котором программное обеспечение для управления полетом может быть так легко и катастрофически выведено из строя из-за вспьшжи на Солнце. В военно-морском флоте США по сей день управление кораблями обеспечивается с помощью систем на основе Windows NT. Может ли случайное изменение одного бита (вызванное, например, всплеском напряжения) в памяти компьютера привести к потере управления над всей системой безопасности? Это вполне реально, если это случайное изменение значения бита происходит в главном контроллере домена. Многие критически важные программные системы устойчивы к случайным ошибкам наподобие изменения значений одиночных битов, но это не относится к Windows NT. Очевидно, что устойчивость к ошибкам не была целью команды разработчиков ядра Windows NT.
Ниже показан восстановленный исходный код одной из важнейших функций ядра Windows NT SeAccessCheck (). Эта функция определяет, могут ли быть предоставлены запрошенные права для доступа к объекту, который защищен с помощью дескриптора безопасности и прав владельца объекта. Эта функция ядра управляет доступом ко всем объектам. Это означает, что при попытке любого пользователя получить доступ к объекту в среде Windows NT, сначала произойдет обращение к этой функции. Это справедливо для всех объектов, включая файлы, параметры реестра, дескрипторы, семафоры и конвейеры. Возвращаемый функцией результат зависит от параметров управления доступом, установленных для запрошенного объекта. При этом выполняется сравнение между правами доступа пользователя и списком контроля доступа для запрашиваемого объекта. Ниже представлен результат восстановления исходного кода этой функции, полученный с помощью программы IDA-Pro.
8019А0Е6 /Exported entry 816.SeAccessCheck
8019A0E6
8019A0E6 ;
8019A0E6
8019A0E6 ; ПОДПРОГРАММА 8019A0E6 ,;IBа^м^етрu:bp-based frame
8019A0E6
8019A0E6 public SeAccessCheck
8019A0E6 SeAccessCheck proc near
8019A0E6 ; sub_80133D06+B0p
8019A0E6
8019A0E6 arg_0 - dWord pttr 8 /похоже, что указывает на
/дескриптор безопасюсти
8019А0Е6 arg_4 - dword ptr OCh
8019A0E6 arg_8 - byte ptr lOh
8019A0E6 arg_C - dword ptr 14h
8019А0Е6 arg_10
8019A0E6 arg_14
8019A0E6 arg_18
8019A0E6 arg_lC
8019A0E6 arg_20
8019A0E6 arg_24
dword ptr 18h
dword ptr ICh
dword ptr 20h
dword ptr 24h
dword ptr 28h
dword ptr 2Ch
Обратите внимание, что программа IDA предоставила нам аргументы вызова
функции. Это очень удобно, поскольку теперь мы видим, как аргументы указываются в приведенном ниже коде. На этапе первого восстановления кода функции SeAccessCheck (), она не была непосредственно документирована компанией Microsoft, но была объявлена в заголовочных файлах, предоставленных в DDK, откуда она и вызывалась. Вызов этой функции выглядел следующим образом.
SeAccessCheck(
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
IN BOOLEAN SubjectContextLocked,
IN ACCESS_MASK DesiredAccess,
IN ACCESS_MASK PreviouslyGrantedAccess,
OUT PPRIVILEGE_SET ^Privileges OPTIONAL,
IN PGENERIC_MAPPING GenericMapping,
IN KPROCESSOR_MODE AccessMode,
OUT PACCESS_MASK GrantedAccess,
OUT PNTSTATUS AccessStatus
) ;
При предоставлении доступа функция возвращает значение TRUE. To есть вся хитрость заключается в том, чтобы создать такую заплату, при которой эта функция всегда будет возвращать значение TRUE. За исключением нескольких внешних действий, большая часть операций при вызове функции SeAccessCheck сконцентрирована в приведенном ниже фрагменте кода. Вызов осуществляется в конце функции SeAccessCheck, о чем можно судить по наличию команды retn. Очевидно, что этот вызов важен, поскольку предоставляется большинство ключевых параметров. Как видите, вызову предшествуют 10 команд push. Это просто огромное количество параметров!
Поскольку большинство параметров передаются функции SeAccessCheck, то похоже, что процедура представляет собой оболочку для чего-то на более глубоком уровне. Что же, проведем более тщательное исследование.
BOOLEAN

Здесь декомпилирован код для вызова sub_80199836. До этого момента мы не вносили никаких изменений в исходный код, поскольку сначала мы хотели лучше разобраться в ситуации. Следующая процедура вызывается непосредственно из функции SeAccessCheck и выполняет реальную работу. Здесь мы и начнем вносить исправления в ядро.
Программа IDA-Рго позволяет вносить комментарии в восстановленный исходный код. Читатели могут видеть наши комментарии, которые мы делали при пошаговом исследовании исходного кода. Чтобы понять происходящее, мы создали файл на своем компьютере и установили для него права доступа, согласно которым доступ был невозможен. Затем мы начали предпринимать попытки получить доступ к этому файлу, одновременно установив с помощью программы Softlce точки останова. При достижении точки останова мы с помощью Softlce начинали пошаговое исследование исходного кода. Ниже представлен результат без преувеличения сотен прогонов через исходный код в реальном времени.
Следующий код представляет собой код программы, вызываемой из функции SeAccessCheck. Похоже, что именно в этой подпрограмме выполняется большая часть работы. Попробуем установить заплату в эту процедуру.
80199836 ;
80199836 80199836
80199836 80199836
80199836 80199836 80199836 80199836 80199836 80199836 80199836 80199836 80199836 80199836 80199836 80199836 80199836 80199836 80199836 80199836 80199836 80199836 80199836 80199836 80199836
80199837
80199839
ПОДПРОГРАММА
Параметры :bp-based frame
sub_8 0199836
var_14
var_10
var_C
var_8
var_2
arg_0
arg_4
arg_8
arg_C
arg_10
arg_16
arg_17
arg_18
arg_lC
arg_2 0
arg_2 4
proc near
CODE XREF:PAGE:8019 9FFA SeAccessCheck+13F
dword ptr -14h dword ptr -lOh
dword ptr -OCh dword ptr -8 byte ptr -2
dword
dword
dword
dword
dword
byte
byte
dword
dword
8
OCh lOh 14h 18h ptr lEh ptr lFh ptr 20h ptr 24h
ptr ptr ptr ptr ptr
dword ptr dword ptr
28h 2Ch
push
mov
sub
ebp
ebp,esp esp,14h

8019983С 8019983D 80199ЭЗЕ 8019983F 80199841 80199844
80199847 8019984А 8019984D 80199850
80199852 80199854
80199863
80199865 80199868 801998 6В 80199871 80199877
8019987С 8019987Е 80199880 80199883 80199885 8019988В 80199890
push
push
push
xor
mov
mov
mov mov mov cmp
jnz mov
80199857
80199857 loc_801998S7: 80199857
8019985A 8019985D
mov test
push push push push call
test
jnz
mov
xor
mov
jmp
ebx esi
edi
ebx,ebx
eax,[ebp+arg_8] [ebp+var_14],ebx
[ebp+var_C],ebx [ebp-1],bl [ebp+var_2],bl eax,ebx ;
short loc_801998 eax, febp+arg_4)
; pulls eax
;ebx равен нулю ,- похоже что он ; инициализирует ; набор локадъных ; переменных
провернем что arg8 равен нулю 57
,-arg4 указывает /на "0SER32 "
edi,[ebp+arg С]
[ebp+var_8J,еах edi,lOOOOOOh
/проверяем несколько ; флагов и снимаем ; этот флаг ;var_8 =arg_4 ; очевидно фшги. . /желаемой маски ;доступа short 1ос_80199ЭСА /обычно здесь переход.. /двигаемся вперед /и делаем переход
[ebp+arg_18] [ebp+var_8] dword_8014EE94 dword_8014EE90 sub 8019ADE0
al, al
short loc_80199 ecx,[ebp+arg_2 4 al, al
dword ptr [ecxj,0C0000061H loc 80199COC
еще одна недокументи-ованная подпрограмма код возврата 890 ]
здесь удаленный исходный код 80199ЭСА /
801998СА
801998СА 1ос_Э0199ЭСА:
801998СА 801998СА
801998CD
801998D1 801998D4 801998D8
801998DA
801998DD 801998DF mov
mov and
mov
test /здесь место выполненного / выше перехода ;sub_80199836 еах, ;ebp-targ 0] ;arg0 указывает на /Дескриптор безопасности dx,[еах+2 ] /смещение 2 которое есть /числом 80 04,..
сх, dx
сх,4 /80 04 становится 00 04
short 1ос_801998ЕА /обычно перехода
;не осуществтается esi, [eax+10h] ;SD [10h] - значение
; смещения до DACL в
/ 3D
esi, esi /убедимся в существовании short loc 801998ЕА

801998Е1 80199SE4 8CU998E6
801998Е8
801998ЕА ;
test dh,80h
jz short loc_801998EC
add esi,eax /переход к первому DACL
;в SD ****** jmp short 1ос_Э01998ЕС /обычно здесь
все хорошо /двигаемся вперед /и выполняем пэреход
801 801 801 801 801 801 301 801 801 801 801 801
801
* * *
801 998ЕА 998ЕА 998ЕА 998ЕА 998ЕС 998ЕС 99SEC 998ЕС 998F0 998F6 998F8 998FE
99904 здесь loc 801998ЕА:
loc 801998ЕС:
г CODE XREF:sub_80199836+A2 /sub 80199836+A9
esi,esi
cmp jnz
test
test
JZ
;CODE XREF:sub_80199836+AE ;Sub_80199836+B2 cx,4 ; здесь цель перехода

loc_80199BC6 esi,esi loc_80199BC6
edi,ЭООООп /обычно здесь нет
/совпадения поэтому, /двигаемся вперед /и выполняем пэреход short loc 8019995Е
удаленный исходный код
8019995Е 8019995Е 8019995Е 8019995Е 80199962
80199965 80199967 8019996А
loc 8019Э95Е:
movzx mov
xor cmp jnz
***здесь удаленный исходный код 801999А2 /
/CODE XREF:sub_80199336+CE /sub_80199836+D4 ... eax,word ptr fesi + 4) /цель герехода (ebp+var_10],eax /смещение 4 это /количество элементов АСЕ в DACL /var_10 =#Ace's
еах,еах
[ebp+var_10J,еах
short 1ос_801999В7 /обычный шреход
*** здесь удаленный исходный код 801999В7 /
801999В7
801999В7 1ос_801999В7: 801999В7 test
Э01999ВВ jz *** здесь удаленный исходный код 80199AD3 /
/CODE XREF;sub_80199836+134 byte ptr [ebp+arg_C+3 ],2 /похоже ; на часть данных ; относительно фтгов, /мы обычно выполняем переход loc 80199AD3
80199AD3
80199AD3 lOc_80199AD3: 80199AD3 mov 8 0199ADA add 80199ADD cmp
80199AE1 jz 80199AE7
80199AE7 loc_80199AE7: 80199AE7 test
;COD XREF:sub_80199836+185 [ebp+var_C] , 0 /здесь цель перзхода esi, 8
lebp+var_10],0 /количество АСЕ
/ равняется нулю? 1ос_80199В79 /обычно это не тах
/CODE XREF:sub_80199836+33D edi,edi ,- регисто EDI очень

80199АЕ9
80199AEF
80199AF3 80199AF5
80199AF7
80199AF9
80199AFB 80199AFE 80199AFF 80199В02
80199В07 80199В09
80199АЕ9
80199В0В
80199AEF 80199В10
80199AF3 80199AF5
80199AF7
80 1 99AF9 ИиВрПнВм jz
test
jnz mov
test jnz
lea push push call
test jz
j z
mov
test and
jnz mov
test
;важен. Мы будем продолжать /чтобы вернуться к этой точке. ;после проверки каждого АСЕ ; при совпадении SID ;С помощью маски доступа ,- каждого элемента АСЕ ; изменяется регистр EDI. /Доступ предоставляется, если /в регистре ED1 нет никакого ; значения (регистр пуст) 1ос_80199В79 /переход к процедуре.
/ выхода (exit) /если регистр EDI пуст byte ptr [esi+l],8 /проверка
; значения АСЕ /равного 8,второй байт.. /я не знаю, что это /но если оно не 8 /это не вычисляется / и не имеет значения short 1ос_80199В64 al, [esi] /это тип АСЕ,
/который может быть 0,1 или 4 al,al /значение 0 это ALLOWEDTYPE
/значение 1 ЭТО DENIEDTYPE short 1ос_80199В14 /переходим к
/следующему блоку если это / не тип 0 еах, [esi + 8] /смещение 8 является SID еах ;команда push для АСЕ [ebp+var_8]
sub_801997C2 /проверка того, что
/если вызывающая функция / находит совпадение с SID / возвращаемое значение 1 /говорит о совпадении, / значение 0 говорит ; об отсутствии совпадения
alral
short 1ос_80199В64 /здесь совпадение ; вполне нас устраивает, /поскольку это список ALLOWED /таким образом а 2-байтовая / заплата стирает этот /переход /<ИСПРАВЬ МЕНЯ> ; важен. Мы будем продолжать /чтобы вернуться к этой точке. ;после проверки каждого АСЕ
при совпадении SID / с помощью маски доступа каждого элемента АСЕ изменяется регистр EDI. /Доступ предоставляется, если з регистре EDI нет никакого значения (регистр пуст) loc_80199B79 /переход к процедуре.
; выхода (exit) ;если регистр EDI пуст byte ptr [esi+l],8 /проверка
; значения АСЕ /равного 8,второй байт., /я не знаю, что это /но если оно не 8 /это не вычисляется /и не имеет значения short loc_80199B64 al,[esi] ~ /это тип АСЕ,
/который может быть 0,1 или 4 al,al /значение 0 это ALLOWEDJTYPE /значение 1 это DENIED_TYPE

80199В14
80199В14 1ос_80199В14:
80199В14
80199В16 cmp jnz
short loc 80199B64
;CODE XREF:sub_80199836+2C3 al, 4 ; проверка типа 4 для АСЕ short 1ос_80199В4В ;обычно другой
; тип, поэтому выполняем
; переход
**'здесь удаленный исходный код 80199В4В ;
80199В4В
80199В4В 1ос_80199В4В: 80199В4В 80199B4D 80199B4F 80199В52 80199В53 80199В56
80199В5В
80199B5D
cmp jnz lea push push call
test
;CODE XREF:sub_80199836+2E0j al,l /проверка типа DENIED short loc 80199B64
eax,[esi+8] eax
[ebp+var_8] sub 801997C2
смещение 8 это SID
проверка SID / запрашивающего al,al /совпадение здесь
/ НЕЖЕЛАТЕЛЬНО, / поскольку это I означает ОТКАЗ / (DENIED)

jz short 1ос_80199В64 /поэтому делаем
/ вместо JZ обычный / переход JMP / <ИСПРАВЬ МЕНЯ>
Мы обнаружили еще одно место, в котором необходимо внести изменения. Если в предыдущем случае сравнивались требования уровня доступа запрашивающего пользователя и требования, необходимые для доступа к цели запроса, то в данном случае при выявлении совпадения происходит отказ в доступе. Очевидно, что мы хотим этого избежать и не допустить совпадения. Переход с помощью команды j z происходит только при выявлении совпадения. Мы можем установить заплату вместо команды j z и использовать вместо нее команду jmp, при которой переход осуществляется всегда, независимо от результатов предшествующих действий.
80199B5F test
80199В62 jnz 80199В64
80199В64 1ос_80199В64: 80199В64
80199В64 mov
80199В67 inc
80199В6А movz
80199B6E add
80199B70 cmp
80199B73 jb 80199B79
80199B79 loc_80199B79: 80199B79
80199B79 xor [esi+4],edi /мы избегаем этой
/ проверки флага с / помощью заплаты
short loc_80199B79
/CODE XREF:siib_80199836+2BD /STib_80199836+2D3
есх,[ebp+var10] /наша процедура
/цикла, вызванная выше /var_10 это количество
/элементов АСЕ [ebp+var_C] /var_C это текущий
/элемент АСЕ eax,word ptr [esi+2] /байт 3 - это /смещение до следующего АСЕ esi,eax /FFWD [ebp+var_C],ecx /проверяем все ли / выполнили, если нет 1ос_80199АЕ7 /возвращаемся назад..
/CODE MREF::su]b_8019983 6+2AB /sub_80199836+2B3
eax,eax /это наша общая

jz
test
; процедура выхода test edi,edi /если регистр EDI не пуст, ;то ранее Выло установлено /состояние DENIED short 1ос_80199В91 /поэтому, ,- исправив JZ на JMP, /мы навсегда избавимся от ,- возвращения ACCESS_DENIED ;<ИСПРАВЬ МЕНЯ>
; процедура выхода ;если регистр EDI не пуст, ;то ранее было установлено /состояние DENIED loc_80199B91 /поэтому, ; исправив JZ на JMP, ;мы навсегда избавимся от ; возвращения ACCESS DENIED ;<ИСПРАВЬ МЕНЯ>
Целью последней выполняемой здесь проверки является определение результата вызова. Если по результатам предыдущих действий достигнуто состояние отказа в доступе, то перехода с помощью команды j z не состоится. Очевидно, что мы хотим добиться перехода при любых обстоятельствах, поэтому мы опять исправляем команду jz на jmp. Это последняя заплата, и теперь процедура всегда будет возвра­щать значение TRUE. Для заинтересованных читателей приводим окончание кода процедуры.
80199В7В
80199B7D
80199В7В
80199B7D
jz
edi,edi
short
80199В91
80199В91 1ос_80199В91:
80199В91
80199В94
80199В97
30199В9А s0199b9c
80199B9F
S0199BA1 30199ВАЗ 30199ВА9 30199ВАВ mov mov
mov mov
3nz
xor mov jmp
;CODE XREF:sub_80199836+347 eax, [ebp+lCh ]
ecx,[ebp+arg_lC]
eax,[ebp+arg_C ]
[ecx], eax ecx, [ebp+arg_24]
/результирующий код ; в arg_lC /передается в /маску
/результирующий код / arg_24,должен / быть равен нулю short 1ос_80199ВАВ /если все раньше /прошло хорошо, мы / должны осуществить / переход
al,al
dword ptr [есх],OC0000022h short loc 80199C0C
30199BAB
30199BAB loc_80199BAB: 3 0199BAB mov 30199BB1 test 30199BB3 jz 30199BB5 push 30199BB8 push 30199BBB push 30199BBE push 30199BBF call 30199BC4 jmp
30199BC6 /
/CODE XREF:sub_80199836+369 dword ptr [ecx],0 ebx,ebx
short loc_80199C0A
[ebp+arg_20]
dword ptr [ebp+var_2]
dword ptr [ebp-1]
ebx
sub_8019DC80 short loc 80199C0A
здесь удаленный исходный код 80199С0А 1ос_80199С0А: 80199С0А
80199С0А mov al,l /CODE XREF:sub_80199836+123 /sub 80199836+152

80199С0С
80199С0С 1ос_80199С0С: 80199С0С 80199С0С 80199C0D 80199С0Е 80199C0F 80199С11 80199С12
80199С12 sub 80199836
pop
edi

pop
esi

pop
ebx

mov
esp,ebp

pop
ebp

retn
28h ;:

endp

;CODE XREF:sub_80199836+55 ;sub_80199836+8F
Результат приведенной здесь заплаты заключается в том, что удаленный пользователь может подключаться к атакуемому компьютеру, используя анонимный конвейер 1РС$, даже без ввода пароля он способен уничтожать любой процесс, изменять и загружать/перезаписывать базу данных SAM. Сложно назвать такую ситуацию хорошей. Анонимному пользователю предоставляются возможности, эквивалентные возможностям драйвера устройства с дост}шом к любой части защищенной вычислительной системы атакованного домена.
На основе примера с военно-морским флотом США, можно сделать вывод, что любая компьютерная программа, которая работает в пределах домена Windows NT, может безнаказанно получать доступ к любой другой части домена. Так почему же военно-морской флот упорно использует Windows NT?

 

На главную | Содержание | Назад | Вперёд
 
Яндекс.Метрика