pretty-linear

Writeup from Junior 35c3CTF’18

Junior 35c3CTF 2018: pretty-linear

Category: Crypto
Challenge Points: 158
Solves: 28

Description:

The NSA gave us these packets, they said it should be just enough to break this crypto.

surveillance.pcap server.py

Difficulty estimate: Medium-Hard

This challenge objective was pretty clear - Solve Linear Equation with 40 unknowns

Now starting with *server.py*, we have three sets of variables,

if __name__ == '__main__':
     # key = keygen()   # generated once & stored in secrets.py
     challenge = keygen()
     print(' '.join(map(str, challenge)))
     response = int(input())
     if response != sum(x*y%p for x, y in zip(challenge, key)):
         print('ACCESS DENIED')
         exit(1)
  1. key
  • unknown, find 40 of them to get flag
  1. challenge
  • 40 integers per stream, we have 40 of them
  1. response
  • 1 response per stream, again 40 of them

Analysing surveilance.pcap with Wireshark

Open the packet capture file with Wireshark. We see mostly TCP packets, and to analyse the TCP packets, click

Analyse > Follow > TCP stream

We have 40 such streams of which, the blue ones are the challenge, and the red ones are response.

Extracting data using tshark

The following bash script will extract the data from surveilance.pcap

extract.sh

infile=surveilance.pcap
outfile=out
ext=txt
for stream in $(tshark -nlr $infile -Y tcp.flags.syn==1 -T fields -e tcp.stream | sort -n | uniq | sed 's/\r//')
do
        echo "Processing stream $stream: ${outfile}_${stream}.${ext}"
            tshark -nlr $infile -qz "follow,tcp,raw,$stream" | tail -n +7 | sed 's/^\s\+//g' | xxd -r -p > ${outfile}_${stream}.${ext}
        done

Solving Linear Equations using Matrices

Again, from *server.py*, we get

response = sum(x*y%p for x, y in zip(challenge, key))

So, finally with all the 40 files, we get 40 such equations (where i=range(40) )

response[i] = sum(x*y%p for x, y in zip(challenge[i], key))

Solving linear equation of 40 unknowns can be done easily with Matrices.
,reference

Thus, the key will be

challenge * key = response
key = challenge_inv * response 

Solving for key using Sage

To calculate the InverseMod(p) of the Matrix challenge, sage provides an easy way to this:

challenge_inv = Matrix(IntegerModRing(p),challenge).inverse()

Matrix multiplication of challenge_inv and response yeilds the key

key = challenge_inv * response

key = [151166356399959194245460055888166966126L,
 23349654305343746371904146512921179610L,
 303231127335861985008837572586617401477L,
 52564325979162295713031020943288299431L,
 318561098467762156502271721157519784045L,
 263049694618319332492436935081367988962L,
 151925705582116739255625584197651639678L,
 46319333286788790879399387215584902926L,
 144250191566113115015826218788418570765L,
 95097625879948609497612754022619234195L,
 40890527924981050968775993543458295905L,
 73015657936779070795829412187806965634L,
 17764129701686300306686689106838999642L,
 325835500394544926294581718484613749556L,
 71443020776832402486826429105359001130L,
 328905290970722092344104084599942510400L,
 246319993494260311894585740502008352891L,
 339251916682414225894494357646852524504L,
 270753355547506496805860877660621175158L,
 266604583518913012106937436764867155955L,
 132952188910249324219774647464400732439L,
 229485064954594431373138165566214808548L,
 273124499649767430591820642695664426994L,
 161206428662237066098654588615704724656L,
 191676246712534509807283243359699775780L,
 110791878778380133926865862999743362183L,
 121869512181659437298676494294916884080L,
 81324902884339942138294016318959955113L,
 219404824444265280645688236691554688702L,
 169041597038940530794876375975659802012L,
 131851490945732599957487956170326572223L,
 337190018815691236060142455413012785269L,
 215436829468576180414177636304832181536L,
 174614268507338543165725749934608091983L,
 316523955444804263394840392424504742312L,
 215434679427738924369625297037020081680L,
 103769840624100781721896803697739863413L,
 302813910848119681638497129402557822574L,
 104414047167186149419822776294661649936L,
 124689157029586638342169541932443340723L]

All we have to do next is, create the AES instance using the AES_key = sha256(key)

cipher = AES.new(
            sha256(' '.join(map(str, key)).encode('utf-8')).digest(),
            AES.MODE_CFB,
            b'\0'*16
        )
print cipher.decrypt(ct.decode('hex'))

FLAG

35C3_G4uss_w0uld_b3_so_pr0ud_of_y0u_r1ght_n0w

Complete Script

P.S you need to install sage to run the script

$sage exploit.sage

from hashlib import sha256
from Crypto.Cipher import AES
import os

p = 340282366920938463463374607431768211297
challenge=[]    # the x values
response=[]     # the sum of s values
key=[]          # the y values
ct = "923fa1835d8dbdcd9f9b0e6658b24fce54512fbba71d7a0012c37d2c9dd094a6278593d8d9f7a4aa9fecb66042"

# to parse the values from pcap
os.system("./extract.sh") 
for i in range(40):
    f = open("out/out_"+str(i)+".txt").read().split('\n')
    challenge+=[map(int,f[0].split(' '))]
    response+=[int(f[1])]

#Calculate the modularInverse the Matrix challenge
challenge_inv = Matrix(IntegerModRing(p),challenge).inverse()

#Create the 40x1 Matrix for response
rl=[]
for i in response: rl+=[[i]]
response_mat = Matrix(rl)

#multiply  challenge_inv with response_mat to get a 40x1 matrix for y (key)
## Ay=B  implies  y= invA*B
y = challenge_inv*response_mat

key = eval(y.str().strip().replace("]\n[",","))
print key
cipher = AES.new(
            sha256(' '.join(map(str, key)).encode('utf-8')).digest(),
            AES.MODE_CFB,
            b'\0'*16
        )
print cipher.decrypt(ct.decode('hex'))
4 min. read