MemShell huyền bí và nơi tìm ra chúng

Trong quá trình giám sát, rà soát hệ thống, mỗi khi nhắc đến webshell thông thường chúng ta sẽ ngay lập tức truy tìm các sự kiện liên quan đến tạo file, hoặc các request đến các file được nghi ngờ là webshell. Tiếp theo dựa trên các dấu hiệu như tên file, các command được gửi đến file,… Hay remote trực tiếp đến server nơi chứa file nghi ngờ để phân tích nội dung của file rồi đưa ra kết luận chính xác một file có phải là webshell.

Tuy nhiên, gần đây các researcher, red teamer trên thế giới đã phát triển và sử dụng rộng rãi một kỹ thuật với mục đích ẩn mình, che giấu những vết tích mà webshell để lại, làm cho blue team gặp nhiều khó khăn trong quá trình phân tích. Kỹ thuật đó có tên gọi là Memoryshell.

Vậy với vai trò của người làm blueteam làm sao để phát hiện, ngăn chặn memory shell đây?

Lưu ý: Nghiên cứu trong bài viết chỉ được thực hiện trên ngôn ngữ lập trình Java.

 

Tổng quan về memory shell

Trước tiên, để có thể khai thác được memoryshell thì red team phải thực thi được code trên máy chủ

  1. Khai thác thành công lỗ hổng upload file tùy ý
  2. Khai thác dựa trên lỗ hổng Deserialize
  3. Từ một lỗ hổng RCE, có thể drop file lên server một cách tùy ý

So sánh webshell thông thường và Memshell

Sự khác biệt giữa webshell và memshell

 

Đối với webshell thông thường, file shell cần phải tồn tại trên server và người tấn công cần phải truy cập được đến file. Nếu file bị xóa hoặc đổi tên, đổi đuôi file thì sẽ không thể sử dụng được nữa.

Còn đối với memshell, sau khi xác định lỗ hổng upload file tồn tại trên server. Người tấn công chỉ cần upload và truy cập vào một lần duy nhất với mục đích init, inject thẳng vào trong memory. Sau đó hoàn toàn có thể xóa đi file shell init được up lên. Thậm chí, nếu tồn tại lỗ hổng deserialize người tấn công còn không cần file upload một file shell init.

Vậy nên việc phát hiện trở nên rất khó khăn.

Để hiểu chi tiết về các bước xây dựng và tấn công memshell mọi người có thể đọc ở đây: Kỹ thuật Memory Webshell trong các đợt khai thác Redteam – VNPT Cyber Immunity.

Sau khi đã đọc xong và hiểu về cách mà người tấn công (Red Team) sử dụng memshell. Thì dưới góc độ là người phòng thủ (Blue Team) điều chúng ta cần quan tâm là gì?

 

Điều gì xảy ra trong memory khi hệ thống bị tấn công bằng memshell?

Đối với memshell những gì diễn ra trên website, request, respone chỉ là phần nổi của tảng băng chìm.

Dưới góc độ của blue team cần quan tâm điều gì thực sự xảy ra trong memory khi người tấn công inject shell. Nếu unload shell thì có thể khôi phục lại hiện trạng để điều tra không?

Đầu tiên, sử dụng dumpit để dump memory của server. Sau đó sử dụng volatility để thấy được các tiến trình

Trước khi inject shell

Sau khi inject shell và có những action

 

Chúng ta chỉ có thể nhìn thấy các tiến trình như cmd, ping, whoami nếu dump trong điều kiện lý tưởng là đúng thời điểm người tấn công chạy lệnh.Tuy nhiên điều này không khả thi cho lắm. Vì việc dump đúng thời điểm attacker chạy lệnh là rất khó.

Để rà soát memshell chúng ta cần phải dump tiến trình java.exe ra và phân tích.

Sẽ có 2 trạng thái có thể xảy ra trong memory:

  • Attacker inject shell vào memory nhưng chưa unload
  • Attacker inject shell vào memory và unload ngay sau đó

Ở cả hai trạng thái này, đề có thể điều tra theo hướng tìm các hàm mà attacker sử dụng để inject shell và các hàm dùng để unload shell. Dưới đây mình sẽ so sánh hai trạng thái là trước và sau khi thực hiện unload shell xem liệu các dấu vết hay shell có  bị mất đi hay không.

