Theo dõi dữ liệu đánh dấu

Một phần của tài liệu Nghiên cứu lỗ hổng bảo mật Cross-Site Scripting và xây dựng chương trình phát hiện sâu mã độc phát tán (Trang 29)

Các chương trình Javascript là một phần của một trang web được phân tích và biên dịch thành byte-code. Những byte-code intruction sau đó được dịch bởi JavaScript engine.

Các intruction có thể chia thành các loại sau: - Assignment (phép gán).

- Arthmetic and logic operations (+, -, &…) (các phép toán và phép logic). - Control structures and loops (if, while,...) (cấu trúc điều khiển và lặp). - function calls, classes and eval (lời gọi hàm, lớp, và hàm eval).

Khi một intruction được chạy, các phần hay tất cả toán hạng của nó có thể bị đánh dấu. Vì vậy đối với mỗi intruction có một luật để định nghĩa mà theo đó kết quả của phép toán là có bị đánh dấu hay không (hoặc những kiểu khác của thông tin bị ảnh hưởng bởi dữ liệu bị đánh dấu).

3.1.2.1. Assignment

Trong một phép toán gán, giá trị của biến bên trái được thiết lập. Nếu bên phải của phép toán mà bị đánh dấu thì kết quả của bên trái cũng bị đánh dấu. JavaScript engine có các intruction khác nhau để gán giá trị cho một biến đơn, biến hàm, các tham số hàm, các phần tử mảng và các thuộc tính của đối tượng. Một số kiểu gán được chỉ ra trong hình 7:

Hình 7: Ví dụ về phép gán.

Trong một số trường hợp không chỉ một biến mà được gán một giá trị đánh dấu mới bị đánh dấu. Cho ví dụ nếu một phần tử của mảng bị đánh dấu thì sau đó toàn bộ mảng đối tượng cần phải được đánh dấu. Cái này là cần thiết để chắc chắn rằng kết quả của arr.length là một giá trị bị đánh dấu. Hãy xem xét từ dòng 8 tới 12 trong ví dụ hình 7. Ở dòng 8 một mảng mới được khởi tạo với độ dài là 0. Một giá trị được gán cho phần tử đầu tiên ở dòng 10 chỉ khi kí tự đầu tiên của cookie là “a”. Nếu như vậy thì bây giờ độ dài mảng ở dòng 12 là 1. Ở dòng 12 một biến mới được thiết lập giá trị là “a” tùy thuộc vào chiều dài của mảng. Khi mở rộng phương thức này cho tất cả các trương hợp có thể có của kí tự (‘a’-‘z’, ‘A’-‘Z’, ‘0’-‘9’) kẻ tấn công có thể sao chép được kí tự đầu tiên của cookie cho một biến mới đo đó có thể lọt ra sự đánh dấu. Tuy nhiên nếu ở dòng 10 ta không chỉ đánh dấu phần tử đầu tiên của mảng mà còn đánh đấu cả đối tượng mảng thì biến y ở dòng 12 sẽ bị đánh dấu. Tương tự nếu một thuộc tính của đối tượng bị đánh dấu thì toàn bộ đối tượng cần được đánh dấu, lý do là thuộc tính đó có thể là mới và trong trường hợp này số lượng thuộc tính thay đổi điều đó cho phép kẻ tấn công có thể lợi dụng lỗ hổng.

3.1.2.2. Phép toán học và logic

Các phép toán trong hình 8 có thể có một hoặc nhiều toán hạng. Javascript tương tự java byte-code là một ngôn ngữ dựa trên stack. Đó là các instruction mà thực hiện phép toán số học hay logic thì đầu tiên lấy ra số lượng thích hợp các toán hạng từ

stack để tính toán và sau đó nhét kết quả trở lại. Kết quả bị đánh dấu khi sử dụng toán hạng bị đánh dấu

Hình 8: Ví dụ về các phép toán học và logic

3.1.2.3. Cấu trúc điều khiển và lặp

