Статьи

Безопасно зашифрованные пароли разработчика SQL

Недавно, находясь на сайте одного из наших клиентов, мы с клиентом должны были получить доступ к базе данных. На моей машине я хранил пароль, но клиент явно не хотел полагаться на мою машину, а сам пароль хешировался, поэтому мы не могли его угадать. Но угадайте что? Да мы можем! Я немного погуглил и невероятно нашел инструкции написать следующую небольшую утилиту, которую я лицензирую вам на условиях ASL 2.0 :

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Эта программа НИКАКИМИ СРЕДСТВАМИ не предназначена для Вас, чтобы причинить любой вред. Вы могли бы найти эту информацию где-нибудь еще в Интернете. Пожалуйста, используйте это ТОЛЬКО для восстановления ваших собственных «потерянных» паролей. Как я и сделал.

Обратите внимание, что это работает только с версиями SQL Developer менее 4.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
import java.io.File;
import java.security.GeneralSecurityException;
 
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
 
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
 
public class SQLDeveloperDecrypter {
    public static void main(String[] args) throws Exception {
        if (args.length == 0) {
            System.err.println("  Usage 1: " + SQLDeveloperDecrypter.class.getName() + " 0501F83890..... (a single encrypted password)");
            System.err.println("  Usage 2: " + SQLDeveloperDecrypter.class.getName() + " C:\\Users\\...... (the path to the connections.xml file)");
            System.err.println();
            System.err.println("  Pass the password hash code from your connections.xml file. The file might be located at (example)");
            System.err.println("  C:\\Users\\[User]\\AppData\\Roaming\\SQL Developer\\system2.1.1.64.45\\o.jdeveloper.db.connection.11.1.1.2.36.55.30");
 
            System.exit(-1);
        }
 
        if (args[0].startsWith("05")) {
            System.out.println(decryptPassword(args[0]));
        }
        else {
            File file = new File(args[0]);
            if (file.isDirectory())
                file = new File(file, "connections.xml");
 
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document doc = builder.parse(file.toURI().toString());
 
            // The relevant structure is:
            //
            // <Reference name="connection name">
            //   <RefAddresses>
            //     <StringRefAddr addrType="password">
            //       <Contents>057D3DE2...
 
            XPathFactory xPathfactory = XPathFactory.newInstance();
            XPath xpath = xPathfactory.newXPath();
            XPathExpression expr = xpath.compile("//StringRefAddr[@addrType='password']/Contents");
 
            NodeList nodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
            for (int i = 0; i < nodes.getLength(); i++) {
                Element e = (Element) nodes.item(i);
 
                System.out.println("Connection name     : " +
                    ((Element) e.getParentNode().getParentNode().getParentNode()).getAttribute("name")
                );
 
                System.out.println("Password (encrypted): " +
                    e.getTextContent()
                );
 
                System.out.println("Password (decrypted): " +
                    decryptPassword(e.getTextContent())
                );
 
                System.out.println();
            }
        }
    }
 
    public static byte[] hexStringToByteArray(String s) {
        int len = s.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                                 + Character.digit(s.charAt(i+1), 16));
        }
        return data;
    }
 
    public static String decryptPassword(String result) throws GeneralSecurityException {
          return new String(decryptPassword(hexStringToByteArray(result)));
    }
 
    public static byte[] decryptPassword(byte[] result) throws GeneralSecurityException {
        byte constant = result[0];
        if (constant != 5) {
            throw new IllegalArgumentException();
        }
 
        byte[] secretKey = new byte[8];
        System.arraycopy(result, 1, secretKey, 0, 8);
 
        byte[] encryptedPassword = new byte[result.length - 9];
        System.arraycopy(result, 9, encryptedPassword, 0, encryptedPassword.length);
 
        byte[] iv = new byte[8];
        for (int i = 0; i < iv.length; i++) {
            iv[i] = 0;
        }
 
        Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(secretKey, "DES"), new IvParameterSpec(iv));
        return cipher.doFinal(encryptedPassword);
    }
}

Части исходного кода были заимствованы, отсюда и здесь . Другими словами, практически любой хакер мог придумать вышеуказанную программу. А выходной? Этот:

1
2
3
4
5
6
7
Connection name     : SAKILA
Password (encrypted): 0517CB1A41E3C2CC3A3163234A6A8E92F8
Password (decrypted): SAKILA
 
Connection name     : TEST
Password (encrypted): 05B03F45511F83F6CD4D322C9E173B5A94
Password (decrypted): TEST

Замечательный! Все пароли на моей машине теперь восстанавливаются за постоянное время (без перебора).

Это заставляет вас думать? Я надеюсь, что ваш администратор базы данных не хранит свои пароли в SQL Developer. На ноутбуке. Что они забывают в поезде. С доступом к информации о кредитной карте ваших клиентов.

Тем не менее, я рад, что смог восстановить «потерянный» пароль для моего клиента!

Ссылка: Защищенные пароли для разработчиков SQL от нашего партнера по JCG Лукаса Эдера из блога JAVA, SQL и JOOQ .