CVE-2021-21253 (published 2021-01-21T15:15Z)

OnlineVotingSystem is an open source project hosted on GitHub. OnlineVotingSystem before version 1.1.2 hashes user passwords without a salt, which is vulnerable to dictionary attacks. Therefore there is a threat of security breach in the voting system. Without a salt, it is much easier for attackers to pre-compute the hash value using dictionary attack techniques such as rainbow tables to crack passwords. This problem is fixed and published in version 1.1.2. A long randomly generated salt is added to the password hash function to better protect passwords stored in the voting system.

Severity: MEDIUM
CVSS v2 base score: 5.0 (AV:N/AC:L/Au:N/C:P/I:N/A:N)
CVSS v3 base score: 5.3, MEDIUM (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N)

Weakness information:
cwe_id: CWE-916
cwe_name: Use of Password Hash With Insufficient Computational Effort
description: The product generates a hash for a password, but it uses a scheme that does not provide a sufficient level of computational effort that would make password cracking attacks infeasible or expensive.
extended_description: 
            Many password storage mechanisms compute a hash and store the hash, instead of storing the original password in plaintext. In this design, authentication involves accepting an incoming password, computing its hash, and comparing it to the stored hash.
            Many hash algorithms are designed to execute quickly with minimal overhead, even cryptographic hashes. However, this efficiency is a problem for password storage, because it can reduce an attacker's workload for brute-force password cracking. If an attacker can obtain the hashes through some other method (such as SQL injection on a database that stores hashes), then the attacker can store the hashes offline and use various techniques to crack the passwords by computing hashes efficiently. Without a built-in workload, modern attacks can compute large numbers of hashes, or even exhaust the entire space of all possible passwords, within a very short amount of time, using massively-parallel computing (such as cloud computing) and GPU, ASIC, or FPGA hardware. In such a scenario, an efficient hash algorithm helps the attacker.
            There are several properties of a hash scheme that are relevant to its strength against an offline, massively-parallel attack:
               
                  The amount of CPU time required to compute the hash ("stretching")
                  The amount of memory required to compute the hash ("memory-hard" operations)
                  Including a random value, along with the password, as input to the hash computation ("salting")
                  Given a hash, there is no known way of determining an input (e.g., a password) that produces this hash value, other than by guessing possible inputs ("one-way" hashing)
                  Relative to the number of all possible hashes that can be generated by the scheme, there is a low likelihood of producing the same hash for multiple different inputs ("collision resistance")
               
            Note that the security requirements for the product may vary depending on the environment and the value of the passwords. Different schemes might not provide all of these properties, yet may still provide sufficient security for the environment. Conversely, a solution might be very strong in preserving one property, which still being very weak for an attack against another property, or it might not be able to significantly reduce the efficiency of a massively-parallel attack.
         
         

Fix information:
repo_url: https://github.com/dbijaya/OnlineVotingSystem
commit_details: repo_url: https://github.com/dbijaya/OnlineVotingSystem
author: luchua-bc
author_date: 2021-01-10 15:05:50+00:00
msg: Add a salt to SHA-256 hash
repository: repo_url: https://github.com/dbijaya/OnlineVotingSystem
repo_name: visit repo url
description: visit repo url
repo_language: visit repo url
owner: dbijaya
file_changes: filename: AdminLoginController.java
change_type: ModificationType.MODIFY
diff: @@ -18,7 +18,6 @@ import com.bijay.onlinevotingsystem.dao.AdminDaoImpl;
 public class AdminLoginController extends HttpServlet {
 	private static final long serialVersionUID = 1L;
 	AdminDao adminDao = new AdminDaoImpl();
-	SHA256 sha = new SHA256();
 
 	protected void doGet(HttpServletRequest request, HttpServletResponse response)
 			throws ServletException, IOException {
@@ -36,7 +35,7 @@ public class AdminLoginController extends HttpServlet {
 
 		// to get values from the login page
 		String userName = request.getParameter("aname");
-		String password = sha.getSHA(request.getParameter("pass"));
+		String password = request.getParameter("pass");
 		// String password = request.getParameter("pass");
 		String rememberMe = request.getParameter("remember-me");
 
@@ -72,4 +71,4 @@ public class AdminLoginController extends HttpServlet {
 			rd.include(request, response);
 		}
 	}
-}
\ No newline at end of file
+}

programming_language: Java
filename: SHA256.java
change_type: ModificationType.MODIFY
diff: @@ -1,32 +1,21 @@
 package com.bijay.onlinevotingsystem.controller;
 
-import java.math.BigInteger;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.Base64;
 
 public class SHA256 {
+	private static final String SSHA_PREFIX = "{SSHA}";
+	private static final int SSHA_256_LENGTH = 32; // SHA-256 is 32 bytes long
+	private static final int SALT_LENGTH = 16; // Use a 16 byte salt
 
 	public String getSHA(String password) {
-
 		try {
-			// Static getInstance method is called with hashing SHA
-			MessageDigest md = MessageDigest.getInstance("SHA-256");
-
-			// digest() method called
-			// to calculate message digest of an input
-			// and return array of byte
-			byte[] messageDigest = md.digest(password.getBytes());
-
-			// Convert byte array into signum representation
-			BigInteger no = new BigInteger(1, messageDigest);
-
-			// Convert message digest into hex value
-			String hashPass = no.toString(16);
-
-			while (hashPass.length() < 32) {
-				hashPass = "0" + hashPass;
-			}
-			return hashPass;
+			byte[] salt = getSalt();
+			String cipher = getCipher(password, salt);
+			
+			return cipher;
 
 			// For specifying wrong message digest algorithms
 		} catch (NoSuchAlgorithmException e) {
@@ -34,4 +23,54 @@ public class SHA256 {
 			return null;
 		}
 	}
+
+	public static boolean validatePassword(String password, String cipherText) {
+		boolean isValid = false;
+		try {
+			String cipher = cipherText.substring(SSHA_PREFIX.length());
+		
+			byte[] cipherBytes = Base64.getDecoder().decode(cipher.getBytes());
+			byte[] salt = new byte[SALT_LENGTH];
+			System.arraycopy(cipherBytes, SSHA_256_LENGTH, salt, 0, SALT_LENGTH);
+
+			String result = getCipher(password, salt);
+			//Compare the newly hashed password taking the same salt with the input hash
+			isValid = result.equals(cipherText);
+		} catch (NoSuchAlgorithmException e) {
+			e.printStackTrace();
+		}
+		return isValid;
+	}
+	
+	private static byte[] getSalt() throws NoSuchAlgorithmException {
+		SecureRandom random = new SecureRandom();
+		byte[] salt = new byte[SALT_LENGTH];
+		random.nextBytes(salt);
+		return salt;
+	}
+
+	private static String getCipher(String password, byte[] salt) throws NoSuchAlgorithmException {
+		// Static getInstance method is called with hashing SHA
+		MessageDigest md = MessageDigest.getInstance("SHA-256");
+		md.update(salt);
+	
+		byte[] passBytes = password.getBytes();
+		byte[] allBytes = new byte[passBytes.length + SALT_LENGTH];
+		System.arraycopy(passBytes, 0, allBytes, 0, passBytes.length);
+		System.arraycopy(salt, 0, allBytes, passBytes.length, SALT_LENGTH);
+	
+			
+		byte[] cipherBytes = new byte[SSHA_256_LENGTH + SALT_LENGTH];
+			
+		// digest() method called
+		// to calculate message digest of an input
+		// and return array of byte
+		byte[] messageDigest = md.digest(allBytes);
+	
+		System.arraycopy(messageDigest, 0, cipherBytes, 0, SSHA_256_LENGTH);
+		System.arraycopy(salt, 0, cipherBytes, SSHA_256_LENGTH, SALT_LENGTH);
+		
+		String result = SSHA_PREFIX + Base64.getEncoder().encodeToString(cipherBytes);
+		return result;
+	}
 }

programming_language: Java
filename: VoterLoginController.java
change_type: ModificationType.MODIFY
diff: @@ -21,7 +21,6 @@ import com.bijay.onlinevotingsystem.dao.VoterDaoImpl;
 public class VoterLoginController extends HttpServlet {
 	private static final long serialVersionUID = 1L;
 	VoterDao voterDao = new VoterDaoImpl();
-	SHA256 sha = new SHA256();
 
 	private String host;
 	private String port;
@@ -76,7 +75,7 @@ public class VoterLoginController extends HttpServlet {
 		otp = r.nextInt(max - min) + min;
 
 		String userName = request.getParameter("uname");
-		String password = sha.getSHA(request.getParameter("pass"));
+		String password = request.getParameter("pass");
 		String vemail = request.getParameter("vmail");
 
 		String recipient = vemail;
@@ -119,4 +118,4 @@ public class VoterLoginController extends HttpServlet {
 			 */
 		}
 	}
-}
\ No newline at end of file
+}

programming_language: Java
filename: AdminDaoImpl.java
change_type: ModificationType.MODIFY
diff: @@ -6,6 +6,9 @@ import java.sql.SQLException;
 import java.util.ArrayList;
 import java.util.List;
 
+import javax.crypto.Cipher;
+
+import com.bijay.onlinevotingsystem.controller.SHA256;
 import com.bijay.onlinevotingsystem.dto.Admin;
 import com.bijay.onlinevotingsystem.util.DbUtil;
 
@@ -98,14 +101,14 @@ public class AdminDaoImpl implements AdminDao {
 	@Override
 	public boolean loginValidate(String userName, String password) {
 
-		String sql = "select * from admin_table where admin_name=? and password=?";
+		String sql = "select * from admin_table where admin_name=?";
 		try {
 			ps=DbUtil.getConnection().prepareStatement(sql);
 			ps.setString(1, userName);
-			ps.setString(2,password);
 			ResultSet rs =ps.executeQuery();
 			if (rs.next()) {
-				return true;
+				String cipherText = rs.getString("password");
+				return SHA256.validatePassword(password, cipherText);
 			}
 		} catch (SQLException | ClassNotFoundException e) {
 			e.printStackTrace();

programming_language: Java
filename: VoterDaoImpl.java
change_type: ModificationType.MODIFY
diff: @@ -7,6 +7,7 @@ import java.util.ArrayList;
 import java.util.List;
 import java.sql.Date;
 
+import com.bijay.onlinevotingsystem.controller.SHA256;
 import com.bijay.onlinevotingsystem.dto.Voter;
 import com.bijay.onlinevotingsystem.util.DbUtil;
 
@@ -80,15 +81,15 @@ public class VoterDaoImpl implements VoterDao {
 
 	@Override
 	public boolean loginValidate(String userName, String password, String email) {
-		String sql = "select * from voter_table where voter_name=? and password=? and email=?";
+		String sql = "select * from voter_table where voter_name=? and email=?";
 		try {
 			ps = DbUtil.getConnection().prepareStatement(sql);
 			ps.setString(1, userName);
-			ps.setString(2, password);
-			ps.setString(3, email);
+			ps.setString(2, email);
 			ResultSet rs = ps.executeQuery();
 			if (rs.next()) {
-				return true;
+				String cipherText = rs.getString("password");
+				return SHA256.validatePassword(password, cipherText);
 			}
 		} catch (ClassNotFoundException | SQLException e) {
 			e.printStackTrace();

programming_language: Java