File: scripting.md

package info (click to toggle)
gvm-tools 25.4.4-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 1,480 kB
  • sloc: python: 10,611; xml: 445; makefile: 27
file content (285 lines) | stat: -rw-r--r-- 10,155 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
(scripting)=

# Scripting

(xml-scripting)=

## XML Scripting

```{note}
XML scripting via {program}`gvm-cli` should only be considered for
simpler use cases. {ref}`Greenbone Management Protocol (GMP) or
Open Scanner Protocol (OSP) scripts <gvm-scripting>` are often more
powerful and easier to write.
```

Scripting via {program}`gvm-cli` is directly based on [GMP](https://docs.greenbone.net/API/GMP/gmp-22.04.html)
and [OSP](https://docs.greenbone.net/API/OSP/osp-22.04.html). Both protocols make
use of XML command requests and corresponding responses.

A typical example for using GMP is the automatic scan of a new
system. In the example below, it is assumed that an Intrusion Detection
System (IDS) that monitors the systems in the Demilitarized Zone (DMZ) and immediately
discovers new systems and unusual, new TCP ports is in use. If such an
event is being discovered, the IDS should automatically initiate a scan
of the new system. This can be done with the help of a script.

1. Starting point is the IP address of the new suspected system. For this IP
   address, a target needs to be created on the {term}`Greenbone Enterprise`
   Appliance.

   If the IP address is saved in the environment variable {envvar}`IPADDRESS` by
   the IDS, the respective target can be created:

```shell
> gvm-cli socket --xml "<create_target><name>Suspect Host</name><hosts>"$IPADDRESS"</hosts></create_target>"
<create_target_response status="201" status_text="OK, resource created" id="e5adc10c-71d0-49fe-aacf-a442ee31d387"/>
```

See {command}`create_target` command for all [details](https://docs.greenbone.net/API/GMP/gmp-22.04.html#command_create_target).

2. Create a task using the default *Full and Fast* scan configuration with
   UUID {token}`daba56c8-73ec-11df-a475-002264764cea` and the previously generated
   target:

```shell
> gvm-cli socket --xml "<create_task><name>Scan Suspect Host</name><target id=\"e5adc10c-71d0-49fe-aacf-a442ee31d387\"/><config id=\"daba56c8-73ec-11df-a475-002264764cea\"/><scanner id=\"08b69003-5fc2-4037-a479-93b440211c73\"/></create_task>"
<create_task_response status="201" status_text="OK, resource created" id="7249a07c-03e1-4197-99e4-a3a9ab5b7c3b"/>
```

See {command}`create_task` command for all [details](https://docs.greenbone.net/API/GMP/gmp-22.04.html#command_create_task).

3. Start the task using the UUID return from the last response:

```shell
> gvm-cli socket --xml "<start_task task_id=\"7249a07c-03e1-4197-99e4-a3a9ab5b7c3b\"/>"
<start_task_response status="202" status_text="OK, request submitted"><report_id>0f9ea6ca-abf5-4139-a772-cb68937cdfbb</report_id></start_task_response>
```

See {command}`start_task` command for all [details](https://docs.greenbone.net/API/GMP/gmp-22.04.html#command_start_task).

→ The task is running. The response returns the UUID of the report which will
contain the results of the scan.

4. Display the current status of the task:

```shell
> gvm-cli socket --xml "<get_tasks task_id=\"7249a07c-03e1-4197-99e4-a3a9ab5b7c3b\"/>"
<get_tasks_response status="200" status_text="OK">
...
<status>Running</status><progress>98 ... </progress>
...
<get_tasks_response/>
```

See {command}`get_tasks` command for all [details](https://docs.greenbone.net/API/GMP/gmp-22.04.html#command_get_tasks).

→ As soon as the scan is completed, the full report is available and can be
displayed.

5. Display the full report:

```shell
> gvm-cli socket --xml "<get_reports report_id=\"0f9ea6ca-abf5-4139-a772-cb68937cdfbb\"/>"
<get_reports_response status="200" status_text="OK"><report type="scan" id="0f9ea6ca-abf5-4139-a772-cb68937cdfbb" format_id="a994b278-1f62-11e1-96ac-406186ea4fc5" extension="xml" content_type="text/xml">
...
</get_reports_response>
```

See {command}`get_reports` command for all [details](https://docs.greenbone.net/API/GMP/gmp-22.04.html#command_get_reports).

6. Additionally, the report can be downloaded in a specific report format instead
   of plain XML.

   List all report formats:

```shell
> gvm-cli socket --xml "<get_report_formats/>"
<get_report_formats_response status="200" status_text="OK"><report_format id="5057e5cc-b825-11e4-9d0e-28d24461215b">
...
</get_report_formats_response>
```

See {command}`get_report_formats` command for all [details](https://docs.greenbone.net/API/GMP/gmp-22.04.html#command_get_report_formats).

7. Download the report in the desired format.

   Example: download the report as a PDF file:

```shell
> gvm-cli socket --xml "<get_reports report_id=\"0f9ea6ca-abf5-4139-a772-cb68937cdfbb\" format_id=\"c402cc3e-b531-11e1-9163-406186ea4fc5\"/>"
```

```{note}
Please be aware that the PDF is returned as [base64 encoded](https://en.wikipedia.org/wiki/Base64) content of the
*\<get_report_response>\<report>* element in the XML response.
```

(gvm-scripting)=

## GVM Scripts

```{versionchanged} 2.0
```

Scripting of {term}`Greenbone Management Protocol (GMP) <GMP>` and {term}`Open Scanner Protocol
(OSP) <OSP>` via {program}`gvm-script` or interactively via
{program}`gvm-pyshell` is based on the [python-gvm] library. Please take a look
at [python-gvm] for further details about the API.

```{note}
By convention, scripts using {term}`GMP` are called *GMP scripts* and
are files with the ending {file}`.gmp.py`. Accordingly, *OSP scripts* with the
ending {file}`.osp.py` are using {term}`OSP`. Technically both protocols could be
used in one single script file.
```

The following sections are using the same example as it was used in
{ref}`XML Scripting <xml-scripting>` where it was assumed that an Intrusion Detection
System (IDS) that monitors the systems in the Demilitarized Zone (DMZ) and immediately discovers
new systems and unusual, new TCP ports is in use. The IDS will provide the
IP address of a new system to the GMP script.

1. Define the function that should be called when the script is
   started by adding the following code to a file named {file}`scan-new-system.gmp.py`:

```python3
if __name__ == '__gmp__':
  main(gmp, args)
```

→ The script is only called when being run as a GMP script. The
{dfn}`gmp` and {dfn}`args` variables are provided by {program}`gvm-cli` or
{program}`gvm-pyshell`. {dfn}`args` contains arguments for the script, e.g., the
user name and password for the GMP connection. The most important aspect about the example
script is that it contains the {dfn}`argv` property with the list of additional script
specific arguments. The {dfn}`gmp` variable contains a connected and
authenticated instance of a [Greenbone Management Protocol class](https://greenbone.github.io/python-gvm/api/gmp.html#module-gvm.protocols.gmp).

2. The main function begins with the following code lines:

```python3
def main(gmp: Gmp, args: Namespace) -> None:
  # check if IP address is provided to the script
  # argv[0] contains the script name
  if len(args.argv) <= 1:
    print('Missing IP address argument')
    return 1

  ipaddress = args.argv[1]
```

→ The main function stores the first argument passed to the script as the {envvar}`ipaddress`
variable.

3\. Add the logic to create a target, create a new scan task for the target,
start the task and print the corresponding report ID:

```python3
ipaddress = args.argv[1]

target_id = create_target(gmp, ipaddress)

full_and_fast_scan_config_id = 'daba56c8-73ec-11df-a475-002264764cea'
openvas_scanner_id = '08b69003-5fc2-4037-a479-93b440211c73'
task_id = create_task(
    gmp,
    ipaddress,
    target_id,
    full_and_fast_scan_config_id,
    openvas_scanner_id,
)

report_id = start_task(gmp, task_id)

print(
    f"Started scan of host {ipaddress}. Corresponding report ID is {report_id}"
)
```

For creating the target from an IP address (DNS name is also possible), the
following is used. Since target names must be unique, the current date and time in
ISO 8601 format (YYYY-MM-DDTHH:MM:SS.mmmmmm) is added:

```python3
def create_target(gmp, ipaddress):
    import datetime

    # create a unique name by adding the current datetime
    name = f"Suspect Host {ipaddress} {str(datetime.datetime.now())}"
    response = gmp.create_target(name=name, hosts=[ipaddress])
    return response.get('id')
```

The function for creating the task is defined as:

```python3
def create_task(gmp, ipaddress, target_id, scan_config_id, scanner_id):
    name = f"Scan Suspect Host {ipaddress}"
    response = gmp.create_task(
        name=name,
        config_id=scan_config_id,
        target_id=target_id,
        scanner_id=scanner_id,
    )
    return response.get('id')
```

Finally, the function to start the task and get the report ID:

```python3
def start_task(gmp, task_id):
    response = gmp.start_task(task_id)
    # the response is
    # <start_task_response><report_id>id</report_id></start_task_response>
    return response[0].text
```

For getting a PDF document of the report, a second script {file}`pdf-report.gmp.py`
can be used:

```python3
from base64 import b64decode
from pathlib import Path


def main(gmp: Gmp, args: Namespace) -> None:
    # check if report id and PDF filename are provided to the script
    # argv[0] contains the script name
    if len(args.argv) <= 2:
        print('Please provide report ID and PDF file name as script arguments')
        return 1

    report_id = args.argv[1]
    pdf_filename = args.argv[2]

    pdf_report_format_id = "c402cc3e-b531-11e1-9163-406186ea4fc5"
    response = gmp.get_report(
        report_id=report_id, report_format_id=pdf_report_format_id
    )

    report_element = response[0]
    # get the full content of the report element
    content = "".join(report_element.itertext())

    # convert content to 8-bit ASCII bytes
    binary_base64_encoded_pdf = content.encode('ascii')
    # decode base64
    binary_pdf = b64decode(binary_base64_encoded_pdf)

    # write to file and support ~ in filename path
    pdf_path = Path(pdf_filename).expanduser()
    pdf_path.write_bytes(binary_pdf)

    print('Done.')


if __name__ == '__gmp__':
    main(gmp, args)
```

## Example Scripts

All example scripts can be found at [GitHub](https://github.com/greenbone/gvm-tools/tree/main/scripts).

[python-gvm]: https://greenbone.github.io/python-gvm/