RCTF 2022
做了一些crypto和misc,赛后重点复现crypto。
Solved
Misc
checkin
访问ROIS官网,查看网页源码得到flag RCTF{w3lcome_t0_rctf2022}
ez_alient
在alien.bmp尾部有base64编码,解密:pwd="N0bOdy_l0ves_Me",解开压缩包是一个alien_invasion: PE32+ executable (console) x86-64, for MS Windows
文件,具体来说是个python3.8的游戏。把该文件后缀改为exe,再把两个图像和它放在一起可成功执行。
用pyinstxtractor
可对exe进行解包得到一系列的pyc和dll文件,对alien_invasion.pyc
进行反编译可以得到主函数,主函数并没有关于flag输出的逻辑,所以基本不考虑去手动打游戏。
main.py
# uncompyle6 version 3.8.0
# Python bytecode 3.8.0 (3413)
# Decompiled from: Python 3.7.6 (tags/v3.7.6:43364a7ae0, Dec 19 2019, 00:42:30) [MSC v.1916 64 bit (AMD64)]
# Embedded file name: alien_invasion.py
import sys, pygame
from settings import Settings
from ship import Ship
from bullet import Bullet
from alien import Alien
from time import sleep
from game_stats import GameStats
from button import Button
from scoreboard import ScoreBoard
class AlienInvasion:
__doc__ = '管理游戏的资源和行为的类'
def __init__(self):
"""初始化游戏并创建资源"""
pygame.init()
self.settings = Settings()
self.screen = pygame.display.set_mode((self.settings.screen_width, self.settings.screen_height))
pygame.display.set_caption('Alien Invasion')
self.stats = GameStats(self)
self.sb = ScoreBoard(self)
self.ship = Ship(self)
self.bullets = pygame.sprite.Group()
self.aliens = pygame.sprite.Group()
self._creat_fleet()
self.play_button = Button(self, 'play')
def run_game(self):
"""开始游戏的主循环"""
while True:
self._check_events()
if self.stats.game_active:
self.ship.update()
self.bullets.update()
self._update_bullets()
self._update_aliens()
self._update_screen()
def _creat_fleet(self):
"""创建外星人群"""
alien = Alien(self)
alien_width, alien_height = alien.rect.size
avaliable_space_x = self.settings.screen_width - 2 * alien_width
number_aliens_x = avaliable_space_x // (2 * alien_width)
ship_height = self.ship.rect.height
avaliable_space_y = self.settings.screen_height - 3 * alien_height - ship_height
number_rows = avaliable_space_y // (2 * alien_height)
for row_number in range(number_rows):
for alien_number in range(number_aliens_x):
self._creat_alien(alien_number, row_number)
def _creat_alien(self, alien_number, row_number):
alien = Alien(self)
alien_width, alien_height = alien.rect.size
alien.x = alien_width + 2 * alien_width * alien_number
alien.rect.x = alien.x
alien.rect.y = alien.rect.height + 2 * alien.rect.height * row_number
self.aliens.add(alien)
def _check_fleet_edges(self):
for alien in self.aliens.sprites():
if alien.check_edges():
self._change_fleet_direction()
break
def _change_fleet_direction(self):
for alien in self.aliens.sprites():
alien.rect.y += self.settings.fleet_drop_speed
else:
self.settings.fleet_direction *= -1
def _check_events(self):
"""响应键盘和鼠标事件"""
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.KEYDOWN:
self._check_keydown_events(event)
elif event.type == pygame.KEYUP:
self._check_keyup_events(event)
elif event.type == pygame.MOUSEBUTTONDOWN:
mouse_pos = pygame.mouse.get_pos()
self._check_play_button(mouse_pos)
def _check_play_button(self, mouse_pos):
"""玩家点击play按钮是开始新的游戏"""
button_clicked = self.play_button.rect.collidepoint(mouse_pos)
if button_clicked:
if not self.stats.game_active:
self.stats.reset_stats()
self.stats.game_active = True
self.settings.initialize_dynamic_settings()
self.sb.prep_score()
self.aliens.empty()
self.bullets.empty()
self._creat_fleet()
self.ship.center_ship()
pygame.mouse.set_visible(False)
def _check_keydown_events(self, event):
"""响应按键"""
if event.key == pygame.K_RIGHT:
self.ship.moving_right = True
else:
if event.key == pygame.K_LEFT:
self.ship.moving_left = True
else:
if event.key == pygame.K_q:
sys.exit()
else:
if event.key == pygame.K_SPACE:
self._fire_bullet()
def _check_keyup_events(self, event):
"""响应松开"""
if event.key == pygame.K_RIGHT:
self.ship.moving_right = False
else:
if event.key == pygame.K_LEFT:
self.ship.moving_left = False
def _fire_bullet(self):
"""创建一个子弹,并将其加入编组bullets"""
if len(self.bullets) < self.settings.bullets_allowed:
new_bullet = Bullet(self)
self.bullets.add(new_bullet)
def _update_bullets(self):
"""更新现在子弹的位置,并删除消失的子弹"""
self.bullets.update()
for bullet in self.bullets.copy():
if bullet.rect.bottom <= 0:
self.bullets.remove(bullet)
else:
self._check_bullet_alient_collisions()
def _check_bullet_alient_collisions(self):
collisions = pygame.sprite.groupcollide(self.bullets, self.aliens, True, True)
if collisions:
for aliens in collisions.values():
self.stats.score += self.settings.alien_points * len(aliens)
self.sb.prep_score()
self.sb.check_high_score()
if not self.aliens:
self.bullets.empty()
self._creat_fleet()
self.settings.increase_speed()
def _check_aliens_bottom(self):
"""检测外星人到达屏幕底部"""
screen_rect = self.screen.get_rect()
for alien in self.aliens.sprites():
if alien.rect.bottom >= screen_rect.bottom:
self._ship_hit()
break
def _update_aliens(self):
self._check_fleet_edges()
self.aliens.update()
if pygame.sprite.spritecollideany(self.ship, self.aliens):
self._ship_hit()
self._check_aliens_bottom()
def _ship_hit(self):
"""相应飞船与外星人碰撞"""
if self.stats.ship_left > 0:
self.stats.ship_left -= 1
self.bullets.empty()
self.aliens.empty()
self._creat_fleet()
self.ship.center_ship()
sleep(0.5)
else:
self.stats.game_active = False
pygame.mouse.set_visible(True)
def _update_screen(self):
"""重置屏幕以及图像"""
self.screen.fill(self.settings.bg_color)
self.ship.blitme()
for bullet in self.bullets.sprites():
bullet.draw_bullet()
else:
self.aliens.draw(self.screen)
self.sb.show_score()
if not self.stats.game_active:
self.play_button.draw_button()
pygame.display.flip()
if __name__ == '__main__':
ai = AlienInvasion()
ai.run_game()
s = b'VTJreE0yNWpNdz09'
发现注释里这个s多次base解码能得到Si13nc3
,然后发现在该游戏非第三方库中存在一些base64编码,具体就是以下文件:
from settings import Settings
from ship import Ship
from bullet import Bullet
from alien import Alien
from time import sleep
from game_stats import GameStats
from button import Button
from scoreboard import ScoreBoard
把他们全部提取并按照上面顺序排:
alien.pyc:15 bullet.pyc:nEvEr button.pyc:9ivin9 game_stats.pyc:up scoreboard.pyc:&& settings.pyc:6ut ship.pyc:h01din9 On Si13nT1y
连在一起就是flag:RCTF{Si13nc3_15_nEvEr_9ivin9_up_&&_6ut_h01din9_On_Si13nT1y}
K999
直接在exe中搜索flag
,会得到一些lua代码,因此基本确定这是个lua编写的程序,没找到解包的方法,直接binwalk分离。得到main.lua和flag.lua:
main.lua
Railgun = {
Const = {
Category = {
player = 2,
enemy = 3,
bullet = 4,
wall = 5,
effect = 6,
grenade = 7,
},
},
Config = {
debug = false,
Enemy = {
Generate = true,
Max = 30,
},
Bonus = {
Generate = true,
Max = 1,
},
}
}
require('ulits')
class = require('30log')
require('GameObject')
require('World')
require('Player')
require('Bullet')
require('Enemy')
require('Map')
require('Grenade')
local mouse = { x = 0, y = 0 }
local edgePoint = { x = 0, y = 0 }
function love.load(arg)
if arg[#arg] == "-debug" then
require("mobdebug").start()
Railgun.Config.debug = true
end
love.graphics.setPointSize(2)
world = World:create()
if Railgun.Config.debug then
world.debug = {}
end
world:init({ mapPath = 'res/map/stage2' })
end
local num = 0
local getflag = false
function love.update(dt)
local player = world.player
if world.KillenemyCount <= 999 then
if player.alive then
world:update(dt)
end
end
--[[
--edgePoint = updateEdgePoint()
]] --
end
function love.draw()
love.graphics.push('all')
world:draw()
love.graphics.pop()
local player = world.player
--love.graphics.print(love.timer.getFPS())
--love.graphics.print(string.format("%.1f", player.pos.x)..','..string.format("%.1f", player.pos.y))
--drawAimLine()
if not player.alive then
love.graphics.push('all')
love.graphics.setColor(255, 255, 0, 255)
love.graphics.print('Game Over', love.graphics.getWidth() / 2, love.graphics.getHeight() / 2, 0, 2, 2, 37, 7)
love.graphics.pop()
else
if world.KillenemyCount >= 999 then
love.graphics.push('all')
love.graphics.setColor(255, 255, 0, 255)
local flag1 = "MOON\r\n"
local flag2 = "157 89 215 46 13 189 237 23 241\r\n"
local flag3 = "49 84 146 248 150 138 183 119 52\r\n"
local flag4 = "34 174 146 132 225 192 5 220 221\r\n"
local flag5 = "176 184 218 19 87 249 122\r\n"
local flag6 = "Find a Decrypt!\r\n"
love.graphics.print(flag1, love.graphics.getWidth() / 2, love.graphics.getHeight() / 2, 0, 2, 2, 37, 7)
love.graphics.print(flag2, love.graphics.getWidth() / 2, love.graphics.getHeight() / 2 + 20, 0, 2, 2, 37, 7)
love.graphics.print(flag3, love.graphics.getWidth() / 2, love.graphics.getHeight() / 2 + 40, 0, 2, 2, 37, 7)
love.graphics.print(flag4, love.graphics.getWidth() / 2, love.graphics.getHeight() / 2 + 60, 0, 2, 2, 37, 7)
love.graphics.print(flag5, love.graphics.getWidth() / 2, love.graphics.getHeight() / 2 + 80, 0, 2, 2, 37, 7)
love.graphics.print(flag6, love.graphics.getWidth() / 2, love.graphics.getHeight() / 2 + 100, 0, 2, 2, 37, 7)
love.graphics.pop()
getflag = true
else
if world.pause then
love.graphics.push('all')
love.graphics.setColor(255, 255, 255, 255)
love.graphics.print('Pause', love.graphics.getWidth() / 2, love.graphics.getHeight() / 2, 0, 2, 2, 37, 7)
love.graphics.pop()
else
local stats = love.graphics.getStats()
love.graphics.push('all')
love.graphics.print('Kill: ' ..
world.KillenemyCount ..
' | HP:' ..
player.hp ..
'/100' ..
' | AMMO:' ..
player.weapon.ammo ..
'/' ..
player.weapon.maxAmmo ..
' | Enemies:' ..
world.enemyCount ..
' | FPS:' ..
love.timer.getFPS() ..
"\nDrawcalls: " ..
stats.drawcalls .. "\nUsed VRAM: " .. string.format("%.2f MB", stats.texturememory / 1024 / 1024), 0, 0)
-- love.graphics.print(world.bonusCount, love.graphics.getWidth()/2, love.graphics.getHeight()/2, 0, 2, 2, 37, 7)
love.graphics.pop()
end
end
end
end
function love.keypressed(key, isRepeat)
if key == 'escape' then
world.pause = not world.pause
end
if key == 'g' then
local x, y = world:mousePos()
local grenade = Grenade:new(world.physics, world.player.pos.x, world.player.pos.y)
world:add(grenade)
grenade:throw(x, y)
end
end
function love.resize(w, h)
if world.light then
world.light:refreshScreenSize()
end
end
--画瞄准线
local function drawAimLine()
love.graphics.line(player.pos.x, player.pos.y, edgePoint.x, edgePoint.y)
end
flag.lua
function to8(n)
return n % 256
end
function bxor(a, b)
local p = 0
local i = 0
for i = 0, 7, 1 do
p = p + 2 ^ i * ((a % 2 + b % 2) % 2)
a = math.floor(a / 2)
b = math.floor(b / 2)
if a == 0 and b == 0 then break end
end
return p
end
function encrypt(v, k)
local sum = 0
local delta = 0x37
local i = 0
for i = 1, 8, 1 do
sum = to8(sum + delta)
v[1] = to8(v[1] + to8(bxor(bxor(to8((v[2] * 16) + k[1]), to8(v[2] + sum)), to8(math.floor(v[2] / 32) + k[2]))))
v[2] = to8(v[2] + to8(bxor(bxor(to8((v[1] * 16) + k[3]), to8(v[1] + sum)), to8(math.floor(v[1] / 32) + k[4]))))
end
end
function decrypt(v, k)
local sum = 0xB8
local delta = 0x37
local i = 0
for i = 1, 8, 1 do
v[2] = to8(v[2] - to8(bxor(bxor(to8((v[1] * 16) + k[3]), to8(v[1] + sum)), to8(math.floor(v[1] / 32) + k[4]))))
v[1] = to8(v[1] - to8(bxor(bxor(to8((v[2] * 16) + k[1]), to8(v[2] + sum)), to8(math.floor(v[2] / 32) + k[2]))))
sum = sum - delta
end
end
function passGen()
local pw = ""
local j
for i = 1, 4, 1 do
j = math.random(33, 126)
if j == 96 then pw = pw .. "_"
else pw = pw .. string.char(j) end
end
return pw
end
function strDecrypt(s, k)
local b = {}
local c = {}
local i
local j
j = string.gmatch(k, ".")
b = { string.byte(j()), string.byte(j()), string.byte(j()), string.byte(j()) }
j = ""
for i = 1, string.len(s) / 2, 1 do
c = { string.byte(string.sub(s, i * 2 - 1, i * 2 - 1)), string.byte(string.sub(s, i * 2, i * 2)) }
decrypt(c, b)
j = j .. string.char(c[1])
if c[2] == 0 then break end
j = j .. string.char(c[2])
end
return j
end
function Decrypt()
local key = ""
local s = {}
flag = ""
for i = 1, #s, 1 do
flag = flag .. string.char(s[i])
end
flag = strDecrypt(flag, key)
print(flag)
end
可以看到当判定条件world.KillenemyCount >= 999
达成时,会弹出flag1-flag6,然后会进行Decrypt。flag.lua里直接给了Decrypt()函数,而不难猜测key为MOON
,flag2-flag6是密文,直接调用解密即可:
local key = "MOON"
local s = {157,89,215,46,13,189,237,23,241,49,84,146,248,150,138,183,119,52,34,174,146,132,225,192,5,220,221,176,184,218,19,87,249,122}
flag = ""
for i = 1, #s, 1 do
flag = flag .. string.char(s[i])
end
flag = strDecrypt(flag, key)
print(flag)
guess
我的方法是建立LWE规约求x高位,和低位相加得到x。
T = []
U = []
xl = 409689551476
K = [U[index] - xl*T[index] for index in range(len(U))]
#print(K)
A = [T,K]
M = Matrix(ZZ,A)
solv = M.LLL()
print(solv[0])
xh=M.solve_left(solv[0])
print(xh)
赛后发现\(\small u=x\cdot t - random\),那么\(\small x=u/t + 1\)就出了,可能是非预期吧。
IS_THIS_LCG?
partial_challenge是一个基于HNP的LCG,参考链接,照着造格就行。
curve_challenge本质上是椭圆曲线上的点加法,建立方程然后利用Groebner基去解即可恢复p2。
已知椭圆曲线加法:
由点b和Xi之间的关系可得:
将y全部用椭圆曲线表达式替换,得到:
显然可以移项让一边为\(\small 2y_iy_{i-1}\),两边平方一下就能再次通过椭圆曲线表达式替换得到关于\(\small x_b,A,B\)的方程,因为给了7组连续的\(\small x_i\),所以我们是可以建立6组方程的,但这里我参考mtctf的做法,直接在模n下建立方程,只需要4组就能出,当然需要用到Groebner基去解。
matrix_challenge是在方阵上建立的LCG,由于矩阵的线性特征,这和数域上的是基本一样的。问题有二,一是需要去写一个hex转matrix的函数,这可以用模运算实现;二是恢复p3时计算\(\small Ax+b\)的迭代次数太多,需要用类似等比数列的通项公式优化——高中数学知识。
拿到所有因子以后求解rsa即可。全部exp:
from sage.all import *
from Crypto.Util.number import bytes_to_long, getRandomInteger, long_to_bytes,inverse
m = 2 ** 1024
a = bytes_to_long(b'Welcome to RCTF 2022')
b = bytes_to_long(b'IS_THIS_LCG?')
N = 0x614d9a106993a792c144715b0269a2726eb18a2e7b1ea7061bce1f6acb31af6289309d67ce6b28b3e88110c42785c0ca23833cc0e2aa4a30aadb16d25db7a74ef03b0898b7af47d56d4538b0f556b2779ed86e0600f821354d51f8551ccd23bbf8bf91eb9a9283a3d4d5248e3f404b4c6646a7dc805f29940a7e29d2f50343e1acc0d0067606606b331a64881bbafeafeb8ca44e736b41eab4608097216f587a1a4f74518614b46e91505e07c3a280b701ee88ca189e9903d601bc934584409d560027e5b34adb1f4949333ab5db34e95e49374e354d4ddc088855f1aae7a95e32ef195521b33f118169ae613e3fd5bf8d2942c2bde9ef506346698b0b5192c86b1efe24cffb907652afd5f0cb3966c7470195122ced63f5c40a4d9a3b6704e0b186ab7b9e3296b1299b6fa133d2455a8f8d8a9007a22bc61546b357ea314b0d369d72d22063c5ed6c14aa2a7edf31bdf93e63149818ef3724ca1cac367ac22b51260c793212ea221e062fcca68f28a4cd0b3bbeee03b9c73fd064c8298e775ab8a63c94db480a1eba918d09cba975304eed4fa5e874fc964e328547c23790e97102c6ad0bca9810dabb6285906f13d41798d3237333288b4498610d1a8fa79be85a522232a7cb904cd7c9b7fab995f39cd22a9758a5c2b6dcf44299df1e3e2ac360339b341ca6beb31eccba39ebd6f98dee127c6b5298db152fa6920b9703ab
c = 0x3a130d7f737dd7e5901290a55349342a535b94bb89831b1c02539480fe76b07ad64f5d2b618e637f4ddc536d46a1c05b219eafc9b609629ae6d1a9c1a888bc8b34d81b9f681fd9ca3919f8382b09f2ba1d78dedffc093c4795200d89aea37b0ac7f23c8eb621810d7a130fa1e324c9a6ea8c3ad69200057f91003d1305293be05d662505e45ea9172097cb030f8fdde2712070fc6f9def504440cac6c46305f7d81f6e40d53ec8ae6c653298e1989ac8f9616dc1d93cb6976ac1c777fc7e50f1a8ef3100ba4871c769c8a3b52a37e15f523a49f69d9ff93c01639d0d099884e113483b580e224a12cbbc6711ae8c5af3ecd375f6da1be68f7fa7425f6e81ea63456d73f9a24ba56766127d6a2871ff2945dbdc1fc14cce2d94c6aa9c114896a1ef06f992666484bc02eacfe540df0c8138c05c572737f42d4069d3bc254df1c825b3a8844edb38f486f96cf153ac07523e430e0546b58abb6fe4268460b722efbe9ee5c718a586f90588e9ad4c49db0068dc1db942756700142c26d512969428141d70c982b003d1d17450ceab0e7845b1e14ce10db3245366d4cd6f46457e0c6e05827f8e9b8bf4163df1712087aba0bce629951d7f2d5279b793bf8131a4b8ee84916e06b49ae4582eea9b43b58a2ee77e6618103ab28c1978800ad07cd12f1ab6843385d18d33b191abccdc18f6fa90004f0edab5cc1ff3c6049cc1e41e89
e = 0x10001
x0 = 0xc65f1c882be27b574c70f10e155ed3d3792d037d3c7
x1 = 0x142e1a26667e31a70eb58fa1e2b296d31a09675fa687
x2 = 0x17f366e283147917cc044778bbce2816884577126a9c
x3 = 0x2a316775dda35ad9a0e8a038757c85f216e91516f1ce
x4 = 0x3ef873ee8fa84fd071777521c78cb10a929f92f10dc7
x5 = 0x14e228828cb5090361501acac3108f05096fa8976e9c
x6 = 0x2e664838384824369607284ad9950f839f23a85c1974
x7 = 0x11affcbdf3da150c318bcc7096d21e8eb4bdaf904b9e
h = [0,x0,x1,x2,x3,x4,x5,x6,x7]
for i in range(len(h)):
h[i] <<= 850
A = [1]
B = [0]
for i in range(1, len(h)-1):
A.append(a*A[i-1] % m)
B.append((a*B[i-1]+a*h[i]+b-h[i+1]) % m)
A = A[1:]
B = B[1:]
#print(A)
M = matrix(ZZ, 9, 9)
for i in range(7):
M[i, i] = m
M[7, i] = A[i]
M[8, i] = B[i]
M[i, 7] = M[i, 8] = 0
M[7, 7] = 1
M[8, 8] = 2^850
M[7, 8]= 0
#print(B)
vl = M.LLL()[0]
l1 = vl[-2]
h1 = h[1]
s1 = l1+h1
#print(s1)
# recover p1
def gen_p1():
m = 2 ** 1024
a = bytes_to_long(b'Welcome to RCTF 2022')
b = bytes_to_long(b'IS_THIS_LCG?')
x = s1
for i in range(7):
x = (a * x + b) % m
print('x{} = {}'.format(i, hex(x >> 850)))
x = (a * x + b) % m
return next_prime(x)
p1 = gen_p1()
def dec2mt(h):
n, m = 8, next_prime(2^16)
L = [[0]*8,[0]*8,[0]*8,[0]*8,[0]*8,[0]*8,[0]*8,[0]*8]
#print(L)
s = 0
for i in range(n):
for j in range(n):
tmp = (h % (m ^ (i * n + j + 1)) - s) / m ^ (i * n + j)
L[i][j]=tmp
s = s + tmp * m ^ (i * n + j)
return matrix(Zmod(m),L)
# test
def mt2dec(X):
x = 0
n, m = 8, next_prime(2^16)
for i in range(n):
for j in range(n):
x = x + int(X[i, j]) * (m ** (i * n + j))
return x
# recover p3
# https://www.yulucn.com/question/949814096
X7 = 0xcf3b0d393c7ef1753e602e0b088fc15d0c06f949631cc9083ef7ab16c65148b47aa63eabb6151e39d85a5a339c065d9f1b4a33ab587f6093eb097fc6bab25a6b27cc8ae7d77775869b0864f6bdb7c1d8dcfa1a28dce4df346d95eaf90047020f4f8ad7e9496ed86e7c1bd840724348d88a308ca21174c61cf759ba106c548458
X8 = 0xe8e2ba97f5bdb287984e2a61b5a489ea4dc45c2c8e3601f151a20b92d1d6b7c0800712d07e4de5d2ca6f9cbfff25a64989e0779b98e56df1f4d8c301d3d743b86690d567c7f3a6bd74aa08b7df1970eb4b53ef2d5f8a7c3be585462dc3a972f99cd99b4ee1738a719476ebe70ba5a89447e020566e2a98ebab5747be0758a312
X9 = 0xcb1cd415bb82a8036035396806a37e28f23a709a51301fea6b0195e3da1a5ff7f71c6bd89387b955ff9e0d743f00e09286cf32520428791bac19368936f2e9bda4ffc4487a2bc999bb22249cffedc16dd686ac91d9a4cfe459e114ce38858f2b4972b09fd3c463c5b40cd553e640afe5803a390766842d2b6a74152923f329db
A7 = dec2mt(X7)
A8 = dec2mt(X8)
A9 = dec2mt(X9)
A = (A9 - A8) * ((A8 - A7) ^ (-1))
B = A9 - A * A8
X0 = 0xc54aad8bd2b3233576847209ad1ade5f535622aab2a6279464832dea3dc88e7898a58130e36273143a90fcd4497079010e50658c2981e66e09ae86de089bf1f7123abb7d71fe68cf8d9eab3a2fc4792f1cb6444eff47c0f666995096c43ef8149fa78c061ca62809a2eadb00ac0dff81fb4163335c0a8014082e95b5007a2e2c
A0 = dec2mt(X0)
ex = 1337^1337
C = (A - matrix.identity(8))^ (-1) * B
#print(C)
p3_mt = A ^ (ex) * (A0 + C) - C
p3 = next_prime(mt2dec(p3_mt))
x_0 = 0x524456278d175edd6bcc3f2bbb8160a87dfe07092db7eedd1e4e3521e9cef7925e9c965a47ce9b7349456938fbf6d1d92095cfe7cdc06c8dbeac5284982d027179d8d363b1d1a9b95c2bb1334e589ac3c013d8cff1c904d0c2aed1f281e997be89abe3d0d2d668dc53adc4ae9870474a23ff993598bf2b51679179c8a1568619
x_1 = 0x2f340fb1c6761e084b1465c5078f36e9caf7f9d6deecb969cc84fb5b85b1e4070157094c835333349f3d317e6a78a31a27d1ff0f8dfb103ead7444f26ac7b8be6b8ec346a8c8b4fe6f983db2729b6490ce0e1ea115b62f5e2888911d278153e3377a7456705c4f1a56588d8f727a91a8a401a852dd26573b2dc2ccf6a4af1de2
x_2 = 0x92b2bbdf0c336be756ac47cc0b98fbc76b9ac679db96a5afd8fe500d16f4997503ee33d0508a59fff172042d6dcc4994a2d8220adcd8f5e591458b9409468c51b92dcacf73e793af3f793b9becb9cbb0704834861a43e1d1fd5cd5a9be14fafa8ec02df059fb3e1a3b0e7a8fb9969d42ffc13e2e3404fd539cd0d95b15f69f33
x_3 = 0x795e49d6bc45ecf4d349d16058166f6422311344e3e6d8913a8b0a28225c92e203dfba92dd809a58a3630bfbb4cdf6d3118f172f6d6cb7c35cd4f9cf70947d091659e2ec4e248eaf2c456d58a149dd1fff7667630504cbb55cd82e3a2fe681f9b23de329d70a85f4badce87168dfb37b96b9edbeeb39a3d4ab28c130e9150140
x_4 = 0x5a2bf69e31eef5ec1b990a2d2e3f8ccb08ba9996db2022775770b3b486909653b5347c15ceab62b167ad1dfa6a997efb56315fd6afde2e6c1b5af5a6e9b818556669992f148525c990bdb61e712339856dcf6e0f27ed8279bb32aba553bbab2ad3ac4accf3084638528a34434ec80df33705e381b39e9786593cff3e04a5b23d
x_5 = 0x5baeb38339d662e8c16b1f16cd6129af38adebb264ffb197d6245f56df813c64b7ef28e60137b54d15a4227ca6ecd08f6ccbbbcb598bc94b1f326d8d488e13179d2999fb2c922165c9f27c2d7d0267e6924ce6395c33ec52a35776e88874877d8ccbbf8ca9ee214a7b73a8f7da23db02978f3b8bd145c2cca66b17638169f5e9
x_6 = 0x4c2fc188b5cd7f4d19a4b120402946d7f8ca11c711e7771c39814e01c692160b7545edcff82a22d4634c416185eb58ff44adfdce5dc36a6d7c663f57eb19fc34f1a6c7e493518b094ad46fb8f9b6eb741c4666878ac91898116eb353a0a5aab9289322aaca6bed2ee104db17be339af54538635208f756da15bf46d18b0549a
# recover p2
xx=[x_0,x_1,x_2,x_3,x_4,x_5,x_6]
P.<xb,A,B>=PolynomialRing(Zmod(N))
def y2(x,A,B):
return x^3+A*x+B
F=[]
# 4个式子就可以把p求出来
for i in range(4):
f=xb*(xx[i+1]-xx[i])^2-y2(xx[i+1],A,B)-y2(xx[i],A,B)+(xx[i]+xx[i+1])*(xx[i+1]-xx[i])^2
f=f^2-4*y2(xx[i+1],A,B)*y2(xx[i],A,B)
F.append(f)
Ideal=Ideal(F)
I = Ideal.groebner_basis()
print(I)
res=[x.constant_coefficient() for x in I]
p2 = int(res[-1])
q = N/p1/p2/p3
assert N == q * p1 * p2 * p3
phi = (p1-1)*(p2-1)*(p3-1)*(q-1)
d = inverse_mod(e,phi)
print(long_to_bytes(int(pow(c,d,N))))
# RCTF{Wo0oOoo0Oo0W_LCG_masT3r}
easyRSA
和HFCTF的RRSSAA很相似,比赛的时候用了一个类似于Wiener的格,\(\small \beta\)取0.5即可分解n,然后解密就和虎符那题一模一样。
e = 3121363059746835628022404544403822724460605553641332612055010587129451973002475126644668174294955070747985002800863652917895939538596303356113483509581841527286351537287500304267975061675901109982875778527827742120878835367386538561039072391997357702421691095861694681707017921391244519593945584755632901987840338065879901115934561426583008838453244051629340056867760923894623105542463500022221236457852502822707466528439969484890601953615303609725566617126458934095119670087068752543521167517461730977044465374505011791902510131823556603316457085145886999220426746234986984619161299098173535371540923264898459106461
c = 3023313363629909506923927199426293187583112749147539699346723655095868214179291222441307436555352537055690155418715652987685459938250844145450675418187664719327350488160722838989675928696633353180233455017609936874014883975932740217672705286265535646106053294507962613498142617741362730709360885118905440314573392981528077265110441270212385951070591696827167771592664502652520790612367259434545169836933571343480057141790292296952743986731389468760364416344837575740236416472589700581583016227273449673820568427641136163703116276104550877191839851640920430919278802098196408637904780725723268371465670950321881886863
n = 101946888552605033726177837709738163930032970477361664394564134626639467843553634920510447339985842689387519517553714582991506722045078696771986052246306068257957261478416093188640437503481862825381241480405463985516598520453211217206308826779669980833596066677262549841524134539729279446910817169620871929289
size=1024
beta = 0.5
n2 = (n-1)^2
R = int(n^(2*beta))
m = matrix(ZZ, [
[R, -n2],
[0, e]
])
L = m.LLL()
print(L)
w = L[0]
v = m.solve_left(w)
print('v = %s' % v)
k, d = v
print(len(bin(d)))
print((e*d-1) % k)
phi = (e*d-1)//k
pq_p = Integer(sqrt(n^2+2*n+1-phi))
pq_m = Integer(sqrt(n^2-2*n+1-phi))
p = (pq_p+pq_m)//2
q = pq_p-p
if p*q == n:
print('p = %d' % p)
print('q = %d' % q)
d = e.inverse_mod(phi)
print('d = %d' % d)
from Crypto.Util.number import inverse, long_to_bytes
from sage.all import *
MOD = n * n
phi = (p**2 - 1) * (q**2 - 1)
def seq(r, k):
A = matrix(Zmod(MOD), [[r, -1], [1, 0]])
x = matrix(Zmod(MOD), [[2], [r]])
return int((A**k * x)[0, 0])
def L(x):
return (x % MOD - 1) // n
d = inverse(e, phi)
r = seq(c, d) % n
v = seq(r, e)
print(long_to_bytes(L(c * inverse(v, MOD))))
赛后发现题目给的界限很宽,连分数也能做,具体来说:
这里用\(\small N^2\)近似\(\phi\)做不出来,调整为\(\small N^2-2N\)就行。
from Crypto.Util.number import *
e = 3121363059746835628022404544403822724460605553641332612055010587129451973002475126644668174294955070747985002800863652917895939538596303356113483509581841527286351537287500304267975061675901109982875778527827742120878835367386538561039072391997357702421691095861694681707017921391244519593945584755632901987840338065879901115934561426583008838453244051629340056867760923894623105542463500022221236457852502822707466528439969484890601953615303609725566617126458934095119670087068752543521167517461730977044465374505011791902510131823556603316457085145886999220426746234986984619161299098173535371540923264898459106461
c = 3023313363629909506923927199426293187583112749147539699346723655095868214179291222441307436555352537055690155418715652987685459938250844145450675418187664719327350488160722838989675928696633353180233455017609936874014883975932740217672705286265535646106053294507962613498142617741362730709360885118905440314573392981528077265110441270212385951070591696827167771592664502652520790612367259434545169836933571343480057141790292296952743986731389468760364416344837575740236416472589700581583016227273449673820568427641136163703116276104550877191839851640920430919278802098196408637904780725723268371465670950321881886863
N = 101946888552605033726177837709738163930032970477361664394564134626639467843553634920510447339985842689387519517553714582991506722045078696771986052246306068257957261478416093188640437503481862825381241480405463985516598520453211217206308826779669980833596066677262549841524134539729279446910817169620871929289
nu=N^2-2*N
de=e
t=de/nu
a=t.continued_fraction()
alist = a.convergents()[1:]
nn = N^2
print(alist)
for i in alist:
print(alist.index(i))
d=i.denominator()
k=i.numerator()
phi=(e*d-1)//k
# 利用phi N的关系及平方数进行判定
if (nn-phi+1)%2==0 and sqrt(pow((nn-phi+1)//2,2)-nn).is_integer():
x,y=var('x y')
p=ZZ(solve([x*y==N,(x^2-1)*(y^2-1)==phi],[x,y])[0][0].rhs())
q=N//p
if p*q == N:
print(p)
break
print('done')
Unsolved
Crypto
magic_sign
一个废弃的密码系统——Xifrat,其安全性基于具有Restricted-Commutativity
性质的群。
而漏洞也正因为这个性质而暴露,具体地,\(\small S = mix(H, Q),e(H+Q)+Q=(e(H)+e(Q))+Q\),S相当于一个N个元素的数组,而\(\small e(H)\)可以通过将H带入函数e中求出。那么问题转化为了爆破\(\small e(Q)\)和Q,由于我们是按列表每个元素爆破,总的次数为\(\small 2^3\cdot 2^3\cdot137\),运算时间非常短。exp如下,正确性没有验证:
import re
from Crypto.Hash import SHAKE256
from tqdm import tqdm
N = 137
Magic_Latin_Square = [[5, 3, 1, 6, 7, 2, 0, 4],
[3, 5, 0, 2, 4, 6, 1, 7],
[6, 2, 4, 5, 0, 3, 7, 1],
[4, 7, 6, 1, 3, 0, 2, 5],
[0, 1, 3, 7, 6, 4, 5, 2],
[7, 4, 2, 0, 5, 1, 6, 3],
[2, 6, 7, 3, 1, 5, 4, 0],
[1, 0, 5, 4, 2, 7, 3, 6]]
def generator(x):
U = [(7 * (3 * i + 5) ** 17 + 11) % N for i in range(N)]
for i in range(x):
yield U[i % N]
if N - i % N == 1:
V = U[:]
for j in range(N):
U[j] = V[U[j]]
i = i + 1
return
def shake(something):
H = SHAKE256.new()
H.update(something)
H = re.findall(r'\d{3}', bin(int.from_bytes(
H.read(384 // 8), 'big'))[2:].zfill(3 * N))
return [int(i, 2) for i in H]
def f(a,b):
return Magic_Latin_Square[a][b]
def e(A):
A_ = A[:]
e_ = U[0]
for i in range(N * N):
d = U[1+i]
A_[d] = f(A_[d],A_[e_])
e_ = d
return A_
U = [_ for _ in generator(N**2+1)]
H = shake(b'Never gonna give you flag~')
e_H = e(H)
print(H)
print(e_H)
S = ['*'] * N
SS = []
for Q in tqdm(range(2 ** 3)):
for e_Q in range(2 ** 3):
for i in range(N):
S[i] = str(f(f(e_H[i],e_Q),Q))
SS.append(S)