Skip to content

Commit 9a8c5fe

Browse files
committed
Solution for ASR has been added.
1 parent 92fb7ae commit 9a8c5fe

File tree

6 files changed

+341
-2
lines changed

6 files changed

+341
-2
lines changed

ASR/solution/nepair

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-n 0x02cfc00c275c567f12f284d4864b152a2499962e25506d7e184fa2be1a0d50ddc905dde979dc27753c9060bb01ecfbb5406d1d78ce8dc8ca296b19c3204aa18ce69485a8bc9a57a1715085109a50181b37e48cd2627b8da380a152e971f246e5ab89e0cb5e76f551fd9925105f887ad39b2d8677331ab66802b5e4ac7466d7bf8b -e 0x0242a4b40a0abb560e50e47ce61926fc2af216c76ad6dc52e6e9a78e9b42609d575f30dd6dac3ac3233e862514d89eee389cba430f601f81a57f1b7e9158dc6f3a0070dee3791f9cbdb34b9de1b12cbb7da46eb0e9e605a975a0973b18255239a27988259312f1446886388be320d2897e1cf9841898d931776c3831306062f01d

ASR/solution/pqetuple

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-p 18332366563702812004827960767382231259638895239413282702812009894882946745998553209194850588719103121402937914114124705492672432051453957400966421943458751 -q 27570132131170104824343411575743642180767006452664381672813278738573428245458845155699724266932275812596878966520171881476555627563245277293094684982034229 -e 406337194415078969135388012199768851534892405717049052243569601877767720306971351962115455780742226903789914852797732475115620696182173145519244169134462893981827754911556903443954791096825007180380141420831124481112731502488517569927478504486615667200169344800534739661939136312208398465567840505121573367837

ASR/solution/private.key

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
-----BEGIN RSA PRIVATE KEY-----
2+
MIICOQIBAAKBgQLPwAwnXFZ/EvKE1IZLFSokmZYuJVBtfhhPor4aDVDdyQXd6XncJ3U8kGC7Aez7
3+
tUBtHXjOjcjKKWsZwyBKoYzmlIWovJpXoXFQhRCaUBgbN+SM0mJ7jaOAoVLpcfJG5auJ4MtedvVR
4+
/ZklEF+IetObLYZ3Mxq2aAK15Kx0Zte/iwKBgQJCpLQKCrtWDlDkfOYZJvwq8hbHatbcUubpp46b
5+
QmCdV18w3W2sOsMjPoYlFNie7jicukMPYB+BpX8bfpFY3G86AHDe43kfnL2zS53hsSy7faRusOnm
6+
Bal1oJc7GCVSOaJ5iCWTEvFEaIY4i+Mg0ol+HPmEGJjZMXdsODEwYGLwHQIgDCwQPH4iUFihfz18
7+
b8epCYLzO2r9l8thz3zOdX47QT0CQQFeBr2+s8lKeGiG0FsWa55NDi0h6ShCTLBfUM8BkBNmMbqz
8+
5VybaKajeDTGwHpZYBTgVf8hmloh/geJcuBfwJu/AkECDmf/q3wc9yzgKUNtaWuGf+krVnmSCXeS
9+
7Lhrf2SbygyjjT7emWpgpzw/EoJ2wCg4JBZBmmZ82d8F8imYYTy/NQIgDCwQPH4iUFihfz18b8ep
10+
CYLzO2r9l8thz3zOdX47QT0CIAwsEDx+IlBYoX89fG/HqQmC8ztq/ZfLYc98znV+O0E9AkAoVQGA
11+
5pcRKjARucSUlUIq/+XiZZ7qh9Ip7nSm+XCz90UyHEHBTsQ15YgfEfELeZATXLC2BXma+XZU4IIk
12+
TMRw
13+
-----END RSA PRIVATE KEY-----

ASR/solution/rsatool.py

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
#!/usr/bin/python2
2+
import base64, fractions, optparse, random
3+
import gmpy
4+
5+
from pyasn1.codec.der import encoder
6+
from pyasn1.type.univ import *
7+
8+
PEM_TEMPLATE = '-----BEGIN RSA PRIVATE KEY-----\n%s-----END RSA PRIVATE KEY-----\n'
9+
DEFAULT_EXP = 65537
10+
11+
def factor_modulus(n, d, e):
12+
"""
13+
Efficiently recover non-trivial factors of n
14+
15+
See: Handbook of Applied Cryptography
16+
8.2.2 Security of RSA -> (i) Relation to factoring (p.287)
17+
18+
http://www.cacr.math.uwaterloo.ca/hac/
19+
"""
20+
t = (e * d - 1)
21+
s = 0
22+
23+
while True:
24+
quotient, remainder = divmod(t, 2)
25+
26+
if remainder != 0:
27+
break
28+
29+
s += 1
30+
t = quotient
31+
32+
found = False
33+
34+
while not found:
35+
i = 1
36+
a = random.randint(1,n-1)
37+
38+
while i <= s and not found:
39+
c1 = pow(a, pow(2, i-1, n) * t, n)
40+
c2 = pow(a, pow(2, i, n) * t, n)
41+
42+
found = c1 != 1 and c1 != (-1 % n) and c2 == 1
43+
44+
i += 1
45+
46+
p = fractions.gcd(c1-1, n)
47+
q = (n / p)
48+
49+
return p, q
50+
51+
class RSA:
52+
def __init__(self, p=None, q=None, n=None, d=None, e=DEFAULT_EXP):
53+
"""
54+
Initialize RSA instance using primes (p, q)
55+
or modulus and private exponent (n, d)
56+
"""
57+
58+
self.e = e
59+
60+
if p and q:
61+
assert gmpy.is_prime(p), 'p is not prime'
62+
assert gmpy.is_prime(q), 'q is not prime'
63+
64+
self.p = p
65+
self.q = q
66+
elif n and d:
67+
self.p, self.q = factor_modulus(n, d, e)
68+
else:
69+
raise ArgumentError('Either (p, q) or (n, d) must be provided')
70+
71+
self._calc_values()
72+
73+
def _calc_values(self):
74+
self.n = self.p * self.q
75+
76+
phi = (self.p - 1) * (self.q - 1)
77+
self.d = gmpy.invert(self.e, phi)
78+
79+
# CRT-RSA precomputation
80+
self.dP = self.d % (self.p - 1)
81+
self.dQ = self.d % (self.q - 1)
82+
self.qInv = gmpy.invert(self.q, self.p)
83+
84+
def to_pem(self):
85+
"""
86+
Return OpenSSL-compatible PEM encoded key
87+
"""
88+
return PEM_TEMPLATE % base64.encodestring(self.to_der())
89+
90+
def to_der(self):
91+
"""
92+
Return parameters as OpenSSL compatible DER encoded key
93+
"""
94+
seq = Sequence()
95+
96+
for x in [0, self.n, self.e, self.d, self.p, self.q, self.dP, self.dQ, self.qInv]:
97+
seq.setComponentByPosition(len(seq), Integer(x))
98+
99+
return encoder.encode(seq)
100+
101+
def dump(self, verbose):
102+
vars = ['n', 'e', 'd', 'p', 'q']
103+
104+
if verbose:
105+
vars += ['dP', 'dQ', 'qInv']
106+
107+
for v in vars:
108+
self._dumpvar(v)
109+
110+
def _dumpvar(self, var):
111+
val = getattr(self, var)
112+
113+
parts = lambda s, l: '\n'.join([s[i:i+l] for i in xrange(0, len(s), l)])
114+
115+
if len(str(val)) <= 40:
116+
print '%s = %d (%#x)\n' % (var, val, val)
117+
else:
118+
print '%s =' % var
119+
print parts('%x' % val, 80) + '\n'
120+
121+
122+
if __name__ == '__main__':
123+
parser = optparse.OptionParser()
124+
125+
parser.add_option('-p', dest='p', help='prime', type='int')
126+
parser.add_option('-q', dest='q', help='prime', type='int')
127+
parser.add_option('-n', dest='n', help='modulus', type='int')
128+
parser.add_option('-d', dest='d', help='private exponent', type='int')
129+
parser.add_option('-e', dest='e', help='public exponent (default: %d)' % DEFAULT_EXP, type='int', default=DEFAULT_EXP)
130+
parser.add_option('-o', dest='filename', help='output filname')
131+
parser.add_option('-f', dest='format', help='output format (DER, PEM) (default: PEM)', type='choice', choices=['DER', 'PEM'], default='PEM')
132+
parser.add_option('-v', dest='verbose', help='also display CRT-RSA representation', action='store_true', default=False)
133+
134+
try:
135+
(options, args) = parser.parse_args()
136+
137+
if options.p and options.q:
138+
print 'Using (p, q) to initialise RSA instance\n'
139+
rsa = RSA(p=options.p, q=options.q, e=options.e)
140+
elif options.n and options.d:
141+
print 'Using (n, d) to initialise RSA instance\n'
142+
rsa = RSA(n=options.n, d=options.d, e=options.e)
143+
else:
144+
parser.print_help()
145+
parser.error('Either (p, q) or (n, d) needs to be specified')
146+
147+
rsa.dump(options.verbose)
148+
149+
if options.filename:
150+
print 'Saving %s as %s' % (options.format, options.filename)
151+
152+
153+
if options.format == 'PEM':
154+
data = rsa.to_pem()
155+
elif options.format == 'DER':
156+
data = rsa.to_der()
157+
158+
fp = open(options.filename, 'wb')
159+
fp.write(data)
160+
fp.close()
161+
162+
except optparse.OptionValueError, e:
163+
parser.print_help()
164+
parser.error(e.msg)
165+

