Hướng dẫn defeating javascript obfuscation - đánh bại sự xáo trộn javascript
Câu chuyện về Retterner - một JavaScript deobfuscator nguồn mở mới. Show TL;DRBây giờ đây là một câu chuyện tất cả về cách Mã của tôi bị đảo lộn lộn ngược Và tôi muốn dành một phút Chỉ ngồi ngay đó Tôi sẽ nói cho bạn biết làm thế nào tôi đến để xây dựng deobfuscator của tôi - Retterner. Để làm cho một câu chuyện dài ngắn, tôi đã phát hành một công cụ deobfuscation của JavaScript có tên Restringer, cả dưới dạng mã và là một công cụ trực tuyến. Để làm cho một câu chuyện ngắn dài - Tôi muốn chia sẻ động lực của mình để tạo ra công cụ, một số quyết định thiết kế và quá trình mà tôi đã thêm các khả năng mới vào nó - vì vậy bạn có thể tham gia vào cuộc vui! Đó là thời gian để đưa niềm vui vào Deobfunsca-- OK điều này có thể cần một số công việc. Giải quyết, thay thế, lặp lạiTên là Carter. MAITECARTER.Một vài năm trước, tôi bắt đầu làm việc trong nhóm nghiên cứu Code Defender tại Perimeterx. Một phần trong quy trình làm việc của tôi bao gồm điều tra các cuộc tấn công Magecart, hiện gần như đồng nghĩa với các cuộc tấn công chuỗi cung ứng của khách hàng và skimming. Các cuộc tấn công này thường có dạng các tệp JavaScript được tiêm trực tiếp vào các trang web bị xâm phạm hoặc được gửi thông qua một tập lệnh của bên thứ ba bị xâm phạm. Tôi thu thập các tập tin này và phân tích chúng. Tôi sẽ chia sẻ thêm về quá trình điều tra của tôi trong một bài đăng trong tương lai. Có nhiều cách tiếp cận để phân tích mã, mặc dù về cơ bản chúng ta có thể phân loại chúng là:
Trừ khi tập lệnh thực sự đơn giản để hiểu hoặc bạn đã xử lý mã tương tự, bạn đã sử dụng kết hợp hai cách tiếp cận để có được bức tranh hoàn chỉnh về những gì tập lệnh làm và làm thế nào. Ví dụ: khi phân tích một tập lệnh như skimmer chống VM được đề cập trong các bài đăng trên blog trước đây của tôi (điểm xa của một cuộc gặp gỡ tĩnh và tự động hóa skimmer deobfuscation), tôi sẽ sử dụng kết hợp đánh giá tĩnh về cấu trúc của mã và chỉ chạy các phần của Mã (trong Chrome DevTools, trên trang 8):
Bây giờ, tôi lấy giá trị đó và thay thế cuộc gọi chức năng bằng nó. Điều đó không quá tệ, phải không? Làm điều này một hoặc hai lần là tốt và bảnh bao, nhưng nó có thể khá tốn thời gian và mệt mỏi khi phải đối mặt với ngày càng nhiều trường hợp của cùng một sự che giấu. Theo lời của Raymond Hettinger - Hồi phải có một cách tốt hơn! Và tất nhiên là có! Nếu bạn có thể làm điều đó bằng tay, bạn gần như có thể viết một kịch bản để làm điều đó.There must be a better way”! And of course there is! If you can do it manually, you can almost definitely write a script to do it. Cách tốt hơn (Take 1) - Một loạt các tài liệu tham khảoHãy để bắt đầu với một cái gì đó đơn giản và hoạt động theo cách của chúng tôi. Một trong những loại obfuscation cơ bản hơn được sử dụng tích cực bởi những kẻ tấn công Magecart là phương pháp thay thế mảng. Nó được đặc trưng bởi một mảng được khởi tạo với các chuỗi và các tham chiếu trong suốt mã cho các chỉ số bên trong mảng. Cái này trông thế nào?
Tôi sẽ theo ví dụ về Guy Bary, trong phần mềm độc hại phân tích Magecart - từ không đến bài đăng trên blog Hero:
Mặc dù điều này hoạt động, nó đòi hỏi sự can thiệp thủ công - cung cấp tên của mảng. Tất nhiên, chúng tôi có thể và cũng nên lưu trữ kết quả hoặc sử dụng thay thế để cải thiện hiệu suất, nhưng điều đó sẽ làm mất đi sự đơn giản của ví dụ. Lưu ý rằng tôi ban đầu chỉ chạy dòng đầu tiên của mã, sử dụng 4, để tránh bất kỳ tác dụng phụ nào có thể phát sinh do chạy toàn bộ mã, cụ thể là thông điệp ‘Hello World được in trong khi thực hiện. Mặc dù tôi quản lý để tránh điều đó ở đây, tập lệnh nguồn có thể đã đặt mảng khá nhiều ở bất cứ đâu, hoặc không hoàn toàn các dòng mới (như thường thấy với mã được tiêm). Tác dụng phụ cũng có thể tồi tệ hơn nhiều so với chỉ là một thông điệp lành tính.Tăng độ phức tạpHãy để trộn nó lên một chút. Hãy xem xét một sự phát triển của kỹ thuật obfuscation này, phương pháp thay thế mảng tăng cường. Nó có các đặc điểm tương tự như đối tác chưa được cung cấp của nó, nhưng với một biểu thức chức năng được gọi thêm ngay lập tức (hoặc iife) với một tham chiếu đến mảng là một trong những đối số của nó, thay đổi (tăng) các giá trị trong mảng theo một cách nào đó. Cái này trông thế nào?
Sau khi 5 được khai báo, iife đảo ngược thứ tự của mình để mỗi chỉ mục bây giờ trỏ đến giá trị chính xác. Nếu chúng tôi cố gắng chạy tập lệnh deobfuscation trên đó, chúng tôi sẽ nhận được mã bị hỏng này:
Giải pháp dễ dàng là chạy tập lệnh là để có được ngữ cảnh vừa phải, và sau đó tìm kiếm và thay thế. Để làm điều đó, chúng tôi loại bỏ sự phân tách dòng từ 6 và bị bỏ lại với 7.Bẫy mà kỳ nghỉ xuân (thực hiện)Blog Guy sườn liên quan đến một loại obfuscation khác nhau mặc dù - thay thế chức năng mảng tăng cường (đã bỏ qua phiên bản chưa được điều khiển). Loại obfuscation này cũng chứa một mảng có chuỗi và iife tăng cường nó, nhưng thay vì được tham chiếu trực tiếp trong suốt mã, một cuộc gọi hàm được sử dụng với các tham số chuỗi/số. Hàm này lấy giá trị từ mảng tương ứng với các tham số được cung cấp. Cái này trông thế nào?
Ví dụ đồ chơi này cho thấy giá trị gia tăng của hàm và lý do tại sao nó được ưu tiên hơn các thay thế mảng đơn giản hơn: bạn có thể làm bất cứ điều gì bạn muốn bên trong hàm, cụ thể là khoảng cách giá trị tham số từ chỉ mục mảng thực tế để tránh tìm kiếm đơn giản và thay thế chiến thuật và chiến thuật và Đặt bẫy cho các nhà điều tra bắt giữ và chọc vào đôi mắt tò mò. Cái bẫy mà tôi đã bao gồm thực thi treo (nhưng không phá vỡ nó) nếu các devtool được mở hoặc nếu nó chạy bên trong IDE với khả năng gỡ lỗi. Do kịch bản rất ngắn chỉ có 4 cuộc gọi đến chức năng 8, nó không thực sự làm nhiều hơn là khó chịu, nhưng hãy tưởng tượng một kịch bản với hàng trăm cuộc gọi - nó đã trở nên khá cũ khá nhanh. Tất nhiên bạn có thể chống lại cái bẫy đơn giản này theo một số cách như vô hiệu hóa các điểm dừng, loại bỏ dòng đó theo cách thủ công, v.v.Dưới đây là một vài ví dụ về các điều kiện mà bẫy có thể gây ra và phá vỡ thực thi:
Một thủ thuật hữu ích khác để ném điều tra về kịch bản bị xáo trộn này là bằng cách cung cấp chức năng 8 bằng một đối số vứt bỏ, được sử dụng tất cả (như đối số 0 trong ví dụ). Tất cả các cuộc gọi chứa khác nhau và một tham số đầu tiên ngẫu nhiên, giúp làm phức tạp các mẫu tìm kiếm và thay thế. Thêm sự xúc phạm vào thương tích, bạn thậm chí có thể quá tải các cuộc gọi chức năng với các tham số không cần thiết bổ sung để thay đổi hoàn toàn chữ ký cuộc gọi.Vì những người thích chơi với mã trong khi đọc có thể nhận thấy, ví dụ này vẫn có thể bị phá hủy bằng kỹ thuật tìm kiếm và thay thế đơn giản của chúng tôi, như vậy:
Vì vậy, chỉ để làm phức tạp các vấn đề (vì tại sao không?), Tôi đã di chuyển hàm 8 sang cuối mã và ghi đè lên chức năng mảng và 8 sau khi thực thi:
Tôi có thể viết một tìm kiếm phức tạp hơn và thay thế tập lệnh chỉ trích xuất mảng và chức năng và sau đó tìm ra các chuỗi, nhưng đó không thể mở rộng. Thế bây giờ thì thế nào? Cách tốt hơn (Take 2) - Tổng quát hóa giải pháp: Ra khỏi tập lệnhHãy để tôi nhảy thẳng vào đầu sâu của hồ bơi bằng cách đưa bạn đi qua việc xây dựng một deobfuscator cho loại hình che giấu này. Nó sẽ là một giải pháp cơ bản và tôi sẽ đề cập đến những thiếu sót của việc thực hiện cụ thể vào cuối phần này, vì vậy hãy viết ra bất cứ điều gì bạn nghĩ bạn sẽ làm khác đi và cho tôi biết nếu tôi bỏ lỡ bất cứ điều gì . Chỉ cần một lời nhắc nhở nhanh chóng - Tôi sẽ sử dụng gói FLAST để làm phẳng AST để giúp dễ dàng tìm thấy các cấu trúc mã đang đề cập đến nhau và thay thế chúng bằng các chuỗi deobfuscate. Hãy để bắt đầu với mảng. Trong kịch bản ngắn này, chúng tôi đã phá hoại, chỉ có một mảng, vì vậy, nó đơn giản, nhưng hãy để giả vờ rằng có thể có nhiều hơn một. Làm thế nào chúng ta tìm thấy mảng cụ thể mà chúng ta đang tìm kiếm? Trước tiên, chúng tôi mô tả nó và sau đó viết nó bằng mã. Chúng tôi đang tìm kiếm:
Sử dụng FLAST, chúng tôi có thể xem xét các tài liệu tham khảo định danh và phạm vi của chúng:
Điều này là quá chung chung. Chúng ta có thể kiểm tra mức độ này mô tả cấu trúc mà chúng ta đang tìm kiếm bằng cách nối thêm mã bị lỗi vào phiên bản không bị che giấu của một tập lệnh lớn (như tập lệnh jQuery tùy ý này) và đếm số mảng khớp với mô tả. Trong trường hợp này, ba mảng phù hợp: 0Vì vậy, chúng tôi là chung chung, nhưng không quá chung chung. Chúng ta có thể sử dụng mối quan hệ giữa mảng và hàm 8 để trau dồi đúng. Những gì chúng tôi đang tìm kiếm là:
Trong mã, mô tả này trông như thế này: 1Vì vậy, đối với mỗi mảng chúng tôi tìm thấy, chúng tôi có thể tìm kiếm một hàm giải mã phù hợp với mô tả cho đến khi chúng tôi tìm thấy một trận đấu. Bây giờ, tất cả những gì còn lại là tìm thấy chức năng tăng cường bằng cách tìm kiếm biểu thức cuộc gọi trong đó callee là một biểu thức chức năng (nghĩa là một hàm được khai báo tại chỗ, thay vì định danh hoặc một đối tượng) và mảng của chúng tôi là một trong các đối số của nó. Chúng tôi thậm chí không cần phải tìm kiếm toàn bộ mã cho mô tả đó, chỉ các tham chiếu đến mảng của chúng tôi: 2Đặt tất cả lại với nhau, tôi có ý nghĩa hơn khi đặt từng mô tả vào chức năng riêng của nó và tất cả các chức năng trong một lớp duy nhất và chia sẻ AST được phân tích cú pháp thay vì truyền nó mỗi lần. 3Bạn có thể thay thế mã nội tuyến bằng mã jQuery được tiêm và kiểm tra nó. Tất cả trong tất cả, đó là một mã hóa phô mai. Giải pháp này vẫn còn thiếu, vì nó không bao gồm tất cả các kịch bản có thể. Ở đây, một danh sách một phần các kịch bản không được bảo hiểm:
Những gì tôi hy vọng tôi đã quản lý để truyền đạt là làm thế nào để sử dụng FLAST để tạo ra mã ngắn gọn và hiệu quả nói trong các cấu trúc và mối quan hệ nút. Cuộc hành trình cho đến nayNhững gì tôi đã chia sẻ cho đến bây giờ là cách hành trình deobfuscation của tôi bắt đầu. Nó trông như thế nào bây giờ? Bạn có thể kiểm tra nó cho chính mình! Tôi đã thu hẹp một số mô tả obfuscation trong máy dò obfuscation của tôi và đối với một số loại che giấu đó, Restringer cung cấp các bộ xử lý tiền cụ thể để loại bỏ bẫy hoặc đơn giản hóa mã trước khi chuyển sang các nỗ lực khử trùng chung hơn. Ví dụ, Caesar+ obfuscation, mà tôi đã phá vỡ trong DEOBFuscating Caesar+, có một lớp bên ngoài hoạt động như một loại Packer kết hợp chuỗi mã được đóng gói bằng các phần tử HTML. Phương pháp này được sử dụng để ngăn chặn việc chạy mã bên ngoài môi trường trình duyệt. Để tự động hóa việc giải nén trong môi trường NodeJS, môi trường trình duyệt mô phỏng được sử dụng thông qua JSdom. Logic deobfuscation chính được trình bày trong phương pháp DEOBFuscate chính: 4Điều đầu tiên là xác định loại obfuscation, chạy các tiền xử lý thích hợp và thiết lập bất kỳ bộ xử lý hậu nào có liên quan. Sau đó, các phương pháp deobfuscation chính chạy trong một vòng lặp cho đến khi chúng không còn hiệu quả. Các bộ xử lý sau đó được áp dụng cho mã hiện đang bị khử trùng. Cuối cùng, mã được chuẩn hóa (ví dụ: biến khung [‘ký hiệu] thành dot.notation) và bị tước bất kỳ mã chết nào nếu tùy chọn đó được chọn. Các điều khoản an toàn và không an toàn là các thuật ngữ cho biết liệu một phương thức đang sử dụng Eval để giải quyết đầu ra của mã hay không. Sử dụng Eval được coi là không an toàn, ngay cả khi chạy nó trong môi trường hộp cát như VM2. 5Cách thức vòng lặp này được xây dựng, các phương pháp an toàn chạy cho đến khi chúng tự cạn kiệt, và sau đó các phương thức không an toàn chạy một lần. Chu kỳ sau đó được lặp lại. Điều này được thực hiện để tránh chạy các eval quá háo hức trên mã có thể bị khử trùng bằng các phương pháp khác, tạo ra kết quả chính xác hơn. Phương pháp Runloop có một loạt các phương pháp deobfuscation và tốt, chạy chúng trong một vòng lặp (ngạc nhiên, phải không?). Nó có một ảnh chụp nhanh của mã của mã trước khi chạy và so sánh nó sau mỗi vòng lặp. Nếu ảnh chụp phù hợp với trạng thái hiện tại của mã, điều đó có nghĩa là không có phương thức nào thực hiện bất kỳ thay đổi nào và vòng lặp có thể kết thúc. Một số chu kỳ tối đa cũng được quan sát để tránh một kịch bản vòng vô tận. Ở đây, một phiên bản làm sạch của phương pháp này: 6Mô tả này sẽ hoàn thành mà không có danh sách các phương pháp an toàn và không an toàn hiện có: 7Tôi rất thích đi sâu vào chi tiết trong tương lai về cách tôi thực hiện cơ chế EVAL, cách thức hoạt động của bộ nhớ đệm, những lựa chọn tôi thực hiện về những gì có thể và không thể bị phá hoại tại bất kỳ thời điểm nào, hoặc cách tôi thu thập bối cảnh. Có rất nhiều tài liệu và ý kiến trong repo. Chúng ta đi đâu? Bây giờ chúng ta đi đâu?Dự án này đã ở với tôi kể từ những ngày bắt đầu của tôi tại Perimeterx, và tôi thực sự biết ơn vì đã được trao cơ hội và thời gian để làm việc trong một dự án dài hạn thú vị như vậy. Retterner đã được mã hóa, tái cấu trúc, viết lại, băm nhỏ, dán lại với nhau, dịch, đổi tên, bị mất, tìm thấy, chịu sự điều tra công khai, hỏi, bị mất một lần nữa và cuối cùng được phê duyệt để được phát hành dưới dạng nguồn mở.Thật là một hành trình! Resterner là tuyệt vời, nhưng nó khác xa với sự hoàn hảo hoặc đầy đủ.Tôi sẽ giữ nó, và tôi hy vọng nó sẽ hữu ích cho các nhà nghiên cứu và những người đam mê obfuscation khác, tất cả những người đều được chào đón để sử dụng và đóng góp khi họ thấy phù hợp. Cảm ơn vì đã đọc!Tôi hy vọng tất cả chúng ta lại gặp lại nhau trong Restringer 2: Việc tìm kiếm nhiều sự che giấu hơn và có thể Schwartz sẽ ở bên bạn! |