Vulnerabilities in the code. How can you tell a dangerous flaw from a minor error?

How does it usually look like checking application code for vulnerabilities? The security specialist initiates the procedure, the code is scanned, and thousands of vulnerabilities are discovered in the application. Everyone - both the security guy and the developers - is shocked. The natural reaction of the developer: "Yes, surely half are false positives, and the other are non-critical vulnerabilities!"



As for false positives, everything is simple here: you can take and look directly at those places in the code where vulnerabilities were discovered with suspicion of false positive. Indeed, some of them may turn out to be false positives (although obviously not half of the total).



But about what is critical and what is not, I would like to talk more substantively. If you understand why it is no longer possible to use SHA-1 and why escape ";", perhaps this article will not open you something new. But if the scan results from the found vulnerabilities dazzle, welcome under the cut - we will tell you what "holes" are most often found in mobile and web applications, how they work, how to fix them, and most importantly - how to understand what is in front of you - a dangerous flaw or a minor error in the code.







Implementation



Well, a very common type of vulnerability. They are embedded everywhere: in SQL, LDAP, XML, XPath, XSLT, Xquery queries ... All these injections are distinguished by the use of untrusted data, thanks to which an attacker gains access to information or changes the behavior of the application. For example, with user input that is not validated enough.



According to the international classification of vulnerabilities OWASP , attacks using the injection method are ranked first in the level of criticality of threats to web application security. Let's consider the most typical types of implementations.



SQL injection . Untrusted data gets into the SQL query to the database.







If the query to the database does not implement correct authentication of the input data, an attacker can corrupt the SQL query:



  • send malicious code to it;
  • add the symbol "-" or ";" and terminate the correct SQL command: everything after the "-" is interpreted as a comment, and the character ";" marks the end of a command;
  • guess the password by sequentially executing a series of SQL queries.


How to defend yourself? Here are some recommendations from OWASP :



  • Use an API that provides a parameterized interface or object-relational mapping (ORM) tools.
  • Implement a validation mechanism for user-entered data. Use a server side validation whitelist.
  • Escape special characters (";", "-", "/ *", "* /", "'"; the exact list depends on the database).
  • Use stored procedures in conjunction with a filtering mechanism for their parameters to validate user input.


XML injection . Applications use XML to store or exchange data, so they can contain valuable information.







If an attacker can write data to an XML document, then he can change its semantics. In this case, the most harmless scenario will allow you to inject extra tags into the document, as a result of which the XML parser will exit with an error. But you can face a more serious incident: for example, with the substitution of authentication data in the customer database or the price in the store's product database. XML injection can also lead to cross-site scripting (XSS) - the injection of malicious code that is executed in the user's browser when the page is opened.



What can we advise?



  • Do not create tags and attributes whose names are derived from data from an untrusted source (for example, entered by a user).
  • Encode (XML entity encode) the data entered by the user before writing it to an XML document.


XQuery injection is a form of classic SQL injection, but in this case the attack will target the XML database, and untrusted data will end up in the XQuery expression.



In the example below, the application creates and executes an XQuery expression based on parameters usernameand passwordfrom an HTTP request (untrusted source):



XQDataSource xqs = new XQDataSource();
XQConnection conn = xqs.getConnection();
String query = "for \$user in doc(users.xml)//user[username='" + request.getParameter("username") + "'and pass='" + request.getParameter("password") + "'] return \$user";
XQPreparedExpression xqpe = conn.prepareExpression(query);
XQResultSequence rs = xqpe.executeQuery();


If the data is correct, the request will return information about the user with the appropriate name and password:



for \$user in doc(users.xml)//user[username='test_user' and pass='pass123'] return \$user


If an attacker specifies a string containing special characters (for example, admin' or 1=1 or ''=') as a parameter , the semantics of the request will change:



//user[username='admin']


The received request will return data about all users.



Safe option (uses prepared statements):



XQDataSource xqs = new XQDataSource();
XQConnection conn = xqs.getConnection();
String query = "declare variable $username as xs:string external; declare variable $password as xs:string external; for \$user in doc(users.xml)//user[username='$username' and pass='$password'] return \$user";
XQPreparedExpression xqpe = conn.prepareExpression(query);
xqpe.bindString(new QName("username"), request.getParameter("username"), null);
xqpe.bindString(new QName("password"), request.getParameter("password"), null);
XQResultSequence rs = xqpe.executeQuery();


Embedding in XSLT (XML Document Transformation Language) is possible if the application uses data from an untrusted source when working with XSL.



