Bypass Sandbox trong AngularJS để thực hiện các cuộc tấn công XSS

Trong bài viết này, chúng ta sẽ có cái nhìn tổng quan về các vấn đề bảo mật trong AngularJs bao gồm những kiến thức cơ bản nhất về AngularJs cũng như cơ chế làm việc bên trong sandbox của AngularJS (trong các phiên bản khác nhau).

GIỚI THIỆU

AngularJs là một framework JavaScript được sử dụng để tạo ra các ứng dụng giàu tính năng trên Internet (RIA – Rich Internet Application), bao gồm cả ứng dụng Web App và ứng dụng Mobile App. Nó cho phép lập trình viên tạo ra những mẫu client-side dựa trên kiến trúc Model-View-Control (MVC). Angular JS sử dụng các biểu thức tích hợp nhằm tạo ra HTML template. Những biểu thức này được dùng để lấy ra hoặc hiển thị trên HTML. Chi tiết về Angular JS các bẹn có thể xem tại đây:

KIẾN THỨC NỀN TẢNG CỦA ANGULARJS

Để có thể sử dụng AngularJS an toàn, chúng ta cần hiểu cơ bản về cách hoạt động của Angular JS như thế nào. Hãy cùng tạo ra một ứng dụng đơn giản và tìm hiểu thông qua các đoạn code.Để tạo ra một ứng dụng, ta cần định nghĩa tên module và các thành phần điều khiển controllers nơi mà ta sẽ lấy dữ liệu ra và hiển thị trong ngữ cảnh HTML (view). Ứng dụng sẽ nhận đầu vào một chuỗi bất kì.

Lưu đoạn code sau vào tệp tin app.js

Tệp tin index.php có nội dung như sau:

Như ta thấy trong đoạn code trên, sử dụng biểu thức {{ }} để lấy ra dữ liệu. Tuy nhiên có một biến khác có tên “alert(1)” có thể hiển thị đúng như hàm alert(). Vậy có phải tất cả các biểu thức được thực thi trong ngữ cảnh JavaScript thì những hàm JavaScript cũng có thể thực thi hay không?

AngularJS sử dụng đối tượng scope để kiểm tra sự tồn tại của biến định nghĩa trong biểu thức. Trong trường hợp này, biến scope “{{alert(1)}}” không được định nghĩa., AngularJS sẽ kiểm tra “alert” có phải là một thuộc tính của đối tượng scope không thông qua hàm con trỏ sau

Do “alert” không được định nghĩa nên giá trị của nó sẽ được gán bằng noop – không thực hiện hành động gì tiếp theo và sẽ không hiển thị. Điều này tương tự với các hàm toàn cục của JavaScript như document.cookie hoặc document.domain.

Sandbox Bypass Trong AngularJs 1.0.8

Như ở phần trước, ta không thể từ đối tượng scope thực thi bất cứ hàm toàn cục nào. Trong phần này chúng ta sẽ thực hiện “thoát” khỏi đối tượng scope và thực thi hàm tùy ý JavaScript thông qua khai thác ‘Gareth Hayes’.

Đối tượng scope cũng khống như các đối tượng JavaScript khác và có một hàm khởi tạo

Hàm khởi tạo có thể được dùng để tạo ra một hàm và ta có thể đưa code JavaScript vào trong ngoặc tròn ( ) để thực thi như sau

Tương tự ta sẽ dùng hàm khởi tạo của hàm khởi tạo đối tượng của đối tượng scope để gán đoạn code như sau {{constructor.constructor(‘alert(0)’)()}}

Kết quả đoạn code alert(0) được thực thi với sự giúp đỡ của hàm khởi tạo.

Sandbox Bypass trong AngularJs 1.2.19

Phiên bản này đã cài đặt một số kiểm tra bảo mật trước khi dữ liệu được đưa vào. Thực chất bao gồm việc kiểm tra các thuộc tính khác nhau của một đối tượng, kiểm tra các hàm thuộc tính như CALL, APPLY, BIND….

Hãy cùng xem đoạn code bypass phiên bản 1.2.18 của Jan Horn

Chi tiết về đoạn khai thác trên như sau:

Đoạn code sử dụng thuộc tính CALL để thực thi đoạn mã JavaScript. Trong phiên bản 1.2.19 một hàm mới được đưa ra nhằm kiểm tra các thuộc tính khác nhau trong đối tượng hàm được sử dụng để bypass trong 1.2.18

Cùng tiếp tục tìm hiểu bypass phiên bản này của “Mathias Karlsson”

Đầu tiên khai thác thay đổi hàm toString bằng cách gán thuộc tính mới “call” và khi hàm khởi tạo toString được gọi bởi hàm sort, toSctring sẽ bằng call và thực thi alert(1).

Sandbox Bypass trong AngularJs 1.4.7

Trong phiên bản này AngularJs đã đưa một trình biên dịch tích hợp và sử dụng kĩ thuật AST (Abstract Syntax Tree) nhằm mã hóa biểu thức và thực thi hàm an toàn. Khi thử khai thác giống phiên bản 1.2.19 chúng ta nhận được thông báo lỗi như sau

Tại sao lại có thông báo lỗi và tại sao ta không thể trỏ đến đối tượng hàm nữa ?

Như đoạn code ở trên, khởi tạo của một hàm khởi tạo lại là một hàm khởi tạo, do đó dòng “if (obj.constructor === obj)” trong hàm ensureSafeObject chặn thành công các khai thác bằng cách kiểm tra nếu đối tượng của hàm khởi tạo không phải là một đối tượng.

Đoạn mã khai thác của Gareth Heyes tiếp tục bypass AngularJS như sau:

{{‘a’.constructor.prototype.charAt=[].join;$eval(‘x=1} } };alert(1)//’);}}

Đầu tiên  ghi đèn hàm charAt bằng hàm join, đồng nghĩa với viếc khi ta gọi hàm chartAt, nó sẽ nối giá trị của mỗi kí tự thay vì trả về chỉ mục index của kí tự đó

Phân tiếp theo của khai thác là sử dụng vòng lặp để lấy ra token

Kết quả trả về

Mã JavaScript tùy ý được thực thi

Tài liệu tham khảo

http://blog.portswigger.net/2016/01/xss-without-html-client-side-template.html

https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype

https://code.angularjs.org/

https://www.youtube.com/watch?v=67Yc8_Bszlk&list=PLhixgUqwRTjwJTIkNopKuGLk3Pm9Ri1sF