ASR/solution/solution.html

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,89 @@
1+
Делаем следующие действия:
12

2-
Answer: STCTF#Dang3r0u51R5AMay83Vu1n3ra813#
3+
1. openssl rsa -pubin -in public.key -text
4+
5+
Получаем:
6+
7+
Public-Key: (1026 bit)
8+
Modulus:
9+
02:cf:c0:0c:27:5c:56:7f:12:f2:84:d4:86:4b:15:
10+
2a:24:99:96:2e:25:50:6d:7e:18:4f:a2:be:1a:0d:
11+
50:dd:c9:05:dd:e9:79:dc:27:75:3c:90:60:bb:01:
12+
ec:fb:b5:40:6d:1d:78:ce:8d:c8:ca:29:6b:19:c3:
13+
20:4a:a1:8c:e6:94:85:a8:bc:9a:57:a1:71:50:85:
14+
10:9a:50:18:1b:37:e4:8c:d2:62:7b:8d:a3:80:a1:
15+
52:e9:71:f2:46:e5:ab:89:e0:cb:5e:76:f5:51:fd:
16+
99:25:10:5f:88:7a:d3:9b:2d:86:77:33:1a:b6:68:
17+
02:b5:e4:ac:74:66:d7:bf:8b
18+
Exponent:
19+
02:42:a4:b4:0a:0a:bb:56:0e:50:e4:7c:e6:19:26:
20+
fc:2a:f2:16:c7:6a:d6:dc:52:e6:e9:a7:8e:9b:42:
21+
60:9d:57:5f:30:dd:6d:ac:3a:c3:23:3e:86:25:14:
22+
d8:9e:ee:38:9c:ba:43:0f:60:1f:81:a5:7f:1b:7e:
23+
91:58:dc:6f:3a:00:70:de:e3:79:1f:9c:bd:b3:4b:
24+
9d:e1:b1:2c:bb:7d:a4:6e:b0:e9:e6:05:a9:75:a0:
25+
97:3b:18:25:52:39:a2:79:88:25:93:12:f1:44:68:
26+
86:38:8b:e3:20:d2:89:7e:1c:f9:84:18:98:d9:31:
27+
77:6c:38:31:30:60:62:f0:1d
28+
29+
2. Приводим к удобной записи N и e:
30+
31+
-n 0x02cfc00c275c567f12f284d4864b152a2499962e25506d7e184fa2be1a0d50ddc905dde979dc27753c9060bb01ecfbb5406d1d78ce8dc8ca296b19c3204aa18ce69485a8bc9a57a1715085109a50181b37e48cd2627b8da380a152e971f246e5ab89e0cb5e76f551fd9925105f887ad39b2d8677331ab66802b5e4ac7466d7bf8b
32+
q
33+
-e 0x0242a4b40a0abb560e50e47ce61926fc2af216c76ad6dc52e6e9a78e9b42609d575f30dd6dac3ac3233e862514d89eee389cba430f601f81a57f1b7e9158dc6f3a0070dee3791f9cbdb34b9de1b12cbb7da46eb0e9e605a975a0973b18255239a27988259312f1446886388be320d2897e1cf9841898d931776c3831306062f01d
34+
35+
3. Cохраняем их в отдельный файл "nepair" без перевода строки, (^ - начало строки, $ - конец строки): ^-n ... -e ...$
36+
37+
4. Воспользуемся wiener_attack.py из https://ctfcrew.org/writeup/87
38+
или модернизируем код из https://github.com/pablocelayes/rsa-wiener-attack/blob/master/RSAwienerHacker.py
39+
40+
python wiener_attack.py `cat nepair`
41+
42+
Получим факторизованное представление N:
43+
44+
-p 18332366563702812004827960767382231259638895239413282702812009894882946745998553209194850588719103121402937914114124705492672432051453957400966421943458751
45+
-q 27570132131170104824343411575743642180767006452664381672813278738573428245458845155699724266932275812596878966520171881476555627563245277293094684982034229
46+
-e 406337194415078969135388012199768851534892405717049052243569601877767720306971351962115455780742226903789914852797732475115620696182173145519244169134462893981827754911556903443954791096825007180380141420831124481112731502488517569927478504486615667200169344800534739661939136312208398465567840505121573367837
47+
48+
5. Cохраняем их в отдельнный файл "pqetuple" без перевода строки, (^ - начало строки, $ - конец строки): ^-p ... -q ... -e ...$
49+
50+
6. И воспользуемся rsatool (https://github.com/ius/rsatool)
51+
52+
python rsatool.py -o private.key `cat pqetuple`
353

4-
TODO: Some steps to gain answer.
54+
Получаем PEM private.key :
55+
56+
Using (p, q) to initialise RSA instance
57+
58+
n =
59+
2cfc00c275c567f12f284d4864b152a2499962e25506d7e184fa2be1a0d50ddc905dde979dc27753
60+
c9060bb01ecfbb5406d1d78ce8dc8ca296b19c3204aa18ce69485a8bc9a57a1715085109a50181b3
61+
7e48cd2627b8da380a152e971f246e5ab89e0cb5e76f551fd9925105f887ad39b2d8677331ab6680
62+
2b5e4ac7466d7bf8b
63+
64+
e =
65+
242a4b40a0abb560e50e47ce61926fc2af216c76ad6dc52e6e9a78e9b42609d575f30dd6dac3ac32
66+
33e862514d89eee389cba430f601f81a57f1b7e9158dc6f3a0070dee3791f9cbdb34b9de1b12cbb7
67+
da46eb0e9e605a975a0973b18255239a27988259312f1446886388be320d2897e1cf9841898d9317
68+
76c3831306062f01d
69+
70+
d =
71+
c2c103c7e225058a17f3d7c6fc7a90982f33b6afd97cb61cf7cce757e3b413d
72+
73+
p =
74+
15e06bdbeb3c94a786886d05b166b9e4d0e2d21e928424cb05f50cf0190136631bab3e55c9b68a6a
75+
37834c6c07a596014e055ff219a5a21fe078972e05fc09bbf
76+
77+
q =
78+
20e67ffab7c1cf72ce029436d696b867fe92b567992097792ecb86b7f649bca0ca38d3ede996a60a
79+
73c3f128276c028382416419a667cd9df05f22998613cbf35
80+
81+
Saving PEM as private.key
82+
83+
7. И так, мы нашли секретную экспоненту, d. И восстановили private.key. Время расшифровать secret.
84+
85+
openssl rsautl -decrypt -inkey private.key -in secret
86+
87+
Получаем ответ:
88+
89+
Answer: STCTF#Dang3r0u51R5AMay83Vu1n3ra813#

ASR/solution/wiener_attack.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#!/usr/bin/python
2+
import optparse
3+
from sympy.solvers import solve
4+
from sympy import Symbol
5+
6+
def makeNextFraction(fraction):
7+
(a,b) = fraction
8+
res=b/a
9+
a1=b%a
10+
b1=a
11+
return res, (a1,b1)
12+
13+
def makeContinuedFraction(fraction):
14+
(a,b) = fraction
15+
v=[]
16+
v.append(0)
17+
while not a == 1:
18+
r, fraction = makeNextFraction(fraction)
19+
(a,b) = fraction
20+
v.append(r)
21+
v.append(b)
22+
return v
23+
24+
def makeIndexedConvergent(sequence, index):
25+
(a,b)=(1,sequence[index])
26+
while index>0:
27+
index-=1
28+
(a,b)=(b,sequence[index]*b+a)
29+
return (b,a)
30+
31+
def makeConvergents(sequence):
32+
r=[]
33+
for i in xrange(0,len(sequence)):
34+
r.append(makeIndexedConvergent(sequence,i))
35+
return r
36+
37+
def solveQuadratic(a,b,c):
38+
x = Symbol('x')
39+
return solve(a*x**2 + b*x + c, x)
40+
41+
def wienerAttack(N,e):
42+
conv=makeConvergents(makeContinuedFraction((e,N)))
43+
for frac in conv:
44+
(k,d)=frac
45+
if k == 0:
46+
continue
47+
phiN=((e*d)-1)/k
48+
roots=solveQuadratic(1, -(N-phiN+1), N)
49+
if len(roots) == 2:
50+
p,q=roots[0]%N,roots[1]%N
51+
if(p*q==N):
52+
return p, q
53+
54+
if __name__ == '__main__':
55+
parser = optparse.OptionParser()
56+
parser.add_option('-n', dest='n', help='modulus', type='int')
57+
parser.add_option('-e', dest='e', help='public exponent', type='int')
58+
59+
try:
60+
(options, args) = parser.parse_args()
61+
62+
if options.n and options.e:
63+
e=options.e
64+
p, q = wienerAttack(options.n, options.e)
65+
print "-p", p
66+
print "-q", q
67+
print "-e", e
68+
else:
69+
parser.print_help()
70+
parser.error('n and e must be specified')
71+
72+
except optparse.OptionValueError, e:
73+
parser.print_help()
74+
parser.error(e.msg)

0 commit comments

Comments
 (0)