DeadpoolAndObjectOrientedProgramming / icectf-2016

IceCTF 2016 repo
0 stars 0 forks source link

Stage 3 - l33tcrypt #41

Closed ikornaselur closed 8 years ago

ikornaselur commented 8 years ago

Description

l33tcrypt is a new and fresh encryption service. For added security it pads all information with the flag! Can you get it? nc l33tcrypt.vuln.icec.tf 6001 server.py

Solution

Flag is: IceCTF{unleash_th3_Blocks_aNd_find_what_you_seek}

ikornaselur commented 8 years ago
#!/usr/bin/python
from Crypto.Cipher.AES import AESCipher
import SocketServer as ss
import signal
import base64

from secret import KEY, FLAG

PORT = 6001

def pad(text, bs):
    text = text + FLAG
    pad_num = (bs - len(text) % bs)
    return text + chr(pad_num) * pad_num

def recvline(req):
    buf = b""
    while not buf.endswith(b"\n"):
        buf += req.recv(1)
    return buf

class RequestHandler(ss.BaseRequestHandler):
    def handle(self):
        req = self.request

        # Close the connection after 5 seconds.
        signal.alarm(5)

        req.sendall("Welcome to l33tserver where all your encryption needs are served.\n")
        req.sendall("Send me something to encrypt:\n")
        data = recvline(req).strip()
        # Try to base64 decode the message
        try:
            data = base64.b64decode(data)
        except:
            req.sendall("bad data\n")
            req.close()
            return
        # Return a error if the message didn't start with 'l33tserver please'
        if not data.startswith("l33tserver please"):
            req.sendall("You didnt say the magic word :(\n")
            req.close()
            return
        # Encrypt the message using `KEY` and pad the data with the FLAG
        c = AESCipher(KEY).encrypt(pad(data, 16))
        req.sendall("Your l33tcrypted data:\n")
        req.sendall(base64.b64encode(c) + "\n")
        req.close()

class TCPServer(ss.ForkingMixIn, ss.TCPServer):
    pass

ss.TCPServer.allow_reuse_address = True
server = TCPServer(("0.0.0.0", PORT), RequestHandler)

print("Server listening on port %d" % PORT)
server.serve_forever()
ikornaselur commented 8 years ago

I assume that padding each message with the same payload each time (the flag) is a weakness that can be exploited to generate the private key.

koddsson commented 8 years ago

I'm gonna walk through the code and comment it so I can get a better understanding of how it works. So just know that the comments added to @ikornaselur comment above are made by me.

koddsson commented 8 years ago

I don't think this is a padding oracle attack since it "[...] relies on having a "padding oracle" who freely responds to queries about whether a message is correctly padded or not. "

koddsson commented 8 years ago