Applications use XSL to transform XML documents. XSL style files contain functions that describe transformation and, if implemented incorrectly, may include vulnerabilities. In this case, there is an increased risk of attack scenarios in which an attacker changes the structure and content of the XSL style file, and therefore the corresponding XML file. What can we get at the exit?



First, an XSS attack: injecting malicious code into a page issued by a web system and interacting this code with the attacker's server. Second, the hacker gains access to system resources. Third, the execution of arbitrary code. And for dessert - XXE attack (XML eXternal Entity - injection of an external entity into XML).



Embedding Lightweight Directory Access Protocol ( LDAP) in commands can result in data loss or modification. In this case, untrusted data gets into the LDAP request.



Injection of a malicious interpreter command.Untrusted data gets into the interpreter command. An attacker can choose such an input so that the command is executed successfully and additional permissions in the application become available to him.



In the example below, the application runs a script to create a database backup. The application takes the backup type as a parameter and runs the script with elevated privileges:



String btype = request.getParameter("backuptype");
String cmd = new String("cmd.exe /K
\"c:\\util\\rmanDB.bat "+btype+"&&c:\\utl\\cleanup.bat\"")
System.Runtime.getRuntime().exec(cmd);


The problem here is that the parameter is backuptypenot validated. Usually Runtime.exec()does not execute multiple commands, but in this case cmd.exe is launched first to execute multiple commands by invoking Runtime.exec(). Once the command line shell is started, it can execute several commands, separated by " &&" characters . If an attacker && del c:\\dbms\\*.*specifies the string " " as a parameter , the application will delete the specified directory.



Developer Tips:



  • Don't let users have direct control over the commands the application runs. If the behavior of the application must depend on the data entered by the user, offer the user a choice from a certain list of allowed commands.
  • , . , . , .
  • , , . . .


Insecure file upload. In this case, not just individual data comes from an untrusted source, but the entire file. Thus, an attacker can upload malicious data or code to the target server. For example, if users on a corporate network are allowed to upload files to publicly accessible directories, a hacker can remotely run malicious code on the company's server.



Insecure inclusion of external file in HTML.File inclusion vulnerabilities occur when the user enters the path to an included file. The fact is that modern scripting languages ​​allow you to dynamically link code from third-party files in order to reuse it. This mechanism is used for a uniform look of pages or for splitting code into small modules. However, this inclusion can be exploited by an attacker by changing the path and connecting his file.



We recommend that corporate information security professionals create a "white list" of valid file connection paths so that employees can add files only according to scripts from this list.



Bookmarks



Bookmarks are parts that are intentionally introduced into the application code, with the help of which, under certain conditions, you can perform actions not included in the application. Let's consider the most common types of bookmarks.



Special accounts. If the application compares the value of a password or login variable with an unchanged value, beware: this account may be part of a bookmark. Let's see how this happens.







The developer of the application uses a special account (possibly with elevated privileges) when debugging and leaves the corresponding sections of the code in the final version, retaining access to the application. An attacker can restore the original code of the application, extract the constant values ​​of the special account, and gain access to the application.

It is categorically impossible to store logins, passwords, keys in the application source code.
Hidden functionality (NDV). The hidden functionality code is run when a certain trigger fires. In web applications, the trigger is often an "invisible" query parameter. Sometimes, in addition, a check is carried out from which IP the request with the trigger came from, so that only its author could activate the bookmark. Such checks serve as a signal for possible bookmarks.



Undocumented network activity. This type of activity includes: connecting to third-party resources in the background, listening on undocumented ports, transferring information via SMTP, HTTP, UDP, ICMP.

If you find a suspicious connection in the code with an address that is not on the list of known safe addresses, we strongly recommend that you delete it.

Change security settings . The application contains code that changes the value of the variable that stores the success of the authentication. A common mistake is using assignment (=) instead of comparison (==). In authentication methods, it is especially dangerous because it can be part of a backdoor:



if (isAuthenticated = true)
{
    someDangerousAction();
}


Time trigger (timebomb). A bookmark that fires at a specific point in time. The application compares the current date with a specific year, month and day: on January 1, 2021, a surprise awaits everyone:



Date now = java.util.Date();    // current time
if ((now.getYear() == 2021) && (now.getMonth() == 1) && (now.getDate() == 1))
{
    activateNewYearBackdoor();
}


Or maybe not ... In practice, when searching for temporary triggers, false positives often occur. For example, if the time API is used for its intended purpose: logging, calculating the execution time, timestamps for server responses to HTTP requests.



But! We still recommend not to close your eyes to all such alarms, as we know real examples of such vulnerabilities.



