diff --git a/src/assets/Picture1.png b/src/assets/Picture1.png new file mode 100644 index 0000000..b3cbb15 Binary files /dev/null and b/src/assets/Picture1.png differ diff --git a/src/assets/Picture2.png b/src/assets/Picture2.png new file mode 100644 index 0000000..f18f103 Binary files /dev/null and b/src/assets/Picture2.png differ diff --git a/src/assets/TitlePage.png b/src/assets/TitlePage.png new file mode 100644 index 0000000..5ef6200 Binary files /dev/null and b/src/assets/TitlePage.png differ diff --git a/src/reportlab_generator.py b/src/reportlab_generator.py index 9648387..41729e9 100644 --- a/src/reportlab_generator.py +++ b/src/reportlab_generator.py @@ -1,11 +1,12 @@ """Generate a TPT report using a passed data dictionary.""" # Standard Python Libraries +from hashlib import sha256 import os import pandas as pd # Third-Party Libraries -# import numpy as np +import numpy as np from reportlab.lib import utils from reportlab.lib.colors import HexColor from reportlab.lib.pagesizes import letter @@ -34,6 +35,14 @@ BASE_DIR = os.path.abspath(os.path.dirname(__file__)) +EXAMPLE_TABLE1 = { + "id": [12], + "Finding": ["Spear Phishing Weakness"], + "Severity": ["High"], + "Service": ["Phishing Assessment (Infrastructure Only)"], + "Location": ["Phishing"] +} + # Set fonts that you will use in the pdf. pdfmetrics.registerFont( TTFont("Franklin_Gothic_Book", BASE_DIR + "/fonts/FranklinGothicBook.ttf") @@ -156,52 +165,65 @@ def format_table( def report_gen(data_dict): """Generate a TPT report with data passed in the data dictionry.""" """Build the 'templates' for the static pages.""" + def doHeading(text, sty): + """Add a bookmark to heading element to allow linking from the table of contents.""" + # this will add a bookmark to a header allowing it to be linked from the table of contents + # create bookmarkname + bn = sha256((text + sty.name).encode("utf8")).hexdigest() + # modify paragraph text to include an anchor point with name bn + h = Paragraph(text + '' % bn, sty) + # store the bookmark name on the flowable so afterFlowable can see this + h._bookmarkName = bn + return h + def titlePage(canvas, doc): """Build static elements of the cover page.""" canvas.saveState() # Add the background image to the top - # canvas.drawImage(BASE_DIR + "/assets/Cover.png", 0, 0, width=None, h eight=None) - canvas.drawInlineImage( - BASE_DIR + "/assets/cisa.png", 45, 705, width=65, height=65 - ) - # set the font + # canvas.drawImage(BASE_DIR + "/assets/TitlePage.png", 0, 0, width=None, height=None) + # canvas.drawInlineImage( + # BASE_DIR + "/assets/cisa.png", 45, 705, width=65, height=65 + # ) + + # Story.append(get_image(BASE_DIR + "/assets/TitlePage.png", width=6 * inch)) + # # set the font canvas.setFont("Franklin_Gothic_Medium_Regular", 32) - # Write the title to page and add some dynamic content to complete the tile page later - canvas.drawString(50, 660, "TPT Report") - canvas.restoreState() + # # Write the title to page and add some dynamic content to complete the tile page later + # canvas.drawString(50, 660, "TPT Report") + # canvas.restoreState() def summaryPage(canvas, doc): """Build static elements of the summary page.""" - canvas.saveState() - # Set the font - canvas.setFont("Franklin_Gothic_Book", 13) - # add a background image - # canvas.drawImage( - # BASE_DIR + "/assets/summary-background.png", - # 0, - # 0, - # width=PAGE_WIDTH, - # height=PAGE_HEIGHT, + # canvas.saveState() + # # Set the font + # canvas.setFont("Franklin_Gothic_Book", 13) + # # add a background image + # # canvas.drawImage( + # # BASE_DIR + "/assets/summary-background.png", + # # 0, + # # 0, + # # width=PAGE_WIDTH, + # # height=PAGE_HEIGHT, + # # ) + # # Change the color that will be used in the rectangle we add + # canvas.setFillColor(HexColor("#1d5288")) + # canvas.setStrokeColor("#1d5288") + # canvas.rect(inch, 210, 3.5 * inch, 5.7 * inch, fill=1) + # canvas.restoreState() + # # Add header/footer + # canvas.setStrokeColor("#a7a7a6") + # canvas.setFillColor("#a7a7a6") + # canvas.drawInlineImage( + # BASE_DIR + "/assets/cisa.png", 45, 705, width=65, height=65 # ) - # Change the color that will be used in the rectangle we add - canvas.setFillColor(HexColor("#1d5288")) - canvas.setStrokeColor("#1d5288") - canvas.rect(inch, 210, 3.5 * inch, 5.7 * inch, fill=1) - canvas.restoreState() - # Add header/footer - canvas.setStrokeColor("#a7a7a6") - canvas.setFillColor("#a7a7a6") - canvas.drawInlineImage( - BASE_DIR + "/assets/cisa.png", 45, 705, width=65, height=65 - ) - canvas.drawString(130, 745, "TPT Report") - canvas.drawString(130, 725, "Reporting Period: " + data_dict["dateRange"]) - canvas.line(130, 710, PAGE_WIDTH - inch, 710) - canvas.drawRightString( - PAGE_WIDTH - inch, 0.75 * inch, "P&E Report | Page %d" % (doc.page) - ) - canvas.drawString(inch, 0.75 * inch, data_dict["endDate"]) - canvas.setFont("Franklin_Gothic_Medium_Regular", 12) + # canvas.drawString(130, 745, "TPT Report") + # canvas.drawString(130, 725, "Reporting Period: " + data_dict["dateRange"]) + # canvas.line(130, 710, PAGE_WIDTH - inch, 710) + # canvas.drawRightString( + # PAGE_WIDTH - inch, 0.75 * inch, "P&E Report | Page %d" % (doc.page) + # ) + # canvas.drawString(inch, 0.75 * inch, data_dict["endDate"]) + # canvas.setFont("Franklin_Gothic_Medium_Regular", 12) # ***Document Structures***# """Build frames for different page structures.""" @@ -329,7 +351,14 @@ def summaryPage(canvas, doc): #The default template will be the first in the list we create above i.e. TitlePage #These will start at the beginning of the frame we added to that template page # Story.append(point12_spacer) - Story.append(Spacer(1, 55)) + + # Story.append(point12_spacer) + Story.append(get_image(BASE_DIR + "/assets/TitlePage.png", width=7.3 * inch)) + Story.append(point12_spacer) + Story.append(point12_spacer) + Story.append(doHeading("TPT Report", h1)) + Story.append(PageBreak()) + # Story.append(Spacer(1, 55)) Story.append( format_table( data_dict["intro_table"], @@ -347,18 +376,115 @@ def summaryPage(canvas, doc): [body, None], ) ) - Story.append(Spacer(1, 40)) - Story.append(Paragraph("Phishing Results", h1)) - Story.append(Spacer(1, 10)) + # Story.append(Spacer(1, 40)) + # Story.append(Paragraph("Phishing Results", h1)) + # Story.append(Spacer(1, 10)) + # Story.append( + # Paragraph("""The phishing test evaluated the performance of security controls at the border and host. During a phishing attack (Figure 5), an attacker sends an email that must successfully pass through any protections presented by both the network border and the host system that receives the email.""", body) + # ) + Story.append(PageBreak()) + + Story.append(horizontal_line) + Story.append(point12_spacer) + Story.append(doHeading("Phishing Results", h2)) + Story.append( + Paragraph( + """The phishing test evaluated the performance of security controls + at the border and host. During a phishing attack (Figure 5), an + attacker sends an email that must successfully pass through any + protections presented by both the network border and the host system + that receives the email.""", + body, + ) + ) + Story.append(point12_spacer) + Story.append(get_image(BASE_DIR + "/assets/Picture1.png", width=4 * inch)) + Story.append(point12_spacer) + Story.append(get_image(BASE_DIR + "/assets/Picture2.png", width=4 * inch)) + Story.append(point12_spacer) + Story.append( + Paragraph( + """A simulated phishing attack scenario was performed with the Example Organization (EXPL) complicit user, Alisia Romo, during which, the CISA team attempted to execute a variety of simulated malicious payloads on the POC’s workstation. This simulated attack scenario was meant to test EXPL’s security controls in their ability to identify, alert, and prevent such attack vectors. """, + body, + ) + ) + Story.append(point12_spacer) Story.append( - Paragraph("""The phishing test evaluated the performance of security controls at the border and host. During a phishing attack (Figure 5), an attacker sends an email that must successfully pass through any protections presented by both the network border and the host system that receives the email.""", body) + Paragraph( + """The initial email containing a link to the malicious payloads was able to circumvent border protections and reach the complicit user’s inbox (in spam). Of the twenty-five (25) different payloads tested, zero (0) payloads successfully executed and connected to the CISA team’s command-and-control (C2) server. From the attached payload test, one (1) of the eight (8) emails containing malicious attachments was able to circumvent through border protections and reach the POC’s inbox. This payload was unable to successfully execute.""", + body, + ) ) + # Story.append(point12_spacer) + # Story.append(point12_spacer) + Story.append(PageBreak()) - doc.multiBuild(Story) - # Preloaded templates to use on the next page + Story.append(horizontal_line) + Story.append(point12_spacer) + Story.append(doHeading("Noted System Strengths", h2)) + + Story.append( + Paragraph( + """The internal and email infrastructure incorporated several security features that reduced the effectiveness of phishing payloads. The environment’s firewall (Fortinet) prohibited users from accessing the testing team’s malicious web during the phishing assessment. This reduces the likelihood of users obtaining malicious software from known malicious sites and domains. The environment utilized an effective antivirus solution (Trend Micro), which prevented the execution of most malicious payloads during the phishing assessment.""", + body, + ) + ) + Story.append(point12_spacer) + Story.append( + Paragraph( + """The environment utilized Albert real-time monitoring, which alerted security staff upon the detection of malicious code within the environment. The alert notifications were received within minutes of initiating the phishing assessment.""", + body, + ) + ) + + Story.append(point12_spacer) + Story.append( + format_table( + pd.DataFrame.from_dict(EXAMPLE_TABLE1), + table_header, + [.5 * inch, 1.5 * inch, inch, 2.5 * inch, inch], + [None, body, None, body, None], + ) + ) + + Story.append(point12_spacer) + Story.append(point12_spacer) + Story.append(doHeading("Affected Systems", h2)) + + Story.append( + Paragraph( + """@example.org workstations""", + body, + ) + ) + Story.append(point12_spacer) Story.append(point12_spacer) + Story.append(doHeading("Description", h2)) + + Story.append( + Paragraph( + """Successful spear phishing requires an attacker’s email to pass through the network border and execute on the local host with the aid of a user performing some action. Most common phishing attacks can be rebuffed by good border and host-level automated protections. Inadequate protections allow the execution of malicious payloads. """, + body, + ) + ) + + Story.append(point12_spacer) + Story.append(point12_spacer) + Story.append(doHeading("Recommended Mitigation", h2)) + + Story.append( + Paragraph( + """Regularly analyze border and host-level protections, including spam-filtering capabilities, to ensure their continued effectiveness in blocking the delivery and execution of malware. """, + body, + ) + ) + + Story.append(PageBreak()) + Story.append(horizontal_line) + Story.append(point12_spacer) + Story.append(doHeading("Payload Testing Results", h2)) Story.append( Paragraph( """ @@ -377,8 +503,57 @@ def summaryPage(canvas, doc): ) ) Story.append(point12_spacer) + Story.append(PageBreak()) + Story.append(horizontal_line) + Story.append(point12_spacer) + Story.append(doHeading("Payload Testing Results", h2)) + + Story.append( + Paragraph( + """The initial email containing a link to the malicious payloads was able to circumvent border protections and reach the complicit user’s inbox (in spam). Of the twenty-five (25) different payloads tested, no payloads successfully executed and connected to the CISA team’s C2 server. From the attached payload test, one (1) of the eight (8) emails containing malicious attachments were able to circumvent through border protections and reach the POC’s inbox. This payload was unable to successfully execute. """, + body, + ) + ) + + Story.append(point12_spacer) + Story.append(point12_spacer) + Story.append(doHeading("Conclusions/Recommendations", h2)) + + Story.append( + Paragraph( + """Regularly analyze border and host-level protections, including spam-filtering capabilities, to ensure their continued effectiveness in blocking the delivery and execution of malware. """, + body, + ) + ) + + + # Story.append(horizontal_line) + # Story.append(point12_spacer) + # Preloaded templates to use on the next page + # Story.append(point12_spacer) + # Story.append(point12_spacer) + # Story.append( + # Paragraph( + # """ + # Payload Testing Results + # """, + # table, + # ) + # ) + # # Generate a table using ad atabframe passed to my format_table function + # Story.append( + # format_table( + # data_dict["breach_table"], + # table_header, + # [2.5 * inch, inch, inch, inch, inch], + # [body, None, None, None, None], + # ) + # ) + # Story.append(point12_spacer) + # Story.append(PageBreak()) + doc.multiBuild(Story) return 1