예제 #1
0
def write_privesc_graphviz(graph: Graph, filepath: str,
                           file_format: str) -> None:
    """The function to generate the privesc-only visualization with a Graphviz-generated file: this is only the
    nodes that are admins/privesc with blue/red highlights."""

    pydg = pydot.Dot(graph_type='digraph',
                     overlap='scale',
                     layout='dot',
                     splines='ortho',
                     rankdir='LR',
                     forcelabels='true')

    pydot_nodes = {}

    # Need to draw in order of "rank", one new subgraph per-rank, using the edge_list length from the privesc method
    ranked_nodes = {}
    for node in graph.nodes:
        if node.is_admin:
            if 0 not in ranked_nodes:
                ranked_nodes[0] = []
            ranked_nodes[0].append(node)
        else:
            pe, edge_list = can_privesc(graph, node)
            if pe:
                if len(edge_list) not in ranked_nodes:
                    ranked_nodes[len(edge_list)] = []
                ranked_nodes[len(edge_list)].append(node)

    for rank in sorted(ranked_nodes.keys()):
        s = pydot.Subgraph(rank='same')
        for node in ranked_nodes[rank]:
            if node.is_admin:
                # just draw the node and nothing more
                pydot_node = pydot.Node(node.searchable_name(),
                                        style='filled',
                                        fillcolor='#BFEFFF',
                                        shape='box')
                pydot_nodes[node] = pydot_node
                s.add_node(pydot_node)
            else:
                # draw the node + add edge
                pe, edge_list = can_privesc(graph, node)
                pydot_node = pydot.Node(node.searchable_name(),
                                        style='filled',
                                        fillcolor='#FADBD8',
                                        shape='box')
                pydot_nodes[node] = pydot_node
                s.add_node(pydot_node)

                edge_to_add = pydot.Edge(
                    node.searchable_name(),
                    edge_list[0].destination.searchable_name(),
                    xlabel=edge_list[0].short_reason)
                pydg.add_edge(edge_to_add)

        pydg.add_subgraph(s)

    # and draw
    pydg.write(filepath, format=file_format)
예제 #2
0
def generate_graphviz(graph: Graph, nodes: List[Node], edges: List[Edge],
                      filepath: str, file_format: str) -> None:
    """Draws a graph using Graphviz (dot) with a specific set of nodes and edges."""

    pydg = pydot.Dot(graph_type='digraph',
                     overlap='scale',
                     layout='neato',
                     splines='true')
    pyd_nd = {}

    for node in nodes:
        if node.is_admin:
            color = '#BFEFFF'
        elif can_privesc(graph, node)[0]:
            color = '#FADBD8'
        else:
            color = 'white'

        pyd_nd[node] = pydot.Node(node.searchable_name(),
                                  style='filled',
                                  fillcolor=color,
                                  shape='box')
        pydg.add_node(pyd_nd[node])

    for edge in edges:
        if not edge.source.is_admin:
            pydg.add_edge(
                pydot.Edge(pyd_nd[edge.source],
                           pyd_nd[edge.destination],
                           label=edge.short_reason))

    # and draw
    pydg.write(filepath, format=file_format)
예제 #3
0
def handle_request(graph: Graph, path: str, file_format: str) -> None:
    """Meat of the graph_writer.py module, writes graph data in a given file-format to the given path."""
    # Load graph data into pydot
    pydg = pydot.Dot(graph_type='digraph',
                     graph_name='Principal Mapper Visualization: {}'.format(
                         graph.metadata['account_id']),
                     overlap='scale',
                     layout='neato',
                     concentrate='true',
                     splines='true')
    pyd_nd = {}

    for node in graph.nodes:
        if node.is_admin:
            color = '#BFEFFF'
        elif can_privesc(graph, node)[0]:
            color = '#FADBD8'
        else:
            color = 'white'

        pyd_nd[node] = pydot.Node(node.searchable_name(),
                                  style='filled',
                                  fillcolor=color,
                                  shape='box')
        pydg.add_node(pyd_nd[node])

    for edge in graph.edges:
        if not edge.source.is_admin:
            pydg.add_edge(
                pydot.Edge(pyd_nd[edge.source], pyd_nd[edge.destination]))

    # and draw
    pydg.write(path, format=file_format)
예제 #4
0
def write_standard_graphviz(graph: Graph,
                            filepath: str,
                            file_format: str,
                            with_services: Optional[bool] = False) -> None:
    """The function to generate the standard visualization with a Graphviz-generated file: this is all the nodes
    with the admins/privesc highlights in blue/red respectively."""

    # Load graph data into pydot
    pydg = pydot.Dot(graph_type='digraph',
                     graph_name='Principal Mapper Visualization: {}'.format(
                         graph.metadata['account_id']),
                     overlap='scale',
                     layout='neato',
                     concentrate='true',
                     splines='true')
    pyd_nd = {}

    # Draw standard nodes and edges: users/roles
    for node in graph.nodes:
        if node.is_admin:
            color = '#BFEFFF'
        elif can_privesc(graph, node)[0]:
            color = '#FADBD8'
        else:
            color = 'white'

        pyd_nd[node] = pydot.Node(node.searchable_name(),
                                  style='filled',
                                  fillcolor=color,
                                  shape='box')
        pydg.add_node(pyd_nd[node])

    for edge in graph.edges:
        if not edge.source.is_admin:
            pydg.add_edge(
                pydot.Edge(pyd_nd[edge.source], pyd_nd[edge.destination]))

    # draw service nodes and edges
    if with_services:
        sam = compose_service_access_map(graph)
        for service in sam.keys():
            pyd_nd[service] = pydot.Node(service,
                                         style='filled',
                                         fillcolor='#DDFFDD')
            pydg.add_node(pyd_nd[service])

        for service, node_list in sam.items():
            for node in node_list:
                pydg.add_edge(pydot.Edge(pyd_nd[service], pyd_nd[node]))

    # and draw
    pydg.write(filepath, format=file_format)
예제 #5
0
def write_privesc_graphml(graph: Graph, filepath: str) -> None:
    """The function to generate the privesc-only visualization with a GraphML file: this is only the nodes that are
    admins/privesc with blue/red highlights."""

    node_color_mapping = {}
    for node in graph.nodes:
        if node.is_admin:
            node_color_mapping[node] = 'blue'
        elif can_privesc(graph, node)[0]:
            node_color_mapping[node] = 'red'

    result = ET.ElementTree(ET.Element(None))
    generate_graphml(node_color_mapping, graph.edges, result)
    result.write(filepath, 'utf-8', True)
예제 #6
0
def write_standard_graphml(graph: Graph, filepath: str) -> None:
    """The function to generate the standard visualization with a GraphML file: this is all the nodes with the
    admins/privesc highlights in blue/red respectively."""

    node_color_mapping = {}
    for node in graph.nodes:
        if node.is_admin:
            node_color_mapping[node] = 'blue'
        elif can_privesc(graph, node)[0]:
            node_color_mapping[node] = 'red'
        else:
            node_color_mapping[node] = 'white'

    result = ET.ElementTree(ET.Element(None))
    generate_graphml(node_color_mapping, graph.edges, result)
    result.write(filepath, 'utf-8', True)
예제 #7
0
def gen_privesc_findings(graph: Graph) -> List[Finding]:
    """Generates findings related to privilege escalation risks."""
    result = []

    node_path_list = []

    for node in graph.nodes:
        if node.is_admin:
            continue  # skip current admins
        privesc_res, edge_list = can_privesc(graph, node)
        if privesc_res:
            node_path_list.append((node, edge_list))

    if len(node_path_list) > 0:
        description_preamble = 'In AWS, IAM Principals such as IAM Users or IAM Roles have their permissions defined ' \
                               'using IAM Policies. These policies describe different actions, resources, and ' \
                               'conditions where the principal can make a given API call to a service.\n\n' \
                               'Administrative principals can call any action with any resource, as in the ' \
                               'AdministratorAccess AWS-managed policy. However, some permissions may allow another ' \
                               'non-administrative principal to gain access to an administrative principal. ' \
                               'This represents a privilege escalation risk.\n\n' \
                               'The following principals could escalate privileges:\n\n'

        description_body = ''
        for node, edge_list in node_path_list:
            end_of_list = edge_list[-1].destination
            description_body += '* {} can escalate privileges by accessing the administrative principal {}:\n'.format(
                node.searchable_name(), end_of_list.searchable_name())
            for edge in edge_list:
                description_body += '   * {}\n'.format(edge.describe_edge())
            description_body += '\n'

        result.append(
            Finding(
                'IAM {} Can Escalate Privileges'.format(
                    'Principals' if len(node_path_list) > 1 else 'Principal'),
                'High',
                'A lower-privilege IAM User or Role is able to gain administrative privileges. This could lead to the '
                'lower-privilege principal being used to compromise the account and its resources.',
                description_preamble + description_body,
                'Review the IAM Policies that are applicable to the affected IAM User(s) or Role(s). Either reduce the '
                'permissions of the administrative principal(s), or reduce the permissions of the principal(s) that can '
                'access the administrative principals.'))

    return result