Skip to content
This repository was archived by the owner on May 14, 2021. It is now read-only.

[WIP] Add missing support for reports#37

Open
gschneider-r7 wants to merge 9 commits into
masterfrom
report_configs
Open

[WIP] Add missing support for reports#37
gschneider-r7 wants to merge 9 commits into
masterfrom
report_configs

Conversation

@gschneider-r7

Copy link
Copy Markdown
Contributor

Description

Add support for loading and saving report configs.

Motivation and Context

See #12 and #27

How Has This Been Tested?

Added steps to run_demo.py to create, load, and then delete a report configuration.

Types of changes

  • New feature (non-breaking change which adds functionality)

Checklist:

  • I have updated the documentation accordingly (if changes are required).
  • I have added tests to cover my changes.
  • All new and existing tests passed.

Comment thread nexpose/nexpose_report.py
- vuln-status
- cyberscope-component
- cyberscope-bureau
- cyberscope-enclave

@jkee-r7 jkee-r7 Oct 11, 2017

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this work for SQL reports?
I added Adhoc Reports to the Nexpose Komand plugin, and I needed to add version and query to this list.

Edit, I see that this list is not enforced, so it should be okay. Maybe just update the comment? 😄

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep just need to update the comment here.

@jkee-r7

jkee-r7 commented Oct 11, 2017

Copy link
Copy Markdown

The generate report request times out after 60 seconds, so fails for larger reports.
I assume that adhoc report will be affected by the same bug?

I know this bug is not the purpose of this PR and is for later work, I just want to highlight that it may be a problem for Generate Adhoc Report.

Comment thread demo/run_demo.py
report.add_filter('scan', 'last') # this should use site/group/tag filter(s) instead of scan in real world use
report.add_common_vuln_filters() # adds vuln filters to match UI defaults

# these imports should be at the top, or perhaps be auto imports in the init file?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 on that

Comment thread nexpose/nexpose.py
assert data[2] == 'Content-Transfer-Encoding: base64'
assert data[3] == ''
assert data[0] == data[-1][:-2]
body = ''.join(data[4:-1])

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had experience when requesting an XML report & within the response there were multiple report items.
i.e. the response would look like this:

<ReportAdhocGenerateResponse success="1"/>
--AxB9sl3299asdjvbA
Content-Type: image/gif; name=Chart00000001.gif
Content-Transfer-Encoding: base64
<Base64 encoded gif>
--AxB9sl3299asdjvbA
Content-Type: image/gif; name=Chart00000005.gif
Content-Transfer-Encoding: base64
<Base64 encoded gif>
--AxB9sl3299asdjvbA
Content-Type: text/xml; name=report.xml
Content-Transfer-Encoding: base64
<Base64 encoded xml>
--AxB9sl3299asdjvbA
Content-Type: image/gif; name=Chart00000002.gif
Content-Transfer-Encoding: base64
<Base64 encoded gif>

So in this case, there should be some looping trying to find the actual report.

Also, it's really confusing to see splits & then calling each element without an example response.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this is just copied from the existing adhoc report method (which only takes a scan id) and it seems pretty limited. I would like this to be able to work the way the ruby gem does where you can write to file instead of holding it all in memory. Being able to capture the extra attachments (like charts) would be nice, but the ruby gem doesn't support that either so I'm not super concerned about it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with this. After experimenting a bit, it is possible to use Python's MIME parser to handle these cases but you need to modify the input slightly, i.e. prefix some headers. Here is an excerpt of how we implemented it:

        full_msg = 'Content-Type: {}\r\n\r\n{}'.format(
            resp.headers['Content-Type'],
            resp.text,
        )
        msg = email.message_from_string(full_msg)
        if not msg.is_multipart():
            raise RuntimeError("Got unexpected (non multipart MIME) response from GenerateAdhocReport: {}".format(msg))


        vuln_data = None
        # There will be several parts in the response (images etc.),
        # so linear search for the part that is the actual report.
        for part in msg.walk():
            if part.get_filename() == "report.csv":
                assert part.get_content_type() == 'text/csv'

                # Decode the bytes.
                csv_ = part.get_payload(decode=True).decode('utf8')
                vuln_data = csv.DictReader(io.StringIO(csv_))
                break
        else:
            raise RuntimeError("Could not find report in GenerateAdhocReport response")

A similar approach could work here as well I assume. That way all the verification can be deferred to a library but the content needs to be prepared properly.

Comment thread nexpose/nexpose.py Outdated
If successful, the id will also have been updated in the provided ReportConfiguration object.
To create a new report, specify -1 as id.
"""
# TODO: how to pass generate_now param downstream?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As in how to pass to Nexpose to generate the report now? If so:
<ReportSaveRequest session-id="{sid}" generate-now="{1/0}">

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is the magic under the hood with the ExecuteSave() function, it takes in a function as an argument and no way to pass arguments to that function. I suppose I could do something with **args or **kwargs to keep it compatible elsewhere. After this PR is done it's probably time to rewrite all of that stuff to just use the requests lib instead and reduce some of the "magic" involved.

Comment thread nexpose/nexpose_report.py Outdated
self.timezone = timezone
self.language = None
self.filters = []
self.baseline = None

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is baseline not passed as a param?

On line 107, I see that there is check to see if it exists, but I can't seem to locate where it gets set.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been mimicking the ruby gem for these classes and their constructors. I could add more optional arguments I guess, but how many is too many? 🤔

Comment thread nexpose/nexpose_report.py
if delivery is not None:
cfg.delivery = Delivery.CreateFromXML(delivery)

# TODO: draw the rest of the owl

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🦉

@gschneider-r7

Copy link
Copy Markdown
Contributor Author

The generate report request times out after 60 seconds, so fails for larger reports.
I assume that adhoc report will be affected by the same bug?

Yeah I need to figure something out for timeouts here. I think that in the real world adhoc reports don't scale very well anyway and should be avoided IMO. Since the consumer has to wait for the report to finish generating before receiving a response indicating success or failure, it would be better to instead use a regular report and poll the status on it while generating.

@gschneider-r7

Copy link
Copy Markdown
Contributor Author

So the NexposeSession class has a timeout property, which can be set globally for all requests it seems. I don't like the idea of changing "global" properties like that within specific functions, so I guess we'd have to leave it up to the user to set an appropriate timeout when running adhoc reports.

Comment thread nexpose/nexpose.py
else:
return self.ExecuteBasicOnReport("ReportDeleteRequest", report_id)

def RequestAdhocReport(self, report_config):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't quite understand why this is a new function...? Couldn't we just re-use the one below? Or do you want to make breaking changes here that wouldn't work with the below function?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't really remember why I made new ones, but they don't need to be new. I am not terribly concerned about backwards compatibility either at this point since there is a lot of work needed to get this to be on par with the ruby gem for functionality.

@fruechel

Copy link
Copy Markdown
Contributor

I like the changes made in this PR. I would only change one thing: Make the API for the function with real arguments (template_id etc.). This makes the function self-documenting and allows consumers to get started quicker. This can then be augmented by a function that is more flexible, i.e. what you already have - so that the API doesn't limit what can be done (like it does now).

What do you think?

@gschneider-r7

Copy link
Copy Markdown
Contributor Author

Note to self: this branch needs to be updated with changes from #48

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants