Rating: 5.0

html
SECT-CTF 2018 write-up
Matsign

Abstract

This is a write-up for the Matsign challenge of SEC-T CTF 2018 by
LarsH on behalf of team Hacking for Soju.

1. Description
2. Figuring out the behaviour
3. Solution
4. Overkill solution

1. Description

The challenge was running on 178.128.171.133:5555, and the flag was
said to be the lowest 20 digits of d.

When connecting to the service, it presents this interface:

__
/C/
MatSign Cryptographic Service /I/
----------------------------- /B/
|/
Security in | a b c |
multiple | d e f |
dimensions | g h i |

Give comma-separated matrix to sign:

Trying some bogus data gives the error message: "Something went
wrong... size needs to be 3x3" and the service disconnects.

2. Figuring out the behaviour

If we give the service nine comma separated digits, the service
replies with a 2x3 matrix with quite large numbers.

If we supply the identity matrix 1,0,0,0,1,0,0,0,1 the reply from the
service is

[[1L, 0L, 0L], [0L, 1L, 0L]]

And with the hint that the flag has to do with d, this is probably an
RSA signing procedure using matrices, with a reply that is truncated
to only the two first rows of the signed matrix.

To test this hypothesis, we signed 2,4,3 and 9 using only the upper
left matrix element. This gave us

2**d % n = 603679504658226852258733857127676536438248398553526464506065500164789
20892468788806193056971340519189632591200228322574450863215073463938809513217771
27454218151709503308906485870739798369285902552803026690123593617754351285267483
1795529276636510222747378681491655760328983542122308539965569062960418027228183L

4**d % n = 926875655887109007820138851641838494342820714642022256276980010882813
69083202978850909071939396731888865937257211783503498517481458171092865273336295
02679786970301586391509019452500275418214518988911259672731921753902916312843023
0826432702708375423212724719649924819226542225995482529552443813838549471313322L

3**d % n =1002783014045680989162002369174882727519394851004682250516623232951979
31456945237050522241170678807530733514341047234698367738018287290685132701072323
82885426994795428296689422450100483060299748403585984932886370070098500410652752
2352685174235357463416363798084052572369869218241585397948175808995950214249823L

9**d % n = 495967715248440183321179074466677143303668392819564162181938566729935
84684880325084294326181161165473234176322716648852264854729112396270122126314023
39429249451479809223316534416370066154224263543748724963713983872714384359553650
0038124674914252524999992711503900926307291857797248003824799689533766593838147L

And we can now compute the value of n. Because

(2**d % n)**2 = (4**d%n) + a*n and (3**d % n)**2 = (9**d%n) + b*n

then

n = c * GCD( (2**d % n)**2 - (4**d%n) , (3**d % n)**2 - (9**d%n) )

There might be some extra factor c, but in this case c was 1.

n = 1167880879539630321283855857098624385796403183896228302611852628744808193253
58293077704265052032681793494472152951802121882380333334023139095912125054377042
73584260405126879310746133999429385784711382198310144962197080782467305669284876
2006415090468821976791496266369267044931986102543085443093592771899364043L

We finally verify the hypothesis with the commonly used public
exponent e = 0x10001

>>> pow( 60367950465822685225873385712767653643824839855352646450606550016478920
89246878880619305697134051918963259120022832257445086321507346393880951321777127
45421815170950330890648587073979836928590255280302669012359361775435128526748317
95529276636510222747378681491655760328983542122308539965569062960418027228183L,
0x10001, n)
2L

so the signed value of two is indeed two when verified with the
public exponent.

3. Solution

After some thought, we found out that this uppertriangular matrix can
be used to leak out d:

| 1 1 0 |**d | 1 d 0 |
| 0 1 0 | = | 0 1 0 |
| 0 0 0 | | 0 0 0 |

This is because exponentiation is just repeated matrix
multiplication; every multiplication with this matrix to itself will
increase the top middle element by one:

| 1 1 0 | | 1 i 0 | | 1 (i+1) 0 |
| 0 1 0 | * | 0 1 0 | = | 0 1 0 |
| 0 0 0 | | 0 0 0 | | 0 0 0 |

So the solution that leaks out d is

\$ echo 1,1,0,0,1,0,0,0,0 | nc 178.128.171.133 5555

__
/C/
MatSign Cryptographic Service /I/
----------------------------- /B/
|/
Security in | a b c |
multiple | d e f |
dimensions | g h i |

Give comma-separated matrix to sign: [[1L, 5943028721584245726050413176410138283
15456096295820893420591195334533976913911084447172931242701060135959938096181180
21343323376362812940611390044868753126296266703287597058581803060094740282543610
71673572477363571820379071571307323768821649021601211516734751578279378417106050
597533639902738566133769973073L, 0L], [0L, 1L, 0L]]

The value 59...73L is of d, and the flag is the last 20 digits of
this number.

>>> 'SECT{%u}'% (d % 10**20)
'SECT{2738566133769973073}'

4. Overkill solution

As we now have both n, e and d, it is possible to fully factorize n
into p and q. This is because of the relation

e*d = 1 (mod phi(n))
e*d = 1 + K*(p-1)(q-1) = 1 + K(p*q-p-q+1) = 1 + K(n-p-q+1) (eq.A)

and as n is very large compared to p, q and 1, we can compute K as

K = round(e*d/n) = (e*d + n/2) // n = 3335

and we can now rewrite (eq.A) to express the sum of p and q and fully
factorize n

p+q = 1 + n - (e*d-1)/K
q = 1 + n - p - (e*d-1)/K
n = p*q = p * ( 1 + n - p - (e*d-1)/K )
0 = p**2 - p(n+1 - (e*d-1)/K) + n

A = (n + 1 - (e*d-1)/k) / 2
p = A + sqrt(A**2 - n)

The factors p and q are

p = 1336630743399795870796447022252017388701511267269973434672368351156860707692
3294855549140374608334350841149318231659041457171104749357709779496554220581913
q = 8737498260507305634520033781659157635088054656248608647433097973262598642807
611526467945801713981127525934898453762929448056973637158526184438433905851011