570 likes | 591 Views
Learn about the major components of Fortify SCA and how it analyzes source code to find vulnerabilities. Understand the translation, analysis, and audit processes.
E N D
Fortify Source Code Analysis Suite Overview:Section Objectives • In this section, you will learn: • Major components of SCA • How SCA processes various source code into one representative model • How SCA Analyzers and Rules work together to find possible vulnerabilities in source code • How to use SCA results
Fortify Source Code Analysis Suite Overview • Fortify SCA and its Components • Fortify SCA Translation • Fortify SCA Analysis • Fortify SCA Audit
Threat Intelligence Knowledge of Secure Software Vulnerability Identification Source Code Analyzer (SCA) Program Trace Analyzer (PTA) Real-Time Analyzer (RTA) Remediation Governance Fortify 360
Static source code analysis. Visual interface for analysis of software vulnerabilities. Integrated vulnerability detection into Integrated Development Environments (IDEs). Management for multiple audit projects from a single centralized console. Fortify SCA Audit WorkBench (AWB) Secure Coding Plug-ins Fortify SCA Suite Fortify 360 Server
Fortify SCA Process Flow Translation Analysis Audit Source Code to Fortify Intermediate Model Fortify Intermediate Model to List of Possible Vulnerabilities Vulnerability Verification, Triage and Remediation
Fortify Source Code Analysis Suite Overview • Fortify SCA and its Components • Fortify SCA Translation • Fortify SCA Analysis • Fortify SCA Audit
Fortify SCA – Translation Translation Analysis Audit Source Code to Fortify Intermediate Model Fortify Intermediate Model to List of Possible Vulnerabilities Vulnerability Verification, Triage and Remediation
Fortify SCA – Translation (cont’d) Translate Scan Diagnose Build Integration - Touch - Touchless Source Code to Fortify Intermediate Model Fortify Intermediate Model to FPR File Vulnerability Triage and Remediation GUI (IDE & AWB) Translation Phase
Translation Phase = Compilation • Source code need to be in a compile-able state • C / C++: needs C/C++ compiler to run SCA translator • Java: Java code is translated directly by SCA translator • Partial results can be obtained if the project is not build-able • .NET: Solutions must be build-able and compiled in Debug configuration to create a program database (pdb) file • SCA translator expects .NET assemblies and the pdb files. • Other languages such as JavaScript, SQL, PHP are translated directly by SCA translator
Normalized Syntax Tree (NST Files) • All language translated into a single format • NST Files • Uniform representation of a full application • X-Tier™ Analysis • Static analysis across different tiers of an application
Fortify Source Code Analysis Suite Overview • Fortify SCA and its Components • Fortify SCA Translation • Fortify SCA Analysis • Fortify SCA Audit
Fortify SCA – Analysis Analysis Translation Audit Fortify Intermediate Model to List of Possible Vulnerabilities Source Code to Fortify Intermediate Model Vulnerability Verification, Triage and Remediation
Fortify SCA – Analysis (cont’d) Analysis Phase
Analysis Phase = Analyzers + Rules • Analyzers • Comprised of six distinct analyzers • Analyzers are static analysis algorithms • Each analyzer is associate with a rule type • Rules • Vulnerability definition (pattern) • There are as many number of rule types as the number of analyzers • Each rule type works with one analyzer • Each rule type has its own syntax • Secure Coding Rulepacks • A set of files that contains rules
Fortify SCA Analyzer • Fortify SCA provides six primary analyzers • Data flow • Control flow • Structural • Semantic • Configuration • buffer
Understanding Data Flow – Sample importjava.sql.*; public classSQLInjection { public static void main (Stringargs[]) { Connectionconn = null; try { StringuserName = args [0]; Stringpasswd = args [1]; Stringquery = "select uname, passwd from users where uname like "+userName+"%"; conn = DriverManager.getConnection ("jdbc:odbc:logistics", "admin", "letmein"); Statementstmnt = conn.createStatement (); ResultSetrs = stmnt.executeQuery (query); while ( rs.next() ) { ... } rs.close (); stmnt.close (); conn.close (); } catch (SQLExceptionerr) { err.printStackTrace (); } } }
Data Flow • Find vulnerabilities where non-trusted input can potentially control application operation. • Vulnerabilities to injection attacks • Analyzer uses global taint propagation to trace the flow of non-trusted data • Source • Non-trusted (user controlled) input • Sink • Potentially dangerous function call or operation
Data Flow (cont’d) importjava.sql.*; public classSQLInjection { public static void main (Stringargs[]) { Connectionconn = null; try { StringuserName = args [0]; Stringpasswd = args [1]; Stringquery = "select uname, passwd from users where uname like "+userName+"%"; conn = DriverManager.getConnection ("jdbc:odbc:logistics", "admin", "letmein"); Statementstmnt = conn.createStatement (); ResultSetrs = stmnt.executeQuery (query); while ( rs.next() ) { ... } rs.close (); stmnt.close (); conn.close (); } catch (SQLExceptionerr) { err.printStackTrace (); } } } Source
Data Flow (cont’d) importjava.sql.*; public classSQLInjection { public static void main (Stringargs[]) { Connectionconn = null; try { StringuserName = args [0]; Stringpasswd = args [1]; Stringquery = "select uname, passwd from users where uname like "+userName+"%"; conn = DriverManager.getConnection ("jdbc:odbc:logistics", "admin", "letmein"); Statementstmnt = conn.createStatement (); ResultSetrs = stmnt.executeQuery (query); while ( rs.next() ) { ... } rs.close (); stmnt.close (); conn.close (); } catch (SQLExceptionerr) { err.printStackTrace (); } } } Source args[] is tainted
Data Flow (cont’d) importjava.sql.*; public classSQLInjection { public static void main (Stringargs[]) { Connectionconn = null; try { StringuserName = args [0]; Stringpasswd = args [1]; Stringquery = "select uname, passwd from users where uname like "+userName+"%"; conn = DriverManager.getConnection ("jdbc:odbc:logistics", "admin", "letmein"); Statementstmnt = conn.createStatement (); ResultSetrs = stmnt.executeQuery (query); while ( rs.next() ) { ... } rs.close (); stmnt.close (); conn.close (); } catch (SQLExceptionerr) { err.printStackTrace (); } } } Source args[] is tainted Taint propagated to userName
Data Flow (cont’d) importjava.sql.*; public classSQLInjection { public static void main (Stringargs[]) { Connectionconn = null; try { StringuserName = args [0]; Stringpasswd = args [1]; Stringquery = "select uname, passwd from users where uname like "+userName+"%"; conn = DriverManager.getConnection ("jdbc:odbc:logistics", "admin", "letmein"); Statementstmnt = conn.createStatement (); ResultSetrs = stmnt.executeQuery (query); while ( rs.next() ) { ... } rs.close (); stmnt.close (); conn.close (); } catch (SQLExceptionerr) { err.printStackTrace (); } } } Source args[] is tainted Taint propagated to userName Tainted input userName used in assignment
Data Flow (cont’d) importjava.sql.*; public classSQLInjection { public static void main (Stringargs[]) { Connectionconn = null; try { StringuserName = args [0]; Stringpasswd = args [1]; Stringquery = "select uname, passwd from users where uname like "+userName+"%"; conn = DriverManager.getConnection ("jdbc:odbc:logistics", "admin", "letmein"); Statementstmnt = conn.createStatement (); ResultSetrs = stmnt.executeQuery (query); while ( rs.next() ) { ... } rs.close (); stmnt.close (); conn.close (); } catch (SQLExceptionerr) { err.printStackTrace (); } } } Source args[] is tainted Taint propagated to userName Sink Tainted input userName used in assignment
Data Flow (cont’d) importjava.sql.*; public classSQLInjection { public static void main (Stringargs[]) { Connectionconn = null; try { StringuserName = args [0]; Stringpasswd = args [1]; Stringquery = "select uname, passwd from users where uname like "+userName+"%"; conn = DriverManager.getConnection ("jdbc:odbc:logistics", "admin", "letmein"); Statementstmnt = conn.createStatement (); ResultSetrs = stmnt.executeQuery (query); while ( rs.next() ) { ... } rs.close (); stmnt.close (); conn.close (); } catch (SQLExceptionerr) { err.printStackTrace (); } } } Source args[] is tainted Taint propagated to userName Sink Tainted input userName used in assignment Vulnerability Reported
Understanding Control Flow – Sample private void cmdLogin_Click(object sender, System.EventArgs e) { Label lblMsg = null; try { stringtxtUser = Request.Form["txtUsername"]; stringtxtPassword = Request.Form["txtPassword"]; stringstrCnx ="server=localhost;database=northwind;uid=sa;pwd=;"; SqlConnection cnx = new SqlConnection(strCnx); cnx.Open(); string strQry ="SELECT Count(*) FROM Users WHERE UserName='" + txtUser + "' AND Password='" + txtPassword + "'"; int intRecs; SqlCommand cmd = new SqlCommand(strQry, cnx); intRecs = (int)cmd.ExecuteScalar(); if (intRecs > 0) { FormsAuthentication.RedirectFromLoginPage(txtUser, false); cnx.Close(); } else { lblMsg.Text = "Login attempt failed."; } } catch (Exception err) { Response.Write(err.StackTrace.ToString()); }
Control Flow • Detects potentially dangerous sequences of operations/function calls • Control Flow analyzer simulates state machines using your source code (NST files) to determine the state transitions • Each state machines has an error state which represents the state where a vulnerability could be exploited • Control Flow rules are definition of the state machines
Control Flow (cont’d) private void cmdLogin_Click(object sender, System.EventArgs e) { Label lblMsg = null; try { stringtxtUser = Request.Form["txtUsername"]; stringtxtPassword = Request.Form["txtPassword"]; stringstrCnx ="server=localhost;database=northwind;uid=sa;pwd=;"; SqlConnection cnx = new SqlConnection(strCnx); cnx.Open(); string strQry ="SELECT Count(*) FROM Users WHERE UserName='" + txtUser + "' AND Password='" + txtPassword + "'"; int intRecs; SqlCommand cmd = new SqlCommand(strQry, cnx); intRecs = (int)cmd.ExecuteScalar(); if (intRecs > 0) { FormsAuthentication.RedirectFromLoginPage(txtUser, false); cnx.Close(); } else { lblMsg.Text = "Login attempt failed."; } } catch (Exception err) { Response.Write(err.StackTrace.ToString()); } Variable initialized & assigned
Control Flow (cont’d) private void cmdLogin_Click(object sender, System.EventArgs e) { Label lblMsg = null; try { stringtxtUser = Request.Form["txtUsername"]; stringtxtPassword = Request.Form["txtPassword"]; stringstrCnx ="server=localhost;database=northwind;uid=sa;pwd=;"; SqlConnection cnx = new SqlConnection(strCnx); cnx.Open(); string strQry ="SELECT Count(*) FROM Users WHERE UserName='" + txtUser + "' AND Password='" + txtPassword + "'"; int intRecs; SqlCommand cmd = new SqlCommand(strQry, cnx); intRecs = (int)cmd.ExecuteScalar(); if (intRecs > 0) { FormsAuthentication.RedirectFromLoginPage(txtUser, false); cnx.Close(); } else { lblMsg.Text = "Login attempt failed."; } } catch (Exception err) { Response.Write(err.StackTrace.ToString()); } Variable initialized & assigned Connection closed
Control Flow (cont’d) private void cmdLogin_Click(object sender, System.EventArgs e) { Label lblMsg = null; try { stringtxtUser = Request.Form["txtUsername"]; stringtxtPassword = Request.Form["txtPassword"]; stringstrCnx ="server=localhost;database=northwind;uid=sa;pwd=;"; SqlConnection cnx = new SqlConnection(strCnx); cnx.Open(); string strQry ="SELECT Count(*) FROM Users WHERE UserName='" + txtUser + "' AND Password='" + txtPassword + "'"; int intRecs; SqlCommand cmd = new SqlCommand(strQry, cnx); intRecs = (int)cmd.ExecuteScalar(); if (intRecs > 0) { FormsAuthentication.RedirectFromLoginPage(txtUser, false); cnx.Close(); } else { lblMsg.Text = "Login attempt failed."; } } catch (Exception err) { Response.Write(err.StackTrace.ToString()); } Variable initialized & assigned Connection closed Safe Sequence
Control Flow (cont’d) private void cmdLogin_Click(object sender, System.EventArgs e) { Label lblMsg = null; try { stringtxtUser = Request.Form["txtUsername"]; stringtxtPassword = Request.Form["txtPassword"]; stringstrCnx ="server=localhost;database=northwind;uid=sa;pwd=;"; SqlConnection cnx = new SqlConnection(strCnx); cnx.Open(); string strQry ="SELECT Count(*) FROM Users WHERE UserName='" + txtUser + "' AND Password='" + txtPassword + "'"; int intRecs; SqlCommand cmd = new SqlCommand(strQry, cnx); intRecs = (int)cmd.ExecuteScalar(); if (intRecs > 0) { FormsAuthentication.RedirectFromLoginPage(txtUser, false); cnx.Close(); } else { lblMsg.Text = "Login attempt failed."; } } catch (Exception err) { Response.Write(err.StackTrace.ToString()); } Variable initialized & assigned
Control Flow (cont’d) private void cmdLogin_Click(object sender, System.EventArgs e) { Label lblMsg = null; try { stringtxtUser = Request.Form["txtUsername"]; stringtxtPassword = Request.Form["txtPassword"]; stringstrCnx ="server=localhost;database=northwind;uid=sa;pwd=;"; SqlConnection cnx = new SqlConnection(strCnx); cnx.Open(); string strQry ="SELECT Count(*) FROM Users WHERE UserName='" + txtUser + "' AND Password='" + txtPassword + "'"; int intRecs; SqlCommand cmd = new SqlCommand(strQry, cnx); intRecs = (int)cmd.ExecuteScalar(); if (intRecs > 0) { FormsAuthentication.RedirectFromLoginPage(txtUser, false); cnx.Close(); } else { lblMsg.Text = "Login attempt failed."; } } catch (Exception err) { Response.Write(err.StackTrace.ToString()); } Variable initialized & assigned Control Flow Observe all possible paths
Control Flow (cont’d) private void cmdLogin_Click(object sender, System.EventArgs e) { Label lblMsg = null; try { stringtxtUser = Request.Form["txtUsername"]; stringtxtPassword = Request.Form["txtPassword"]; stringstrCnx ="server=localhost;database=northwind;uid=sa;pwd=;"; SqlConnection cnx = new SqlConnection(strCnx); cnx.Open(); string strQry ="SELECT Count(*) FROM Users WHERE UserName='" + txtUser + "' AND Password='" + txtPassword + "'"; int intRecs; SqlCommand cmd = new SqlCommand(strQry, cnx); intRecs = (int)cmd.ExecuteScalar(); if (intRecs > 0) { FormsAuthentication.RedirectFromLoginPage(txtUser, false); cnx.Close(); } else { lblMsg.Text = "Login attempt failed."; } } catch (Exception err) { Response.Write(err.StackTrace.ToString()); } Variable initialized & assigned Observes all possible paths Analyze until cnx is out of scope
Control Flow (cont’d) private void cmdLogin_Click(object sender, System.EventArgs e) { Label lblMsg = null; try { stringtxtUser = Request.Form["txtUsername"]; stringtxtPassword = Request.Form["txtPassword"]; stringstrCnx ="server=localhost;database=northwind;uid=sa;pwd=;"; SqlConnection cnx = new SqlConnection(strCnx); cnx.Open(); string strQry ="SELECT Count(*) FROM Users WHERE UserName='" + txtUser + "' AND Password='" + txtPassword + "'"; int intRecs; SqlCommand cmd = new SqlCommand(strQry, cnx); intRecs = (int)cmd.ExecuteScalar(); if (intRecs > 0) { FormsAuthentication.RedirectFromLoginPage(txtUser, false); cnx.Close(); } else { lblMsg.Text = "Login attempt failed."; } } catch (Exception err) { Response.Write(err.StackTrace.ToString()); } Vulnerability Reported Variable initialized & assigned Observes all possible paths Analyze until cnx is out of scope
Control Flow (cont’d) private void cmdLogin_Click(object sender, System.EventArgs e) { Label lblMsg = null; try { stringtxtUser = Request.Form["txtUsername"]; stringtxtPassword = Request.Form["txtPassword"]; stringstrCnx ="server=localhost;database=northwind;uid=sa;pwd=;"; SqlConnection cnx = new SqlConnection(strCnx); cnx.Open(); string strQry ="SELECT Count(*) FROM Users WHERE UserName='" + txtUser + "' AND Password='" + txtPassword + "'"; int intRecs; SqlCommand cmd = new SqlCommand(strQry, cnx); intRecs = (int)cmd.ExecuteScalar(); if (intRecs > 0) { FormsAuthentication.RedirectFromLoginPage(txtUser, false); cnx.Close(); } else { lblMsg.Text = "Login attempt failed."; } } catch (Exception err) { Response.Write(err.StackTrace.ToString()); } Variable initialized & assigned Observes all possible paths
Control Flow (cont’d) private void cmdLogin_Click(object sender, System.EventArgs e) { Label lblMsg = null; try { stringtxtUser = Request.Form["txtUsername"]; stringtxtPassword = Request.Form["txtPassword"]; stringstrCnx ="server=localhost;database=northwind;uid=sa;pwd=;"; SqlConnection cnx = new SqlConnection(strCnx); cnx.Open(); string strQry ="SELECT Count(*) FROM Users WHERE UserName='" + txtUser + "' AND Password='" + txtPassword + "'"; int intRecs; SqlCommand cmd = new SqlCommand(strQry, cnx); intRecs = (int)cmd.ExecuteScalar(); if (intRecs > 0) { FormsAuthentication.RedirectFromLoginPage(txtUser, false); cnx.Close(); } else { lblMsg.Text = "Login attempt failed."; } } catch (Exception err) { Response.Write(err.StackTrace.ToString()); } Variable initialized & assigned Observes all possible paths Analyze until cnx is out of scope
Control Flow (cont’d) private void cmdLogin_Click(object sender, System.EventArgs e) { Label lblMsg = null; try { stringtxtUser = Request.Form["txtUsername"]; stringtxtPassword = Request.Form["txtPassword"]; stringstrCnx ="server=localhost;database=northwind;uid=sa;pwd=;"; SqlConnection cnx = new SqlConnection(strCnx); cnx.Open(); string strQry ="SELECT Count(*) FROM Users WHERE UserName='" + txtUser + "' AND Password='" + txtPassword + "'"; int intRecs; SqlCommand cmd = new SqlCommand(strQry, cnx); intRecs = (int)cmd.ExecuteScalar(); if (intRecs > 0) { FormsAuthentication.RedirectFromLoginPage(txtUser, false); cnx.Close(); } else { lblMsg.Text = "Login attempt failed."; } } catch (Exception err) { Response.Write(err.StackTrace.ToString()); } Vulnerability Reported Variable initialized & assigned Observes all possible paths Analyze until cnx is out of scope
Structural • Detects potentially dangerous flaws in the structure or definition of a program • Wide varieties of flaws related to source Structure: • Insecure declaration and use of variables • Overly broad catch blocks • Comments that contain sensitive information, such as password
Structural (cont’d) • Erroneous String comparison publicstaticvoidaddResourceRequest( WikiContext ctx, String type, String resource ) { ... if( type ==RESOURCE_SCRIPT) { resourceString = "<script type='text/javascript' src='"+resource+"'></script>"; } elseif( type ==RESOURCE_STYLESHEET ) { resourceString = "<link rel='stylesheet' type='text/css' href='"+resource+"' />"; } elseif( type ==RESOURCE_INLINECSS ) { resourceString = "<style type='text/css'>\n"+resource+"\n</style>\n"; } elseif( type ==RESOURCE_JSFUNCTION ) { resourceString = resource; } ... Using the == operator does not compare the values of the objects but the objects themselves. Always use the equals () method to compare the values of two strings.
Notice how the comment in the empty catch block. However the code has made it into production now ! Structural (cont’d) • Empty catch block try { page = m_engine.getURLConstructor().parsePage( requestContext, request, m_engine.getContentEncoding() ); if( page !=null) { try { String finalPage = getFinalPageName( page ); if( finalPage !=null) { page = finalPage; } } catch( ProviderException e ) { // FIXME: Should not ignore! } returnpage; } } catch( IOException e ) { log.error("Unable to create context", e ); thrownewInternalWikiException("Big internal booboo, please check logs."); } ...
Structural (cont’d) • Password in Comment //DB password is adminadmin //Set the connection string for the database string connectionstring="Initial Catalog=TestCatalog; Data Source=myDataSource;user id=admin;password=adminadmin;"; string tainted_query= "Select name=" + args[1].Clone() + " fromEmployees"; Password written in a comment block
Structural (cont’d) • Member variables are not thread safe • Unless a Servlet implements the SingleThreadModel, it is a singleton importjavax.servlet.http.*; importjava.io.*; publicclassCreditCardServletextendsHttpServlet { privateStringcreditCardNum; publicvoid doPost (HttpServletRequest request, HttpServletResponse response) throws IOException { creditCardNum = request.getParameter("CC_NUM"); if (creditCardNum != null) { PrintWriterout = response.getWriter(); out.println (creditCardNum); } } } } The value of creditCardNum will keep changing as the application serves web requests. These requests are potentially from different users, so it may expose one user’s data to another
Semantic • Looks for unsafe function calls based on their signature • Signature consists of • Package / namespace • Return types of the method • Types passed to the method • Has similarity to a search function • More limited: Semantic look only at function calls • More powerful: Knows about object oriented concepts • Ability to specify object type that is expected in the function signature
Semantic (cont’d) importjava.sql.*; public classSQLInjection { public static void main (Stringargs[]) { Connectionconn = null; try { StringuserName = args [0]; Stringpasswd = args [1]; Stringquery = "select uname, passwd from users where uname like "+userName+"%"; conn = DriverManager.getConnection ("jdbc:odbc:logistics", "admin", "letmein"); Statementstmnt = conn.createStatement (); ResultSetrs = stmnt.executeQuery (query); while ( rs.next() ) { ... } rs.close (); stmnt.close (); conn.close (); } catch (SQLExceptionerr) { err.printStackTrace (); } } } Hardcoded Password
Semantic (cont’d) char *cmd = new char[256]; const char *safe = "safe_command "; int returnCode; while (1) { cout << "Enter command: "; cin >> cmd; if (strncmp(cmd, safe, strlen(safe)) != 0) { cout << "Unsafe command entered\n"; break; } returnCode = system(cmd); cout << "Command returned " << returnCode << '\n'; } Dangerous Function/Operator
Configuration • Uses XPath queries and name-value matching to identify issues in application’s configuration files • web.xml - J2EE applications • web.config - .NET web applications • other.xml - any well-formed XML files • Properties files - name/value pairs • Primarily searches application configuration files for • Mistakes • Omissions • Weaknesses
Configuration (cont’d) • Incomplete Error Handling … </welcome-file-list> <error-page> <error-code>403</error-code> <location>/error/Forbidden.html</location> </error-page> <resource-ref> … Missing java.lang.Throwable, error code 404, and 500
Configuration (cont’d) • Misconfiguration: Directory Listing <servlet> ... <init-param> <param-name>listings</param-name> <param-value>true</param-value> </init-param> … Listings is set to true. Allow users to view files in the file system.
buffer • buffer analyzer detect access to buffer beyond its boundaries • Converts code to integer programs • Uses constraint solver to detect possible access beyond the buffer boundaries. • Trace the buffer size used in allocation and buf operations • Memory allocation: malloc(), static string, etc • Buffer operation: strcpy(), strncat(), etc
buffer (cont’d) • Buffer Overflow voidfoo() { char* str = "hello world"; char*buf = (char*)malloc(strlen(str)+5); strcpy(buf, str); strcat(buf, "overflow"); return 0; } Buffer size = 16 Written 12 bytes Written 8 bytes
Secure Coding Rules - Fortify • Fortify Rules cover commonly use API • Library that comes with the programming language • Common 3rd party and extension library for the language • Developed by Fortify Security Research Group (SRG) • New rule update every quarter • End of February, May, August, and November • Distributed as encrypted files • Fortify’s intellectual property
Secure Coding Rules - Custom • Why develop custom rules? • Create precise validation rules to improve results accuracy • Enforce your organization security or coding practices • Need to cover your proprietary libraries • Fortify doesn’t know about your library • Need to cover libraries that Fortify Rules have not covered • Contact Fortify and request for rule coverage on the libraries • Custom rule development class is available from Fortify • Different classes for different programming languages