DeadpoolAndObjectOrientedProgramming / icectf-2016

IceCTF 2016 repo
0 stars 0 forks source link

Stage 3 - Contract #25

Closed stebbib closed 8 years ago

stebbib commented 8 years ago

Description

Our contractors stole the flag! They put it on their file server and challenged us to get it back. Can you do it for us? nc contract.vuln.icec.tf 6002 server.py. We did intercept someone connecting to the server though, maybe it will help. contract.pcapng

Solution

Flag is: IceCTF{a_f0rged_signatur3_is_as_g00d_as_a_real_1}

ikornaselur commented 8 years ago

Just looking at the capture, we can see two commands sent:

time:c0e1fc4e3858ac6334cc8798fdec40790d7ad361ffc691c26f2902c41f2b7c2fd1ca916de687858953a6405423fe156c0cbebcec222f83dc9dd5b0d4d8e698a08ddecb79e6c3b35fc2caaa4543d58a45603639647364983301565728b504015d

and

help:c0e1fc4e3858ac6334cc8798fdec40790d7ad361ffc691c26f2902c41f2b7c2fd1ca916de687858953a6405423fe156cfd7287caf75247c9a32e52ab8260e7ff1e46e55594aea88731bee163035f9ee31f2c2965ac7b2cdfca6100d10ba23826

Looking at the server.py, we can see what comes before the : is the command and after is the signature. We need to send read flag.txt:<signature> to the server. Assuming that the flag is in flag.txt.

We at least probably need to figure out the private key from the two signatures above to construct our own signature for the read command.

ikornaselur commented 8 years ago

I really liked this one.

The commands are signed with ecdsa and the signature is sent with the command to verify that it's "real" or whatever.

Apparently there's a nonce that's used when creating the signature. It should never be reused. It was reused here though, hence the signature starting with the same value above.

There was a similar issue on another CTF, here's the writeup for that challenge. It was really helpful to get started. I had to modify their code a little bit, but was able to reuse most of it.

The crack script to recover the private key is as follows:

import hashlib
import binascii
from ecdsa import SigningKey, NIST384p
from ecdsa import VerifyingKey
from ecdsa.numbertheory import inverse_mod

def string_to_number(tstr):
    return int(binascii.hexlify(tstr), 16)

def sha256(content):
    sha256_hash = hashlib.sha256()
    sha256_hash.update(content)
    hash = sha256_hash.digest()
    return hash

def recover_key(c1, sig1, c2, sig2, pubkey):
    curve_order = pubkey.curve.order

    n = curve_order
    s1 = string_to_number(sig1[-48:])
    print "s1: " + str(s1)
    s2 = string_to_number(sig2[-48:])
    print "s2: " + str(s2)
    r = string_to_number(sig1[-96:-48])
    print "r: " + str(r)
    print "R values match: " + str(string_to_number(sig2[-96:-48]) == r)

    z1 = string_to_number(sha256(c1))
    z2 = string_to_number(sha256(c2))

    sdiff_inv = inverse_mod(((s1 - s2) % n), n)
    k = (((z1 - z2) % n) * sdiff_inv) % n
    r_inv = inverse_mod(r, n)
    da = (((((s1 * k) % n) - z1) % n) * r_inv) % n

    print "Recovered Da: " + hex(da)

    recovered_private_key_ec = SigningKey.from_secret_exponent(
        da, curve=NIST384p)
    return recovered_private_key_ec.to_pem()

public_key_ec_pem = '''
-----BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEgTxPtDMGS8oOT3h6fLvYyUGq/BWeKiCB
sQPyD0+2vybIT/Xdl6hOqQd74zr4U2dkj+2q6+vwQ4DCB1X7HsFZ5JczfkO7HCdY
I7sGDvd9eUias/xPdSIL3gMbs26b0Ww0
-----END PUBLIC KEY-----
'''.strip()

def recover():
    txt1 = "time"
    sig1 = '''c0e1fc4e3858ac6334cc8798fdec40790d7ad361ffc691c26f2902c41f2b7c2fd1ca916de687858953a6405423fe156c0cbebcec222f83dc9dd5b0d4d8e698a08ddecb79e6c3b35fc2caaa4543d58a45603639647364983301565728b504015d'''.strip().decode('hex')  # noqa

    txt2 = "help"
    sig2 = '''c0e1fc4e3858ac6334cc8798fdec40790d7ad361ffc691c26f2902c41f2b7c2fd1ca916de687858953a6405423fe156cfd7287caf75247c9a32e52ab8260e7ff1e46e55594aea88731bee163035f9ee31f2c2965ac7b2cdfca6100d10ba23826'''.strip().decode('hex')  # noqa

    public_key_ec = VerifyingKey.from_pem(public_key_ec_pem.strip())
    print "Verify1: " + str(
        public_key_ec.verify(sig1, txt1, hashfunc=hashlib.sha256))
    print "Verify2: " + str(
        public_key_ec.verify(sig2, txt2, hashfunc=hashlib.sha256))
    print "curve order:", public_key_ec.curve.order

    key = recover_key(txt1, sig1, txt2, sig2, public_key_ec)
    print key

if __name__ == "__main__":
    print "---Attempting to recover unknown key---"
    recover()
    print "---Done!---"

Having the private key meant that I could sign my own mesages. Here's the signature script I created

from ecdsa import SigningKey, VerifyingKey
from binascii import unhexlify
import hashlib

PRIVATE_KEY = '''
-----BEGIN EC PRIVATE KEY-----
MIGkAgEBBDD+qZQfEucEMokaAWn0wrTsPz3nMwIlBasVdyQpi/zT3X7UdF7WDD23
EChyxQOSWMigBwYFK4EEACKhZANiAASBPE+0MwZLyg5PeHp8u9jJQar8FZ4qIIGx
A/IPT7a/JshP9d2XqE6pB3vjOvhTZ2SP7arr6/BDgMIHVfsewVnklzN+Q7scJ1gj
uwYO9315SJqz/E91IgveAxuzbpvRbDQ=
-----END EC PRIVATE KEY-----
'''.strip()
PUBLIC_KEY = """
-----BEGIN PUBLIC KEY-----
MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEgTxPtDMGS8oOT3h6fLvYyUGq/BWeKiCB
sQPyD0+2vybIT/Xdl6hOqQd74zr4U2dkj+2q6+vwQ4DCB1X7HsFZ5JczfkO7HCdY
I7sGDvd9eUias/xPdSIL3gMbs26b0Ww0
-----END PUBLIC KEY-----
""".strip()

vk = VerifyingKey.from_pem(PUBLIC_KEY)
sk = SigningKey.from_pem(PRIVATE_KEY)

command = 'read flag.txt'
signature = sk.sign(command, hashfunc=hashlib.sha256)
signature_hex = signature.encode('hex')

print "Verifying.."
vk.verify(unhexlify(signature_hex), command, hashfunc=hashlib.sha256)

print "{}:{}".format(command, str(signature.encode('hex')))

aaaand then just putting it all together

image