Closed stebbib closed 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.
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
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.pcapngSolution
Flag is:
IceCTF{a_f0rged_signatur3_is_as_g00d_as_a_real_1}