Cấu trúc điều khiển và lặp được sử dụng để điều khiển dòng thực hiện của chương trình và để lặp lại một số instruction nhất định. Dưới đây là các cấu trúc điều khiển và lặp:

- Khối if (có hoặc không có else). - Câu lệnh switch.

- Câu lệnh with.

- Vòng lặp (do - while, for, hay for in). - Khối try – catch – finally.

Nếu điều kiện của cấu trúc điều khiển kiểm tra một giá trị đã đánh dấu thì một “scope” (vùng) được tạo ra cho đến cuối của cấu trúc điều khiển đó.

Hình 9: Ví dụ về vòng lặp for với điều kiện bị đánh dấu.

Hình 9 trên chỉ ra một ví dụ cho khối if. Điều kiện của câu lệnh if bị đánh dấu (dòng 2) vì thể một phạm vi được tạo ra đến cuối khối (dòng 4). Kết quả của tất cả các phép toán và phép gán trong vòng lặp là bị đánh dấu vì thể kết quả của lệnh gán ở dòng 3 là bị đánh dấu. Điều này được sử dụng để thực hiện kiểm soát sự phụ thuộc để ngăn chặn nỗ lực rửa sạch giá trị đánh dấu bằng cách sao chép chúng vào các giá trị không bị đánh dấu như minh họa hình 10. Bởi vì một giá trị bị đánh dấu (document.cookie) được sử dụng trong điều kiện của vòng lặp for trong dòng 4. Một phạm vi từ dòng 4 đến dòng 10 được tạo ra. Một phạm vi bổ xung được tạo ra từ dòng 5 tới dòng 9 bởi vì điều kiện trong câu lệnh switch là bị đánh dấu . Khi sử dụng các phép toán trong một phạm vi bị đánh dấu thì tất cả các kết quả đều bị đánh dấu. Bất kể toán hạng có bị đánh dấu hay không. Kết quả là biến “dut” bị đánh dấu.

Hình 10: Ví dụ cho một điều khiển phụ thuộc

Để xác định một phạm vi bao phủ câu lệnh if, ta có khái niệm đường bao. Xem xét trong hình 11 một câu lệnh if với điều kiện bị đánh dấu (document.cookie == ‘a’ ở dòng 1) được dùng để gán cho biến x giá trị 5 ở dòng 2:

Hình 11: Phạm vi bị đánh dấu của khối if

Hình 12: Opcode của ví dụ trong hình 11

Đoạn script này được biên soạn thành byte-code với các op-code như hình 12 trên. Cột đầu tiên là là giá trị bộ đếm chương trình (program counter PC) của opcode. Cột thứ hai là số tương ứng với số hiệu dòng trong hình 11. Cột cuối cùng là cột chứa opcode và một số thông tin tùy chọn. Opcode ifeq tại PC = 00013 kiểm tra giá trị trong stack (điều kiện của câu lệnh if) và nhẩy tới PC = 00026 nếu điều đó là sai và tiếp tục thực hiện câu lệnh sau câu lệnh if. Opcode từ PC = 00016 tới PC = 00025 là thân của câu lệnh if. Vì thế bắt đầu của phạm vi là PC = 00013 và chiều dài của nó được xác định ở thông tin tùy chọn của opcode (chiều dài là 13 và opcode tiếp theo là PC = 00026). Khi câu if mà có else một goto-opcode có thể được tìm thấy ở PC = 00026 mà chứa độ dài của nhánh else. Các câu lệnh điều khiển khác cũng tương tự. Nếu đố tượng được sử dụng trong câu lệnh with mà nó bị đánh dấu một phạm vi bị đánh dấu đến cuối khối được tạo ra. Câu lệnh if-else sẽ tạo ra phạm vi cho cả hai nhánh nếu điều kiện bị đánh dấu. Vòng lặp while, for, for-in được xử lý giống như câu lệnh if và câu lệnh with. Trong vòng lặp do-while phạm vi không được tạo đến khi điều kiện bị đánh dấu

