Giới thiệu kỹ thuật khai thác exploit Heap Spray.
1. Tổng quan về Heap Spray
Chúng ta có thể nhận thấy được rằng trong các khai thác lỗ hổng bảo mật gần đây có sử dụng nhiều đến kỹ thuật Heap Spray.
Heap Spray không phải là một cách để khai thác lỗ hổng bảo mật. Đây là một kỹ thuật được sử dụng để cấp phát payload được sử dụng như một phần của một khai thác lỗ hổng. Heap Spray tận dụng lợi thế là cho phép đặt shellcode ở một vị trí bất kỳ (có thể dự đoán được) trong bộ nhớ và sau đó “jump” về lại địa chỉ đó để thực thi đoạn shellcode.
Để một Heap Spray có thể làm việc được thì cần phải được quyền cấp phát và điền vào các chunks trong bộ nhớ heap trước ghi chiếm được quyền điều khiển thanh ghi EIP.
Các browser (trình duyệt) cung cấp một cơ chế dễ dàng cho việc này. Nó được hỗ trợ bởi các ngôn ngữ script (javascript hay vbscript…) để cấp phát một vài thứ vào trong bộ nhớ trước khi gây ra lỗi. Điều này không có nghĩa là chỉ có khai thác các lỗ hổng của trình duyệt mới sử dụng được kỹ thuật này. Một số app cũng cho phép sử dụng javascript hay ActionScript như trong Adobe Reader… Có nghĩa là chúng ta có thể sử dụng kỹ thuật này trong nhiều trường hợp chỉ cần thỏa mãn yêu cầu là có một cơ chế cho phép chúng ta cấp phát dữ liệu trong bộ nhớ ở một vị trí có thể dự đoán được trước khi gây ra bug và kiểm soát EIP.
Để thực hiện Heap Spray cần phải làm các bước:
Cần lưu ý trong Windows ban đầu khi chúng ta cấp phát một lượng nhỏ dữ liệu lên bộ nhớ heap, nó có thể nằm ở bất kỳ vị trí nào trong bộ nhớ heap (do phân mảnh hay do một nguyên nhân nào đó). Điều này gây khó khăn cho việc dự đoán địa chỉ để jump của chúng ta. Tuy nhiên khi cấp phát dữ liệu lên các địa chỉ cao hơn trong bộ nhớ heap thì khối dữ liệu được cấp phát sẽ tập trung và rơi vào các chunks liền kề trong bộ nhớ như hình dưới
Ngoài ra chúng ta để đảm bảo có thể jump về địa chỉ chứa NOP (để thực hiện shellcode từ đầu) mà không phải là một vị trí nằm giữa shellcode thì chúng ta cần cấp phát các bytes NOP lớn gấp nhiều lần so với kích thước của shellcode. Việc này đảm bảo cho khả năng jump đến địa chỉ chứa NOP sẽ cao hơn và đáng tin cậy hơn.
Sau khi thực hiện kỹ thuật heap spray và bố trí data lên heap chúng ta sẽ gây ra bug để chiếm quyền điều khiển EIP và cho trỏ về đúng địa chỉ dự đoán trên heap và cho chương trình thực hiện. Các địa chỉ dự đoán thường được sử dụng là:
0x05050505
0x06060606
0x07070707
…
0x0a0a0a0a
0x0b0b0b0b
0x0c0c0c0c
…
Trước đây trong các khai thác heap spray thường sử dụng địa chỉ 0x05050505 hay 0x06060606. Tuy nhiên trong các khai thác gần đây chúng ta thường thấy nhiều người sử dụng địa chỉ cao 0x0c0c0c0c. Cần chú ý khi sử dụng địa chỉ cao 0x0c0c0c0 không mang lại nhiều ý nghĩa về độ tin cậy cho khai thác của chúng ta mà ngược lại nó sẽ khiên chúng ta gặp phải một số vấn đề phức tạp. Để sử dụng địa chi 0x0c0c0c0c của chúng ta thì đồng nghĩa với việc chúng ta sẽ phải cấp phát một lượng dữ liệu lớn hơn gấp nhiều so với việc sử dụng ở địa chỉ thấp hơn. Khi đó việc cấp phát và truy xuất cũng sẽ tốn time hơn rất nhiều làm cho trình duyệt có thể bị treo hoặc rất chậm. Có thể một số người dùng sẽ nghi ngờ và kill process trước khi shellcode được thực thi.
Tuy nhiên việc sử dụng 0x0c0c0c0c trong một số trường hợp cụ thể sẽ mang lại cho chúng ta một số lợi ích nhất định.
2. Đặt vấn đề
Như đã thấy Heap Spray là một kỹ thuật cung cấp payload rất hiệu quả và nó được thực hiện rất dễ dàng do cơ chế mà trình duyệt cung cấp. Vì vậy nó rất trở nên rất phổ biến trong các khai thác lỗi của các trình duyệt. Và gần như hầu hết các khai thác lỗ hổng bảo mật của các browser có sử dụng kỹ thuật này.
Để ngăn chặn lại và bảo vệ bộ nhớ chống các khai thác BOF nói chung và Heap Spray nói riêng thì MS đưa ra cơ chế DEP (Data Execution Prevention). Là một cơ chế ngăn chặn việc thực thi code trên một số vùng nhớ được đánh dấu. DEP có trong Windows XP và các version sau này của Windows với Windows 7 DEP được enable mặc định.
Với DEP enable sẽ ngăn không cho chúng ta thực thi code trên vùng nhớ heap mà chúng ta vừa cấp phát. Vì vậy sau khi kiểm soát EIP và đưa trỏ về địa chỉ chuỗi NOP của chúng ta thì cũng không thể thực hiện được các lệnh NOPs để đến đầu shellcode. Vì vậy phương pháp khai thác Heap Spray thông thường không cho kết quả khi chạy trên các OS Windows có DEP enable.
Một phương pháp phổ biến được sử dụng để bypass DEP là sử dụng chuỗi ROP (ROP chain). Chúng ta có thể cấp cấp phát ROP chain như một phần của Heap Spray. Và vấn đề ở đây là chúng ta phải return về chính xác điểm bắt đầu của chuỗi ROP hoặc return đên một ROP Nop sled được đặt ở trước chuỗi ROP. Điều đó đồng nghĩa với địa chỉ dự đoán của chúng ta chính là điểm bắt đầu của ROP chain.
3. Giải pháp
Như đã trình bày ở trên để bypass được cơ chế DEP của Windows thì ta sử dụng cơ kỹ thuật ROP. Và mục tiêu là xác định một địa chỉ dự đoán được trên vùng nhớ chúng ta đã cấp phát mà đảm bảo chắc chắn là mỗi lần ta thực hiện heap spray thì địa chỉ đó luôn là điểm bắt đầu của ROP chain.
Để thực hiện được việc này chúng ta cần hiểu rõ cách cấp phát một vùng nhớ trên bộ nhớ heap của OS (Để hiểu rõ hơn về việc cấp phát này các bạn có thể tìm hiểu ở trong nhiều tài liệu chi tiết hơn liên quan đến việc cấp phát trên vùng nhớ heap của OS. Vì đây là một chủ đề lớn và gồm nhiều vấn đề nên không được trình bày trong khuôn khổ của bài viết này).
Ở đây chúng ta chỉ lưu tâm đến một số vấn đề cần thiết về việc cấp phát vùng nhớ heap như sau:
Để thực hiện được điều này ta thực hiện các bước sau:
Quan sát bộ nhớ sau khi được heap spray với các khối đã chuẩn bị sẵn ta nhận thấy do các khối chúng ta cấp phát sử dụng hàm hệ thống VirtualAllocEx() nên luôn có địa chỉ bắt đầu là 0x????0018. Và các block của chúng ta là 0x1000 bytes nên trong một block chưa địa chỉ 0x0c0c0c0c thì khoảng cách tương đối giữa đầu của khối và địa chỉ 0x0c0c0c0c là xác định và có thể tính chính xác được. Mà trong mỗi block thì khoảng cách từ đầu của block đó đến đầu Shellcode thuộc block đó cũng là cố định không đổi. Điều đó có nghĩa là trong block chứa địa chỉ 0x0c0c0c0c (địa chỉ mà chúng ta sẽ dự đoán dể jump đến sau khi điều khiển được EIP) thì khoảng cách từ 0x0c0c0c0c đến đầu của Shellcode tương ứng của khối đó là hoàn toàn xác định. Vì vậy ta có thể lợi dụng việc này để tính toán và dịch chuyển điểm bắt đầu của shellcode về đúng địa chỉ 0x0c0c0c0c mong muốn.
Cuối cùng bằng cách này chúng ta có thể kiểm soát được địa chỉ của điểm bắt đầu của một shellcode hay là một Rop chain. Điều này là cần thiết để ta có thể bypass DEP ở trên các OS Windows.
Chúng ta có thể nhận thấy được rằng trong các khai thác lỗ hổng bảo mật gần đây có sử dụng nhiều đến kỹ thuật Heap Spray.
Heap Spray không phải là một cách để khai thác lỗ hổng bảo mật. Đây là một kỹ thuật được sử dụng để cấp phát payload được sử dụng như một phần của một khai thác lỗ hổng. Heap Spray tận dụng lợi thế là cho phép đặt shellcode ở một vị trí bất kỳ (có thể dự đoán được) trong bộ nhớ và sau đó “jump” về lại địa chỉ đó để thực thi đoạn shellcode.
Để một Heap Spray có thể làm việc được thì cần phải được quyền cấp phát và điền vào các chunks trong bộ nhớ heap trước ghi chiếm được quyền điều khiển thanh ghi EIP.
Các browser (trình duyệt) cung cấp một cơ chế dễ dàng cho việc này. Nó được hỗ trợ bởi các ngôn ngữ script (javascript hay vbscript…) để cấp phát một vài thứ vào trong bộ nhớ trước khi gây ra lỗi. Điều này không có nghĩa là chỉ có khai thác các lỗ hổng của trình duyệt mới sử dụng được kỹ thuật này. Một số app cũng cho phép sử dụng javascript hay ActionScript như trong Adobe Reader… Có nghĩa là chúng ta có thể sử dụng kỹ thuật này trong nhiều trường hợp chỉ cần thỏa mãn yêu cầu là có một cơ chế cho phép chúng ta cấp phát dữ liệu trong bộ nhớ ở một vị trí có thể dự đoán được trước khi gây ra bug và kiểm soát EIP.
Để thực hiện Heap Spray cần phải làm các bước:
- Spray Heap
- Gây ra bug
- Kiểm soát EIP và đưa nó trực tiếp về Heap.
Cần lưu ý trong Windows ban đầu khi chúng ta cấp phát một lượng nhỏ dữ liệu lên bộ nhớ heap, nó có thể nằm ở bất kỳ vị trí nào trong bộ nhớ heap (do phân mảnh hay do một nguyên nhân nào đó). Điều này gây khó khăn cho việc dự đoán địa chỉ để jump của chúng ta. Tuy nhiên khi cấp phát dữ liệu lên các địa chỉ cao hơn trong bộ nhớ heap thì khối dữ liệu được cấp phát sẽ tập trung và rơi vào các chunks liền kề trong bộ nhớ như hình dưới
Ngoài ra chúng ta để đảm bảo có thể jump về địa chỉ chứa NOP (để thực hiện shellcode từ đầu) mà không phải là một vị trí nằm giữa shellcode thì chúng ta cần cấp phát các bytes NOP lớn gấp nhiều lần so với kích thước của shellcode. Việc này đảm bảo cho khả năng jump đến địa chỉ chứa NOP sẽ cao hơn và đáng tin cậy hơn.
Sau khi thực hiện kỹ thuật heap spray và bố trí data lên heap chúng ta sẽ gây ra bug để chiếm quyền điều khiển EIP và cho trỏ về đúng địa chỉ dự đoán trên heap và cho chương trình thực hiện. Các địa chỉ dự đoán thường được sử dụng là:
0x05050505
0x06060606
0x07070707
…
0x0a0a0a0a
0x0b0b0b0b
0x0c0c0c0c
…
Trước đây trong các khai thác heap spray thường sử dụng địa chỉ 0x05050505 hay 0x06060606. Tuy nhiên trong các khai thác gần đây chúng ta thường thấy nhiều người sử dụng địa chỉ cao 0x0c0c0c0c. Cần chú ý khi sử dụng địa chỉ cao 0x0c0c0c0 không mang lại nhiều ý nghĩa về độ tin cậy cho khai thác của chúng ta mà ngược lại nó sẽ khiên chúng ta gặp phải một số vấn đề phức tạp. Để sử dụng địa chi 0x0c0c0c0c của chúng ta thì đồng nghĩa với việc chúng ta sẽ phải cấp phát một lượng dữ liệu lớn hơn gấp nhiều so với việc sử dụng ở địa chỉ thấp hơn. Khi đó việc cấp phát và truy xuất cũng sẽ tốn time hơn rất nhiều làm cho trình duyệt có thể bị treo hoặc rất chậm. Có thể một số người dùng sẽ nghi ngờ và kill process trước khi shellcode được thực thi.
Tuy nhiên việc sử dụng 0x0c0c0c0c trong một số trường hợp cụ thể sẽ mang lại cho chúng ta một số lợi ích nhất định.
2. Đặt vấn đề
Như đã thấy Heap Spray là một kỹ thuật cung cấp payload rất hiệu quả và nó được thực hiện rất dễ dàng do cơ chế mà trình duyệt cung cấp. Vì vậy nó rất trở nên rất phổ biến trong các khai thác lỗi của các trình duyệt. Và gần như hầu hết các khai thác lỗ hổng bảo mật của các browser có sử dụng kỹ thuật này.
Để ngăn chặn lại và bảo vệ bộ nhớ chống các khai thác BOF nói chung và Heap Spray nói riêng thì MS đưa ra cơ chế DEP (Data Execution Prevention). Là một cơ chế ngăn chặn việc thực thi code trên một số vùng nhớ được đánh dấu. DEP có trong Windows XP và các version sau này của Windows với Windows 7 DEP được enable mặc định.
Với DEP enable sẽ ngăn không cho chúng ta thực thi code trên vùng nhớ heap mà chúng ta vừa cấp phát. Vì vậy sau khi kiểm soát EIP và đưa trỏ về địa chỉ chuỗi NOP của chúng ta thì cũng không thể thực hiện được các lệnh NOPs để đến đầu shellcode. Vì vậy phương pháp khai thác Heap Spray thông thường không cho kết quả khi chạy trên các OS Windows có DEP enable.
Một phương pháp phổ biến được sử dụng để bypass DEP là sử dụng chuỗi ROP (ROP chain). Chúng ta có thể cấp cấp phát ROP chain như một phần của Heap Spray. Và vấn đề ở đây là chúng ta phải return về chính xác điểm bắt đầu của chuỗi ROP hoặc return đên một ROP Nop sled được đặt ở trước chuỗi ROP. Điều đó đồng nghĩa với địa chỉ dự đoán của chúng ta chính là điểm bắt đầu của ROP chain.
3. Giải pháp
Như đã trình bày ở trên để bypass được cơ chế DEP của Windows thì ta sử dụng cơ kỹ thuật ROP. Và mục tiêu là xác định một địa chỉ dự đoán được trên vùng nhớ chúng ta đã cấp phát mà đảm bảo chắc chắn là mỗi lần ta thực hiện heap spray thì địa chỉ đó luôn là điểm bắt đầu của ROP chain.
Để thực hiện được việc này chúng ta cần hiểu rõ cách cấp phát một vùng nhớ trên bộ nhớ heap của OS (Để hiểu rõ hơn về việc cấp phát này các bạn có thể tìm hiểu ở trong nhiều tài liệu chi tiết hơn liên quan đến việc cấp phát trên vùng nhớ heap của OS. Vì đây là một chủ đề lớn và gồm nhiều vấn đề nên không được trình bày trong khuôn khổ của bài viết này).
Ở đây chúng ta chỉ lưu tâm đến một số vấn đề cần thiết về việc cấp phát vùng nhớ heap như sau:
- Khi chúng ta yêu cầu cấp phát một vùng nhớ heap để lưu trữ một biến (Ex: string) thì OS xem xét dựa trên kích thước của biến để yêu cầu cấp phát một vùng nhớ phù hợp (có kích thước lớn hơn kích thước yêu cầu của biến).
- Khi yêu cầu cấp phát một vùng nhớ lớn hơn hoặc bằng 512KB (>= 0x80000 bytes) thì việc cấp phát sẽ được thực hiện bởi hàm VirtuaAllocEX() của hệ thống.
- Khối được cấp phát bởi hàm luôn là bội của một page 64KB (= 10000 bytes) giúp cho HĐH dễ dàng xác định địa chỉ của các vùng nhớ được cấp phát.
Để thực hiện được điều này ta thực hiện các bước sau:
- Chuẩn bị các khối nhớ gồm 0x1000 bytes gồm NOP + Shellcode.
- Lặp lại các khối nhớ này đến khi đạt được kích thước 512KB (= 800000 bytes).
- Thực hiện Heap Spray (Cấp phát một mảng gồm các khối dữ liệu giống nhau đã chuẩn bị vào bộ nhớ)
Quan sát bộ nhớ sau khi được heap spray với các khối đã chuẩn bị sẵn ta nhận thấy do các khối chúng ta cấp phát sử dụng hàm hệ thống VirtualAllocEx() nên luôn có địa chỉ bắt đầu là 0x????0018. Và các block của chúng ta là 0x1000 bytes nên trong một block chưa địa chỉ 0x0c0c0c0c thì khoảng cách tương đối giữa đầu của khối và địa chỉ 0x0c0c0c0c là xác định và có thể tính chính xác được. Mà trong mỗi block thì khoảng cách từ đầu của block đó đến đầu Shellcode thuộc block đó cũng là cố định không đổi. Điều đó có nghĩa là trong block chứa địa chỉ 0x0c0c0c0c (địa chỉ mà chúng ta sẽ dự đoán dể jump đến sau khi điều khiển được EIP) thì khoảng cách từ 0x0c0c0c0c đến đầu của Shellcode tương ứng của khối đó là hoàn toàn xác định. Vì vậy ta có thể lợi dụng việc này để tính toán và dịch chuyển điểm bắt đầu của shellcode về đúng địa chỉ 0x0c0c0c0c mong muốn.
Cuối cùng bằng cách này chúng ta có thể kiểm soát được địa chỉ của điểm bắt đầu của một shellcode hay là một Rop chain. Điều này là cần thiết để ta có thể bypass DEP ở trên các OS Windows.
Chỉnh sửa lần cuối bởi người điều hành: