Ir al contenido

Commnet

Autor
Santiago Chavarro

Análisis
#

Exploit
#

if __name__ == "__main__":
    username, password = random_string(8), random_string(8)

    register_user(username, password)
    cookies = login_user(username, password).cookies

    # flag sample is on messageId 3
    response = get_messages(cookies, 3)

    match = re.search(r'HTB\{.*?\}', response.text)
    if match:
        print("Exploit success. Got the flag!")
        print(match.group())
    else:
        print("Exploit failed. No flag found.")    

Gracias al exploit que trae el reto nos damos cuenta de que se trata de una vulnerabilidad de tipo IDOR ya que simplemente con el id del mensaje podemos acceder a el aunque no seamos ni el que lo envió ni el que lo recibe.

Código inseguro
#

// VULNERABLE: View specific message by ID (IDOR vulnerability)
async function viewMessage(messageId) {
    try {
        // This endpoint doesn't check if the user has permission to view this message
        const response = await fetch(getApiUrl(`/messages/${messageId}`));
        const data = await response.json();
        
        if (data.success) {
            showMessageDetail(data.message);
            
            // Check if this reveals sensitive information
            if (data.message.sender_id !== currentUser.id && 
                data.message.recipient_id !== currentUser.id && 
                data.message.recipient_id !== null) {
                // User accessed a message they shouldn't have access to
                showError('Unauthorized');
            }
        } else {
            showError('Message not found');
        }
    } catch (error) {
        showError('Failed to load message');
    }
}

Los comentarios del código me imagino al ser un reto very easy nos dan una pista de donde esta la vulnerabilidad y el tipo, entonces revisamos el endpoint:

// get message by ID
router.get('/:id', requireAuth, (req, res) => {
  const messageId = req.params.id;
  const userId = req.session.userId;
  
    // Proceed with order retrieval logic
  req.db.get(`
    SELECT m.*, 
           sender.username as sender_username, 
           sender.enclave as sender_enclave,
           recipient.username as recipient_username,
           recipient.enclave as recipient_enclave
    FROM messages m
    LEFT JOIN users sender ON m.sender_id = sender.id
    LEFT JOIN users recipient ON m.recipient_id = recipient.id
    WHERE m.id = ?
  `, [messageId], (err, message) => {
    if (err || !message) {
      return res.status(404).json({ success: false, error: 'Message not found' });
    }
    res.json({ success: true, message: message });
  });
});

Vemos que como nos comentaban no tiene una verificación para ver si el usuario que consulta el mensaje tiene permisos para hacerlo, no uso mucho JavaScript por lo que me puse a mirar alguna otra función que verificara esto para tratar de aplicarlo a este caso para eso use este trozo del código que nos avisaba si era vulnerable:

// Check if this reveals sensitive information
if (data.message.sender_id !== currentUser.id && 
    data.message.recipient_id !== currentUser.id && 
    data.message.recipient_id !== null) {
    // User accessed a message they shouldn't have access to
    showError('Unauthorized');
}

Acá data es el mensaje que recibió por lo que podemos hacer la consulta con message.sender_id y message.recipient_id por lo que la condición para que pueda acceder a el mensaje sera que haya iniciado sesión y que sea o el que envió el mensaje o el que lo recibió.

Solución
#

// get message by ID
router.get('/:id', requireAuth, (req, res) => {
  const messageId = req.params.id;
  const userId = req.session.userId;
  
    // Proceed with order retrieval logic
  req.db.get(`
    SELECT m.*, 
           sender.username as sender_username, 
           sender.enclave as sender_enclave,
           recipient.username as recipient_username,
           recipient.enclave as recipient_enclave
    FROM messages m
    LEFT JOIN users sender ON m.sender_id = sender.id
    LEFT JOIN users recipient ON m.recipient_id = recipient.id
    WHERE m.id = ?
  `, [messageId], (err, message) => {
    if (err || !message) {
      return res.status(404).json({ success: false, error: 'Message not found' });
    }
    if (!userId || userId !== message.sender_id && userId !== message.recipient_id){
        return res.status(404).json({ success: false, error: 'Message not found' });
    }
    res.json({ success: true, message: message });
  });

});

Referencias
#