Visualizing Computer Networks
Using Dash Cytoscape to create interactive visuals based on real computer network data
Background
I work in Cyber Security. And if I’m being really honest, it’s not very interesting. There are plenty of tools, open-source or proprietary, that can automate many of the tedious tasks within a security role, but I always find myself more fascinated by how the tool works than by how much it’s helping me.
Cyber Security, Incident Response, Digital Forensics…They’re all very destructive by nature. But I’m a creative. How can a creative add value to an intrinsically destructive field? And how can I make my job more interesting by mixing it with my interests?
By creating tools.
This post is a walkthrough of a personal project I completed, which is a small step towards the creation of my own tool.
The Project
Turning thousands of lines of pcap data into interactive network visualisations.
Complete code available on my github repository.
Too often an Incident Responder asks for a map of the network, only to discover it’s more than 2 years old and not much more than a ghost of the current network topology.
So we try to create the ‘ground truth’ of what the network actually looks like. Various tools can be used for this, netflow, nmap, tracert. This example will focus on network packet captures, but a bit of Python applied in the right doses could adapt this project to any network mapping command line tool output.
Wireshark is a great tool, but as the size of the packet capture increases, the performance drops as it tries to parse the potential hundreds of thousands of lines of traffic data.
So I began by exporting my pcap data to a csv. Which python loves.
Parsing Script
The script is on the repo. It’s not fancy. It was a quick and dirty creation to produce a quick result. Python is good for that. Which I love.
I managed to turn about 10,000 lines of this (data is publicly available and not licensed)
"No.","Time","Source","Destination","Protocol","Length","Info""1","0.000000","220.175.8.56","80.171.48.1","Messenger","488","NetrSendMessage request""2","300.930215","222.136.251.117","80.171.48.1","Messenger","488","NetrSendMessage request""3","300.931213","222.136.251.117","80.171.48.1","Messenger","488","NetrSendMessage request""4","860.029588","70.85.177.186","80.171.48.1","Messenger","521","NetrSendMessage request""5","860.030586","70.85.177.186","80.171.48.1","Messenger","521","NetrSendMessage request""6","919.623101","61.239.151.129","80.171.48.1","Messenger","843","NetrSendMessage request"+ ~10000 more lines
Into this (feel free to scroll past) The output isn’t too interesting…yet.
[{"data": {"id": "10.200.1.18", "label": "10.200.1.18"}},{"data": {"id": "10.50.1.54", "label": "10.50.1.54"}},{"data": {"id": "10.200.1.252", "label": "10.200.1.252"}},{"data": {"id": "10.200.1.162", "label": "10.200.1.162"}},{"data": {"id": "54.175.48.202", "label": "54.175.48.202"}},{"data": {"id": "10.200.1.184", "label": "10.200.1.184"}},{"data": {"id": "10.200.1.45", "label": "10.200.1.45"}},{"data": {"id": "10.200.1.51", "label": "10.200.1.51"}},{"data": {"id": "10.200.1.191", "label": "10.200.1.191"}},{"data": {"id": "10.200.1.231", "label": "10.200.1.231"}},{"data": {"id": "10.200.1.86", "label": "10.200.1.86"}},{"data": {"id": "10.200.1.178", "label": "10.200.1.178"}},{"data": {"id": "8.8.8.8", "label": "8.8.8.8"}},{"data": {"source": "10.200.1.18", "target": "10.50.1.54", "weight": 10901}},{"data": {"source": "10.50.1.54", "target": "10.200.1.18", "weight": 10794}},{"data": {"source": "10.200.1.18", "target": "10.200.1.252", "weight": 54}},{"data": {"source": "10.200.1.252", "target": "10.200.1.18", "weight": 86}},{"data": {"source": "10.200.1.162", "target": "10.200.1.18", "weight": 1040}},{"data": {"source": "10.200.1.18", "target": "10.200.1.162", "weight": 67}},{"data": {"source": "10.200.1.18", "target": "8.8.8.8", "weight": 11}},{"data": {"source": "10.200.1.18", "target": "54.175.48.202", "weight": 46}},{"data": {"source": "54.175.48.202", "target": "10.200.1.18", "weight": 29}},{"data": {"source": "10.200.1.18", "target": "10.200.1.184", "weight": 45}},{"data": {"source": "10.200.1.184", "target": "10.200.1.18", "weight": 70}},{"data": {"source": "10.200.1.45", "target": "10.200.1.18", "weight": 1636}},{"data": {"source": "10.200.1.18", "target": "10.200.1.45", "weight": 102}},{"data": {"source": "10.200.1.18", "target": "10.200.1.51", "weight": 55}},{"data": {"source": "10.200.1.51", "target": "10.200.1.18", "weight": 85}},{"data": {"source": "10.200.1.18", "target": "10.200.1.191", "weight": 14}},{"data": {"source": "10.200.1.191", "target": "10.200.1.18", "weight": 16}},{"data": {"source": "10.200.1.231", "target": "10.200.1.18", "weight": 979}},{"data": {"source": "10.200.1.86", "target": "10.200.1.18", "weight": 3}},{"data": {"source": "10.200.1.18", "target": "10.200.1.86", "weight": 2}},{"data": {"source": "10.200.1.18", "target": "10.200.1.178", "weight": 15}},{"data": {"source": "10.200.1.178", "target": "10.200.1.18", "weight": 12}},{"data": {"source": "10.200.1.18", "target": "10.200.1.231", "weight": 6}},{"data": {"source": "10.200.1.18", "target": "10.200.1.231", "weight": 6}}]
So we managed to turn over 10,000 lines of csv data into about 38 lines of json. The nifty thing about the script was that it created a weight attribute.
For example.
{"data": {"source": "10.200.1.162", "target": "10.200.1.18", "weight": 1040}}
See above how the weight is 1040? That means that the source connected to the target 1040 times. We don’t need to see every connection, just how many there were.
Dash
Dash was built by Plotly on top of Flask to integrate python data visualisations into web apps.
Cytoscape
According to https://js.cytoscape.org/, cytoscape.js is
[A]Graph theory (network) library for visualisation and analysis.
Dash-Cytoscape
“Dash Cytoscape is a graph visualization component for creating easily customizable, high-performance, interactive, and web-based networks. It extends and renders Cytoscape.js, and offers deep integration with Dash layouts and callbacks, enabling the creation of powerful networks in conjunction with the rich collection of Dash components, as well as established computational biology and network science libraries such as Biopython and networkX.”
— xhlulu and the Dash Team, https://dash.plotly.com/cytoscape
Building the app
I can’t take too much credit for this code, as the Plotly team made it so easy.
All you really need to do is copy the quickstart guide on the dash-cytoscape page, plug in your data, select your layout and run the app.
I added a json import to load my newly created json data.
import dashimport dash_cytoscape as cytoimport dash_html_components as htmlimport json
Create an instance for your app
app = dash.Dash(__name__)
Load your data
with open('net.json', 'r') as f:data = json.load(f)
Create your layout
app.layout = html.Div([cyto.Cytoscape(id='cytoscape-compound',layout={'name': 'breadthfirst','roots': '[id = "Web"]'},style={'width': '100%', 'height': '1000px'},stylesheet=[{'selector': 'node','style': {'content': 'data(label)'}},{'selector': '.segment','style': {'width': 5}},{'selector': '.joiner','style': {'line-style': 'dashed'}}],elements=data)])
Run the server when you run the file
if __name__ == '__main__':app.run_server(debug=True)
And the result!
You can head over to my repo for more detailed instructions on creating the code, cloning the repo and inserting your own data.
Thank you for reading. See you in the cloud.
Eamon
Twitter @ejooco
Github https://github.com/ejooco