Last active
August 29, 2015 14:02
-
-
Save bountin/7206f35e89a1edc4beb2 to your computer and use it in GitHub Desktop.
Attacking ECDSA with two messages in a PCAP file which have the same k - This also implements a client to test the found private key (University assignment of Internet Security 1 in the summer term of 2014)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import socket | |
import ecdsa | |
import base64 | |
import sys | |
from hashlib import sha1 | |
from ecdsa.util import string_to_number | |
# Fixing fucking scapy warnings - fuck python | |
import logging | |
logging.getLogger("scapy.runtime").setLevel(logging.ERROR) | |
from scapy.all import PcapReader, NoPayload | |
# Read arguments into variables | |
host = sys.argv[1] | |
port = int(sys.argv[2]) | |
dump_file = sys.argv[3] | |
key_file = sys.argv[4] | |
# Extract data from the public key | |
public = open(key_file).read() | |
vk = ecdsa.VerifyingKey.from_pem(public) | |
# Extract pcap stuff | |
packets = [] | |
pcap = PcapReader(dump_file) | |
for pkt in pcap: | |
packet = pkt.payload.payload | |
if isinstance(pkt.payload.payload.payload, NoPayload): | |
continue | |
data = packet.payload | |
packets.append(data.load) | |
challenge1 = packets[2].strip() | |
response1 = packets[3].strip() | |
challenge2 = packets[11].strip() | |
response2 = packets[12].strip() | |
# challenge1 = 'GPivoWzCNWJdvDdswouhZTPkclBrjVxjNFHYQkneNnsDWDbsQNMzsQYEnyerBFARLGczgXExgJXLAWoHOthLTxBjXlBEOmkVmpdOFkLNIAwIZnjLCIxMAHkkZFWNIMcQYCNaEETyVRGjfMiZScAbtPUyoJmmQAFEMHApjvyGPSiZCiFGdGUPjiBKTFzqGmzJteNMcHYJ' | |
# challenge2 = 'NDazqEkHwSsXnTfoXBogLRxvnKAcmMqaFrEMTjAoKSnyTrpobojaHpnLNofQLLnqJtBBmcBQyxjtMYhGejQUJxkfldqWYfnLDuDaoHimbSlUyZsyHLbOEnuceuwegBtWugzgjTKxPnwzNDLfHNdANeSnRDGbcoNCjmarZVfEHQZxsFeTALRHxmKguvzJovRcxaDtFdsH' | |
# response1 = 'WFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWBC7jphABJsYPgeNnDAOFgVZARjr3X/1kIDlLHmG/O0J9rJ+RJmv0Gm0wwGZZPWFKg==' | |
# response2 = 'WFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWBC7jphABJsYPgeNnDAOFgVZARjr3X/1kBp+xhMkcZp3mA4QBk+a2R+976y1tnloqg==' | |
response1 = base64.b64decode(response1) | |
response2 = base64.b64decode(response2) | |
message1 = response1[:-48] | |
message2 = response2[:-48] | |
sig1 = response1[-48:] | |
sig2 = response2[-48:] | |
digest1 = sha1(challenge1).digest() | |
z1 = string_to_number(digest1) | |
digest2 = sha1(challenge2).digest() | |
z2 = string_to_number(digest2) | |
r1, s1 = ecdsa.util.sigdecode_string(sig1, vk.pubkey.order) | |
r2, s2 = ecdsa.util.sigdecode_string(sig2, vk.pubkey.order) | |
assert r1 == r2 | |
assert vk.verify(sig1, challenge1) | |
assert vk.verify(sig2, challenge2) | |
n = vk.pubkey.generator.order() | |
k = (z1 - z2) * ecdsa.numbertheory.inverse_mod(s1 - s2, n) | |
k %= n | |
privateKey = (s1 * k - z1) * ecdsa.numbertheory.inverse_mod(r1, n) | |
privateKey %= n | |
sk = ecdsa.SigningKey.from_secret_exponent(privateKey) | |
sig = sk.privkey.sign(z1, k) | |
assert sig.r == r1 | |
assert sig.s == s1 | |
sig = sk.privkey.sign(z2, k) | |
assert sig.r == r2 | |
assert sig.s == s2 | |
conn = socket.create_connection((host, port), 4) | |
# Skip first two lines | |
data = conn.recv(2048) | |
if not "Please authenticate" in data: | |
data = conn.recv(2048) | |
challenge = conn.recv(2048) | |
challengeDigest = string_to_number(sha1(challenge.strip()).digest()) | |
challengeSig = sk.privkey.sign(challengeDigest, k) | |
sigString = ecdsa.util.sigencode_string(challengeSig.r, challengeSig.s, n) | |
msg = base64.b64encode(message1 + sigString) + "\n" | |
assert len(msg) == 345 | |
x = conn.send(msg) | |
assert x == 345 | |
data = conn.recv(2048) | |
if not "Log out" in data: | |
data = conn.recv(2048) | |
conn.send("1\n") | |
secret = conn.recv(2048) | |
sys.stdout.write(secret.strip()) | |
conn.send("2\n") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment