diff --git a/contrib/descriptions/__init__.py b/contrib/descriptions/__init__.py
index 91ca8bd963477fc009a4ead1f083268db6b41515..13b6727ab8b5a2550bf7da4c5288d456393d1d1a 100644
--- a/contrib/descriptions/__init__.py
+++ b/contrib/descriptions/__init__.py
@@ -1,2 +1,2 @@
-from .description_provider import VulnDescriptionProvider
+from .description_provider import VulnDescription, VulnDescriptionProvider
 from .cveproject import CveProjectProvider
diff --git a/contrib/descriptions/cveproject.py b/contrib/descriptions/cveproject.py
index 794ef700b8abd207e250ecee4549d894071100f3..a984fc5ea80695731c72d39e1bed94a3805ceb45 100644
--- a/contrib/descriptions/cveproject.py
+++ b/contrib/descriptions/cveproject.py
@@ -1,7 +1,6 @@
 from requests import Session, HTTPError
 
-from contrib.descriptions import VulnDescriptionProvider
-
+from contrib.descriptions import VulnDescriptionProvider, VulnDescription
 
 __all__ = ['CveProjectProvider']
 
@@ -16,7 +15,7 @@ class CveProjectProvider(VulnDescriptionProvider):
         self.sess = session
         self.cache = {}
 
-    def get_description(self, vuln: str, vuln_type: str) -> str:
+    def get_description(self, vuln: str, vuln_type: str) -> VulnDescription:
         if vuln in self.cache:
             return self.cache[vuln]
 
@@ -30,8 +29,8 @@ class CveProjectProvider(VulnDescriptionProvider):
                 cve_json = response.json()
                 description = cve_json['description']['description_data'][0]['value']
                 self.cache[vuln] = description
-                return description
+                return VulnDescription(description, url)
         except HTTPError as he:
-            return 'Description fetching error: ' + str(he)
+            return VulnDescription('', 'Description fetching error: ' + str(he))
 
-        return ''
+        return VulnDescription('', '')
diff --git a/contrib/descriptions/description_provider.py b/contrib/descriptions/description_provider.py
index 26a6a8e39f76516f3151ecd6968a805edeb22363..4c6da2532713f5482e1038e97c1ec3fa25a5a657 100644
--- a/contrib/descriptions/description_provider.py
+++ b/contrib/descriptions/description_provider.py
@@ -1,6 +1,13 @@
 import abc
+from typing import Optional
 
-__all__ = ['VulnDescriptionProvider']
+__all__ = ['VulnDescriptionProvider', 'VulnDescription']
+
+
+class VulnDescription:
+    def __init__(self, text: str, url: Optional[str] = None):
+        self.text = text
+        self.url = url
 
 
 class VulnDescriptionProvider(metaclass=abc.ABCMeta):
@@ -8,5 +15,5 @@ class VulnDescriptionProvider(metaclass=abc.ABCMeta):
     Provides extended vulnerability description by vulnerablity identifier and type
     """
     @abc.abstractmethod
-    def get_description(self, vuln: str, vuln_type: str) -> str:
+    def get_description(self, vuln: str, vuln_type: str) -> VulnDescription:
         pass
diff --git a/contrib/internal_types/flan_types.py b/contrib/internal_types/flan_types.py
index 8ea0a2b3684f954262bc9bef42dd377a79c01cee..f1c0da4c425162ed8090338e1a59ed025366e8b2 100644
--- a/contrib/internal_types/flan_types.py
+++ b/contrib/internal_types/flan_types.py
@@ -1,5 +1,5 @@
 from collections import defaultdict
-
+from typing import List, Dict
 
 __all__ = ['SeverityLevels', 'Vuln', 'ScanResult']
 
@@ -46,5 +46,5 @@ class ScanResult:
     Scan result representation
     """
     def __init__(self):
