Projects STRLCPY LogonTracer Commits b89112c4
🤬
  • ■ ■ ■ ■ ■
    docker/Dockerfile
    skipped 25 lines
    26 26   curl \
    27 27   python \
    28 28   py-pip \
    29  - libssl1.0
     29 + libssl1.1
    30 30   
    31 31  ## python3.6 and modules install
    32 32  ENV PYTHON_VERSION=3.6.2 \
    skipped 18 lines
    51 51   
    52 52  RUN git clone https://github.com/JPCERTCC/LogonTracer.git \
    53 53   && cd LogonTracer \
     54 + && pip3.6 install --upgrade pip \
     55 + && pip3.6 install --upgrade setuptools \
    54 56   && pip3.6 install numpy \
    55 57   && pip3.6 install -r requirements.txt \
    56 58   && unlink /var/lib/neo4j/data \
    skipped 35 lines
  • images/diff_pannel.png
  • images/filter_pannel.png
  • images/nav_bar.png
  • images/sample.png
  • images/side_bar.png
  • images/timeline.png
  • images/timeline_graph.png
  • ■ ■ ■ ■ ■
    logontracer.py
    skipped 209 lines
    210 210  statement_r = """
    211 211   MATCH (user:Username{ user:{user} })
    212 212   MATCH (ip:IPAddress{ IP:{IP} })
    213  - CREATE (ip)-[event:Event]->(user) set event.id={id}, event.logintype={logintype}, event.status={status}, event.count={count} , event.authname={authname}
     213 + CREATE (ip)-[event:Event]->(user) set event.id={id}, event.logintype={logintype}, event.status={status}, event.count={count}, event.authname={authname}, event.date={date}
    214 214   
    215 215   RETURN user, ip
    216 216   """
    skipped 29 lines
    246 246  statement_pr = """
    247 247   MATCH (id:ID{ id:{id} })
    248 248   MATCH (user:Username{ user:{user} })
    249  - CREATE (user)-[group:Policy]->(id)
     249 + CREATE (user)-[group:Policy]->(id) set group.date={date}
    250 250   
    251 251   RETURN user, id
    252 252   """
    skipped 294 lines
    547 547   
    548 548  # Parse the EVTX file
    549 549  def parse_evtx(evtx_list):
    550  - event_set = pd.DataFrame(index=[], columns=["eventid", "ipaddress", "username", "logintype", "status", "authname"])
     550 + event_set = pd.DataFrame(index=[], columns=["eventid", "ipaddress", "username", "logintype", "status", "authname", "date"])
    551 551   count_set = pd.DataFrame(index=[], columns=["dates", "eventid", "username"])
    552 552   ml_frame = pd.DataFrame(index=[], columns=["date", "user", "host", "id"])
    553 553   username_set = []
    skipped 123 lines
    677 677   sid = "-"
    678 678   authname = "-"
    679 679   
     680 + ###
     681 + # Detect admin users
     682 + # EventID 4672: Special privileges assigned to new logon
     683 + ###
    680 684   if eventid == 4672:
    681 685   for data in event_data:
    682 686   if data.get("Name") in "SubjectUserName" and data.text is not None and not re.search(UCHECK, data.text):
    skipped 4 lines
    687 691   username = "-"
    688 692   if username not in admins and username != "-":
    689 693   admins.append(username)
     694 + ###
     695 + # Detect removed user account and added user account.
     696 + # EventID 4720: A user account was created
     697 + # EventID 4726: A user account was deleted
     698 + ###
    690 699   elif eventid in [4720, 4726]:
    691 700   for data in event_data:
    692 701   if data.get("Name") in "TargetUserName" and data.text is not None and not re.search(UCHECK, data.text):
    skipped 6 lines
    699 708   addusers[username] = etime.strftime("%Y-%m-%d %H:%M:%S")
    700 709   else:
    701 710   delusers[username] = etime.strftime("%Y-%m-%d %H:%M:%S")
     711 + ###
     712 + # Detect Audit Policy Change
     713 + # EventID 4719: System audit policy was changed
     714 + ###
    702 715   elif eventid == 4719:
    703 716   for data in event_data:
    704 717   if data.get("Name") in "SubjectUserName" and data.text is not None and not re.search(UCHECK, data.text):
    skipped 6 lines
    711 724   category = data.text
    712 725   if data.get("Name") in "SubcategoryGuid" and data.text is not None and re.search(r"\A{[\w\-]*}\Z", data.text):
    713 726   guid = data.text
    714  - policylist.append([etime.strftime("%Y-%m-%d %H:%M:%S"), username, category, guid.lower()])
     727 + policylist.append([etime.strftime("%Y-%m-%d %H:%M:%S"), username, category, guid.lower(), int(stime.strftime("%s"))])
     728 + ###
     729 + # Detect added users from specific group
     730 + # EventID 4728: A member was added to a security-enabled global group
     731 + # EventID 4732: A member was added to a security-enabled local group
     732 + # EventID 4756: A member was added to a security-enabled universal group
     733 + ###
    715 734   elif eventid in [4728, 4732, 4756]:
    716 735   for data in event_data:
    717 736   if data.get("Name") in "TargetUserName" and data.text is not None and not re.search(UCHECK, data.text):
    skipped 1 lines
    719 738   elif data.get("Name") in "MemberSid" and data.text not in "-" and data.text is not None and re.search(r"\AS-[0-9\-]*\Z", data.text):
    720 739   usid = data.text
    721 740   addgroups[usid] = "AddGroup: " + groupname + "(" + etime.strftime("%Y-%m-%d %H:%M:%S") + ") "
     741 + ###
     742 + # Detect removed users from specific group
     743 + # EventID 4729: A member was removed from a security-enabled global group
     744 + # EventID 4733: A member was removed from a security-enabled local group
     745 + # EventID 4757: A member was removed from a security-enabled universal group
     746 + ###
    722 747   elif eventid in [4729, 4733, 4757]:
    723 748   for data in event_data:
    724 749   if data.get("Name") in "TargetUserName" and data.text is not None and not re.search(UCHECK, data.text):
    skipped 1 lines
    726 751   elif data.get("Name") in "MemberSid" and data.text not in "-" and data.text is not None and re.search(r"\AS-[0-9\-]*\Z", data.text):
    727 752   usid = data.text
    728 753   removegroups[usid] = "RemoveGroup: " + groupname + "(" + etime.strftime("%Y-%m-%d %H:%M:%S") + ") "
     754 + ###
     755 + # Detect DCSync
     756 + # EventID 4662: An operation was performed on an object
     757 + ###
    729 758   elif eventid == 4662:
    730 759   for data in event_data:
    731 760   if data.get("Name") in "SubjectUserName" and data.text is not None and not re.search(UCHECK, data.text):
    skipped 6 lines
    738 767   if dcsync_count[username] == 3:
    739 768   dcsync[username] = etime.strftime("%Y-%m-%d %H:%M:%S")
    740 769   dcsync_count[username] = 0
     770 + ###
     771 + # Detect DCShadow
     772 + # EventID 5137: A directory service object was created
     773 + # EventID 5141: A directory service object was deleted
     774 + ###
    741 775   elif eventid in [5137, 5141]:
    742 776   for data in event_data:
    743 777   if data.get("Name") in "SubjectUserName" and data.text is not None and not re.search(UCHECK, data.text):
    skipped 6 lines
    750 784   dcshadow[username] = etime.strftime("%Y-%m-%d %H:%M:%S")
    751 785   else:
    752 786   dcshadow_check.append(etime.strftime("%Y-%m-%d %H:%M:%S"))
     787 + ###
     788 + # Parse logon logs
     789 + # EventID 4624: An account was successfully logged on
     790 + # EventID 4625: An account failed to log on
     791 + # EventID 4768: A Kerberos authentication ticket (TGT) was requested
     792 + # EventID 4769: A Kerberos service ticket was requested
     793 + # EventID 4776: The domain controller attempted to validate the credentials for an account
     794 + ###
    753 795   else:
    754 796   for data in event_data:
     797 + # parse IP Address
    755 798   if data.get("Name") in ["IpAddress", "Workstation"] and data.text is not None and (not re.search(HCHECK, data.text) or re.search(IPv4_PATTERN, data.text) or re.search(r"\A::ffff:\d+\.\d+\.\d+\.\d+\Z", data.text) or re.search(IPv6_PATTERN, data.text)):
    756 799   ipaddress = data.text.split("@")[0]
    757 800   ipaddress = ipaddress.lower().replace("::ffff:", "")
    758 801   ipaddress = ipaddress.replace("\\", "")
    759  - 
     802 + # Parse hostname
    760 803   if data.get("Name") == "WorkstationName" and data.text is not None and (not re.search(HCHECK, data.text) or re.search(IPv4_PATTERN, data.text) or re.search(r"\A::ffff:\d+\.\d+\.\d+\.\d+\Z", data.text) or re.search(IPv6_PATTERN, data.text)):
    761 804   hostname = data.text.split("@")[0]
    762 805   hostname = hostname.lower().replace("::ffff:", "")
    763 806   hostname = hostname.replace("\\", "")
    764  - 
     807 + # Parse username
    765 808   if data.get("Name") in "TargetUserName" and data.text is not None and not re.search(UCHECK, data.text):
    766 809   username = data.text.split("@")[0]
    767 810   if username[-1:] not in "$":
    768 811   username = username.lower() + "@"
    769 812   else:
    770 813   username = "-"
    771  - 
     814 + # Parse targeted domain name
    772 815   if data.get("Name") in "TargetDomainName" and data.text is not None and not re.search(HCHECK, data.text):
    773 816   domain = data.text
    774  - 
     817 + # parse trageted user SID
    775 818   if data.get("Name") in ["TargetUserSid", "TargetSid"] and data.text is not None and re.search(r"\AS-[0-9\-]*\Z", data.text):
    776 819   sid = data.text
    777  - 
     820 + # parse lonon type
    778 821   if data.get("Name") in "LogonType" and re.search(r"\A\d{1,2}\Z", data.text):
    779 822   logintype = int(data.text)
    780  - 
     823 + # parse status
    781 824   if data.get("Name") in "Status" and re.search(r"\A0x\w{8}\Z", data.text):
    782 825   status = data.text
    783  - 
     826 + # parse Authentication package name
    784 827   if data.get("Name") in "AuthenticationPackageName" and re.search(r"\A\w*\Z", data.text):
    785 828   authname = data.text
    786 829   
    787  - if username != "-" and ipaddress != "::1" and ipaddress != "127.0.0.1" and (ipaddress != "-" or hostname != "-"):
     830 + if username != "-" and username != "anonymous logon" and ipaddress != "::1" and ipaddress != "127.0.0.1" and (ipaddress != "-" or hostname != "-"):
     831 + # generate pandas series
    788 832   if ipaddress != "-":
    789  - event_series = pd.Series([eventid, ipaddress, username, logintype, status, authname], index=event_set.columns)
     833 + event_series = pd.Series([eventid, ipaddress, username, logintype, status, authname, int(stime.strftime("%s"))], index=event_set.columns)
    790 834   ml_series = pd.Series([etime.strftime("%Y-%m-%d %H:%M:%S"), username, ipaddress, eventid], index=ml_frame.columns)
    791 835   else:
    792  - event_series = pd.Series([eventid, hostname, username, logintype, status, authname], index=event_set.columns)
     836 + event_series = pd.Series([eventid, hostname, username, logintype, status, authname, int(stime.strftime("%s"))], index=event_set.columns)
    793 837   ml_series = pd.Series([etime.strftime("%Y-%m-%d %H:%M:%S"), username, hostname, eventid], index=ml_frame.columns)
     838 + # append pandas series to dataframe
    794 839   event_set = event_set.append(event_series, ignore_index=True)
    795 840   ml_frame = ml_frame.append(ml_series, ignore_index=True)
    796 841   # print("%s,%i,%s,%s,%s,%s" % (eventid, ipaddress, username, comment, logintype))
    skipped 18 lines
    815 860   
    816 861   if authname in "NTML" and authname not in ntmlauth:
    817 862   ntmlauth.append(username)
    818  - 
     863 + ###
     864 + # Detect the audit log deletion
     865 + # EventID 1102: The audit log was cleared
     866 + ###
    819 867   if eventid == 1102:
    820 868   logtime = node.xpath("/Event/System/TimeCreated")[0].get("SystemTime")
    821 869   try:
    skipped 30 lines
    852 900   
    853 901   if hosts:
    854 902   event_set = event_set.replace(hosts)
     903 + event_set_bydate = event_set
     904 + event_set_bydate["count"] = event_set_bydate.groupby(["eventid", "ipaddress", "username", "logintype", "status", "authname", "date"])["eventid"].transform("count")
     905 + event_set_bydate = event_set_bydate.drop_duplicates()
     906 + event_set = event_set.drop("date", axis=1)
    855 907   event_set["count"] = event_set.groupby(["eventid", "ipaddress", "username", "logintype", "status", "authname"])["eventid"].transform("count")
    856 908   event_set = event_set.drop_duplicates()
    857 909   count_set["count"] = count_set.groupby(["dates", "eventid", "username"])["dates"].transform("count")
    skipped 36 lines
    894 946   hostname = hosts_inv[ipaddress]
    895 947   else:
    896 948   hostname = ipaddress
     949 + # add the IPAddress node to neo4j
    897 950   tx.append(statement_ip, {"IP": ipaddress, "rank": ranks[ipaddress], "hostname": hostname})
    898 951   
    899 952   i = 0
    skipped 21 lines
    921 974   ustatus += "DCShadow(" + dcshadow[username] + ") "
    922 975   if not ustatus:
    923 976   ustatus = "-"
     977 + 
     978 + # add the username node to neo4j
    924 979   tx.append(statement_user, {"user": username[:-1], "rank": ranks[username], "rights": rights, "sid": sid, "status": ustatus,
    925 980   "counts": ",".join(map(str, timelines[i*6])), "counts4624": ",".join(map(str, timelines[i*6+1])),
    926 981   "counts4625": ",".join(map(str, timelines[i*6+2])), "counts4768": ",".join(map(str, timelines[i*6+3])),
    skipped 2 lines
    929 984   i += 1
    930 985   
    931 986   for domain in domains:
     987 + # add the domain node to neo4j
    932 988   tx.append(statement_domain, {"domain": domain})
    933 989   
    934  - for _, events in event_set.iterrows():
     990 + for _, events in event_set_bydate.iterrows():
     991 + # add the (username)-(event)-(ip) link to neo4j
    935 992   tx.append(statement_r, {"user": events["username"][:-1], "IP": events["ipaddress"], "id": events["eventid"], "logintype": events["logintype"],
    936  - "status": events["status"], "count": events["count"], "authname": events["authname"]})
     993 + "status": events["status"], "count": events["count"], "authname": events["authname"], "date": events["date"]})
    937 994   
    938 995   for username, domain in domain_set_uniq:
     996 + # add (username)-()-(domain) link to neo4j
    939 997   tx.append(statement_dr, {"user": username[:-1], "domain": domain})
    940 998   
     999 + # add the date node to neo4j
    941 1000   tx.append(statement_date, {"Daterange": "Daterange", "start": datetime.datetime(*starttime.timetuple()[:4]).strftime("%Y-%m-%d %H:%M:%S"),
    942 1001   "end": datetime.datetime(*endtime.timetuple()[:4]).strftime("%Y-%m-%d %H:%M:%S")})
    943 1002   
    944 1003   if len(deletelog):
     1004 + # add the delete flag node to neo4j
    945 1005   tx.append(statement_del, {"deletetime": deletelog[0], "user": deletelog[1], "domain": deletelog[2]})
    946 1006   
    947 1007   if len(policylist):
    skipped 8 lines
    956 1016   else:
    957 1017   sub = policy[3]
    958 1018   username = policy[1]
     1019 + # add the policy id node to neo4j
    959 1020   tx.append(statement_pl, {"id": id, "changetime": policy[0], "category": category, "sub": sub})
    960  - tx.append(statement_pr, {"user": username[:-1], "id": id})
     1021 + # add (username)-(policy)-(id) link to neo4j
     1022 + tx.append(statement_pr, {"user": username[:-1], "id": id, "date": policy[4]})
    961 1023   id += 1
    962 1024   
    963 1025   tx.process()
    skipped 66 lines
  • sample/graph.db.tar.gz
    Binary file.
  • ■ ■ ■ ■ ■ ■
    static/js/script.js
    1 1  function buildGraph(graph, path, root) {
     2 + var objidList = []
    2 3   for (idx in path) {
    3 4   if (Object.keys(path[idx]).length == 3) {
    4 5   objid = parseInt(path[idx].identity.low) + 100;
    5 6   } else {
    6 7   objid = parseInt(path[idx].identity.low) + 1000;
    7 8   }
    8  - 
    9 9   // Node
    10 10   if (Object.keys(path[idx]).length == 3) {
    11 11   var ndupflg = false;
    skipped 5 lines
    17 17   if (ndupflg) {
    18 18   continue;
    19 19   }
    20  - 
    21 20   nprivilege = "";
    22 21   nsub = "";
    23 22   ncategory = "";
    skipped 92 lines
    116 115   });
    117 116   } else {
    118 117   // Relationship
    119  - var ldupflg = false;
    120  - for (nidx in graph.edges) {
    121  - if (graph.edges[nidx].data.objid == objid) {
    122  - ldupflg = true;
    123  - }
    124  - }
    125  - if (ldupflg) {
     118 + if (objidList.indexOf(objid) >= 0) {
    126 119   continue;
     120 + } else {
     121 + objidList.push(objid)
    127 122   }
     123 + 
    128 124   if (path[idx].type == "Event") {
    129 125   var label_count = document.getElementById("label-count").checked;
    130 126   var label_type = document.getElementById("label-type").checked;
    131 127   var label_authname = document.getElementById("label-authname").checked;
     128 + var sourceid = parseInt(path[parseInt(idx) - 1].identity.low) + 100
     129 + var targetid = parseInt(path[parseInt(idx) + 1].identity.low) + 100
     130 + 
     131 + var filterdArray = $.grep(graph.edges,
     132 + function(elem, index, array) {
     133 + return (!(elem.data.source == sourceid && elem.data.target == targetid && elem.data.label == path[idx].type &&
     134 + elem.data.eid == path[idx].properties.id && elem.data.logontype == path[idx].properties.logintype &&
     135 + elem.data.status == path[idx].properties.status && elem.data.authname == path[idx].properties.authname));
     136 + }
     137 + );
     138 + var matchArray = $.grep(graph.edges,
     139 + function(elem, index, array) {
     140 + return (elem.data.source == sourceid && elem.data.target == targetid && elem.data.label == path[idx].type &&
     141 + elem.data.eid == path[idx].properties.id && elem.data.logontype == path[idx].properties.logintype &&
     142 + elem.data.status == path[idx].properties.status && elem.data.authname == path[idx].properties.authname);
     143 + }
     144 + );
     145 + var ecount = parseInt(path[idx].properties.count)
     146 + if (Object.keys(matchArray).length) {
     147 + ecount = ecount + parseInt(matchArray[0].data.count)
     148 + }
     149 + graph.edges = filterdArray
    132 150   var ename = path[idx].properties.id;
    133 151   if (label_count) {
    134 152   ename += " : " + path[idx].properties.count;
    skipped 7 lines
    142 160   graph.edges.push({
    143 161   "data": {
    144 162   "id": objid,
    145  - "source": parseInt(path[parseInt(idx) - 1].identity.low) + 100,
    146  - "target": parseInt(path[parseInt(idx) + 1].identity.low) + 100,
     163 + "source": sourceid,
     164 + "target": targetid,
    147 165   "objid": objid,
    148 166   "elabel": ename,
    149 167   "label": path[idx].type,
    150 168   "distance": 5,
    151 169   "ntype": "edge",
    152  - "eid": path[idx].properties.id,
    153  - "count": path[idx].properties.count,
    154  - "logontype": path[idx].properties.logintype,
     170 + "eid": parseInt(path[idx].properties.id),
     171 + "count": ecount,
     172 + "logontype": String(path[idx].properties.logintype),
    155 173   "status": path[idx].properties.status,
    156 174   "authname": path[idx].properties.authname
    157 175   }
    skipped 13 lines
    171 189   }
    172 190   }
    173 191   }
    174  - 
    175 192   return (graph);
    176 193  }
    177 194   
    skipped 3 lines
    181 198   var flagCircle = document.getElementById("modeCircle").checked;
    182 199   var flagTree = document.getElementById("modeTree").checked;
    183 200   var flagMode = "";
    184  - 
    185 201   if (flagGrid) {
    186 202   flagMode = "grid";
    187 203   }
    skipped 6 lines
    194 210   if (flagTree) {
    195 211   flagMode = "breadthfirst";
    196 212   }
    197  - 
    198  - 
    199 213   cy = cytoscape({
    200 214   container: document.getElementById("cy"),
    201 215   boxSelectionEnabled: false,
    skipped 40 lines
    242 256   padding: 10
    243 257   }
    244 258   });
    245  - 
    246 259   cy.on("layoutstop", function() {
    247 260   loading.classList.add("loaded");
    248 261   });
    249  - 
    250 262   cy.nodes().forEach(function(ele) {
    251 263   ele.qtip({
    252 264   content: {
    skipped 10 lines
    263 275   }
    264 276   });
    265 277   });
    266  - 
    267 278   cy.edges().forEach(function(ele) {
    268 279   ele.qtip({
    269 280   content: {
    skipped 12 lines
    282 293   });
    283 294  }
    284 295   
     296 +/*
     297 +qtipNode
     298 +This function generate the description text for each node.
     299 +*/
    285 300  function qtipNode(ndata) {
    286 301   var qtext = 'Name: ' + ndata._private.data["nlabel"];
    287 302   if (ndata._private.data["ntype"] == "User") {
    skipped 8 lines
    296 311   qtext += '<br>Category: ' + ndata._private.data["ncategory"];
    297 312   qtext += '<br>Subcategory: ' + ndata._private.data["nsub"];
    298 313   }
    299  - 
    300 314   if (ndata._private.data["ntype"] != "Policy") {
    301 315   qtext += '<br><button type="button" class="btn btn-primary btn-xs" onclick="createRankQuery(\'' + ndata._private.data["nlabel"] + '\',\'' + ndata._private.data["ntype"] + '\')">search</button>';
    302 316   }
    303  - 
    304 317   return qtext;
    305 318  }
    306 319   
     320 +/*
     321 +qtipEdge
     322 +This function generate the description text for each edge.
     323 +*/
    307 324  function qtipEdge(ndata) {
    308 325   var qtext = "";
    309 326   if (ndata._private.data["label"] == "Event") {
    skipped 24 lines
    334 351   return qtext;
    335 352  }
    336 353   
     354 +/*
     355 +createAllQuery
     356 +This function execute neo4j query and show all users in specific time period with graph.
     357 +The result is filtered by Event ID selected in the check box.
     358 +*/
    337 359  function createAllQuery() {
    338  - eidStr = getQueryID();
     360 + var eidStr = getQueryID();
     361 + var dateStr = getDateRange();
    339 362   eidStr = eidStr.slice(4);
    340  - queryStr = 'MATCH (user)-[event:Event]-(ip) WHERE ' + eidStr + ' RETURN user, event, ip';
     363 + var queryStr = 'MATCH (user)-[event:Event]-(ip) WHERE ' + eidStr + dateStr + ' RETURN user, event, ip';
    341 364   //console.log(queryStr);
    342 365   executeQuery(queryStr, "noRoot");
    343 366  }
    344 367   
     368 +/*
     369 +createSystemQuery
     370 +This function execute neo4j query and show all system privilege users in specific time period with graph.
     371 +The result is filtered by Event ID selected in the check box.
     372 +*/
    345 373  function createSystemQuery() {
    346  - eidStr = getQueryID();
    347  - queryStr = 'MATCH (user)-[event:Event]-(ip) WHERE user.rights = "system" ' + eidStr + ' RETURN user, event, ip';
     374 + var eidStr = getQueryID();
     375 + var dateStr = getDateRange();
     376 + var queryStr = 'MATCH (user)-[event:Event]-(ip) WHERE user.rights = "system" ' + eidStr + dateStr + ' RETURN user, event, ip';
    348 377   //console.log(queryStr);
    349 378   executeQuery(queryStr, "noRoot");
    350 379  }
    351 380   
     381 +/*
     382 +createRDPQuery
     383 +This function execute neo4j query and show RDP logon users in specific time period with graph.
     384 +The result is filtered by Event ID selected in the check box.
     385 +*/
    352 386  function createRDPQuery() {
    353  - eidStr = getQueryID();
    354  - queryStr = 'MATCH (user)-[event:Event]-(ip) WHERE event.logintype = 10 ' + eidStr + ' RETURN user, event, ip';
     387 + var eidStr = getQueryID();
     388 + var dateStr = getDateRange();
     389 + var queryStr = 'MATCH (user)-[event:Event]-(ip) WHERE event.logintype = 10 ' + eidStr + dateStr + ' RETURN user, event, ip';
    355 390   //console.log(queryStr);
    356 391   executeQuery(queryStr, "noRoot");
    357 392  }
    358 393   
     394 +/*
     395 +createNetQuery
     396 +This function execute neo4j query and show users who logon via network in specific time period with graph.
     397 +The result is filtered by Event ID selected in the check box.
     398 +*/
    359 399  function createNetQuery() {
    360  - eidStr = getQueryID();
    361  - queryStr = 'MATCH (user)-[event:Event]-(ip) WHERE event.logintype = 3 ' + eidStr + ' RETURN user, event, ip';
     400 + var eidStr = getQueryID();
     401 + var dateStr = getDateRange();
     402 + var queryStr = 'MATCH (user)-[event:Event]-(ip) WHERE event.logintype = 3 ' + eidStr + dateStr + ' RETURN user, event, ip';
    362 403   //console.log(queryStr);
    363 404   executeQuery(queryStr, "noRoot");
    364 405  }
    365 406   
     407 +/*
     408 +createBatchQuery
     409 +This function execute neo4j query and show users who logon by batch script in specific time period with graph.
     410 +The result is filtered by Event ID selected in the check box.
     411 +*/
    366 412  function createBatchQuery() {
    367  - eidStr = getQueryID();
    368  - queryStr = 'MATCH (user)-[event:Event]-(ip) WHERE event.logintype = 4 ' + eidStr + ' RETURN user, event, ip';
     413 + var eidStr = getQueryID();
     414 + var dateStr = getDateRange();
     415 + var queryStr = 'MATCH (user)-[event:Event]-(ip) WHERE event.logintype = 4 ' + eidStr + dateStr + ' RETURN user, event, ip';
    369 416   //console.log(queryStr);
    370 417   executeQuery(queryStr, "noRoot");
    371 418  }
    372 419   
     420 +/*
     421 +createServiceQuery
     422 +This function execute neo4j query and show users who logon from windows service in specific time period with graph.
     423 +The result is filtered by Event ID selected in the check box.
     424 +*/
    373 425  function createServiceQuery() {
    374  - eidStr = getQueryID();
    375  - queryStr = 'MATCH (user)-[event:Event]-(ip) WHERE event.logintype = 5 ' + eidStr + ' RETURN user, event, ip';
     426 + var eidStr = getQueryID();
     427 + var dateStr = getDateRange();
     428 + var queryStr = 'MATCH (user)-[event:Event]-(ip) WHERE event.logintype = 5 ' + eidStr + dateStr + ' RETURN user, event, ip';
    376 429   //console.log(queryStr);
    377 430   executeQuery(queryStr, "noRoot");
    378 431  }
    379 432   
     433 +/*
     434 +create14068Query
     435 +This function execute neo4j query and show users who attempted to exploit MS14-068 in specific time period with graph.
     436 +*/
    380 437  function create14068Query() {
    381  - queryStr = 'MATCH (user)-[event:Event]-(ip) WHERE event.status =~ ".*0F" AND event.id = 4769 RETURN user, event, ip';
     438 + var dateStr = getDateRange();
     439 + var queryStr = 'MATCH (user)-[event:Event]-(ip) WHERE event.status =~ ".*0F" AND event.id = 4769 ' + dateStr + ' RETURN user, event, ip';
    382 440   //console.log(queryStr);
    383 441   executeQuery(queryStr, "noRoot");
    384 442  }
    385 443   
     444 +/*
     445 +createFailQuery
     446 +This function execute neo4j query and show users who failed to logon in specific time period with graph.
     447 +*/
    386 448  function createFailQuery() {
    387  - queryStr = 'MATCH (user)-[event:Event]-(ip) WHERE event.id = 4625 RETURN user, event, ip';
     449 + var dateStr = getDateRange();
     450 + var queryStr = 'MATCH (user)-[event:Event]-(ip) WHERE event.id = 4625 ' + dateStr + ' RETURN user, event, ip';
    388 451   //console.log(queryStr);
    389 452   executeQuery(queryStr, "noRoot");
    390 453  }
    391 454   
     455 +/*
     456 +createNTLMQuery
     457 +This function execute neo4j query and show users who login with NTLM authentication in specific time period with graph.
     458 +*/
    392 459  function createNTLMQuery() {
    393  - queryStr = 'MATCH (user)-[event:Event]-(ip) WHERE event.id = 4624 and event.authname = "NTLM" and event.logintype = 3 RETURN user, event, ip';
     460 + var dateStr = getDateRange();
     461 + var queryStr = 'MATCH (user)-[event:Event]-(ip) WHERE event.id = 4624 and event.authname = "NTLM" and event.logintype = 3 ' + dateStr + ' RETURN user, event, ip';
    394 462   //console.log(queryStr);
    395 463   executeQuery(queryStr, "noRoot");
    396 464  }
    397 465   
     466 +/*
     467 +adddelUserQuery
     468 +This function execute neo4j query and show users who had be created or deleted in specific time period with graph.
     469 +*/
    398 470  function adddelUsersQuery() {
    399  - queryStr = 'MATCH (user)-[event:Event]-(ip) WHERE (user.status =~ "Created.*") OR (user.status =~ ".*Deleted.*") OR (user.status =~ ".*RemoveGroup.*") OR (user.status =~ ".*AddGroup.*") RETURN user, event, ip';
     471 + var dateStr = getDateRange();
     472 + var queryStr = 'MATCH (user)-[event:Event]-(ip) WHERE (user.status =~ "Created.*") OR (user.status =~ ".*Deleted.*") OR (user.status =~ ".*RemoveGroup.*") OR (user.status =~ ".*AddGroup.*") ' + dateStr + ' RETURN user, event, ip';
    400 473   //console.log(queryStr);
    401 474   executeQuery(queryStr, "noRoot");
    402 475  }
    403 476   
     477 +/*
     478 +dcsQuery
     479 +This function execute neo4j query and show users who executed DCSync or DCShadow in specific time period with graph.
     480 +*/
    404 481  function dcsQuery() {
    405  - queryStr = 'MATCH (user)-[event:Event]-(ip) WHERE (user.status =~ ".*DCSync.*") OR (user.status =~ ".*DCShadow.*") RETURN user, event, ip';
     482 + var dateStr = getDateRange();
     483 + var queryStr = 'MATCH (user)-[event:Event]-(ip) WHERE (user.status =~ ".*DCSync.*") OR (user.status =~ ".*DCShadow.*") ' + dateStr + ' RETURN user, event, ip';
    406 484   //console.log(queryStr);
    407 485   executeQuery(queryStr, "noRoot");
    408 486  }
    409 487   
     488 +/*
     489 +dcsQuery
     490 +This function execute neo4j query and show users who executed DCSync or DCShadow in specific time period with graph.
     491 +*/
    410 492  function createDomainQuery() {
    411  - queryStr = 'MATCH (user)-[event:Group]-(ip) RETURN user, event, ip';
     493 + var queryStr = 'MATCH (user)-[event:Group]-(ip) RETURN user, event, ip';
    412 494   //console.log(queryStr);
    413 495   executeQuery(queryStr, "noRoot");
    414 496  }
    415 497   
     498 +/*
     499 +policyQuery
     500 +This function execute neo4j query and show users who changed the audit policy in specific time period with graph.
     501 +*/
    416 502  function policyQuery() {
    417  - queryStr = 'MATCH (user)-[event:Policy]-(ip) RETURN user, event, ip';
     503 + var dateStr = getDateRange();
     504 + dateStr = dateStr.slice(5);
     505 + queryStr = 'MATCH (user)-[event:Policy]-(ip) WHERE ' + dateStr + ' RETURN user, event, ip';
    418 506   //console.log(queryStr);
    419 507   executeQuery(queryStr, "noRoot");
    420 508  }
    421 509   
    422 510  function createRankQuery(setStr, qType) {
     511 + var dateStr = getDateRange();
    423 512   if (qType == "User") {
    424 513   whereStr = 'user.user = "' + setStr + '" ';
    425 514   }
    skipped 3 lines
    429 518   
    430 519   if (qType != "Domain") {
    431 520   eidStr = getQueryID();
    432  - queryStr = 'MATCH (user)-[event:Event]-(ip) WHERE (' + whereStr + ') ' + eidStr + ' RETURN user, event, ip';
     521 + queryStr = 'MATCH (user)-[event:Event]-(ip) WHERE (' + whereStr + ') ' + eidStr + dateStr + ' RETURN user, event, ip';
    433 522   } else {
    434 523   queryStr = 'MATCH (user)-[event:Group]-(ip) WHERE user.domain = "' + setStr + '" RETURN user, event, ip'
    435 524   }
    skipped 1 lines
    437 526   executeQuery(queryStr, setStr);
    438 527  }
    439 528   
     529 +/*
     530 +getQueryID
     531 +This function generate the neo4j query strings to filter Windows Event ID and ID count.
     532 +*/
    440 533  function getQueryID() {
    441 534   var id4624Ch = document.getElementById("id4624").checked;
    442 535   var id4625Ch = document.getElementById("id4625").checked;
    skipped 23 lines
    466 559   return eidStr;
    467 560  }
    468 561   
     562 +/*
     563 +getDateRange
     564 +This function generates a neo4j query strings to filter events in specific time period.
     565 +*/
     566 +function getDateRange() {
     567 + var fromDate = new Date(document.getElementById("from-date").value).getTime() / 1000;
     568 + var toDate = new Date(document.getElementById("to-date").value).getTime() / 1000;
     569 + var dateStr = " AND (event.date >= " + fromDate + " AND event.date <= " + toDate + ")";
     570 + 
     571 + return dateStr;
     572 +}
     573 + 
     574 +/*
     575 +createQuery
     576 +This function generates a neo4j query strings from search box and execute the query.
     577 +*/
    469 578  function createQuery() {
    470 579   var selectVal = document.getElementById("InputSelect").value;
    471 580   var setStr = document.getElementById("query-input").value;
     581 + var dateStr = getDateRange();
    472 582   
    473 583   if (selectVal == "Username") {
    474 584   whereStr = 'user.user =~ "' + setStr + '" ';
    skipped 17 lines
    492 602   }
    493 603   
    494 604   eidStr = getQueryID()
    495  - queryStr = 'MATCH (user)-[event:Event]-(ip) WHERE (' + whereStr + ') ' + eidStr + ' RETURN user, event, ip';
     605 + queryStr = 'MATCH (user)-[event:Event]-(ip) WHERE (' + whereStr + ') ' + eidStr + dateStr + ' RETURN user, event, ip';
    496 606   //console.log(queryStr);
    497 607   executeQuery(queryStr, setStr);
    498 608  }
    499 609   
     610 +/*
     611 +searchPath
     612 +This function execute a neo4j query strings to search the shortest path to system privilege in specific time period.
     613 +*/
    500 614  function searchPath() {
    501 615   var setStr = document.getElementById("query-input").value;
     616 + var dateStr = getDateRange();
     617 + dateStr = dateStr.slice(5);
    502 618   
    503 619   queryStr = 'MATCH (from:Username) WHERE from.user = "' + setStr + '" \
    504 620   MATCH (to:Username) WHERE to.rights = "system" \
    505 621   MATCH (user:Username) WHERE user IN shortestPath((from)-[:Event*]-(to)) \
    506 622   MATCH (ip:IPAddress) WHERE ip IN shortestPath((from)-[:Event*]-(to)) \
    507  - MATCH (user)-[event]-(ip)\
     623 + MATCH (user)-[event]-(ip) WHERE ' + dateStr + ' \
    508 624   RETURN user, ip, event'
    509 625   
    510 626   //console.log(queryStr);
    511 627   executeQuery(queryStr, setStr);
    512 628  }
    513 629   
     630 +/*
     631 +sendQuery
     632 +This function sends the query to neo4j.
     633 +If the query success, this function build the graph and draw it from the neo4j query result.
     634 +*/
    514 635  function sendQuery(queryStr, root) {
    515 636   var graph = {
    516 637   "nodes": [],
    skipped 35 lines
    552 673   });
    553 674  }
    554 675   
     676 +/*
     677 +executeQuery
     678 +This function executes the neo4j query.
     679 +*/
    555 680  function executeQuery(queryStr, root) {
    556  - var countStr = queryStr.replace("user, event, ip" , "COUNT(event)");
     681 + var countStr = queryStr.replace("user, event, ip", "COUNT(event)");
    557 682   
    558 683   session.run(countStr)
    559 684   .subscribe({
    skipped 19 lines
    579 704   });
    580 705  }
    581 706   
     707 +/*
     708 +diffQuery
     709 +This function compare 2 days events from neo4j.
     710 +If the query success, this function build the graph and draw it from the neo4j query result.
     711 +*/
     712 +function diffQuery() {
     713 + var graph1 = {
     714 + "nodes": [],
     715 + "edges": []
     716 + };
     717 + 
     718 + root = "noRoot"
     719 + var date1st = new Date(document.getElementById("from-day").value).getTime() / 1000;
     720 + 
     721 + queryStr1st = 'MATCH (user)-[event:Event]-(ip) WHERE event.date >= ' + date1st + ' AND event.date <= ' + (date1st + 86400) + ' RETURN user, event, ip';
     722 + 
     723 + session.run(queryStr1st)
     724 + .subscribe({
     725 + onNext: function(record) {
     726 + //console.log(record.get('user'), record.get('event'), record.get('ip'));
     727 + graph1 = buildGraph(graph1, [record.get("user"), record.get("event"), record.get("ip")], root);
     728 + },
     729 + onCompleted: function() {
     730 + session.close();
     731 + if (graph1.nodes.length == 0) {
     732 + searchError();
     733 + } else {
     734 + diffNext(graph1);
     735 + }
     736 + },
     737 + onError: function(error) {
     738 + searchError();
     739 + console.log("Error: ", error);
     740 + }
     741 + });
     742 +}
     743 + 
     744 +function diffNext(graph1) {
     745 + var graph2 = {
     746 + "nodes": [],
     747 + "edges": []
     748 + };
     749 + 
     750 + root = "noRoot"
     751 + var date2nd = new Date(document.getElementById("to-day").value).getTime() / 1000;
     752 + 
     753 + queryStr2nd = 'MATCH (user)-[event:Event]-(ip) WHERE event.date >= ' + date2nd + ' AND event.date <= ' + (date2nd + 86400) + ' RETURN user, event, ip';
     754 + 
     755 + var loading = document.getElementById('loading');
     756 + loading.classList.remove('loaded');
     757 + 
     758 + session.run(queryStr2nd)
     759 + .subscribe({
     760 + onNext: function(record) {
     761 + //console.log(record.get('user'), record.get('event'), record.get('ip'));
     762 + graph2 = buildGraph(graph2, [record.get("user"), record.get("event"), record.get("ip")], root);
     763 + },
     764 + onCompleted: function() {
     765 + session.close();
     766 + if (graph2.nodes.length == 0) {
     767 + searchError();
     768 + loading.classList.add("loaded");
     769 + } else {
     770 + graph2.edges = getArrayDiff(graph1, graph2);
     771 + graph2.nodes = nodeConcat(graph1, graph2);
     772 + if (graph2.edges.length > 0) {
     773 + drawGraph(graph2, graph2.nodes[0].data.id);
     774 + } else{
     775 + searchError();
     776 + loading.classList.add("loaded");
     777 + }
     778 + }
     779 + },
     780 + onError: function(error) {
     781 + searchError();
     782 + console.log("Error: ", error);
     783 + }
     784 + });
     785 +}
     786 + 
     787 +function getArrayDiff(arr1, arr2) {
     788 + let arr = arr1.edges.concat(arr2.edges);
     789 + return arr.filter((v, i)=> {
     790 + return !(arr1.edges.findIndex(obj => obj.data.source === v.data.source, obj => obj.data.target === v.data.target) >= 0 &&
     791 + arr2.edges.findIndex(obj => obj.data.source === v.data.source, obj => obj.data.target === v.data.target) >= 0);
     792 + });
     793 +}
     794 + 
     795 +function nodeConcat(arr1, arr2) {
     796 + let arr = arr1.nodes.concat(arr2.nodes);
     797 + return arr.filter((v, i)=> {
     798 + console.log(arr2.edges)
     799 + console.log(arr)
     800 + return (arr2.edges.findIndex(obj => obj.data.source === v.data.id) >= 0 ||
     801 + arr2.edges.findIndex(obj => obj.data.target === v.data.id) >= 0);
     802 + });
     803 +}
     804 + 
    582 805  var setqueryStr = "";
     806 + 
    583 807  function contQuery() {
    584 808   sendQuery(setqueryStr, "noRoot");
    585 809  }
    skipped 227 lines
    813 1037   var html = '<div class="table-responsive"><table class="table table-bordered table-condensed table-striped table-wrapper" style="background-color:#EEE;"><thead><tr>\
    814 1038   <th ' + span + '>Username</th>';
    815 1039   
    816  - for (i = 0; i < chartArray.length; i ++) {
     1040 + for (i = 0; i < chartArray.length; i++) {
    817 1041   if (chartArray[i]) {
    818 1042   chartArray[i].destroy();
    819 1043   }
    skipped 140 lines
    960 1184   var timelineElem = document.getElementById("cy");
    961 1185   timelineElem.innerHTML = html;
    962 1186   
    963  - $(function(){
     1187 + $(function() {
    964 1188   $(".table.table-wrapper").floatThead({
    965  - responsiveContainer: function($table){
     1189 + responsiveContainer: function($table) {
    966 1190   return $table.closest(".table-responsive");
    967 1191   }
    968 1192   });
    skipped 7 lines
    976 1200  }
    977 1201   
    978 1202  var chartArray = new Array();
     1203 + 
    979 1204  function createTimelineGraph(queryStr) {
    980 1205   var users = new Array();
    981 1206   var dates = new Array();
    skipped 6 lines
    988 1213   dateData = record.get("date");
    989 1214   nodeData = record.get("user");
    990 1215   users.push([nodeData.properties.user, nodeData.properties.counts4624, nodeData.properties.counts4625, nodeData.properties.counts4768,
    991  - nodeData.properties.counts4769, nodeData.properties.counts4776, nodeData.properties.detect]);
     1216 + nodeData.properties.counts4769, nodeData.properties.counts4776, nodeData.properties.detect
     1217 + ]);
    992 1218   starttime = dateData.properties.start;
    993 1219   endtime = dateData.properties.end;
    994 1220   },
    skipped 13 lines
    1008 1234   type: "line",
    1009 1235   data: {
    1010 1236   labels: dates,
    1011  - datasets: [
    1012  - {
    1013  - label: "4624",
    1014  - borderColor: "rgb(141, 147, 200)",
    1015  - backgroundColor: "rgb(141, 147, 200)",
    1016  - pointHoverBorderColor: "rgb(255, 0, 0)",
    1017  - lineTension: 0,
    1018  - fill: false,
    1019  - data: users[i][1].split(","),
    1020  - pointRadius: 5,
    1021  - pointHoverRadius: 10,
    1022  - },
    1023  - {
    1024  - label: "4625",
    1025  - borderColor: "rgb(89, 195, 225)",
    1026  - backgroundColor: "rgb(89, 195, 225)",
    1027  - pointHoverBorderColor: "rgb(255, 0, 0)",
    1028  - lineTension: 0,
    1029  - fill: false,
    1030  - data: users[i][2].split(","),
    1031  - pointRadius: 5,
    1032  - pointHoverRadius: 10,
    1033  - },
    1034  - {
    1035  - label: "4768",
    1036  - borderColor: "rgb(30, 44, 92)",
    1037  - backgroundColor: "rgb(30, 44, 92)",
    1038  - pointHoverBorderColor: "rgb(255, 0, 0)",
    1039  - lineTension: 0,
    1040  - fill: false,
    1041  - data: users[i][3].split(","),
    1042  - pointRadius: 5,
    1043  - pointHoverRadius: 10,
    1044  - },
    1045  - {
    1046  - label: "4769",
    1047  - borderColor: "rgb(1, 96, 140)",
    1048  - backgroundColor: "rgb(1, 96, 140)",
    1049  - pointHoverBorderColor: "rgb(255, 0, 0)",
    1050  - lineTension: 0,
    1051  - fill: false,
    1052  - data: users[i][4].split(","),
    1053  - pointRadius: 5,
    1054  - pointHoverRadius: 10,
    1055  - },
    1056  - {
    1057  - label: "4776",
    1058  - borderColor: "rgb(0, 158, 150)",
    1059  - backgroundColor: "rgb(0, 158, 150)",
    1060  - pointHoverBorderColor: "rgb(255, 0, 0)",
    1061  - lineTension: 0,
    1062  - fill: false,
    1063  - data: users[i][5].split(","),
    1064  - pointRadius: 5,
    1065  - pointHoverRadius: 10,
    1066  - },
    1067  - {
    1068  - label: "Anomaly Score",
    1069  - borderColor: "rgb(230, 0, 57)",
    1070  - backgroundColor: "rgb(230, 0, 57)",
    1071  - pointHoverBorderColor: "rgb(255, 0, 0)",
    1072  - lineTension: 0,
    1073  - fill: false,
    1074  - data: users[i][6].split(","),
    1075  - pointRadius: 5,
    1076  - pointHoverRadius: 10,
    1077  - yAxisID: "y-right",
    1078  - },
     1237 + datasets: [{
     1238 + label: "4624",
     1239 + borderColor: "rgb(141, 147, 200)",
     1240 + backgroundColor: "rgb(141, 147, 200)",
     1241 + pointHoverBorderColor: "rgb(255, 0, 0)",
     1242 + lineTension: 0,
     1243 + fill: false,
     1244 + data: users[i][1].split(","),
     1245 + pointRadius: 5,
     1246 + pointHoverRadius: 10,
     1247 + },
     1248 + {
     1249 + label: "4625",
     1250 + borderColor: "rgb(89, 195, 225)",
     1251 + backgroundColor: "rgb(89, 195, 225)",
     1252 + pointHoverBorderColor: "rgb(255, 0, 0)",
     1253 + lineTension: 0,
     1254 + fill: false,
     1255 + data: users[i][2].split(","),
     1256 + pointRadius: 5,
     1257 + pointHoverRadius: 10,
     1258 + },
     1259 + {
     1260 + label: "4768",
     1261 + borderColor: "rgb(30, 44, 92)",
     1262 + backgroundColor: "rgb(30, 44, 92)",
     1263 + pointHoverBorderColor: "rgb(255, 0, 0)",
     1264 + lineTension: 0,
     1265 + fill: false,
     1266 + data: users[i][3].split(","),
     1267 + pointRadius: 5,
     1268 + pointHoverRadius: 10,
     1269 + },
     1270 + {
     1271 + label: "4769",
     1272 + borderColor: "rgb(1, 96, 140)",
     1273 + backgroundColor: "rgb(1, 96, 140)",
     1274 + pointHoverBorderColor: "rgb(255, 0, 0)",
     1275 + lineTension: 0,
     1276 + fill: false,
     1277 + data: users[i][4].split(","),
     1278 + pointRadius: 5,
     1279 + pointHoverRadius: 10,
     1280 + },
     1281 + {
     1282 + label: "4776",
     1283 + borderColor: "rgb(0, 158, 150)",
     1284 + backgroundColor: "rgb(0, 158, 150)",
     1285 + pointHoverBorderColor: "rgb(255, 0, 0)",
     1286 + lineTension: 0,
     1287 + fill: false,
     1288 + data: users[i][5].split(","),
     1289 + pointRadius: 5,
     1290 + pointHoverRadius: 10,
     1291 + },
     1292 + {
     1293 + label: "Anomaly Score",
     1294 + borderColor: "rgb(230, 0, 57)",
     1295 + backgroundColor: "rgb(230, 0, 57)",
     1296 + pointHoverBorderColor: "rgb(255, 0, 0)",
     1297 + lineTension: 0,
     1298 + fill: false,
     1299 + data: users[i][6].split(","),
     1300 + pointRadius: 5,
     1301 + pointHoverRadius: 10,
     1302 + yAxisID: "y-right",
     1303 + },
    1079 1304   ]
    1080 1305   },
    1081 1306   options: {
    1082 1307   responsive: true,
    1083 1308   legend: {
    1084  - position: "bottom",
     1309 + position: "bottom",
    1085 1310   fontSize: 15,
    1086  - },
    1087  - scales: {
    1088  - xAxes: [{
    1089  - display: true,
    1090  - scaleLabel: {
    1091  - display: true,
     1311 + },
     1312 + scales: {
     1313 + xAxes: [{
     1314 + display: true,
     1315 + scaleLabel: {
     1316 + display: true,
    1092 1317   fontSize: 15,
    1093  - labelString: "Date"
    1094  - }
    1095  - }],
    1096  - yAxes: [{
    1097  - display: true,
    1098  - scaleLabel: {
    1099  - display: true,
    1100  - fontSize: 15,
    1101  - labelString: "Count"
    1102  - }
    1103  - },
    1104  - {
    1105  - display: true,
    1106  - id: "y-right",
    1107  - position: "right",
    1108  - scaleLabel: {
    1109  - display: true,
    1110  - fontSize: 15,
    1111  - labelString: "Score"
    1112  - },
    1113  - ticks: {
    1114  - max: 20
     1318 + labelString: "Date"
     1319 + }
     1320 + }],
     1321 + yAxes: [{
     1322 + display: true,
     1323 + scaleLabel: {
     1324 + display: true,
     1325 + fontSize: 15,
     1326 + labelString: "Count"
     1327 + }
     1328 + },
     1329 + {
     1330 + display: true,
     1331 + id: "y-right",
     1332 + position: "right",
     1333 + scaleLabel: {
     1334 + display: true,
     1335 + fontSize: 15,
     1336 + labelString: "Score"
     1337 + },
     1338 + ticks: {
     1339 + max: 20
     1340 + }
    1115 1341   }
    1116  - }]
    1117  - },
     1342 + ]
     1343 + },
    1118 1344   title: {
    1119  - display: true,
    1120  - fontSize: 18,
    1121  - text: users[i][0]
     1345 + display: true,
     1346 + fontSize: 18,
     1347 + text: users[i][0]
    1122 1348   },
    1123 1349   elements: {
    1124  - point: {
    1125  - pointStyle: "crossRot"
    1126  - }
    1127  - }
     1350 + point: {
     1351 + pointStyle: "crossRot"
     1352 + }
     1353 + }
    1128 1354   }
    1129 1355   });
    1130 1356   }
    skipped 8 lines
    1139 1365   });
    1140 1366  }
    1141 1367   
    1142  -function addCanvas(users){
    1143  - var canvasArray = new Array();
    1144  - var obj = document.getElementById("addcanvas");
    1145  - obj.textContent = null;
     1368 +function addCanvas(users) {
     1369 + var canvasArray = new Array();
     1370 + var obj = document.getElementById("addcanvas");
     1371 + obj.textContent = null;
    1146 1372   
    1147  - for (i = 1; i <= users.length; i++) {
    1148  - var canvas = document.createElement("canvas");
    1149  - canvas.id = "canvas" + i;
    1150  - canvas.style = "height:400px;";
    1151  - canvasArray.push(canvas);
     1373 + for (i = 1; i <= users.length; i++) {
     1374 + var canvas = document.createElement("canvas");
     1375 + canvas.id = "canvas" + i;
     1376 + canvas.style = "height:400px;";
     1377 + canvasArray.push(canvas);
    1152 1378   
    1153  - obj.appendChild(canvas);
    1154  - }
     1379 + obj.appendChild(canvas);
     1380 + }
    1155 1381   
    1156  - return canvasArray;
     1382 + return canvasArray;
    1157 1383  }
    1158 1384   
    1159 1385  function createAlltimeline() {
    skipped 41 lines
    1201 1427   }
    1202 1428  }
    1203 1429   
    1204  - 
     1430 +/*
     1431 +logdeleteCheck
     1432 +push alert if the event log had deleted.
     1433 +*/
    1205 1434  function logdeleteCheck() {
    1206 1435   var queryStr = "MATCH (date:Deletetime) RETURN date";
    1207 1436   var ddata = "";
    skipped 23 lines
    1231 1460   });
    1232 1461  }
    1233 1462   
     1463 +/*
     1464 +searchError
     1465 +push alert if search has failed.
     1466 +*/
    1234 1467  function searchError() {
    1235 1468   var elemMsg = document.getElementById("error");
    1236 1469   elemMsg.innerHTML =
    skipped 4 lines
    1241 1474   });
    1242 1475  }
    1243 1476   
     1477 +/*
     1478 +file_upload
     1479 +Upload EVTX file or XML file to LogonTracer Server.
     1480 +*/
    1244 1481  function file_upload() {
    1245 1482   var upfile = document.getElementById("lefile");
    1246 1483   var timezone = document.getElementById("utcTime").value;
    skipped 54 lines
    1301 1538   document.getElementById("status").innerHTML = '<div class="alert alert-info">Upload Aborted</div>';
    1302 1539  }
    1303 1540   
     1541 +/*
     1542 +parseEVTX
     1543 +Get EVTX parsing progress from log.
     1544 +*/
    1304 1545  function parseEVTX() {
    1305 1546   var xmlhttp2 = new XMLHttpRequest();
    1306 1547   xmlhttp2.open("GET", "/log");
    skipped 24 lines
    1331 1572   }
    1332 1573  }
    1333 1574   
    1334  -var formatDate = function (date) {
     1575 +/*
     1576 +loaddate
     1577 +load date info from neo4j
     1578 +*/
     1579 +function loaddate() {
     1580 + var queryStr = 'MATCH (date:Date) RETURN date';
     1581 + 
     1582 + session.run(queryStr)
     1583 + .subscribe({
     1584 + onNext: function(record) {
     1585 + dateData = record.get("date");
     1586 + starttime = dateData.properties.start;
     1587 + endtime = dateData.properties.end;
     1588 + },
     1589 + onCompleted: function() {
     1590 + session.close();
     1591 + var minDate = new Date(starttime);
     1592 + var maxDate = new Date(endtime);
     1593 + maxDate.setTime(maxDate.getTime() + 3600000);
     1594 + 
     1595 + var minDay = new Date(starttime);
     1596 + var maxDay = new Date(endtime);
     1597 + var setminDate = new Date(minDay.getFullYear(), minDay.getMonth(), minDay.getDate())
     1598 + minDay.setTime(setminDate.getTime());
     1599 + var setmaxDate = new Date(maxDay.getFullYear(), maxDay.getMonth(), maxDay.getDate())
     1600 + maxDay.setTime(setmaxDate.getTime());
     1601 + 
     1602 + $('.fromdate').datetimepicker({
     1603 + locale: "en",
     1604 + format: "YYYY-MM-DD HH:00:00",
     1605 + useCurrent: false,
     1606 + defaultDate: minDate,
     1607 + maxDate: maxDate,
     1608 + minDate: minDate
     1609 + });
     1610 + 
     1611 + $('.todate').datetimepicker({
     1612 + locale: "en",
     1613 + format: "YYYY-MM-DD HH:00:00",
     1614 + useCurrent: false,
     1615 + defaultDate: maxDate,
     1616 + maxDate: maxDate,
     1617 + minDate: minDate
     1618 + });
     1619 + 
     1620 + $('.fromday').datetimepicker({
     1621 + locale: "en",
     1622 + format: "YYYY-MM-DD",
     1623 + useCurrent: false,
     1624 + defaultDate: minDay,
     1625 + maxDate: maxDay,
     1626 + minDate: minDay
     1627 + });
     1628 + 
     1629 + $('.today').datetimepicker({
     1630 + locale: "en",
     1631 + format: "YYYY-MM-DD",
     1632 + useCurrent: false,
     1633 + defaultDate: maxDay,
     1634 + maxDate: maxDay,
     1635 + minDate: minDay
     1636 + });
     1637 + },
     1638 + onError: function(error) {
     1639 + console.log("Error: ", error);
     1640 + }
     1641 + });
     1642 +}
     1643 + 
     1644 +var formatDate = function(date) {
    1335 1645   format = "YYYY-MM-DD hh:00:00";
    1336 1646   format = format.replace(/YYYY/g, date.getFullYear());
    1337 1647   format = format.replace(/MM/g, ('0' + (date.getMonth() + 1)).slice(-2));
    skipped 6 lines
  • ■ ■ ■ ■ ■
    templates/index.html
    skipped 7 lines
    8 8   <link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/qtip2/2.2.1/jquery.qtip.css" integrity="sha384-EG4MkHYaMXjB6f2q1t0Jfs+W6DpGsGZls4D6PYHr9yhXwZf27Z10ReappeV2ZXcU" crossorigin="anonymous">
    9 9   <link rel="stylesheet" href="https://gitcdn.github.io/bootstrap-toggle/2.2.2/css/bootstrap-toggle.min.css" integrity="sha384-yBEPaZw444dClEfen526Q6x4nwuzGO6PreKpbRVSLFCci3oYGE5DnD1pNsubCxYW" crossorigin="anonymous">
    10 10   <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.2.0/css/font-awesome.min.css" integrity="sha384-CmLV3WR+cw/TcN50vJSYAs2EAzhDD77tQvGcmoZ1KEzxtpl2K5xkrpFz9N2H9ClN" crossorigin="anonymous">
     11 + <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.47/css/bootstrap-datetimepicker.css" integrity="sha384-BxjoNa5Gy6HWpewxZeRKXOU9soDrevzGlc0x2UrFD5yPVuBIz/3YUxvzchy7Q1+k" crossorigin="anonymous">
    11 12   <link rel="stylesheet" href="static/css/style.css">
    12 13   <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
    13 14   <script src="https://cdnjs.cloudflare.com/ajax/libs/qtip2/2.2.1/jquery.qtip.js" integrity="sha384-6pAYkjo39N26cI9QEzy7zTD9xr9XzSnaWywG02LeFyoJnBEyYvWvqomLU+uGAlaw" crossorigin="anonymous"></script>
    skipped 1 lines
    15 16   <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
    16 17   <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cytoscape.min.js" integrity="sha384-vTbGUxFr0qyAqYeS+nIqQ6Row8uIp1PgvAWiA4GuEtdkD8scsArkL9QgZHWaHuv5" crossorigin="anonymous"></script>
    17 18   <script src="https://cdn.jsdelivr.net/npm/[email protected]/cytoscape-qtip.min.js" integrity="sha384-0S5MX36ySZW8tkZEooDZdxYdGvtwdVxA/1bl0U0zoqsrHBJbv4LxKxc8Hp8LpxlE" crossorigin="anonymous"></script>
     19 + <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js" integrity="sha384-sIzeKWIAHvT0Vm8QbfLCqZwBG0WMCkWVAOYd/330YSNeeQ1Y57N3T9lQz5Ry/EHH" crossorigin="anonymous"></script>
     20 + <script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.47/js/bootstrap-datetimepicker.min.js" integrity="sha384-eRwUWQDbnWMRrNpCKFsqmkfL7PMM8a4uUw5AvjTuLRoYFfozRz7g9BS696LvdNrE" crossorigin="anonymous"></script>
    18 21   <!-- Neo4j JavaScript Driver -->
    19 22   <script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/browser/neo4j-web.min.js" integrity="sha384-XVrDQNh79isRQKoUzX8pCHmnP2HmtLQJTDMfzI5/4gxbjqz7mHlGjTKyQxVGKR7k" crossorigin="anonymous"></script>
    20 23   <script src="static/js/script.js"></script>
    skipped 17 lines
    38 41   </div>
    39 42   <input type="button" class="btn btn-default" value="+" onclick="ItemField.add();" />
    40 43   <input type="button" class="btn btn-default" value="-" onclick="ItemField.del();" />
    41  - <div class="checkbox">
    42  - <a data-toggle="tooltip" data-placement="bottom" data-original-title="Select Event ID to visualize.">Event ID: </a>
    43  - <label data-toggle="tooltip" data-placement="bottom" data-original-title="Successful logon">
    44  - <input type="checkbox" id="id4624" checked="checked"> 4624
    45  - </label>
    46  - <label data-toggle="tooltip" data-placement="bottom" data-original-title="Logon failure">
    47  - <input type="checkbox" id="id4625" checked="checked"> 4625
    48  - </label>
    49  - <label data-toggle="tooltip" data-placement="bottom" data-original-title="Kerberos Authentication (TGT Request)">
    50  - <input type="checkbox" id="id4768" checked="checked"> 4768
    51  - </label>
    52  - <label data-toggle="tooltip" data-placement="bottom" data-original-title="Kerberos Service Ticket (ST Request)">
    53  - <input type="checkbox" id="id4769" checked="checked"> 4769
    54  - </label>
    55  - <label data-toggle="tooltip" data-placement="bottom" data-original-title="NTLM Authentication">
    56  - <input type="checkbox" id="id4776" checked="checked"> 4776
    57  - </label>
    58  - </div>
    59  - <a data-toggle="tooltip" data-placement="bottom" data-original-title="Set the lower limit of Event ID to visualize."> Count: </a>
    60  - <div class="form-group">
    61  - <input class="form-control" type="text" value=0 id="count-input" size="1">
    62  - </div>
     44 + <input type="button" class="btn btn-default" data-toggle="modal" value="Filter" data-target="#Filters">
    63 45   <button type="button" class="btn btn-default" data-toggle="tooltip" data-placement="bottom" data-original-title="Username/IPAddress/Hostname search" onclick="createQuery()">search</button>
    64 46   <button type="button" class="btn btn-default" data-toggle="tooltip" data-placement="bottom" data-original-title="Search for how exploit the administrator account from the account. (only one Username)" onclick="searchPath()">search path</button>
    65 47   <div class="btn-group">
    skipped 29 lines
    95 77   <button type="button" class="list-group-item" data-toggle="tooltip" data-placement="bottom" data-original-title="Visualizing deleted or added users." onclick="adddelUsersQuery()">Add/Delete Users</button>
    96 78   <button type="button" class="list-group-item" data-toggle="tooltip" data-placement="bottom" data-original-title="Visualizing all domain names. If an attacker is intrude into a network, there may be a malicious domain name." onclick="createDomainQuery()">Domain Check</button>
    97 79   <button type="button" class="list-group-item" data-toggle="tooltip" data-placement="bottom" data-original-title="Visualizing changed audit policy." onclick="policyQuery()">Audit Policy Change</button>
     80 + <button type="button" class="list-group-item" data-toggle="modal" data-target="#Diff">Diff Graph</button>
     81 + <button type="button" class="list-group-item" data-toggle="tooltip" data-placement="bottom" data-original-title="Displays hourly event log counts in time series." onclick="window.open('timeline')">Create Timeline</button>
    98 82   </div>
    99 83   <hr>
    100 84   <a data-toggle="tooltip" data-placement="bottom" data-original-title="Add value to edges of visualization graph.">Add event value</a><br>
    skipped 21 lines
    122 106   <a data-toggle="tooltip" data-placement="bottom" data-original-title="Enable visualization of malicious account ranking.">Rank visualize mode</a><br>
    123 107   <input type="checkbox" data-toggle="toggle" data-on="Enabled" data-height="35" data-off="Disabled" id="rankMode">
    124 108   <hr>
    125  - <a data-toggle="tooltip" data-placement="bottom" data-original-title="Displays hourly event log counts in time series.">Timeline</a><br>
    126  - <button class="btn btn-default" onClick="window.open('timeline')">Create Timeline</button>
    127  - <hr>
    128 109   <a data-toggle="tooltip" data-placement="bottom" data-original-title="Import event logs in EVTX or XML format.">Upload</a><br>
    129 110   <button class="btn btn-default" data-toggle="modal" data-target="#UploadEVTX">Upload Event Log</button>
    130 111   </div>
    skipped 24 lines
    155 136   <div class="modal-dialog modal-lg">
    156 137   <div class="modal-content">
    157 138   <div class="modal-header">
    158  - <button type="button" class="close" data-dismiss="modal"><span>×</span></button>
     139 + <button type="button" class="close" data-dismiss="modal"><span class="glyphicon glyphicon-remove"></span></button>
    159 140   <h4 class="modal-title">Upload Event Log File</h4>
     141 + <p>Import the event log. Supported file format is EVTX or XML (exported Event Viewer or PowerShell).</p>
    160 142   </div>
    161 143   <div class="modal-body">
    162 144   <div id="zoneTime"></div>
    skipped 19 lines
    182 164   </div>
    183 165   </div>
    184 166   </div>
     167 + <!-- Filter -->
     168 + <div class="modal fade" id="Filters" tabindex="-1">
     169 + <div class="modal-dialog">
     170 + <div class="modal-content">
     171 + <div class="modal-header">
     172 + <button type="button" class="close" data-dismiss="modal"><span class="glyphicon glyphicon-remove"></span></button>
     173 + <h4 class="modal-title">Event Log Filter</h4>
     174 + <p>Filter event log by date, count and id.</p>
     175 + </div>
     176 + <div class="modal-body">
     177 + <div class="container-fluid">
     178 + <div class="row">
     179 + <h4 class="col-md-2">Date<h4>
     180 + <div class="col-md-10 ml-auto">
     181 + <div class="form-group">
     182 + <div class="input-group fromdate">
     183 + <input type="text" placeholder="From" id="from-date" class="form-control" />
     184 + <span class="input-group-addon">
     185 + <span class="glyphicon glyphicon-calendar"></span>
     186 + </span>
     187 + </div>
     188 + </div>
     189 + <div class="form-group">
     190 + <div class="input-group todate">
     191 + <input type="text" placeholder="To" id="to-date" class="form-control" />
     192 + <span class="input-group-addon">
     193 + <span class="glyphicon glyphicon-calendar"></span>
     194 + </span>
     195 + </div>
     196 + </div>
     197 + </div>
     198 + </div>
     199 + <hr>
     200 + <div class="row">
     201 + <h4 class="col-md-3">Event ID</h4>
     202 + <div class="col-md-9 ml-auto">
     203 + <div class="checkbox">
     204 + <label data-toggle="tooltip" data-placement="bottom" data-original-title="Successful logon">
     205 + <input type="checkbox" id="id4624" checked="checked"> 4624
     206 + </label>
     207 + <label data-toggle="tooltip" data-placement="bottom" data-original-title="Logon failure">
     208 + <input type="checkbox" id="id4625" checked="checked"> 4625
     209 + </label>
     210 + <label data-toggle="tooltip" data-placement="bottom" data-original-title="Kerberos Authentication (TGT Request)">
     211 + <input type="checkbox" id="id4768" checked="checked"> 4768
     212 + </label>
     213 + <label data-toggle="tooltip" data-placement="bottom" data-original-title="Kerberos Service Ticket (ST Request)">
     214 + <input type="checkbox" id="id4769" checked="checked"> 4769
     215 + </label>
     216 + <label data-toggle="tooltip" data-placement="bottom" data-original-title="NTLM Authentication">
     217 + <input type="checkbox" id="id4776" checked="checked"> 4776
     218 + </label>
     219 + </div>
     220 + </div>
     221 + </div>
     222 + <hr>
     223 + <div class="row">
     224 + <h4 class="col-md-2">Count<h4>
     225 + <div class="col-md-5 ml-auto">
     226 + <div class="form-group">
     227 + <input class="form-control" type="text" value=0 id="count-input" data-toggle="tooltip" data-placement="bottom" data-original-title="Set the lower limit of Event ID to visualize.">
     228 + </div>
     229 + </div>
     230 + </div>
     231 + </div>
     232 + </div>
     233 + <div class="modal-footer">
     234 + <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
     235 + </div>
     236 + </div>
     237 + </div>
     238 + </div>
     239 + </div>
     240 + <!-- Diff Graph -->
     241 + <div class="modal fade" id="Diff" tabindex="-1">
     242 + <div class="modal-dialog">
     243 + <div class="modal-content">
     244 + <div class="modal-header">
     245 + <button type="button" class="close" data-dismiss="modal"><span class="glyphicon glyphicon-remove"></span></button>
     246 + <h4 class="modal-title">Diff Graph</h4>
     247 + <p>Compare two days and view unique events.</p>
     248 + </div>
     249 + <div class="modal-body">
     250 + <div class="container-fluid">
     251 + <div class="row">
     252 + <div class="form-group">
     253 + <div class="input-group fromday">
     254 + <input type="text" placeholder="From" id="from-day" class="form-control" />
     255 + <span class="input-group-addon">
     256 + <span class="glyphicon glyphicon-calendar"></span>
     257 + </span>
     258 + </div>
     259 + </div>
     260 + <div class="form-group">
     261 + <div class="input-group today">
     262 + <input type="text" placeholder="To" id="to-day" class="form-control" />
     263 + <span class="input-group-addon">
     264 + <span class="glyphicon glyphicon-calendar"></span>
     265 + </span>
     266 + </div>
     267 + </div>
     268 + </div>
     269 + </div>
     270 + </div>
     271 + <div class="modal-footer">
     272 + <button type="submit" class="btn btn-primary" data-dismiss="modal" onclick="diffQuery()">Compare</button>
     273 + <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
     274 + </div>
     275 + </div>
     276 + </div>
     277 + </div>
    185 278   <!-- Warning message -->
    186 279   <div class="modal fade" id="warningMessage" tabindex="-1">
    187 280   <div class="modal-dialog modal-sm">
    skipped 25 lines
    213 306   pagerankQuery(userqueryStr, "User", rankpageUser);
    214 307   pagerankQuery(ipqueryStr, "Host", rankpageHost);
    215 308   logdeleteCheck();
     309 + loaddate();
    216 310   
    217 311   var loading = document.getElementById('loading');
    218 312   loading.classList.add('loaded');
    skipped 58 lines
Please wait...
Page is in error, reload to recover