---
# System prepended metadata

title: OpenVas (report part 2)

---

# OpenVas (report part 2)

Part 1 : https://hackmd.io/qEfm_SxgTtGZMnYm63smWw

## 1. Điều chỉnh tốc độ quét 

OpenVAS **không hỗ trợ cấu hình trực tiếp tham số “requests per second”**. 
Dưới đây là các cách gián tiếp để kiểm soát tốc độ quét : 

### 1.1 Điều chỉnh tham số `max_hosts` và `max_checks`
- `max_hosts`: Số lượng host được quét đồng thời. Giá trị mặc định là 20.
- `max_checks`: Số lượng kiểm tra (NVTs) chạy đồng thời trên mỗi host. Giá trị mặc định là 4.
- **Cách thực hiện**:
    - Khi tạo new task để scan thì hãy điều chỉnh 2 tham số `Maximum concurrently executed NVTs per host` tương ứng với `max_checks` và `Maximum concurrently scanned hosts` tương ứng với `max_hosts`.
![image](https://hackmd.io/_uploads/S1WPGijPxg.png)

### 1.2 Điều chỉnh các tham số khác ảnh hưởng đến tốc độ quét

#### Điều chỉnh `checks_read_timeout`
- **Mô tả**: thời gian chờ phản hồi từ mục tiêu, mặc định là 5 giây.
- **Điều chỉnh tại**: Configuration > Scan Configs > Preferences.
- **Áp dụng khi**:
    - Giảm `checks_read_timeout` (ví dụ: 2 giây): Tăng req/s, phù hợp cho quét mạng LAN với phản hồi nhanh, ưu tiên tốc độ.
    - Tăng `checks_read_timeout` (ví dụ: 10-15 giây): Giảm req/s, phù hợp cho quét qua Internet, mạng chậm, hoặc máy chủ phản hồi trễ.

#### Điều chỉnh `optimize_test`
- **Mô tả**: Chỉ chạy NVTs phù hợp với dịch vụ được phát hiện.
- **Ảnh hưởng**: Bật chế độ này giảm số kiểm tra không cần thiết, tăng tốc độ quét bằng cách tập trung vào các kiểm tra liên quan.
- **Cách điều chỉnh**: Configuration > Scan Configs > Preferences.

#### Điều chỉnh `time_between_request`
- **Mô tả**: Thời gian chờ giữa các req (mili giây), mặc định là 0 mili giây.
- **Ảnh hưởng**: Giảm giá trị (ví dụ: 0-50ms) tăng req/s; tăng giá trị (ví dụ: 100-500ms) giảm req/s, giúp tránh quá tải mục tiêu hoặc bị chặn bởi WAF/IDS.
- **Cách điều chỉnh**: Configuration > Scan Configs > Preferences.

####  Điều chỉnh `alive_test`

- **Mô tả**: Kiểm tra host sống trước khi quét (ví dụ: ICMP ping hoặc TCP SYN).
- **Ảnh hưởng**: Bật chế độ này giảm req/s không cần thiết, tăng hiệu quả quét bằng cách chỉ quét các host phản hồi.
- **Cách điều chỉnh**: Configuration > Scan Configs > Alive Test trong GSA.

#### Điều chỉnh plugins_timeout
- **Mô tả**: Thời gian chạy tối đa của mỗi plugin (NVT).
- **Ảnh hưởng**: Giảm giá trị (ví dụ: dưới 5 giây) tăng tốc độ quét bằng cách bỏ qua các plugin mất quá nhiều thời gian, nhưng có thể bỏ sót kết quả; tăng giá trị (ví dụ: 10-20 giây) giảm tốc độ nhưng đảm bảo hoàn thành kiểm tra.
- **Cách điều chỉnh**: Configuration > Scan Configs > Preferences trong GSA.
## 2. Quét CVE cụ thể trong OpenVAS

Không hỗ trợ, nếu muốn quét theo CVE em đề xuất nuclei 😁

## 3. Quét services / protocol

- Hỗ trợ quét rất nhiều loại giao thức (protocol) và dịch vụ (services) như HTTP, FTP, SSH, SMB, DNS, SNMP, SMTP, POP3, IMAP, MySQL, RDP, v.v.**Tuy nhiên**, khả năng giới hạn quét theo từng dịch vụ cụ thể phụ thuộc vào :

#### Cách quét theo từng loại dịch vụ

**Hiện OpenVAS không cung cấp tuỳ chọn trực tiếp để chỉ định protocol/service cụ thể khi quét**, tuy nhiên có thể gián tiếp giới hạn bằng cách : 
- Tự điều chỉnh bằng cách bỏ chọn các NVT không liên quan trong Scan Config. 
- **Lưu ý**: Theo thử nghiệm test tay của em thì việc này khá khó khăn tốn nhiều công sức mức độ tùy biến không cao, phải biết **keywords** phù hợp với loại service mình muốn lọc thì mới chọn NVT đúng được. 

## 4. Tự động hóa tác vụ quét để pipe với output từ nmap

### Bước 1 : Thực hiện quét nmap
![image](https://hackmd.io/_uploads/SymvU1Tvxl.png)

Đầu tiên, chúng ta sử dụng Nmap để quét khám phá các máy chủ và dịch vụ đang hoạt động trong mạng. Tùy chọn -oX là cực kỳ quan trọng vì nó sẽ lưu kết quả ra một file XML.

### Bước 2 : Script Python xử lý output Nmap và điều khiển OpenVAS/GVM

Script Python này sẽ thực hiện những tác vụ sau : 

#### 1. Phân tích (Parse) file XML:
   
- Nó sẽ duyệt qua file để trích xuất thông tin quan trọng:
    - Địa chỉ IP của các host có trạng thái up (đang hoạt động).
    - Danh sách các cổng (ports) có trạng thái open cho từng host.
#### 2. Kết nối với GVM
#### 3. Tạo tác vụ quét động:
- Với mỗi host được tìm thấy từ Nmap, script sẽ tự động thực hiện các hành động sau trên GVM:
    - Tạo Port List Động: Tạo một danh sách cổng (Port List) mới chỉ chứa những cổng đang mở của chính host đó. Điều này giúp OpenVAS quét một cách tập trung và nhanh chóng.
    - Tạo Target Động: Tạo một mục tiêu (Target) mới trỏ đến IP của host và gán nó với Port List vừa tạo.
    - Khởi chạy Tác vụ Quét (Scan Task): Tạo một tác vụ quét (Task) mới cho Target này, sử dụng một cấu hình quét có sẵn (ví dụ: "Full and fast") và bắt đầu chạy nó ngay lập tức.
    - Chờ cho mỗi tác vụ quét hoàn thành.Sau khi hoàn thành, tự động tải xuống báo cáo kết quả dưới dạng file XML.
- **Lưu ý** : Ở đây vì sao không tạo Scan Config động tương thích vì việc tạo scan config cần hiệu chỉnh NVTs đây là một công việc khó, em nghĩ nên thực hiện manual sẵn (ví dụ có port 22 thì đã có scan task SSH được tạo manual trước ) từ trước nếu muốn và có thể tinh chỉnh lại code python để phù hợp sau. 
```
import datetime
import os
import time
from concurrent.futures import ThreadPoolExecutor
from xml.etree import ElementTree

from gvm.connections import UnixSocketConnection
from gvm.protocols.gmp import Gmp
from gvm.transforms import EtreeTransform

def parse_nmap_xml(file_path):
    """Phân tích file XML từ Nmap để lấy danh sách IP và các cổng mở tương ứng."""
    if not os.path.exists(file_path):
        print(f"Lỗi: File {file_path} không tồn tại!")
        return {}
    try:
        tree = ElementTree.parse(file_path)
        root = tree.getroot()
        hosts = {}
        
        for host in root.findall('.//host'):
            status_element = host.find('status')
            if status_element is not None and status_element.get('state') == 'up':
                ip_element = host.find("address[@addrtype='ipv4']")
                if ip_element is None:
                    continue
                ip = ip_element.get("addr")
                
                ports = []
                for port_element in host.findall('.//port'):
                    state_element = port_element.find('state')
                    if state_element is not None and state_element.get('state') == 'open':
                        ports.append(port_element.get('portid'))

                if ports:
                    hosts[ip] = ports
        return hosts
    except ElementTree.ParseError as e:
        print(f"Lỗi phân tích file XML: {e}")
        return {}

def get_gvm_resource_id_by_name(gmp, resource_type_func, resource_name):
    """Hàm trợ giúp chung để lấy ID tài nguyên bằng tên."""
    try:
        get_func = getattr(gmp, resource_type_func)
        resources = get_func()
        resource_tag = resource_type_func.split('_')[-1][:-1]
        for resource in resources.findall(resource_tag):
            if resource.find('name').text == resource_name:
                return resource.get('id')
        print(f"Cảnh báo: Không tìm thấy tài nguyên '{resource_name}'")
        return None
    except Exception as e:
        print(f"Lỗi khi lấy ID cho '{resource_name}': {e}")
        return None

def process_and_scan_host(host_ip, open_ports, scanner_id, config_id, user, password):
    """Hàm worker: Tạo Port List, Target động cho MỘT host và bắt đầu quét."""
    try:
        connection = UnixSocketConnection()
        with Gmp(connection, transform=EtreeTransform()) as gmp:
            gmp.authenticate(user, password)
            timestamp = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
            
            port_list_name = f"Ports for {host_ip} ({timestamp})"
            port_range = "T:" + ",".join(open_ports)
            port_list_resp = gmp.create_port_list(name=port_list_name, port_range=port_range)
            port_list_id = port_list_resp.get('id')
            if not port_list_id:
                print(f"[{host_ip}] Lỗi khi tạo Port List.")
                return None
            print(f"[{host_ip}] Đã tạo Port List: {port_list_name}")

            target_name = f"Target for {host_ip} ({timestamp})"
            target_resp = gmp.create_target(name=target_name, hosts=[host_ip], port_list_id=port_list_id)
            target_id = target_resp.get('id')
            if not target_id:
                print(f"[{host_ip}] Lỗi khi tạo Target.")
                return None
            print(f"[{host_ip}] Đã tạo Target: {target_name}")

            task_name = f"Scan for {host_ip} ({timestamp})"
            task_resp = gmp.create_task(
                name=task_name,
                config_id=config_id,
                target_id=target_id,
                scanner_id=scanner_id
            )
            task_id = task_resp.get("id")
            
            gmp.start_task(task_id)
            print(f"[{host_ip}] ĐÃ BẮT ĐẦU QUÉT (Task ID: {task_id})")
            return task_id

    except Exception as e:
        print(f"[{host_ip}] Lỗi nghiêm trọng khi xử lý: {e}")
        return None

def monitor_and_get_report(task_id, user, password):
    """Theo dõi một tác vụ cho đến khi hoàn thành và tải về báo cáo."""
    if not task_id:
        return
        
    print(f"[Task: {task_id}] Bắt đầu theo dõi...")
    
    # UUID cho định dạng báo cáo XML. Đây là định dạng tốt nhất để xử lý tự động.
    XML_REPORT_FORMAT_ID = "a994b278-1f62-11e1-96ac-406186ea4fc5"
    
    try:
        connection = UnixSocketConnection()
        with Gmp(connection, transform=EtreeTransform()) as gmp:
            gmp.authenticate(user, password)
            while True:
                task_status_resp = gmp.get_task(task_id)
                status_node = task_status_resp.find('.//status')
                
                if status_node is None:
                    print(f"[Task: {task_id}] Không thể lấy trạng thái. Dừng theo dõi.")
                    break
                    
                status = status_node.text
                progress = task_status_resp.find('.//progress').text
                
                print(f"[Task: {task_id}] Trạng thái: {status} - Tiến trình: {progress}%")

                if status == "Done":
                    print(f"[Task: {task_id}] Quét hoàn tất! Đang tải báo cáo...")
                    
                    # Lấy report_id từ kết quả của tác vụ đã hoàn thành
                    report_id_node = task_status_resp.find('.//report')
                    if report_id_node is None:
                        print(f"[Task: {task_id}] Lỗi: Không tìm thấy report ID trong kết quả.")
                        break
                    report_id = report_id_node.get('id')
                    
                    # Tải báo cáo
                    report_resp = gmp.get_report(report_id, report_format_id=XML_REPORT_FORMAT_ID)
                    
                    # Lưu báo cáo vào file
                    report_filename = f"report_{task_id}.xml"
                    with open(report_filename, "wb") as f:
                        f.write(ElementTree.tostring(report_resp, encoding="utf-8"))
                    print(f"[Task: {task_id}] Đã lưu báo cáo thành công vào file: {report_filename}")
                    break
                
                if status in ["Stopped", "Abended"]:
                    print(f"[Task: {task_id}] Tác vụ đã dừng với trạng thái: {status}. Ngừng theo dõi.")
                    break
                
                # Chờ 60 giây trước khi kiểm tra lại
                time.sleep(60)
    except Exception as e:
        print(f"[Task: {task_id}] Lỗi trong quá trình theo dõi hoặc tải báo cáo: {e}")

def run_optimized_pipeline(nmap_file, max_threads=5, user="admin", password="your_password_here"):
    """Hàm chính điều phối pipeline đã được tối ưu."""
    hosts_to_scan = parse_nmap_xml(nmap_file)
    if not hosts_to_scan:
        print("Không tìm thấy mục tiêu nào từ file Nmap để quét.")
        return []

    print("Đang kết nối GVM để lấy ID của Scanner và Scan Config...")
    scanner_id, config_id = None, None
    try:
        connection = UnixSocketConnection()
        with Gmp(connection, transform=EtreeTransform()) as gmp:
            gmp.authenticate(user, password)
            scanner_id = get_gvm_resource_id_by_name(gmp, 'get_scanners', "OpenVAS Default")
            config_id = get_gvm_resource_id_by_name(gmp, 'get_scan_configs', "Full and fast")
    except Exception as e:
        print(f"Lỗi kết nối GVM ban đầu: {e}")
        return []

    if not all([scanner_id, config_id]):
        print("Không lấy được ID của Scanner hoặc Config. Dừng chương trình.")
        return []
    
    print(f"Sử dụng Scanner ID: {scanner_id}")
    print(f"Sử dụng Scan Config ID: {config_id}")
    print("-" * 40)

    task_ids = []
    with ThreadPoolExecutor(max_workers=max_threads) as executor:
        future_tasks = {
            executor.submit(process_and_scan_host, ip, ports, scanner_id, config_id, user, password): ip
            for ip, ports in hosts_to_scan.items()
        }
        
        for future in future_tasks:
            task_id = future.result()
            if task_id:
                task_ids.append(task_id)

    print(f"\nHoàn tất khởi chạy. Đã tạo {len(task_ids)} tác vụ quét.")
    return task_ids

if __name__ == "__main__":
    NMAP_FILE = "nmap_output.xml"
    GVM_PASSWORD = "d4fc9eff-f8a3-4d30-9050-43c32f3311de"

    # Bước 1: Khởi chạy tất cả các tác vụ
    created_task_ids = run_optimized_pipeline(
        nmap_file=NMAP_FILE,
        max_threads=5,
        password=GVM_PASSWORD
    )
    
    # Bước 2: Theo dõi và tải báo cáo cho các tác vụ đã tạo
    if created_task_ids:
        print("\n" + "="*50)
        print("BẮT ĐẦU THEO DÕI CÁC TÁC VỤ ĐÃ TẠO")
        print("="*50)
        for task_id in created_task_ids:
            monitor_and_get_report(task_id, user="admin", password=GVM_PASSWORD)
```

Kết quả của script : 

![image](https://hackmd.io/_uploads/rkLempTPxx.png)
![image](https://hackmd.io/_uploads/Hk-EmppPee.png)