-        self.locations = defaultdict(list)
-        self.vulns = []
+        self.locations = defaultdict(list)  # type: Dict[str, List[str]]
+        self.vulns = []  # type: List[Vuln]
diff --git a/contrib/report_builders/__init__.py b/contrib/report_builders/__init__.py
index 6efa8430dad6dfd318e1fdd75ed5054d3c1528c1..5db235371c69a132b59d71227a0b2844a03f40fd 100644
--- a/contrib/report_builders/__init__.py
+++ b/contrib/report_builders/__init__.py
@@ -1,2 +1,3 @@
 from .report_builder import ReportBuilder
 from .latex_report_builder import LatexReportBuilder
+from .markdown_report_builder import MarkdownReportBuilder
diff --git a/contrib/report_builders/latex_report_builder.py b/contrib/report_builders/latex_report_builder.py
index d6f304b64f2e9adb734a735eaa48f604188cbce9..5f1cb618ec9d7cf60d3a052549007332c4dd6612 100644
--- a/contrib/report_builders/latex_report_builder.py
+++ b/contrib/report_builders/latex_report_builder.py
@@ -42,7 +42,8 @@ class LatexReportBuilder(ReportBuilder):
             locations = report.locations
             num_vulns = len(vulns)
 
-            for i, v in enumerate(vulns):
+            for v in vulns:
+                description = self.description_provider.get_description(v.name, v.vuln_type)
                 severity_name = v.severity_str
                 self._append('\\begin{figure}[h!]\n')
                 self._append('\\begin{tabular}{|p{16cm}|}\\rowcolor[HTML]{'
@@ -50,10 +51,10 @@ class LatexReportBuilder(ReportBuilder):
                              + '} \\begin{tabular}{@{}p{15cm}>{\\raggedleft\\arraybackslash} p{0.5cm}@{}}\\textbf{'
                              + v.name + ' ' + severity_name + ' ('
                              + str(v.severity)
-                             + ')} & \href{https://nvd.nist.gov/vuln/detail/'
-                             + v.name + '}{\large \\faicon{link}}'
+                             + ')} & \href{' + description.url
+                             + '}{\large \\faicon{link}}'
                              + '\end{tabular}\\\\\n Summary:'
-                             + self.description_provider.get_description(v.name, v.vuln_type)
+                             + description.text
                              + '\\\\ \hline \end{tabular}  ')
                 self._append('\end{figure}\n')
 