Đầu tiên, đối với vector tấn công inject vào Listener, ta sẽ tìm theo addApplicationEventListener(), setApplicationEventListener()

Dấu hiệu inject Listener Shell

 

Đối với Filter, attacker bắt buộc phải inject vào FilterConfig, FilterDefs, FilterMaps, setFilterName, setFilterClass, setFilter, addFilterDef. Chúng ta sẽ tìm theo những thuộc tính này:

Dấu hiệu inject Filter Shell

 

Đối với Servlet, attacker inject vào hai hashmap là ServletMapings, children, các hàm setServletName, setServlet, setServletClass, addServletMapping, addChild:

Dấu hiệu inject Servlet Shell

 

Dựa vào các dấu vết là các phương thức có thể thấy tên các class được inject vào. Tuy nhiên chưa chắc chắn 100% là shell vì cũng có thể đó là các listener, filter, servlet mà dev thêm vào. Nên từ các tên class chúng ta sẽ đi verify lại trong memory.

Với những ảnh mình show bên trên ở cả hai trạng thái lúc inject shell và sau khi đã unload shell thì dấu hiệu  inject shell và thậm chí cả nội dung của shell đều vẫn còn tồn tại trong Memory.

Shell tồn tại trên memory ngay cả khi đã thực hiện unload

 

Những khó khăn trong quá trình giám sát

Để mô tả những khó khăn trong quá trình giám sát mình có triển khai một mô hình thử nghiệm như sau:

Như đã trình bày ở trên, với các vector tấn công của attacker:

Nếu website không có lỗi deserialize thì attacker buộc phải up môt file shell lên để inject. Nên hoàn toàn có thể check log liên quan đến file create, các tên file bất thường.

Thậm chí trước khi upload file inject memshell, attacker có thể đã upload và thực thi một số file shell thông thường. Hoàn toàn có thể tìm kiếm các command trong access log, hoặc syslog.

Tuy nhiên, vì file shell upload nhằm mục đích inject chỉ được up lên và kích hoạt đúng một lần. Ngoài ra, nếu attacker tận dụng lỗ hổng deserialize thì không cần dùng đến file shell inject này. Nên việc phát hiện là vô cùng khó khăn.

Request với đường dẫn thông thường

Có thể thấy tên file rất bình thường, thậm chí với bất kì đường dẫn nào thì shell vẫn có thể được kích hoạt do đã inject vào memory.

Việc dump tiến trình và điều tra memory thường được thực hiện sau khi hệ thống đã bị tấn công thì lúc này attacker đã tấn công và đạt được mục đích của mình.

Có cách nào để phát hiện sớm để cảnh báo hay ngăn chặn việc tấn công dưới hình thức memshell này không?

 

Phát hiện và cảnh báo ngay khi MemoryShell được inject

Để thực hiện điều này, trước tiên ta sẽ nghiên cứu cách hoạt động của công cụ scan memory shell.

Việc cần làm đó chính là upload một file jsp và rà quét memory BaciHanma/MemoryShell-Scanner (github.com)

 

Giao diện của tool scanner

 

Đối với Listner, attacker sẽ inject vào với addApplicationEventListener():

Phương thức được attacker sử dụng để inject listener shell

 

StandardContext cũng có một phương thức nữa là getApplicationEventListeners():

Phương thức sử dụng để lấy các class Listener

 

Ta sẽ lấy được list các Listener đang được load trong memory:

List các class Filter trong memory

 

Có thể thấy ListenerShell_jsp$ListenerShell rất đáng nghi. Hoàn toàn có thể dump class này ra và kiểm tra nội dung. Nếu đúng là MemShell chúng ta có thể unload:

Code dump byte class trong memory

 

Trên đây là cách để dump class. Có thể dump được cả Listener, Filter và Servlet. Sau khi xác định shell hoàn toàn có thể unload như sau:

Unload Listener Shell

 

Tiếp theo là Filter, để thêm được custom filter attacker cần phải thêm vào filterDefs, filterConfig và filterMaps.

Vậy thì chúng ta sẽ lấy ra từng fields này ra với Java Reflection.

