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\)就出了,可能是非预期吧。

image

IS_THIS_LCG?

partial_challenge是一个基于HNP的LCG,参考链接,照着造格就行。
curve_challenge本质上是椭圆曲线上的点加法,建立方程然后利用Groebner基去解即可恢复p2。
已知椭圆曲线加法:

\[\lambda = \frac{y_q-y_p}{x_q-x_p}\quad x_r=\lambda^2-x_p-x_q \]

由点b和Xi之间的关系可得:

\[X_{i-1}+b=X_i\quad b=X_i +(-X_{i-1})\quad x_b=(\frac{y_i+y_{i-1}}{x_i-x_{i-1}})^2-x_i-x_{i-1}\;mod\;p \]

将y全部用椭圆曲线表达式替换,得到:

\[x_b(x_i-x_{i-1})^2 + (x_i+x_{i-1})(x_i-x_{i-1})^2=(x_i^3+ax_i+b)+(x_{i-1}^3+ax_{i-1}+b)+2y_iy_{i-1} \]

显然可以移项让一边为\(\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))))

赛后发现题目给的界限很宽,连分数也能做,具体来说:

\[\phi=(p^2-1)(q^2-1)=N^2-p^2-q^2+1\quad \frac{e}{\phi}-\frac{k}{d}=\frac{1}{d\phi}< \frac{1}{2d^2} \]

这里用\(\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性质的群。

image

image
而漏洞也正因为这个性质而暴露,具体地,\(\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)
posted @ 2022-12-13 12:35  ZimaB1ue  阅读(888)  评论(0编辑  收藏  举报