diff --git a/contrib/report_builders/markdown_report_builder.py b/contrib/report_builders/markdown_report_builder.py
new file mode 100644
index 0000000000000000000000000000000000000000..f51b346a34072380b13e3fb76b86c3c45e1f60f4
--- /dev/null
+++ b/contrib/report_builders/markdown_report_builder.py
@@ -0,0 +1,95 @@
+from datetime import datetime
+from typing import Any, Dict, List
+
+from contrib.descriptions import VulnDescriptionProvider
+from contrib.internal_types import ScanResult
+from contrib.report_builders import ReportBuilder
+
+__all__ = ['MarkdownReportBuilder']
+
+
+class MarkdownReportBuilder(ReportBuilder):
+    def __init__(self, description_provider: VulnDescriptionProvider):
+        self.description_provider = description_provider
+        self._buffer = ''
+
+    def init_report(self, start_date: str, nmap_command: str):
+        self._append_line(self.header)
+        self._append_line('## {date:%B %d, %Y}'.format(date=datetime.utcnow()))
+        self._append_line('### **Summary**')
+        self._append_line('Flan Scan ran a network vulnerability scan with the following Nmap command on {date}'
+                          .format(date=start_date))
+        self._append_line('`{command}`'.format(command=nmap_command))
+
+    def build(self) -> Any:
+        return self._buffer
+
+    def add_vulnerable_section(self):
+        self._append_line('### Services with vulnerabilities')
+
+    def add_non_vulnerable_section(self):
+        self._append_line('### Services with no *known* vulnerabilities')
+
+    def add_vulnerable_services(self, scan_results: Dict[str, ScanResult]):
+        for i, pair in enumerate(scan_results.items(), start=1):
+            app_name, report = pair  # type: str, ScanResult
+            self._append_service(i, app_name)
+            num_vulns = len(report.vulns)
+
+            for v in report.vulns:
+                description = self.description_provider.get_description(v.name, v.vuln_type)
+                self._append_line('- [**{name}** {severity} ({severity_num})]({link} "{title}")'
+                                  .format(name=v.name, severity=v.severity_str, severity_num=v.severity,
+                                          link=description.url, title=v.name), spaces=4)
+                self._append_line('```text', separators=1, spaces=6)
+                self._append_line(description.text, separators=1, spaces=6)
+                self._append_line('```', spaces=6)
+
+            self._append_line('The above {num} vulnerabilities apply to these network locations'.format(num=num_vulns),
+                              spaces=4)
+            self._append_line('```text', separators=1, spaces=4)
+            for addr, ports in report.locations.items():
+                self._append_location(addr, ports, spaces=4)
+            self._append_line('```', spaces=4)
+
+    def add_non_vulnerable_services(self, scan_results: Dict[str, ScanResult]):
+        for i, pair in enumerate(scan_results.items(), start=1):
+            app_name, report = pair  # type: str, ScanResult
+            self._append_service(i, app_name)
+
+            for addr, ports in report.locations.items():
+                self._append_location(addr, ports, spaces=4)
+            self._append('\n')
+
+    def initialize_section(self):
+        pass
+
+    def add_ips_section(self):
+        self._append_line('### List of IPs Scanned')
+
+    def add_ip_address(self, ip: str):
+        self._append_line('- {ip}'.format(ip=ip), separators=1)
+
+    def finalize(self):
+        pass
+
+    @property
+    def header(self) -> Any:
+        return '# Flan scan report'
+
+    def _append(self, text: str, spaces: int = 0):
+        if spaces:
+            self._buffer += ' ' * spaces
+        self._buffer += text
+
+    def _append_line(self, text: str, separators: int = 2, spaces: int = 0):
+        self._append(text, spaces)
+        self._append('\n' * separators)
+
+    def _append_service(self, index: int, name: str, spaces: int = 0):
+        self._append_line('{index}. **{service}**'.format(index=index, service=name.strip()), spaces=spaces,
+                          separators=1)
+
+    def _append_location(self, address: str, ports: List[str], spaces: int):
+        self._append_line('- {address} Ports: {ports}'.format(address=address, ports=', '.join(ports)), spaces=spaces,
+                          separators=1)
diff --git a/output_report.py b/output_report.py
index 239a13b372721d1ee9944068d1e980a9db075082..2f5b45e751f1d4d3e3edfddafba97a6a8703f6b6 100644
--- a/output_report.py
+++ b/output_report.py
@@ -6,7 +6,7 @@ from requests import Session
 
 from contrib.descriptions import CveProjectProvider
 from contrib.parsers import FlanXmlParser
-from contrib.report_builders import ReportBuilder, LatexReportBuilder
+from contrib.report_builders import ReportBuilder, LatexReportBuilder, MarkdownReportBuilder
 
 
 def create_report(parser: FlanXmlParser, builder: ReportBuilder, nmap_command: str, start_date: str, output_writer: IO,
@@ -38,12 +38,15 @@ def parse_nmap_command(raw_command: str) -> str:
     return ' '.join(nmap_split)
 
 
+def create_default_provider():
+    return CveProjectProvider(Session())
+
+
 def create_report_builder(report_type: str) -> ReportBuilder:
     if report_type == 'latex':
-        session = Session()
-        description_provider = CveProjectProvider(session)
-        report_bilder = LatexReportBuilder(description_provider)
-        return report_bilder
+        return LatexReportBuilder(create_default_provider())
+    if report_type == 'md':
+        return MarkdownReportBuilder(create_default_provider())
     raise NotImplementedError(report_type)