Dead code. Pieces of injected code that do nothing useful. Dead code itself is not dangerous, but it can be part of a bookmark that has been spread across multiple files. Or, the bookmark trigger is planned to be implemented later. In any case, dead code should be suspicious.



Lack of encryption and use of weak encryption algorithms



The main problems with encryption are that it is either not used at all, or weak algorithms are used, and the keys and salt are too simple or stored insecurely. The consequence of all these vulnerabilities is the same - it is easier to steal confidential data.



The example shows the initialization of encryption using the legacy DES algorithm:



Cipher cipher = Cipher.getInstance("DES");


Examples of vulnerable encryption algorithms: RC2, RC4, DES. Safe option:



Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");


According to the OWASP international classification , vulnerabilities such as β€œconfidential data leakage” are ranked third in terms of criticality of web application security threats.



Our recommendation to developers: Be sure to use encryption with security in mind.



Using the insecure HTTP protocol instead of HTTPS is fraught with a Man in the middle attack.



The secure HTTPS protocol is based on HTTP, but also supports encryption via the SSL / TLS cryptographic protocols. HTTPS encrypts all data transmitted over it, in particular, login and password entry pages or user bank card data, protecting them from unauthorized access and changes. Unlike HTTP, which does not protect the transmitted data. As a result, an attacker can spoof an informational website via HTTP and force the user to enter data on a fake page (phishing attack).



The encryption key is specified in the source code . As a result, such keys are available to every application developer. In addition, after installing the application, you can only remove the key from the code using an update.



In general, constant strings are easily extracted from an executable file using a source code recovery program (decompiler). Therefore, an attacker does not need to have access to the source code to find out the value of the key being used. In our practice, we often come across cases when developers specify nulleither an empty string as the key value , which is simply unacceptable.



Our advice: Generate keys with cryptographically strong pseudo-random number generators (PRNGs) and store them using special modules.



Insecure padding algorithm for encryption . If the RSA encryption algorithm is used without the OAEP padding, the encrypted data becomes vulnerable .



The OAEP algorithm is needed to process messages before using RSA. The message is first padded to a fixed length using OAEP, then encrypted using RSA. This encryption scheme is called RSA-OAEP and is part of the current standard .



This is an example of initializing RSA encryption without padding:



rsa = javax.crypto.Cipher.getInstance("RSA/NONE/NoPadding");


Safe option:



rsa = javax.crypto.Cipher.getInstance("RSA/ECB/OAEPWithMD5AndMGF1Padding");


Insufficient encryption key size . If you use a short key, this encryption is vulnerable to brute force attacks.



Cryptanalysis does not stand still, new attack algorithms are constantly emerging, computers are gaining greater power. Encryption settings that were previously considered secure are deprecated and are no longer recommended for use. So, RSA with a key length of 1024 bits was no longer considered secure in 2010-2015.



Weak hashing algorithm . For the reasons described in the previous paragraph, the hash functions MD2, MD5, SHA1 are insecure. To find collisions for the MD2 and MD5 functions, no significant resources are required.



For SHA1, there are examples of two different files with the same hashes. Hacking algorithm suggestedemployees of Google and the Center for Mathematics and Computer Science in Amsterdam.







If user passwords are stored as hashes, but using an insecure hash function, an attacker could easily gain access to them by implementing the following scenario. Knowing the hash of the password and exploiting the vulnerability of the hashing algorithm, it is possible to calculate a string for which the hash is the same as for the password. The attacker authenticates using the computed string.



The hash function for storing passwords must be collision resistant and not too fast so that a brute-force attack cannot be implemented. The secure algorithms PBKDF2, bcrypt, scrypt should be used.



Some interesting numbers: with PBKDF2the speed of searching keys was reduced to 70 pieces per second for Intel Core2 and about 1 thousand for FPGA Virtex-4 FX60. In comparison, the classic LANMAN password hashing functions have a search speed of about hundreds of millions of options per second.



Weak encryption algorithm . As with hashing algorithms, the security of an encryption algorithm is determined by the time and resources that will have to be spent on decrypting it. RC2, RC4, DES are considered vulnerable algorithms. The latter, due to its small key length (56 bits), can be cracked by brute force.



A weak pseudo-random number generator (PRNG) generates predictable sequences. A hacker can bypass authentication and hijack a user's session.



Let's go a little deeper into the nature of PRNGs. They generate strings of numbers based on the initial value of the parameter seed. There are two types of PRNGs - statistical and cryptographic.



Statistical PRNGs generate predictable sequences that are statistically similar to random ones. They cannot be used for security purposes.