được kiểm tra. Kết quả là lần đầu tiên khối được chạy và không có phạm vi được tạo ra. Nếu điều kiện kiểm tra có bị đánh dấu một phạm vi đánh dấu bao trùm cả khối được tạo ra, và sẽ giữ nguyên cho đến hết vòng lặp. Trong câu lệnh try-catch-finally phạm vi được tạo ra cho khối catch khi một đối tượng ngoại lệ bị đánh dấu.

Một vài ví dụ cho cấu trúc điều khiển được thể hiện trong hình 13 dưới đây:

Hình 13: Các vị dụ về cấu trúc điều khiển và lặp

3.1.2.4. Function calls và eval

Các hàm trong Javascript có thể từ các nguồn khác nhau như: một đối tượng hàm ẩn danh, đối tượng hàm có tên, hay định nghĩa cho một lớp mới (ví dụ hình 14, hình 15). Hàm có thể bị đánh dấu nếu được định nghĩa trong vùng bị đánh dấu (vùng được tạo từ dòng 1 tới dòng 3 bởi điểu kiện bị đánh dấu ở dòng 1) như đã thấy trong hình 14. Ở dòng 5 hàm func được gọi với tham số bị đánh dấu mà kết quả trả về của hàm chính là tham số đó (dòng 4) nên kết quả của hàm func ở dòng 5 phải được đánh dấu. Từ dòng 6 đến dòng 7 trong hình 15 chỉ ra rằng argument.length là bị đánh dấu.

Tham số thứ 2 trong dòng 7 là bị đánh dấu nên vì thế kết quả trả về ở dòng 6 bị đánh dấu, kết quả đó truyền cho biến x ở dòng 7 nên biến x bị đánh dấu. Một ví dụ phức tạp hơn được chỉ ra từ dòng 8 đến dòng 10 trong hình 14. Trong dòng 8 – 9 một chuỗi được lắp ráp và trả về giá trị của hàm count (hàm này trả về số lượng tham số truyền vào trừ đi 1 được định nghĩa trong dòng 6) cho biến dut. Tham số thứ nhất của lời gọi hàm là không bị đánh dấu trong khi các tham số khác là bị đánh dấu (bởi vì một phạm vi đánh dấu trong vòng lặp được tạo ra). Đoạn chú thích trong dòng ở dòng 10 cho ta thấy chi tiết hơn: các tham số bị gạch chân là những cái mà được thêm bởi vòng lặp bị đánh dấu và vì thế chúng bị đánh dấu. Evaluation của chuỗi này dẫn tới biến dut bị đánh dấu. (adsbygoogle = window.adsbygoogle || []).push({});

Hình 14: Các tham số hàm

Hình 15: Ví dụ về hàm

Hàm eval là một hàm đặc biệt vì đối số của nó được coi như là một chương trình Javascript và được chạy. Nếu hàm eval được gọi trong phạm vi bị đánh dấu một phạm

vi xung quanh chương trình thực hiện được tạo ra (hình 16), và tất cả các toán tử trong chuỗi evaluted là bị đánh dấu.

Hình 16: Sử dụng của eval với thông tin bị đánh dấu.

3.1.2.5. Dom Tree

Một kẻ tấn công có thể cố gắng loại bỏ trạng thái bị đánh dấu của một số yếu tố dữ liệu bằng cách tạm thời lưu trữ nó trong một node nào đó trên DomTree và sau đó sẽ lấy nó ra (hình 17). Để ngăn chặn việc làm sạch dữ liệu qua DomTree thông tin bị đánh dấu phải được mở rộng ra ngoài Java script- engine (trong trình duyệt), cuối cùng đối tượng là một Dom node sẽ bị đánh dấu khi một chương trình Java script lưu trữ giá trị bị đánh dấu lên node đó. Khi nó được truy cập ở lần sau, thì giá trị trả về sẽ bị đánh dấu.

Hình 17: Ví dụ cho lưu trữ tạm thời dữ liệu bị đánh dấu vào DomTree

Một phần của tài liệu Nghiên cứu lỗ hổng bảo mật Cross-Site Scripting và xây dựng chương trình phát hiện sâu mã độc phát tán (Trang 29)