Skip to content

Instantly share code, notes, and snippets.

@fqodry
Last active August 28, 2019 09:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fqodry/8b4292eb193f25a7ec52feed98b49ea6 to your computer and use it in GitHub Desktop.
Save fqodry/8b4292eb193f25a7ec52feed98b49ea6 to your computer and use it in GitHub Desktop.
Anabatic - Payment Team: Penetration Testing Finding and Solution

PENETRATION TESTING FINDING + SOLUTION

APP VULNERABILITY FINDINGS

FINDING 1 - Broken URL Access Control

- Problem: The URL(s) accessible without login to the application

- Affected To: Sensitive information exposure (email, username, sensitive key/parameter, hashed password, etc.)

- Solution: Giving auth validation for each URL access.

BACKEND

//Check if user role allowed to this Page
if(!isActionAllowed((String) model.get("roleRequest"))){
    if(getUserRole().equals("NO_ROLE")){
        model.clear();
        return "redirect:/login";
    } else {
        model.clear();
        return "redirect:/403";
    }
}

FRONTEND

Object mySession = session.getAttribute(SESSION_KEY);
if(mySession == null) {
    return "redirect:/login";
}

FINDING 2 - XSS Vulnerability

- Problem: Attacker could target unsuspecting application users with a malicious client-side script that steals session cookies and allows an attacker control of user session (session hijacking). Such malicious script could also compromise private information e.g. steal sensitive information, account owner details or other sensitive information accessible to application user.

- Affected To: Steal sensitive information, possible to do phising attack, launch browser exploit.

- Solution:

  • On client side: do filter for using special characters (< > & " ') this solution has weakness, when user know how to disable Javascript
  • On server side: remove or replace any special characters (< > & " ') after submitting form (special characters validation). Or halt submit process when there is any special characters.
protected String ptrnNOHTML = ".*[<].*|.*[>].*";

if(BILLER_NAME.isEmpty()) { 
    isValid = false;Error+="|bill-name:<li>`Biller Name` must not be empty</li>|"; 
} else if(BILLER_NAME.matches(ptrnNOHTML)) {
    isValid = false;Error+="|bill-name:<li>`Biller Name` must not be filled with `<` and/or `>`</li>|";
}

FINDING 3 - No Protection Against Automated Password Attack

- Problem: Attacker launched dictionary attack on username pentest02; the account was not locked out even after brute-forcing it with more than 50 wrong passwords. By exploiting this vulnerability, an attacker could perform an arbitrary number of authentication attempts using different username/passwords or performing brute force attack on the application. Password quality policy was also not implemented in the application.

Lack of protection from automated password attacks could expose application to password brute forcing or dictionary attacks.

- Affected To: Possibility to do infinity brute force attack, could perform an arbitrary number of auth attempts using different username/password combination.

- Solution: There are 2 solutions for this case:

  • Validation for maximum authentication failed attempt. (in MRP Mandiri case set to max. 3 failed attempt and account locked for 10 minutes). If failed for next attempt, updated locked time for next 10 minutes. (for MRP Mandiri case, we use this solution)
  • Block attacker IP address. (put it on database for blacklist IP address)
@Service("serviceUserAttempt")
public class Service_UserAttemptImpl implements Service_UserAttempt {
    @Autowired private Mapper_UserAttempt mapperUserAttempt;
    @Autowired private Mapper_AppUserLogin mapperUserLogin;
    
    private static final int MAX_ATTEMPT = 3;
    private static final int BLOCKED_EXPIRED_MIN = 10;
    
    @Override
    public UserAttempt getUserAttempt(String username) {
        try {
            UserAttempt userAttempt = mapperUserAttempt.getUserAttempt(username);
            
            return userAttempt;
        } catch(NullPointerException npe) {
            return null;
        }
    }
    
    @Override
    public void updateFailAttempt(String username) {
        DateTime currentDatetime = new DateTime();
        UserAttempt userAttempt = getUserAttempt(username);
        if(userAttempt == null) {
            if(isUserExists(username)) {
                // if no record, insert a new
                UserAttempt data = new UserAttempt();
                data.setUsername(username);
                data.setAttempt(1);
                data.setLastModified(currentDatetime.toString());
                mapperUserAttempt.insertAttempt(data);
            }
        } else {
            // check if last_modified is more than 10 minutes
            if(userAttempt.getLastModified() != null) {
                DateTimeFormatter f = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.SSSSSS");
                DateTime lastModified = f.parseDateTime(userAttempt.getLastModified());
//                DateTime lastModified = DateTime.parse(userAttempt.getLastModified());
                DateTime expiredDatetime = lastModified.plusMinutes(BLOCKED_EXPIRED_MIN);

                if(expiredDatetime.isBeforeNow()) {
                    AppUserLogin dataUser = new AppUserLogin();
                    dataUser.setUsrACCOUNT_NON_LOCKED(true);
                    dataUser.setUsrUNAME(username);
                    mapperUserAttempt.resetAttempt(username);
                    mapperUserLogin.updateUserLocked(dataUser);
                    
                    //throw unlocked exception
                }
            }
            
            // update attempt + 1
            if(isUserExists(username)) {
                UserAttempt data = new UserAttempt();
                data.setUsername(username);
                data.setLastModified(currentDatetime.toString());
                System.out.println("data"+data);
                mapperUserAttempt.updateAttempt(data);
            }
            
            // if attempt >= max attempt, then throw LockedException
            UserAttempt userAttemptNew = getUserAttempt(username);            
            if(userAttemptNew.getAttempt() >= MAX_ATTEMPT) {
                // locked user
                AppUserLogin dataUser = new AppUserLogin();
                dataUser.setUsrACCOUNT_NON_LOCKED(false);
                dataUser.setUsrUNAME(username);
                mapperUserLogin.updateUserLocked(dataUser);
                // throw exception
                throw new LockedException("User Account is blocked due to maximum failed login attempt. Please contact bank admin for further assistance.");
            }
        }
    }
    
    @Override
    public void resetFailAttempt(String username) {
        mapperUserAttempt.resetAttempt(username);
    }
    
    private boolean isUserExists(String username) {
        boolean result = false;
        
        AppUserLogin userLogin = mapperUserLogin.getUser_byUsername(username);
        if(userLogin != null) {
            result = true;
        }
        
        return result;
    }
}

FINDING 4 - Autocomplete Not Disabled

- Problem: Attacker who gains access to the computer can capture stored credentials like Username, Email, and maybe Password too.

- Affected To: Steal credential information.

- Solution: Include html attribute autocomplete="off" within the <form> tag to protect all fields OR within each <input> tag to protect specific individual fields.

<form name="loginForm" id="loginForm" action="<c:url value="login" />" autocomplete="off" method='POST'>
</form>

OR

<input type="text" name="username" class="form-control" id="inlineFormInput" autocomplete="off" placeholder="Username">

<input type="password" name="password" class="form-control" id="inlineFormInputGroup" autocomplete="off" placeholder="Password">

FINDING 5 - Lack of Protection Against CSRF Attack

- Problem: Attacker successfully demonstrated scenario, where they could use CSRF attacks to change Report Setting on behalf of targeted customer.

- Affected To: Report Setting information changed without knowledge of the targeted user.

- Solution: (null)

FINDING 6 - Username Enumeration

FINDING 7 - Insufficient Session Expiration for Reset Password Link

FINDING 8 - Concurrent User Login

FINDING 9 - Host Header Attack

FINDING 10 - Use of Vulnerable Components

FINDING 11 - Version Information Exposure in Server's Response Header

FINDING 12 - Misconfigured Security Headers

FINDING 13 - Cacheable HTTPS Response

FINDING 14 - Cookie Without HttpOnly and Secure Flag Set

FINDING 15 - Multiple Web Parameter Tampering

FINDING 16 - Information Exposure Through Query Strings in GET Request

FINDING 17 - Sensitive Information Exposure in Response Message

FINDING 18 - Missing Custom Error Page

SERVER VULNERABILITY FINDINGS

FINDING 19 - Insufficient Transport Protection

FINDING 20 - Missing Security Patches OS

FINDING 21 - Weak SSL Certificate Configuration

FINDING 22 - Use of Vulnerable Software

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment