概述
Birds of randomness是来自于HTB(hackthebox.com)的一个中级密码学挑战,完成该挑战所需要掌握的知识点在于椭圆曲线算法。
题目分析
相关的任务文件包括source.py源代码和一个在线环境。
source.py内容节选如下
class TrainRoute:
def __init__(self, a=None):
self.p = 17101937747109687265202713197737423
self.Gx = 3543321030468950376213178213609418
self.Gy = 14807290861072031659976937040569354
self.ec_order = 17101937747109687496599931614463506
self.E = ecc.CurveFp(self.p, 2, 3)
self.G = ecc.Point(self.E, self.Gx, self.Gy, self.ec_order)
a, x = divmod(a, 30268)
a, y = divmod(a, 30306)
a, z = divmod(a, 30322)
self.seed = int(x) + 1, int(y) + 1, int(z) + 1
def rotate(self):
x, y, z = self.seed
x = (171 * x) % 30269
y = (172 * y) % 30307
z = (170 * z) % 30323
self.seed = x, y, z
def goToNextStation(self):
while True:
self.rotate()
x, y, z = self.seed
if(isPrime(x) and isPrime(y) and isPrime(z)):
d = x * y * z
new_point = d * self.G
return int(new_point.x()), int(new_point.y())
def getTicketNumber():
return int(os.urandom(32).hex(), 16)
def sendMessage(s, msg):
s.send(msg.encode())
def receiveMessage(s, msg):
sendMessage(s, msg)
return s.recv(4096).decode().strip()
def main(s):
route = TrainRoute(getTicketNumber())
station_coords = route.goToNextStation()
sendMessage(
s, f'The coordinates of the departing station were: {station_coords}\n')
destination_coords = route.goToNextStation()
sendMessage(s, 'Your lover has arrived to the destination. Hurry up!\n')
pegions_left = 6
sendMessage(
s, f'Luckily you have {pegions_left} mechapegions in your pockets.\n')
sendMessage(
s, 'Use them to find out if your lover is at the destination you think.\n')
while True:
sendMessage(s, f'{pegions_left} mechapegions awaiting instructions.\n')
x = receiveMessage(s, 'Enter the x coordinate: ')
y = receiveMessage(s, 'Enter the y coordinate: ')
pegions_left -= 1
try:
guessed_coords = ecc.Point(route.E, int(x), int(y), route.ec_order)
guessed_coords = (int(guessed_coords.x()), int(guessed_coords.y()))
except Exception as e:
print(e)
sendMessage(
s, 'The mechapegion got lost, maybe try valid coordinates next time\n')
exit()
if guessed_coords == destination_coords:
sendMessage(
s, f'You found your lover. Here is your flag: {FLAG}\n')
exit()
if pegions_left:
sendMessage(s, f'Try again, your lover is not there\n')
else:
sendMessage(s, 'Maybe it wasn\'t meant to be\n')
exit()
以上代码实现了一个基本的椭圆曲线算法, 代码提供了曲线参数(p,a, b, ec_order)和初始点(G)坐标, 连接到服务器后,可以得到第一个点(station_coords)的坐标, 获得flag的条件在于计算出第二个点(destination_coords)的坐标。
解题过程
首先连接服务器后以获得第一个点的坐标,
$ nc 159.65.52.8 30762
The coordinates of the departing station were: (10387607239579938257626703795439257, 10616885760882242602781569890429191)
Your lover has arrived to the destination. Hurry up!
Luckily you have 6 mechapegions in your pockets.
Use them to find out if your lover is at the destination you think.
6 mechapegions awaiting instructions.
Enter the x coordinate:
通过给出的曲线参数,我们可以对ec_order进行质因数分解
$ sage: factor(17101937747109687496599931614463506)
2 * 3 * 7^2 * 1487 * 3761 * 176489 * 439693 * 3113111 * 43054831
然后使用Pohlig-Hellman算法对第一个点计算其离散对数以获取其对应的d, 并进行进行质因数分解, sagemath代码如下
p = 17101937747109687265202713197737423
a = 2
b = 3
EC = EllipticCurve(GF(p), [a, b])
Gx = 3543321030468950376213178213609418
Gy = 14807290861072031659976937040569354
G = EC(Gx, Gy)
Gn = EC(10387607239579938257626703795439257, 10616885760882242602781569890429191)
ecOrder = 17101937747109687496599931614463506
primes = [2, 3, 7, 7, 1487, 3761, 176489, 439693, 3113111, 43054831]
dlogs = []
product = 1
for fac in primes:
t = ecOrder // fac
dlog = discrete_log(t*Gn, t*G, operation="+")
dlogs.append(dlog)
print("factor: ", fac, ", Discrete Log: ", dlog)
product = product * fac
d = crt(dlogs, primes)
print("d =", d)
# d = 1498230992101
assert d * G == Gn
factor(d)
4157 * 16183 * 22271
得到d的质因数之后,我们可以根据代码中给出的rotate和goToNextStation方法对上述质因数的6个可能的组合计算出第二个点对应的d以及其坐标
def rotate(seed):
x, y, z = seed
x = (171 * x) % 30269
y = (172 * y) % 30307
z = (170 * z) % 30323
seed = x, y, z
return seed
def goToNextStation(seed):
while True:
seed = rotate(seed)
x, y, z = seed
if(isPrime(x) and isPrime(y) and isPrime(z)):
d = x * y * z
print("d = ", d)
new_point = d * G
print("new_point = ", new_point)
return new_point
factors = [4201, 11329, 20921]
goToNextStation((factors[0], factors[1], factors[2]))
goToNextStation((factors[0], factors[2], factors[1]))
goToNextStation((factors[1], factors[0], factors[2]))
goToNextStation((factors[1], factors[2], factors[0]))
goToNextStation((factors[2], factors[0], factors[1]))
goToNextStation((factors[2], factors[1], factors[0]))
以上代码输出6个可能的坐标,其中的一个可以获取flag。