Lấy từng filtermaps

 

Lấy các filterconfig

 

Lấy các filterDef

 

Danh sách các class Filter trong memory

 

Để unload filter shell thì Filter cũng đã có sẵn phương thức removeFilterDefremoveFilterMap chỉ gọi đến và truyền vào filterdeffiltermap muốn xóa là đã có thể unload.

Unload Filter shell với các phương thức có sẵn

 

Cuối cùng là servlet, attacker sẽ inject vào hashmap childrenservletMappings. Thì ta cũng sẽ lấy các thông tin này trong memory bằng java reflection.

Lấy các hashmap children trong memory

 

Lấy các servletMappings trong memory

 

Danh sách các Servet trong memory

 

Khi đã có toàn bộ các servlet trong memory có thể unload với các phương thức mà servlet có sẵn là removeChildremoveServletMapping, chỉ cần truyền urlpattern, hashmap children muốn xóa là đã có thể unload:

Unload Servlet shell với removeChild và removeServletMapping

 

Từ nguyên lý này, ta sẽ tìm hiểu cách tạo cảnh báo ngay khi có servlet mới được load.

 

Cấu hình logging properties

Bất kỳ hệ thống giám sát nào cũng sử dụng đầu vào là các nhật ký sự kiện hệ thống hoặc ứng dụng (log). Như vậy, ta sẽ cấu hình riêng 1 tệp log mới, có nhiệm vụ ghi lại sự kiện mỗi khi có servlet mới được load (hoặc unload).

Đầu tiên hãy cùng xem log mặc định của tomcat có gì:

Log mặc định của tomcat

 

Có thể thấy thông tin quá ít, chỉ có các sự kiện liên quan đến deploy. Để có thêm thông tin cần phải sửa file logging.properties trong config của apache tomcat.

File cấu hình logging.properties

 

Ở đây mình đã cấu hình để tomcat log thêm các sự kiện liên quan đến Catalina. Sau đó cấu hình chi tiết để tomcat log hoạt động của từng class như StandardService, StandardContext, StandardWrapper.

Ngay sau khi thêm những class cần monitor, log đã có thêm sự kiện readObject() và filter start

Log tomcat sau khi cấu hình File cấu hình logging.properties

 

Ở đây mình thực nghiệm với Filter, đối tượng hàng đầu bị nhắm đến mỗi khi inject. Còn đối với Lister và Servlet cần có cách cấu hình khác.

Mỗi khi filter start hàm FilterStart được gọi và trong hàm này trong lib đã có sẵn đoạn ghi log:

Code log mặc định của filter

 

Và sau khi inject Filter thì attacker cần phải regenerate lại filterConfig bằng StandardContext#filterStart() nên sẽ có log, còn đối với Listener và Servlet thì không

Để làm được điều này và có thêm thông tin chi tiết về class được inject vào chúng ta có thể kết hợp scan memory và log4j để dump class ra log. Và việc này cần phải thực hiện trước khi deploy dịch vụ

Như đoạn code dưới đây mình sẽ lấy thông tin các filter trong memory và sử dụng log.info để ghi những class này vào log dưới dạng base64. Đối với servlet và listener cũng có thể làm tương tự.

 

Sau khi ghi vào trong log, mình dựng demo IBM Qradar Community để tiến hành đẩy log về:

Log đẩy về Qradar

 

Sau đó kết hợp với log được cấu hình ở logging.properties để cảnh báo sớm cho đội giám sát:

Xây dựng rule dựa trên log kết hợp và sinh cảnh báo

 

Alert về telegram

 

Trên đây mình đã trình bày cách phòng chống và phát hiện sớm phương thức tấn công memory shell. Trong nội dung bài viết này, mình mới chỉ giới thiệu về cách phát hiện memoryshell trong java cụ thể hơn là servlet, với các kỹ thuật memshell trong ngôn ngữ khác hoặc sử dụng các chain khác trong java, các bạn có thể tự tìm hiểu và phát triển cách thức riêng. Các kỹ thuật khó hơn mình xin dành để ở phần sau. Cảm ơn mọi người đã đọc!

 

Nguồn tham khảo

 

By,

Blue_Team

1.102 lượt xem