This might be some attack where when you know the padding and the beginning you can bruteforce the rest. Looking for resource on a attack like that right now but coming up with very little :(

koddsson commented 8 years ago

Pretty sure this is a plaintext attack due to the encryption is AES ECB. Looking at this guide.

ikornaselur commented 8 years ago
import sys

with open('strings.txt', 'r') as f:
    strings = [x.strip() for x in f.readlines()]

argument = sys.argv[1]

for l in range(len(argument)):
    test_string = argument[:len(argument)-l]
    found = []
    for s in strings:
        match = s[2:]
        if match.startswith(test_string):
            found.append(s)

    if len(found):
        for f in found:
            print f
        print "  " + test_string
        break

How to run:

have strings.txt with strings in format:

A asjdilas
B ajisdijsa

and then do python script.py test_string

koddsson commented 8 years ago

I've been working on this for a bit now and recently got stuck. Pretty sure this is a known-plaintext attack but my scripts aren't working.

Here they are:

☁  l33tscript  cat hax.sh
#!/bin/bash

echo -e "l33tserver please $1" | base64 | nc l33tcrypt.vuln.icec.tf 6001 | tail -n 1
☁  l33tscript  cat looper.sh
#!/bin/bash

echo > outputs
grep -o . <<< "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_{}?%$@#^*()[];:" | while read letter; do echo "$letter $(./hax.sh "...............$letter")" >> outputs; done
ikornaselur commented 8 years ago
import socket
import time
import sys
from base64 import b64encode

chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_ {}'

PORT = 6001
HOST = 'l33tcrypt.vuln.icec.tf'

block = 'l33tserver please '
key = ''

def send_msg(msg):
    answer = ''
    retries = 1
    while len(answer) == 0:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect((HOST, PORT))

        s.recv(256)  # Welcome to l33tserver where all your encryption needs are served. # noqa
        s.recv(256)  # Send me something to encrypt:
        s.send('{}\n'.format(b64encode(msg)))
        s.recv(256)  # Your l33tcrypted data:

        answer = s.recv(256)
        s.close()
        if len(answer) == 0:
            retries += 1
            sys.stdout.write('.')
            sys.stdout.flush()
            sleep = 1 if retries < 5 else 5
            time.sleep(sleep)
    sys.stdout.write('\n')
    sys.stdout.flush()
    return answer

def get_longest(string, arr):
    for l in range(len(string)):
        test_string = string[:len(string)-l]
        found = []
        for s in arr:
            if s.startswith(test_string):
                found.append(s)

        if len(found) == 1:
            return found[0]
        elif len(found) > 1:
            raise Exception('what the shit')

pad_len = 16 - len(block + key) % 16
padding = 'A' * pad_len
msg = block + key + padding
initial = send_msg(msg)

while True:
    print "Testing base string string: {}".format(block + key)
    results = []
    for char in chars:
        pad_len = 16 - len(block + key) % 16
        msg = block + padding + key + char
        sys.stdout.write("Testin {}.".format(msg))
        result = send_msg(msg)
        results.append((char, result))

    longest = get_longest(initial, [x[1] for x in results])
    char = [x[0] for x in results if x[1] == longest][0]
    print 'Found "{}" as the next char'.format(char)
    key += char
ikornaselur commented 8 years ago

Seemed to work for a bit.. but second letter hit a snag, so it's not correct.

image

ikornaselur commented 8 years ago
import socket
import time
import sys
from base64 import b64encode

chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_ {}'

PORT = 6001
HOST = 'l33tcrypt.vuln.icec.tf'

block = 'l33tserver please '
key = 'IceCTF{unle'

def send_msg(msg):
    answer = ''
    retries = 1
    while len(answer) == 0:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect((HOST, PORT))

        s.recv(256)  # Welcome to l33tserver where all your encryption needs are served. # noqa
        s.recv(256)  # Send me something to encrypt:
        s.send('{}\n'.format(b64encode(msg)))
        s.recv(256)  # Your l33tcrypted data:

        answer = s.recv(256)
        s.close()
        if len(answer) == 0:
            retries += 1
            sys.stdout.write('.')
            sys.stdout.flush()
            sleep = 1 if retries < 5 else 5
            time.sleep(sleep)
    sys.stdout.write('\n')
    sys.stdout.flush()
    return answer

def get_longest(string, arr):
    for l in range(len(string)):
        test_string = string[:len(string)-l]
        found = []
        for s in arr:
            if s.startswith(test_string):
                found.append(s)

        if len(found) == 1:
            return found[0]
        elif len(found) > 1:
            raise Exception('what the shit')

while True:
    # First iteration, last block padded with 15 A
    pad_len = 15 - len(block + key) % 16
    padding = 'A' * pad_len
    msg = block + padding
    # len(msg) == 31

    print "pad_len: {}".format(pad_len)

    print "Getting base for: {} (len {})".format(msg, len(msg))
    initial = send_msg(msg)

    results = []
    for char in chars:
        msg = block + padding + key + char
        sys.stdout.write("Testin {}.".format(msg))
        result = send_msg(msg)
        results.append((char, result))

    longest = get_longest(initial, [x[1] for x in results])
    char = [x[0] for x in results if x[1] == longest][0]
    print 'Found "{}" as the next char'.format(char)
    key += char

HOW DO YOU LIKE ME NOW

image

ikornaselur commented 8 years ago

IT'S HARD WORK CRACKING DEM CODES

image

ikornaselur commented 8 years ago

image

ikornaselur commented 8 years ago

Final version of the script

import socket
import time
import sys
from base64 import b64encode

class WhatTheFuck(Exception):
    pass

chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_}'

PORT = 6001
HOST = 'l33tcrypt.vuln.icec.tf'

block = 'l33tserver please '
key = 'IceCTF{unleash_th3_Blocks_aNd_find_what_yo'

def send_msg(msg):
    answer = ''
    retries = 1
    while len(answer) == 0 or not answer.startswith('OH7ZG'):
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.connect((HOST, PORT))

        s.recv(128)  # Welcome to l33tserver where all your encryption needs are served. # noqa
        s.recv(64)  # Send me something to encrypt:
        s.send('{}\n'.format(b64encode(msg)))
        s.recv(64)  # Your l33tcrypted data:

        answer = ''
        while not len(answer):
            answer = s.recv(256)
        s.close()
        if not answer.startswith('OH7ZG'):
            retries += 1
            sys.stdout.write('.')
            sys.stdout.flush()
            sleep = 0 if retries < 5 else 1
            time.sleep(sleep)
    sys.stdout.write('\n')
    sys.stdout.flush()
    return answer

def get_longest(string, arr):
    for l in range(len(string)):
        test_string = string[:len(string)-l]
        found = []
        for s in arr:
            if s.startswith(test_string):
                found.append(s)

        if len(found) == 1:
            return found[0]
        elif len(found) > 1:
            raise WhatTheFuck('u wot')

while True:
    # First iteration, last block padded with 15 A
    pad_len = 15 - len(block + key) % 16
    padding = 'A' * pad_len
    msg = block + padding
    # len(msg) == 31

    print "pad_len: {}".format(pad_len)

    print "Getting base for: {} (len {})".format(msg, len(msg))
    initial = send_msg(msg)

    results = []
    for char in chars:
        msg = block + padding + key + char
        sys.stdout.write("Testing {}.".format(msg))
        result = send_msg(msg)
        results.append((char, result))

    longest = get_longest(initial, [x[1] for x in results])
    char = [x[0] for x in results if x[1] == longest][0]
    if char == '}':
        print 'HEHEHEHEHE'
        print key
        break
    print 'Found "{}" as the next char'.format(char)
    key += char