From 21c23b7bd009c2e47029fc586530a833e4e3e307 Mon Sep 17 00:00:00 2001 From: noah Date: Sun, 27 Mar 2022 19:03:51 -0500 Subject: [PATCH] Editing Chapter 3 --- Chapter3.tex | 70 ++++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/Chapter3.tex b/Chapter3.tex index 561ba02..98ee87f 100644 --- a/Chapter3.tex +++ b/Chapter3.tex @@ -8,10 +8,10 @@ separate program, and has two primary modes of usage. The goal of this feature i root state to a designated state. The first mode is a manual mode, where a user can input the desired state to walk to, and the program will output a separate graph of all possible paths to the specified state. The second mode is an automatic mode, where the program will output separate subgraphs to all states in the network that have qualities of $``compliance$\_$vio = true"$,$``compliance$\_$vios > 0"$, or any other quality that can be specified by the user. The automatic mode can produce multiple subgraphs simultaneously if multiple states contain the quality being examined, and these subgraphs can then be -separately fed into an analysis program. +separately input into an analysis program. Figure \ref{fig:PW} demonstrates an output of the Path Walking feature when walking to state 14. In this figure, the primary observable feature is that the -graph was reduced from 16 states to 6 states, and from 32 edges to 12 edges. The reduction from the original graph to the subset varies on the overall connectivity +graph was reduced from 16 states to 6 states, and from 32 edges to 12 edges. The amount of reduction from the original graph to the subset varies on the overall connectivity of the original attack graph. \begin{figure}[htp] \includegraphics[width=\linewidth]{"./Chapter3_img/PW.png"} @@ -21,26 +21,26 @@ of the original attack graph. \end{figure} \TUsection{Compound Operators} \label{sec:compops} -Many of the graphs previously generated by RAGE comprise of states with features that can be fully enumerated. In many of the generated graphs, there is an -established set of qualities that will be used, with an established set of values. These typically have included $``compliance$\_$vio=true/false"$, +Many of the graphs previously generated by RAGE comprise of states with features that can be fully enumerated. In many of these generated graphs, there was an +established set of qualities that was used, with an established set of values. These typically have included $``compliance$\_$vio=true/false"$, $``root=true/false"$, or other general $``true/false"$ values or $``version=X"$ qualities. To expand on the types and complexities of graphs that can be generated, compound operators have been added to RAGE. When updating a state, rather than setting a quality to a specific value, the previous value can now be modified by an amount specified through standard compound operators such as $\mathrel{+}=$, $\mathrel{-}=$, $\mathrel{*}=$, or $\mathrel{/}=$. -The work conducted by the author of \cite{cook_rage_2018} when designing the software architecture included specifications for a quality encoding scheme. As the +The work conducted by the author of \cite{cook_rage_2018} when designing the software architecture of RAGE included specifications for a quality encoding scheme. As the author discusses, qualities have four fields, which include the asset ID, attributes, operator, and value. The operator field is 4 bits, which allows for a total of 16 operators. Since the only operator in use at the time was the $``\mathrel{=}"$ operator, the addition of four compound operators does not surpass the 16 operator limit, and no encoding scheme changes were necessary. This also allows for additional compound operators to be incorporated in the future. -A few changes were necessary to allow for the addition of compound operators. Before the generation of an Attack Graph begins, all values are stored in a hash -table. For previous networks generated by RAGE, this was not a difficulty, since all values could be fully enumerated and all possible values were known. When -using compound operators however, not all values can be fully known. The concept of approximating which exploits will be applicable and what absolute minimum -or maximum values will be prior to generation is a difficult task, and not all values can be enumerated and stored into the hash table. As a result, real-time +A few changes were necessary to allow for the addition of compound operators. Before the generation of an attack graph begins, all values are stored in a hash +table. For previous networks generated by RAGE, this was not a concern since all values could be fully enumerated and all possible values were known. When +using compound operators however, not all values can be fully known. The task of approximating which exploits will be applicable and what absolute minimum +or maximum value bounds will be prior to generation is difficult, and not all values can be enumerated and stored into the hash table. As a result, real-time updates to the hash table needed to be added to the generator. The original key-value scheme for hash tables relied on utilizing the size of the hash table for values. Since the order in which updates happen may not always remain consistent (and is especially true in distributed computing environments), it is possible for states to receive different hash values with the original hashing scheme. To prevent this, the hashing scheme was adjusted so that the new value of the compound operator is inserted into the hash table values if it was not found, rather than the size of the hash table. Previously, there was no safety check -for the hash table, so if the value was not found, the program would end execution. The assumption that this value can be inserted into the hash table is safe +for the hash table, so if the value was not found, the program would end execution. The assertion that the new value can be inserted into the hash table is safe to make, since compound operators are conducted on numeric values, and matches the numeric type of the hash table. Other changes involved updating classes (namely the Quality, EncodedQuality, ParameterizedQuality, NetworkState, and Keyvalue classes) to include a new member for the operator in question. Auxiliary functions related to this new member, such as prints and getters, were also added. In addition, preconditions were altered to include operator overloads to check the asset identifier, quality name, and quality values for the update process. @@ -48,10 +48,10 @@ Other changes involved updating classes (namely the Quality, EncodedQuality, Par \TUsection{Color Coding} As a visual aid for analysis purposes, color coding was another feature implemented as a postprocessing tool for RAGE. When viewing the output graph of RAGE, all states are visibly identical in appearance apart from number of edges, edge IDs, and state IDs. To allow for visual differentiation, color coding can be enabled in the run script. -Color coding currently functions by working through the graph output text file, but it can be extended to read directly from Postgres instead. The feature scans through the +Color coding currently functions by working through the graph output text file, but it can be extended to read directly from the PostgreSQL database instead. The feature scans through the output file, and locates states that have $``compliance$\_$vios = X"$ (where \textit{X} is a number greater than 0), or $``compliance$\_$vio = true"$. For states that meet these -properties, the color coding feature will add a color to the graphviz DOT file through the $[color=COL]$ attribute for the given node, where \textit{COL} is assigned based on severity. -For this version of color coding, severity is determined by the total number of compliance violations, but future versions can alter the severity measure through alternative means. +properties, the color coding feature will add a color to the Graphviz DOT file through the $[color=COL]$ attribute for the given node, where \textit{COL} is assigned based on severity. +For this version of color coding, severity is determined by the total number of compliance violations a node has, but future versions can alter the severity measure through alternative means. Figure \ref{fig:CC} displays an example graph that leverages color coding to easily identify problem states. \begin{figure}[htp] \includegraphics[width=\linewidth]{"./Chapter3_img/CC.png"} @@ -63,43 +63,43 @@ Figure \ref{fig:CC} displays an example graph that leverages color coding to eas \TUsection{Intermediate Database Storage}\label{sec:db-stor} \TUsubsection{Memory Constraint Difficulties} Previous works with RAGE have been designed around maximizing performance to limit the longer runtime caused by the state space explosion, such as the works seen by the authors of \cite{cook_rage_2018}, -\cite{li_concurrency_2019}, and \cite{li_combining_2019}. To this end, the output graph is stored in memory during the generation process to minimize disk writing and reading, as well as leverage the -performance benefits of memory operations since graph computation relies less on processor speed than that of data dependency complexity, parallelism coarseness, and memory access time - \cite{zhang_boosting_2017}, \cite{ainsworth_graph_2016}, \cite{berry_graph_2007}. The author of \cite{cook_rage_2018} does incorporate PostgreSQL as a final storage mechanism to write the resulting +\cite{li_concurrency_2019}, and \cite{li_combining_2019}. To this end, the output graph is contained in memory during the generation process to minimize disk writing and reading. This also allows for leveraging the +performance benefits of memory operations, since graph computation relies less on processor speed and more on data dependency complexity, parallelism coarseness, and memory access time + \cite{zhang_boosting_2017}, \cite{ainsworth_graph_2016}, \cite{berry_graph_2007}. The author of \cite{cook_rage_2018} does incorporate PostgreSQL as an initial and final storage mechanism to write the starting and resulting graph information, but no intermediate storage is otherwise conducted. - While the design decision to not use intermediate storage maximizes performance for graph generation, it does suffer from a few complications. When generating large networks, the system runs the risk - of running out of memory. This typically does not occur when generation is conducted on small graphs, and is especially true when relatively small graphs are generated on a High Performance Computing - system with substantial memory. However, when running on local systems, or when the graph is large, memory can quickly be depleted due to state space explosion. The memory depletion is due to two primary - memory consumption points: the frontier which contains all of the states that still need to be explored, and the graph instance, which holds all of the network states and their state information + While the design decision to not use intermediate storage maximizes performance for graph generation, it does suffer from a few complications. When generating large graphs, the system runs the risk + of running out of memory. This typically does not occur when generation is conducted on small graphs, and is especially true when relatively small graphs are generated on an HPC + system with substantial amounts of memory. However, when running on local systems or when the graph is large, memory can quickly be depleted due to state space explosion. The memory depletion is due to two primary + memory consumption points: the frontier which contains all of the states that still need to be explored, and the graph instance which holds all of the states and their information, as well as all of the edges. - The frontier quickly becomes a problem point with large networks that contain many layers before reaching leaf nodes. During the generation process, RAGE works on a Breadth-First Search approach, and new states + The frontier quickly becomes a problem point with large graphs that have a large height value, and contain many layers before reaching leaf nodes. During the generation process, RAGE works on a Breadth-First Search approach, and new states are continuously discovered each time a state from the frontier is explored. In almost all cases, this means that for every state that is removed from the frontier, several more are added, leading to an ever-growing - frontier that can not be adequately reduced for large networks. Simultaneously, the graph instance is ever-growing as states are explored. When the network contains numerous assets, each with their own large sets of - qualities, the size of each state becomes noticeably larger. With some graphs containing millions of nodes and billions of edges, like those mentioned by the authors of \cite{zhang_boosting_2017}, it becomes increasingly + frontier that can not be adequately reduced for large networks. Simultaneously, the graph instance continues to grow as states are explored. When the network contains numerous assets, each with their own large sets of + qualities, the size of each state becomes noticeably larger. With some graphs containing millions of nodes and billions of edges like those mentioned by the authors of \cite{zhang_boosting_2017}, it becomes increasingly unlikely that the graph can be fully contained within system memory. \TUsubsection{Maximizing Performance with Intermediate Database Storage} - Rather than a static implementation of storing to the database on disk at a set interval or a set size, the goal was to dynamically store to the database only when necessary. Since there is an associated cost with preparing - the writes to disk, the communication cost across nodes, the writing to disk itself, and with retrieving items from disk, it is desirable to store as much in memory for as long as possible and only write when necessary. When + Rather than a static implementation of storing to the database on disk at a set interval or a set size, the goal was to dynamically store to the database only when necessary. This would allow for proper utilization of systems with greater memory, and would reduce fine-tuning of a maximum size variable before database writes on different systems. Since there is an associated cost with preparing + the writes to disk, the communication cost across nodes, the writing to disk itself, and a cost for retrieving items from disk, it is desirable to store as much in memory for as long as possible and only write when necessary. When running RAGE, a new argument can be passed \textit{(-a $<$double$>$)} to specify the amount of memory the tool should use before writing to disk. This argument is a value between 0 and 0.99 to specify a percentage. This double is immediately reduced by 10\%. For instance, if 0.6 is passed, it is immediately reduced to 0.5. This acts as a buffer for PostgreSQL. Since queries will consume a variable amount of memory through parsing or preparation, an additional 10\% is saved as a precaution. This can be changed later as needed or desired for future optimizations. Specific to the graph data, the statement is made that the frontier is allowed to consume half of the allocated memory, - and that the instance is allowed to consume the other half. + and that the graph instance is allowed to consume the other half. To decide when to store to the database instead of memory, two separate checks are made. The first check is for the frontier. If the size of the frontier consumes equal to or more than the allowed allocated memory, then all new states are stored into a new table in the database called “unexplored states”. Each new state from this point forward is stored in the table, regardless of if room is freed in the frontier. This is to ensure proper ordering of the FIFO queue. The only time new states are stored directly into the frontier is when the unexplored states table is empty. Once the frontier has been completely emptied, new states are then pulled from the database into the frontier. To pull from the database, the parent loop for the generator process has been altered. Instead of a while loop for when the frontier is not empty, it has been adjusted to when the frontier is not empty or the unexplored states table is not empty. Due - to C++ using short-circuit evaluation, some performance is gained since no SQL statement must be passed to disk to check the size of the unexplored states table unless the frontier is empty. The original design was to store new states - into the frontier during the critical section to avoid testing on already-explored states. As a result, writing new states to the database is also performed during the critical section. + to C++ using short-circuit evaluation where the first argument is completely evaluated before processing the second, some performance is gained. The performance gained is due to not having to pass a SQL statement to disk to check the size of the unexplored states table unless the frontier is empty. The original generation design stored new states + into the frontier during the critical section to avoid testing on already-explored states. To follow this design decision, writing new states to the database is also performed during the critical section. - For the instance, a check in the critical section determines if the size of the instance consumes more than its allocated share of the memory. If it does, the edges, network states, and network state items are written to the database, + For the graph instance, a check in the critical section determines if the size of the graph instance consumes more than its allocated share of the memory. If it does, the edges, network states, and network state items are written to the database, and are then removed from memory. However, a new issue arose with database storage. The original design was to save staging, preparation, and communication cost by writing all the data in one query (as in, writing all of the network states in one query, all the network - state items in one query, and all the edges in one query). While this was best in terms of performance, it was also not feasible. Building the SQL queries themselves quickly began depleting the already constrained memory with large storage + state items in one query, and all the edges in one query). While this was best option in terms of performance, it was also not scalable. Building the SQL queries themselves quickly began depleting the already constrained memory with large storage requests. As a result, the storage process would consume too much memory and crash the generator tool. To combat this, all queries had to be broken up into multiple queries. As previously mentioned, an extra 10\% buffer was saved for the storage process. SQL query strings are now built until they consume the 10\% buffer, where they are then processed by PostgreSQL, cleared, and the query building process resumes. @@ -112,12 +112,12 @@ performance benefits of memory operations since graph computation relies less on \TUsection{Relational Operators} -As discussed in Section \ref{sec:compops}, many of the networks previously generated by RAGE compromise of states with an established set of qualities and values. These typically have included $``compliance$\_$vio=true/false"$, -$``root=true/false"$, or other general $``true/false"$ values or $``version=X"$ qualities. To further expand the dynamism of attack graph generation, it is important to distinguish when a quality has a value that satisfies a -relational comparison to an exploit. An example application can be seen through CVE-2019-10747, where "set-value is vulnerable to Prototype Pollution in versions lower than 3.0.1" \cite{CVE-2019-10747}. Prior to the implementation +As discussed in Section \ref{sec:compops}, many of the graphs previously generated by RAGE comprise of states with an established set of qualities and values. These typically have included $``compliance$\_$vio=true/false"$, +$``root=true/false"$, or other general $``true/false"$ values or $``version=X"$ qualities. To further expand the dynamism of graph generation, it is important to distinguish when a quality has a value that satisfies a +relational comparison to an exploit. An example application for attack graphs can be seen through CVE-2019-10747, where "set-value is vulnerable to Prototype Pollution in versions lower than 3.0.1" \cite{CVE-2019-10747}. An example compliance graph application using the aforementioned car example can be seen in the Toyota Corolla Maintenance Schedule, which states an engine coolant replacement should be conducted after 24,000 miles. Prior to the implementation of relational operators, to determine whether this exploit was applicable to a network state, multiple exploit qualities must be enumerated for all versions prior to 3.0.1. This would mean that the exploit needed to check if -\textit{version=3.0.0}, or \textit{version=2.0.0}, or \textit{version=1.0.0}, or \textit{version=0.4.3}, etc. This becomes increasingly tedious when there are many versions, and not only reduces readability, but is also more -prone to human error when creating the exploit files. As a result, relational operators were implemented. +\textit{version=3.0.0}, or \textit{version=2.0.0}, or \textit{version=1.0.0}, or \textit{version=0.4.3}, etc. For the compliance graph exploit check, this could lead to even worse scaling where checks needed to be conducted at a much more granular level like \textit{engine_coolant_miles=24001}, or \textit{engine_coolant_miles=24002}, or \textit{engine_coolant_miles=24003}, etc. This becomes increasingly tedious when there are many checks to perform, and this not only reduces readability, but is also more +prone to human error when creating the exploit files. Relational operators work to alleviate these difficulties. To implement the relational operators, operator overloads were placed into the Quality class. At the time of writing, the following are implemented: $==$, $<$, $>$, $\leq$, $\geq$. However, these operators do not take up room in the encoding scheme, so additional operators can be freely implemented as needed. The overloads ensure that the Quality asset IDs and Quality names match, and then compares the Quality values based on the operator in question.