S4CTF had some nice crypto challenges. I played solo this time and managed to reach #6.
Baby-XoR
The first technique one should learn, in order to enter the fun world of modern cryptography, is XoR. Have you learned it well?
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from flag import flag
def xor(u, v):
return ''.join(chr(ord(cu) ^ ord(cv)) for cu, cv in zip(u, v))
u = flag
v = flag[1:] + flag[0]
enc = open('flag.enc', 'w')
enc.write(xor(u, v))
enc.close()
Solution
For some reason the flag
variable seems to contain stuff other than the flag, and the flag actually starts from the 13th byte. I figured this out by XORing S4CTF
with 4CTF{
and seeing where the result occurs in the given ciphertext. We can build up the flag byte by byte since XORing the encryption with the current known flag reveals the next byte of the flag.
from pwn import xor
enc = open('flag.enc', 'rb').read()[13:]
flag = 'S4CTF{'.encode()
for _ in enc:
f = xor(enc[:len(flag)], flag)
flag += f[-1:]
if flag[-1] == 125:
break
print(flag.decode())
Flag: S4CTF{XOR_x0r_XoR_X0r_xOr!!!}
Baby-IQ
Use this simple crypto algorithm to learn about padding and evaluate yourself on how well you understand algorithms.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from math import sqrt
from flag import flag
import os
import random
import base64
def chunkchunk(msg, l):
return [msg[l*i:l*(i + 1)] for i in range(0, len(msg) // l)]
def pad(msg):
r = int(sqrt(len(msg))) + 1
head = base64.b64encode(os.urandom(r**2))[:r**2 - (len(msg))]
msg = head + msg.encode('utf-8')
msg = chunkchunk(msg, r)
return [list(m) for m in msg]
def encrypt(A):
row = len(A)
col = len(A[0])
top = 0
left = 0
tmp = []
while (top < row and left < col) :
for i in range(left, col) :
tmp.append(A[top][i])
top += 1
for i in range(top, row) :
tmp.append(A[i][col - 1])
col -= 1
if ( top < row) :
for i in range(col - 1, left - 1, -1) :
tmp.append(A[row - 1][i])
row -= 1
if (left < col) :
for i in range(row - 1, top - 1, -1) :
tmp.append(A[i][left])
left += 1
result = []
for i in range(len(A)):
r = []
for j in range(len(A[0])):
r.append(tmp[i*len(A[0]) + j])
result.append(r)
return result
A = pad(flag)
for _ in range(len(A)):
_ = encrypt(A)
A = _
print('enc =', A)
Solution
Without doing much reversing to find out what encrypt
is actually doing, we can see from the ciphertext that all that's happening is transposing and shuffling. This gave me the idea to just repeatedly apply encrypt
on the ciphertext in hopes that the transformation becomes the identity map after enough applications.
def encrypt(A):
row = len(A)
col = len(A[0])
top = 0
left = 0
tmp = []
while (top < row and left < col) :
for i in range(left, col) :
tmp.append(A[top][i])
top += 1
for i in range(top, row) :
tmp.append(A[i][col - 1])
col -= 1
if ( top < row) :
for i in range(col - 1, left - 1, -1) :
tmp.append(A[row - 1][i])
row -= 1
if (left < col) :
for i in range(row - 1, top - 1, -1) :
tmp.append(A[i][left])
left += 1
result = []
for i in range(len(A)):
r = []
for j in range(len(A[0])):
r.append(tmp[i*len(A[0]) + j])
result.append(r)
return result
enc = [[122, 83, 52, 67, 84, 70], [89, 114, 79, 48, 67, 125], [95, 121, 114, 53, 116, 55], [123, 95, 80, 51, 52, 95], [102, 115, 114, 95, 119, 107], [52, 117, 109, 33, 97, 112]]
for _ in range(32*len(enc)):
_ = encrypt(enc)
enc = _
d = ''.join(''.join(map(chr, r)) for r in enc)
if 'S4CTF{' in d and d[-1] == '}':
print(d[1:])
Flag: S4CTF{34sY_CryPtO_7a5k_f0r_w4rmup!}
Baby-RSA
A widely used crypto protocol, which may have been implemented weakly. What do you think?
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from Crypto.Util.number import *
from flag import flag
def create_tuple(nbit): # sorry for dirty code that is not performant!
while True:
p, q = [getPrime(nbit) for _ in range(2)]
P = int(str(p) + str(q))
Q = int(str(q) + str(p))
if isPrime(P) and isPrime(Q):
return P, Q
def encrypt(msg, pkey):
return pow(bytes_to_long(msg), 31337, pkey)
nbit = 256
P, Q = create_tuple(nbit)
pkey = P * Q
enc = encrypt(flag.encode('utf-8'), pkey)
print('pkey =', pkey)
print('enc =', enc)
Solution
A 1024 bit RSA modulus is generated by choosing two random 256 bit primes p
and q
, and constructing the 512 bit primes P = int(str(p) + str(q))
and Q = int(str(q) + str(p))
(such primes p
and q
seem to be quite rare; I wasn't able to get local values to test with before I solved the challenge).
We'll make the assumption that there are 77 digits in both and (this is the case in the challenge, but if it weren't, we could just try with different values around 77). Then
So the bivariate polynomial
has "small" roots, which can be easily found using Coppersmith's theorem. As usual, we use defund's implementation, but it seems to not work over ZZ
that well, so we construct the polynomial ring over Zmod(N^2)
(or some other big number such that doesn't vanish).
from Crypto.Util.number import long_to_bytes
load('small_roots.sage')
N = 48564396752059338791464352725210493148212425902751190745668164451763507023284970474595680869078726765719920168392505794415687815488076204724659643390252172928332322944711949999326843460702414647825442748821062427474599006915155109396213406624079900714394311217571510958430682853948004734434233860146109894977
enc = 28767981118696173499362412795754123415661648348744243377735885542432968964926551295510845917978847771440173910696607195964650864733310997503291576565605508828208679238871651079005335403223194484223700571589836641593207297310906538525042640141507638449129445170765859354237239005410738965923592173867475751585
R.<x,y> = PolynomialRing(Zmod(N^2))
P = 10^77 * x + y
Q = 10^77 * y + x
f = N - P*Q
p, q = small_roots(f, (10^77, 10^77), m=3)[0]
P = int(str(p) + str(q))
Q = int(str(q) + str(p))
d = pow(31337, -1, (P-1)*(Q-1))
m = pow(enc, d, N)
print(long_to_bytes(m).decode())
Flag: S4CTF{Wh3n_mY_BrA1n_w45_UltR4_4CtIVe_ABOut_RSA!!!}
Khayyam
Khayyam was an innovative Iranian mathematician. Let's see if you are up to the challenge with one of his innovations.
#!/usr/bin/env python3
from gmpy import *
from flag import FLAG
l = len(FLAG) // 2
x = int(FLAG[:l].encode("utf-8").hex(), 16)
y = int(FLAG[l:].encode("utf-8").hex(), 16)
p = next_prime(x)
q = next_prime(y)
e, n = 65537, p * q
m_1 = x + int(sqrt(y))
m_2 = y + int(sqrt(x))
c_1, c_2 = pow(m_1, e, n), pow(m_2, e, n)
print('A =', n**2 + c_1)
print('B =', c_2**2 - c_1**2)
print('C =', n**2 + c_2)
Solution
An RSA modulus is generated by choosing two primes and which are the closest primes larger than two halves of the flag (which are called and ). Then, the flag is encrypted as
We are given the values
The first step is to recover , and . This is easy since we have three equations and three unknowns. We can avoid doing the algebra by hand by simply using Sage's inbuilt methods to solve for the unknowns.
Once we have , we'll notice that it's quite small, so it can be easily factored. To recover and , we use the approximations and .
from Crypto.Util.number import long_to_bytes
A = 844298886536102102829429887239442280531833016184944310667136996459156918746405824828816678218599392201232600192410066055059804298579937332814877553581599184247404
B = 468537588442373918531086438736512221547939545041235667415870300706375550653375662041142909124331906341047098854068053691093683234579835326888942621717466998635211
C = 844298886536102102829429887239442280531833016184944310667136996459156918746405825386446222977715112808855904143526125215759285866282041700190923770523214222144611
P.<n, c_1, c_2> = PolynomialRing(QQ)
f1 = n^2 + c_1 - A
f2 = c_2^2 - c_1^2 - B
f3 = n^2 + c_2 - C
vals = Ideal([f1, f2, f3]).variety(ring=ZZ)[0]
n, c_1, c_2 = vals[n], vals[c_1], vals[c_2]
print('n:', n)
print('c_1:', c_1)
print('c_2:', c_2)
# factordb (unintended?) but numbers are small
p = 28312905903414733214096354352151962531937
q = 32453658557333630932034374992046016455903
d = inverse_mod(0x10001, (p-1)*(q-1))
m_1 = pow(c_1, d, n)
m_2 = pow(c_2, d, n)
flag1 = long_to_bytes(m_1 - int(sqrt(q)))
flag2 = long_to_bytes(m_2 - int(sqrt(p)))
flag = flag1 + flag2
print('flag:', flag.decode())
Flag: S4CTF{0n3_T45k_8Y__kH4YyAm_M37h0d}
Merles
Solving an equation with multiple unknowns is very much like finding the secret key and deciphering the secret message.
nc 157.90.231.113 3776
Solution
No source for this challenge, but the service tells us what we need to do:
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ The mission is simple and vivid, you should report non-zero solution +
+ for the equation 3*x**3 + 4*y**3 + 5*z**3 = 0 (mod p) for give p. +
+ We mean non-zero solution by this condition: x*y*z != 0 (mod p) +
+ For example 3*13**3 + 4*15**3 + 5*16**3 = 0 (mod 1399). +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| Options:
| [R]eport solution!
| [P]rint the p
| [Q]uit
We need to solve the equation
for a given . The approach is to choose any and , then solve for :
with, good enough probability, we will be able to solve for .
import os
os.environ['PWNLIB_NOTERM'] = 'True'
from pwn import *
from parse import parse
conn = remote('157.90.231.113', 3776)
[conn.recvline() for _ in range(10)]
conn.sendline('P')
p = list(parse('| p = {:d}\n', conn.recvline().decode()))[0]
F = GF(p)
x = 1337
y = 1337
z3 = F(3*x^3 + 4*y^3)/F(-5)
z = z3.nth_root(3)
assert F(3*x^3 + 4*y^3 + 5*z^3) == 0
conn.sendline('R')
conn.sendline(str((x, y, z)))
conn.interactive()
Genie
A hand made and complex crypto system which looks secure. Do you agree?
#!/usr/bin/env python3
import numpy as np
import random
from flag import FLAG
p = 8443
def vsum(u, v):
assert len(u) == len(v)
l, w = len(u), []
for i in range(l):
w += [(u[i] + v[i]) % p]
return w
def sprod(a, u):
w = []
for i in range(len(u)):
w += [a*u[i] % p]
return w
def encrypt(msg):
l = len(msg)
genie = [ord(m)*(i+1) for (m, i) in zip(list(msg), range(l))]
V, W = [], []
for i in range(l):
v = [0]*i + [genie[i]] + [0]*(l - i - 1)
V.append(v)
for i in range(l):
R, v = [random.randint(0, 126) for _ in range(l)], [0]*l
for j in range(l):
v = vsum(v, sprod(R[j], V[j]))
W.append(v)
return W
enc = encrypt(FLAG)
print(enc)
Solution
The flag is encrypted by taking the ASCII codes of each character and computing the matrix
where is a matrix with random entries in the range .
There is a linear algebra approach to solving this, which is the intended solution, but it can be solved (in my opinion) a bit easier by noticing the following.
The th column , of , contains entries of the form where is a random number in . The approach is to bruteforce all possible values of and all possible values of , and choose the candidate which satisfies the property that all of the entries in are of the form for some . In the challenge, the flag is 67 characters, so each column has 67 entries. This is enough to determine the correct candidate for each character of the flag.
from string import printable
p = 8443
W = eval(open('./enc.txt').read())
columns = list(zip(*W))
flag = ''
for i,col in enumerate(columns):
pos = []
for m in printable:
g = ord(m)*(i+1)
V = [g*r % p for r in range(127)]
if len(set(col) - set(V)) == 0:
flag += m
print(flag)
Flag: S4CTF{91v3n_a_L1n3AR_7r4n5FoRma7iOn_A_4_V3cT0r_x_5UcH_tHA7_Ax=lx!!}
Phillip
Goal is to find collisions in this hash algorithm. Perhaps not as easy!
nc 157.90.231.113 9999
#!/usr/bin/env python3
from Crypto.Util.number import *
from gmpy2 import next_prime, gcd, lcm
from random import randint
import sys, os, signal
import inspect
from flag import flag
def make_params(nbit):
p, q = [getPrime(nbit) for _ in range(2)]
n, f, g = p * q, lcm(p-1, q-1), p + q
e = pow(g, f, n**2)
u = divmod(e-1, n)[0]
v = inverse(u, n)
params = int(n), int(f), int(v)
return params
def phillip_hash(m, params):
n, f, v = params
if 1 < m < n**2 - 1:
e = pow(m, f, n**2)
u = divmod(e-1, n)[0]
H = divmod(u*v, n)[1]
return H
def die(*args):
pr(*args)
quit()
def pr(*args):
s = " ".join(map(str, args))
sys.stdout.write(s + "\n")
sys.stdout.flush()
def sc():
return sys.stdin.readline().strip()
def main():
border = "+"
pr(border*72)
pr(border, " hi young cryptographers,! Your mission is to find a hash collision ", border)
pr(border, " in the given hash function based on famous cryptographic algorithm ", border)
pr(border, " see the source code and get the flag! ", border)
pr(border*72)
nbit = 256
params = make_params(nbit)
n = params[0]
while True:
pr("| Options: \n|\t[H]ash function \n|\t[R]eport collision! \n|\t[T]ry hash \n|\t[G]et params \n|\t[P]arams function \n|\t[Q]uit")
ans = sc().lower()
if ans == 'h':
pr(inspect.getsource(phillip_hash))
elif ans == 'p':
pr(inspect.getsource(make_params))
elif ans == 'r':
pr("| please send first msg: ")
m_1 = sc()
pr("| please send second msg:")
m_2 = sc()
try:
m_1 = int(m_1)
m_2 = int(m_2)
except:
die("| sorry! your input is invalid, Bye!!")
if m_1 != m_2 and 1 < m_1 < n**2-1 and 1 < m_2 < n**2-1 and phillip_hash(m_1, params) == phillip_hash(m_2, params):
die("| Congrats! You find the collision!! the flag is:", flag)
else:
die("| sorry! your input is invalid or wrong!!")
elif ans == 't':
pr("| please send your message to get the hash: ")
m = sc()
try:
m = int(m)
pr("phillip_hash(m) =", phillip_hash(m, params))
except:
die("| sorry! your input is invalid, Bye!!")
elif ans == 'g':
pr('| params =', params)
elif ans == 'q':
die("Quiting ...")
else:
die("Bye ...")
if __name__ == '__main__':
main()
Solution
Before getting out pen and paper, I noticed that the hash function is based on RSA, so I decided to check what and hash to. I got lucky as it turns out this is a collision, which solves the challenge. It's easy to see why this works:
The parameters are where is an RSA modulus, and . To hash a message , the values
are computed. The hash is then computed as .
Since is dependent on , and is dependent on which in turn is dependent on the message , all we need to do to create a collision is find and such that and are equal. Since is reduced modulo , it's easy to see that for all (since ). So any two multiples of gives a collision.
Flag: S4CTF{A94In__pr0b4b1liStiC__aSymM37r1c_Al9OriThm!!}
Malady
Using finite fields incorrectly in modern cryptography could be dangerous. Try your luck in deciphering this crypto system.
#!/usr/bin/env sage
from flag import flag
def make_matrix(n):
Zn = IntegerModRing(n)
G = GL(2, Zn)
while True:
a, b, c, d = [randint(0, n - 1) for _ in range(4)]
P = G([[a, b], [c, d]])
if P in G:
return P
def bpow(P, n):
if n == 0:
return P
for _ in range(n):
P = P ** 2
return P
def make_keypair(n):
Zn = IntegerModRing(n)
G = GL(2, Zn)
I = G([[1, 0], [0, 1]])
r = randint(1, 2 ** 256)
br = bin(r)[2:][::-1]
J = I
while True:
P, Q = [make_matrix(n) for _ in range(2)]
try:
if Q * (~P) != P * Q:
for i in range(len(br)):
if br[i] == '1':
J = bpow(Q, i) * J
B = (~Q) * (~P) * Q
pubkey = (n, P, B, J)
privkey = Q
return (pubkey, privkey)
except:
continue
def encrypt(m, pubkey):
n, P, B, J = pubkey
Zn = IntegerModRing(n)
G = GL(2, Zn)
I = G([[1, 0], [0, 1]])
s = randint(1, 2 ** 32)
bs = bin(s)[2:][::-1]
D = I
for i in range(len(bs)):
if bs[i] == '1':
D = bpow(J, i) * D
E = (~D) * P * D
K = (~D) * B * D
l = len(str(m))
M = []
for i in range(0, 4):
M.append(int(str(m)[i*l // 4: (i+1)*l // 4]))
U = matrix([[M[1], M[0]], [M[3], M[2]]])
V = K * U * K
return (V, E)
p = next_prime(randint(1, 2 ** 72))
q = next_prime(randint(1, 2 ** 72))
n = p * q
pubkey, privkey = make_keypair(n)
flag = flag.encode('utf-8')
m = int(flag.hex(), 16)
enc = encrypt(m, pubkey)
print('pubkey = ', pubkey)
print('enc = ', enc)
Solution
After a bit of searching, I found the name of the cryptosystem. It's the Cayley-Purser algorithm and an attack is described in the wikipedia page.
The key is generated by choosing two random matrices such that . Then and are computed (for some random, large ). The public key is and the private key is .
To encrypt a message encoded as a matrix , a random value is generated and the matrices
are computed. The ciphertext outputted is the pair .
To decrypt with the private key , the decryption matrix is computed as . Then the plaintext message is recovered by computing .
The attack begins by solving for in the congruence
Then, is a multiple of . A multiple of can be used as a valid private key since .
The challenge should be easy from here on, but I kept getting garbage when trying to decrypt. The issue is in the way the message is encoded as a matrix:
l = len(str(m))
M = []
for i in range(0, 4):
M.append(int(str(m)[i*l // 4: (i+1)*l // 4]))
U = matrix([[M[1], M[0]], [M[3], M[2]]])
The flag is represented as a decimal integer, and each entry of the matrix is created by taking equal chunks of the flag as a string. Even if we recover the plaintext message as above, combining the entries into a single string might not work, because leading zeros are truncated after the call to int
. To fix this, we have to account for the fact that some of the entries in the plaintext message might have a zero in front of them. It turned out that M[1]
had a zero in front of it.
from Crypto.Util.number import long_to_bytes
n = 2419329577094580148790320061829248654877619
P = [[1181968185527581745853359689584528732855897, 153406550412853584463306785000418170296859],
[1454322498540966456231711148502103233345812, 1654517770461057329449871572441944497585269]]
B = [[1268457653971486225679848441105472837265167, 579420771722577779695828127264001257349949],
[2351869917091027496266981633084389584522183, 450983777743266243622871312465133743097962]]
J = [[2358538357277340167153980348659698938509404, 365220208942190647616618122919911425848374],
[47691648572918059476944115452005044039782, 1236869052280934587487352533961953209955284]]
V = [[425149944883810928331948322693601721947824, 1442606353540488031613587882680057605691721],
[2270690430439772938430962982653361813264189, 1607654191517170510458852398046623728536109]]
E = [[177396832593088516072893113015799710489963, 2001682469448750676325856357286302774486863],
[5338037289866014093970785328310590783999, 239759546300970410440018087181424865073584]]
Zn = Zmod(n)
P, B, J, V, E = [Matrix(Zn, X) for X in [P, B, J, V, E]]
L = B - (~P)
R = (~P)*J - J*B
d = Zn(R[0][0])/Zn(L[0][0])
assert d*L == R
Q_ = d*Matrix.identity(Zn, 2) + J
K_ = (~Q_)*E*Q_
U = K_*V*K_
M0, M1, M2, M3 = 10*int(U[0][1]), int(U[0][0]), int(U[1][1]), int(U[1][0])
m = int(''.join(map(str, [M0, M1, M2, M3])))
flag = long_to_bytes(m)
print(flag.decode())
Flag: S4CTF{Flannery_f0rmAliz3D__Cayley-Pursers__ruN71mE!}
Determinant
Try to calculate matrix determinant!
mirror 1: nc 157.90.231.113 2570
mirror 2: nc 198.211.127.76 2570
Solution
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ hi power programmers and coders! Your mission is to find a unknown +
+ variables in each step such that the determinant of matrix A is +
+ equal to 1, for example if A = ([3, 7], [2, x]), then for x = 5 we +
+ have det(A) = 1, now try to find the flag! +
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
| We know that A = [[9, 'x'], [2, 3]]
| Send unknown variables in matrix A, separated by comma, such that all of them are greater than 1
No source, but the task is simple. Given a 2x2 matrix which may contain between one to three unknowns, we need to solve for the unknowns such that the determinant of the matrix is 1
. Additionally, the variables must be greater than a given lower bound. There are quite a few cases to go through, but for each number of unknowns, one case is quite similar to the rest, so I'll go through just a few.
Let
The determinant of is given by .
We will consider the cases when is a variable, when both and are variables, when both and are variables, and when all of and are variables. For each case, let denote the lower bound.
Case 1: One variable
Suppose we are given the matrix
So . Rearranging, we get .
Case 2a: Two variables
Suppose we are given the matrix
So . We notice that this looks similar to Bezout's identity. It is always the case that , so we can solve this by computing the Bezout coefficients for . This can be done with the extended Euclidean algorithm. Suppose we have solutions such that . All that remains is to ensure . Notice that
so it turns out that and are also solutions, for any . Choose a large enough such that our solutions are greater than the lower bound, and we are done.
Case 2b: Two variables
Suppose we are given the matrix
So . Then . So we must choose and such that their product is . We can find candidates for and by iterating over the divisors of , then taking the first pair of divisors such that they are both greater than the lower bound. Since the values are fairly small, factoring and computing the divisors can be done in a reasonable time.
Case 3: Three variables
Suppose we are given the matrix
So . We will reduce this to case (2a) by choosing where is a prime larger than the lower bound. Then we have the matrix
which is exactly the setting as in case (2a), since and are almost guaranteed to be coprime.
from pwn import *
from gmpy2 import gcdext
from primefac import primefac
from functools import reduce
from collections import Counter
from itertools import product
from operator import mul
prod = lambda l: reduce(mul, l, 1)
# https://alexwlchan.net/2019/07/finding-divisors-with-python/
def get_divisors(pf):
pf_with_multiplicity = Counter(pf)
powers = [
[factor ** i for i in range(count + 1)]
for factor, count in pf_with_multiplicity.items()
]
for prime_power_combo in product(*powers):
yield prod(prime_power_combo)
p = 104010985514257800469608790443965305580101693802288262284052742898073797789021
def solv_bezouts(a, b, lb):
_, x, y = gcdext(a, -b)
d_ = lb*b + x
c_ = lb*a + y
return c_, d_
def solve_2(a, b, c, d, lb):
if type(a) == int and type(b) == int:
c_, d_ = solv_bezouts(a, b, lb)
return f'{c_},{d_}'
elif type(a) == int and type(c) == int:
b_, d_ = solv_bezouts(a, c, lb)
return f'{b_},{d_}'
elif type(a) == int and type(d) == int:
N = a*d - 1
F = list(primefac(a*d - 1))
divs = get_divisors(F)
for b_ in divs:
c_ = N//b_
if b_ > lb and c_ > lb:
return f'{b_},{c_}'
elif type(b) == int and type(c) == int:
N = 1 + b*c
F = list(primefac(1 + b*c))
divs = get_divisors(F)
for a_ in divs:
d_ = N//a_
if a_ > lb and d_ > lb:
return f'{a_},{d_}'
elif type(b) == int and type(d) == int:
c_, a_ = solv_bezouts(d, b, lb)
return f'{a_},{c_}'
elif type(c) == int and type(d) == int:
b_, a_ = solv_bezouts(d, c, lb)
return f'{a_},{b_}'
def do_round():
known = eval(conn.recvline().decode().strip().split('= ')[1])
lb = int(conn.recvline().decode().strip().split()[-1])
print('A:')
print('\n'.join(' '.join(map(str, r)) for r in known))
print('lb:', lb)
(a, b), (c, d) = known
if sum(int(type(x) == str) for x in [a,b,c,d]) == 3:
if type(a) == int:
b_ = p
c_, d_ = solv_bezouts(a, b_, lb)
ans = f'{b_},{c_},{d_}'
elif type(b) == int:
a_ = p
c_, d_ = solv_bezouts(a_, b, lb)
ans = f'{a_},{c_},{d_}'
elif type(c) == int:
a_ = p
b_, d_ = solv_bezouts(a_, c, lb)
ans = f'{a_},{b_},{d_}'
elif type(d) == int:
b_ = p
c_, a_ = solv_bezouts(d, b_, lb)
ans = f'{a_},{b_},{c_}'
elif sum(int(type(x) == str) for x in [a,b,c,d]) == 2:
ans = solve_2(a, b, c, d, lb)
elif type(a) == str:
a_ = (1 + b*c)//d
ans = str(a_)
elif type(b) == str:
b_ = (a*d - 1)//c
ans = str(b_)
elif type(c) == str:
c_ = (a*d - 1)//b
ans = str(c_)
elif type(d) == str:
d_ = (1 + b*c)//a
ans = str(d_)
conn.sendline(ans)
print(conn.recvline().decode())
conn = remote('157.90.231.113', 2570)
[conn.recvline() for _ in range(6)]
while 1:
do_round()
Flag: S4CTF{5imPl3_PPC_Us1Ng___MatrIx___}
PTS
Joy of a tour on Graph Matrices, lost in the forest
nc 198.211.127.76 3580
Solution
Similar style challenge to Determinant, we are given a task and have to submit a solution:
------------------------------------------------------------------------
| -+-+ Joy of a tour on Graph Matrices +-+- |
| Count number of subtrees of the given matrix M, such that |
| M is adjacency matrix of G, a connected graph without loops, |
| and this subtree covers all vertices's |
------------------------------------------------------------------------
| M =
[0, 1, 1, 1]
[1, 0, 1, 1]
[1, 1, 0, 0]
[1, 1, 0, 0]
| please send number of subtrees
Just use Sage's graph.spanning_trees_count()
.
import os
os.environ['PWNLIB_NOTERM'] = 'True'
from collections import defaultdict
from pwn import *
# https://www.geeksforgeeks.org/convert-adjacency-matrix-to-adjacency-list-representation-of-graph/
def adjM_to_list(a):
adjList = defaultdict(list)
for i in range(len(a)):
for j in range(len(a[i])):
if a[i][j]== 1:
adjList[i].append(j)
return adjList
def do_round():
conn.recvline()
M = []
while 1:
r = conn.recvline().decode()
if r[0] == '|':
break
M.append(eval(r))
adj_list = adjM_to_list(M)
D = DiGraph(adj_list)
n = D.spanning_trees_count()
conn.sendline(str(n))
print(conn.recvline().decode())
conn = remote('198.211.127.76', 3580)
[conn.recvline() for _ in range(6)]
while 1:
do_round()
Flag: S4CTF{y3s_0lD__8u7__G0lD_s7ufF_0N__G_M4Tr1c3S}