The result of the operation of cryptographic PRNGs, on the contrary, cannot be predicted if the parameter value is seedobtained from a source with high entropy. The current time value has little entropy and is also unsafe in quality seed. In Java, PRNGs from classes java.util.Randomand java.lang.Mathgenerate predictable sequences and should not be used for information security purposes.



Weak seed of a pseudo-random number generator . It is seedunsafe to use a value from an untrusted source as it generates a predictable sequence.



The work of many cryptographic algorithms is based on the use of a cryptanalysis-resistant PRNG. Some algorithms can take a value as an additional argument seedand generate a predictable sequence for each value of this parameter. In this case, the security of the system is based on the assumption that the values seedwill be unpredictable.



The salt is specified in the source code... Let's remember what salt is for. To crack a password by brute-force method, pre-compiled tables with values ​​of hash functions from popular passwords are used. Salt is an arbitrary string that is fed to the input of the hash function along with the password to make such an attack more difficult.



If the salt is stored in the source code, exactly the same problems arise as with passwords and keys. The value of salt is available to developers and can easily be obtained by intruders, and salt can be removed from the final version of the application only with the next update of the application.



Manipulation with logs



Various errors in the logs are fraught with the introduction of malicious code into applications. The most common vulnerabilities associated with logging are log file tampering and unstructured logging.



Log file tampering occurs when an application writes untrusted data to the event log (log). A hacker can fake log entries or inject malicious code into them.



Typically, applications write transaction history to the log for further processing, debugging, or statistics collection. Logs can be parsed manually or automatically.

If the data is written to the log "as is", an attacker can inject fake records into the log, violate the file structure causing the log processor to fail, or inject malicious code that exploits known vulnerabilities in the processor.



In this example, a web application tries to read an integer value from a request parameter. If the entered value could not be converted to an integer, the application logs this value along with an error message:



String val = request.getParameter("val");
try {
    int value = Integer.parseInt(val);
}
catch (NumberFormatException nfe) {
    log.info("Failed to parse val = " + val);
}


An attacker can add an arbitrary entry to the log, for example, the line twenty-one%0a%0aINFO:+User+logged+out%3dbadguywill be reflected in the log as follows:



INFO: Failed to parse val=twenty-one
INFO: User logged out=badguy


Similarly, arbitrary records can be embedded in the log.



Safe option (uses NumberFormatException):



public static final String NFE = "Failed to parse val. The input is required to be an integer value."

String val = request.getParameter("val");
try {
    int value = Integer.parseInt(val);
}
catch (NumberFormatException nfe) {
    log.info(NFE);
}


Unstructured logging , that is, printing error messages to standard out or err streams is an unsafe method. It is recommended to use structured logging instead. The latter allows you to generate a log with levels, timestamps, standard formatting. If the program implements a structured logging mechanism, but error messages are output to standard streams, then the log may not contain critical information.

Outputting error messages to standard streams is acceptable only in the early stages of development.



Insecure cookie handling



The vulnerabilities associated with the collection of user cookies are very diverse.



Insecure handling of cookies . The application includes data from an untrusted source in the cookie, which can lead to cache poisoning, XSS (cross-site scripting) and response splitting attacks.



If malicious code (cross-site scripting) is injected into an application, an attacker can modify the user's cookie.



Because cookies are set in the HTTP response header, failure to acknowledge the data included in the cookie can lead to a split response attack. "HTTP response splitting" is an attack in which a hacker sends such an HTTP request, the response to which will be accepted by the victim in two HTTP responses (instead of the correct one).

If the attacker specifies a authorstring of the form as a parameter Hacker \r\nHTTP/1.1 200 OK\r\n..., the answer will be split into two as follows:



HTTP/1.1 200 OK
...
Set-Cookie: author=Hacker

HTTP/1.1 200 OK
...


The content of the second response is completely under attacker control, leading to cache poisoning, XSS, malicious redirects, and other attacks.



Cookies without HttpOnly . The app creates cookies without a flag httpOnly. If httpOnlyincluded in the response header http, an attacker would not be able to obtain cookies using JavaScript code. And if a user opens a page with a cross-site scripting (XSS) vulnerability, the browser will not disclose cookies to third parties. If the flag is httpOnlynot set, then cookies (usually session cookies) can be stolen using a script.



An example of creating a cookie without a flag httpOnly:



Cookie cookie = new Cookie("emailCookie", email);
response.addCookie(cookie);


Set the flag httpOnlywhen creating cookies. Keep in mind, however, that there are ways to bypass attacks, httpOnlyso you should also take care of carefully validating the input.



NB According to the OWASP international classification , "confidential data leak" vulnerabilities are ranked third in the level of criticality of web application security threats.



Cookies for a too general domain . If the cookie domain is too general (for example .example.com), a vulnerability in one application exposes other applications in the same domain to vulnerabilities.



In the following example, a secure web application installed at an address http://secure.example.comsets a cookie with a domain value .example.com:



Cookie cookie = new Cookie("sessionID", sessionID);
cookie.setDomain(".example.com");


If http://insecure.example.coman application containing, for example, XSS is installed at the address , then the cookies of the authorized user of the secure application who went to the address http://insecure.example.comcan be compromised.



An attacker can also carry out a cookie poisoning attack: cookies with a shared domain created http://insecure.example.comwill overwrite the cookie http://secure.example.com.



Safe option:



Cookie cookie = new Cookie("sessionID", sessionID);
cookie.setDomain("secure.example.com");


Cookies with too general parameterpath . If the path in the cookie is inaccurate (for example, /), the same problem arises as with the shared domain: a vulnerability in one application exposes other applications in the same domain.



In the following example, an application installed at a URL http://pages.example.com/forumsets a cookie with the path /:



Cookie cookie = new Cookie("sessionID", sessionID);
cookie.setPath("/");


Then a malicious application installed at the address http://pages.example.com/evilcan compromise the user's cookies. An attacker can also carry out a cookie poisoning attack: a cookie with a shared path created /evilwill overwrite the cookie /forum.



Safe option:



Cookie cookie = new Cookie("sessionID", sessionID);
cookie.setPath("/forum");


Cookies are not over SSL . The application creates cookies without setting the flag secureequal true. These cookies can be transmitted unencrypted over HTTP. The vulnerability "Use of the insecure HTTP protocol" is immediately recalled.



In the following example, the application creates cookies without a flag secure:



Cookie cookie = new Cookie("emailCookie", email);
response.addCookie(cookie);


If the application uses both HTTPS and HTTP, then in the absence of the secure flag, the cookies created as part of the HTTPS request will be transmitted in unencrypted form on subsequent HTTP requests, which may lead to the compromise of the application. This is especially dangerous if the cookie contains valuable data, in particular the session ID.



Safe option:



Cookie cookie = new Cookie("emailCookie", email);
cookie.setSecure(true);
response.addCookie(cookie);


Cookies with unlimited validity . If you store valuable cookies for too long, an attacker can gain access to them.



By default, non-persistent (session) cookies are used, which are not saved to disk and are deleted after closing the browser. However, the developer of the web application can specify how long the cookies are kept - in this case, they will be written to disk and saved between browser restarts and computer restarts. This gives the attacker a long time to develop an attack plan.



Developer recommendations: make sure the app doesn't create long-lived cookies:



Cookie cookie = new Cookie("longCookie", cookie);
cookie.setMaxAge(5*365*24*3600); // 5 !


Provide a reasonable maximum time limit following the OWASP guidelines .



Information leak



Perhaps the most sensitive type of vulnerability for application users.



External information leakage through error pages . The application uses standard error pages, which can contain information about the system configuration.



Error messages and debug information are written to the log, displayed to the console, or transmitted to the user. From error messages, an attacker can learn about system vulnerabilities, which will make his life easier. For example, a database error may indicate insecurity against SQL injection. Information about the operating system version, application server, and system configuration will make it easier for a hacker to plan an attack on an application.



External leakage of valuable information... In this case, we are talking about the leakage of technical information about the application by transferring it over the network to another computer. In general, external leaks are more dangerous than internal leaks.







Internal leakage of valuable information . The operating mechanism is similar to the previous two types of leaks, but in this case information about the system is written to the log or displayed on the user's screen.



Leak of confidential data . Valuable personal data of users enter the application from different sources: from the user himself, from various databases, from third-party storages. Sometimes this data is not marked as confidential, or it turns out to be valuable not in itself, but only in a certain context.



This is the very case when application security and personal data privacy contradict each other. For security reasons, it is advisable to record detailed information about activities in the system in order to identify malicious activities. From the point of view of data privacy, on the contrary, when logging confidential information, the risk of its leakage is greater. In general, ensuring the confidentiality of personal data of application users is a higher priority.



Afterword



The types of vulnerabilities considered in this article cover most of the "universal" gaps in applications written in different programming languages. However, some languages ​​have their own specific vulnerabilities. But this is already a topic for a separate article. And finally, remember: when creating applications, do not forget to follow the above recommendations, carefully read the documentation and check applications for vulnerabilities using specialized software.



Author: Elizaveta Kharlamova, Head of Analytics Department, Solar appScreener




All Articles