jkent

Creating and Downloading a Web Application Report

Discussion created by jkent on Jan 3, 2013

This bit of Python was written for the express purpose of Creating a Web Application Report based on a TAG and some Vulnerability Criteria.

 

This process requires 4 inputs to be changed in the code.

 

QUALYS_USERNAME

QUALYS_PASSWORD

SEARCHLIST_ID

APPLICATION_TAG_ID

 

#!usr/bin/python

import base64, urllib2
from lxml import objectify
import logging
import argparse, os, datetime
# Declare the command line flags/options we want to allow.
parser = argparse.ArgumentParser(description = 'WAS report CSV download.')
parser.add_argument('-o', '--output', default = datetime.datetime.now().strftime('%Y-%m-%d.%H-%M-%S-report.csv'),
                    help = 'Report output filename.')
parser.add_argument('-p', '--password', default = 'QUALYS_PASSWORD',
                    help = 'QualysGuard password.')
parser.add_argument('-s', '--search_list', default = 'SEARCHLIST_ID',
                    help = 'Web application search list ID to report against.')
parser.add_argument('-t', '--tag', default = 'APPLICATION_TAG_ID',
                    help = 'Web application tag to report against.')
parser.add_argument('-u', '--username', default = 'QUALYS_USERNAME',
                    help = 'QualysGuard username.')
parser.add_argument('-v', '--debug', action = 'store_true',
                    help = 'Outputs additional information to log.')
# Parse arguements.
args = parser.parse_args()# Create log directory.
PATH_LOG = 'log'
if not os.path.exists(PATH_LOG):
    os.makedirs(PATH_LOG)
# Set log options.
LOG_FILENAME = '%s/sync_host_ips.py-%s.log' % (
    PATH_LOG, datetime.datetime.now().strftime('%Y-%m-%d.%H-%M-%S'))
if args.debug:
    # Enable debug level of logging.
    print "Logging level set to debug."
    logging.basicConfig(filename = LOG_FILENAME, format = '%(asctime)s %(message)s',
                        level = logging.DEBUG)
else:
    logging.basicConfig(filename = LOG_FILENAME, format = '%(asctime)s %(message)s',
                        level = logging.INFO)
# Credentials header.
base64string = base64.encodestring('%s:%s' % (args.username, args.password))[:-1]
# Build query to generate report.
query_uri = 'https://qualysapi.qualys.com/qps/rest/3.0/create/was/report'
data = '''<ServiceRequest>
    <data>
        <Report>
            <name><![CDATA[Today's Web App Report]]></name>
            <description><![CDATA[Sev345 based on Tag]]></description>
            <format>CSV</format>
            <type>WAS_WEBAPP_REPORT</type>
                <config>
                    <webAppReport>
                <target>
                <tags>
                    <Tag>
                        <id>%s</id>
                    </Tag>
                </tags>
                        </target>
                    <display>
                        <contents>
            <WebAppReportContent>DESCRIPTION</WebAppReportContent>
                            <WebAppReportContent>SUMMARY</WebAppReportContent>
                            <WebAppReportContent>RESULTS</WebAppReportContent>
                            <WebAppReportContent>INDIVIDUAL_RECORDS</WebAppReportContent>
                            <WebAppReportContent>RECORD_DETAILS</WebAppReportContent>
                            <WebAppReportContent>ALL_RESULTS</WebAppReportContent>
                            <WebAppReportContent>APPENDIX</WebAppReportContent>
                        </contents>
                        <options>
                            <rawLevels>true</rawLevels>
                        </options>
                    </display>
                    <filters>
                        <searchlists>
                <SearchList>
                    <id>%s</id>
                </SearchList>
            </searchlists>
                        <status>
                            <WebAppFindingStatus>NEW</WebAppFindingStatus>
                            <WebAppFindingStatus>ACTIVE</WebAppFindingStatus>
                            <WebAppFindingStatus>REOPENED</WebAppFindingStatus>
                            <WebAppFindingStatus>FIXED</WebAppFindingStatus>
                            <WebAppFindingStatus>IGNORED</WebAppFindingStatus>
                        </status>
                    </filters>
                </webAppReport>
            </config>
        </Report>
    </data>
</ServiceRequest>''' % (args.tag, args.search_list)
req = urllib2.Request(query_uri, data)
# Force add creds.
req.add_header("Authorization", "Basic %s" % base64string)
# Let API know type of content in POST.  This is REQUIRED.
req.add_header("Content-Type", "text/xml")
# Make request
result = urllib2.urlopen(req)
response = result.read()
logging.debug(response)
tree = objectify.fromstring(response)
webappreport_id = tree.data.Report.id
logging.debug(webappreport_id)
# Setup request to download report.
query_uri = 'https://qualysapi.qualys.com/qps/rest/3.0/download/was/report/%s' % (webappreport_id)
req = urllib2.Request(query_uri)
# Force add creds.
req.add_header("Authorization", "Basic %s" % base64string)
# Let API know type of content in POST.  This is REQUIRED.
req.add_header("Content-Type", "text/xml")
# Make request
result = urllib2.urlopen(req)
response = result.read()
# Write string to file.
logging.debug('Report = ')
logging.debug(result)
with open(args.output, 'wb') as f:
    f.write(response)
print 'Successfully downloaded report to: %s' % (args.output)

 

Username and password are pretty simple, we suggest using a service account with only access for performing this type of activity. 

 

The Searchlist ID can be found after you create the searchlist you are wanting to use.  You do not have to do this, if you want a full report, simply remove the data fields for Searchlist.

 

APPLICATION_TAG_ID is another item that needs to be determined.  Once you have a tag you want to draw data from, edit the tag and get the ID.

 

 

The script will then generate the Report and Download it to the local directory where the script was run.

 

Using the basic script you can extend its use by generating different types of reports using different output formats (distribute a secure PDF for instance) or even change the types of targets, vulnerability statuses (New, Active, etc...) or even change the Searchlist Criteria to further report on the worst of the worst vulnerabilities.

 

Let me know if you have any questions!

 

Thanks,

 

Jason Kent

Director, Web Application Security - Qualys

jkent AT qualys DOT com

Outcomes