Ezflag

task

int __fastcall main(int argc, const char **argv, const char **envp)
{
  __int64 v3; // rax
  __int64 v4; // rax
  _BYTE v6[32]; // [rsp+0h] [rbp-50h] BYREF
  _BYTE v7[12]; // [rsp+20h] [rbp-30h] BYREF
  int v8; // [rsp+2Ch] [rbp-24h] BYREF
  char v9; // [rsp+33h] [rbp-1Dh]
  int i; // [rsp+34h] [rbp-1Ch]
  unsigned __int64 v11; // [rsp+38h] [rbp-18h]

  std::string::basic_string(v6, argv, envp);
  std::operator<<<std::char_traits<char>>(&std::cout, "Enter password: ");
  std::getline<char,std::char_traits<char>,std::allocator<char>>(&std::cin, v6);
  if ( (unsigned __int8)std::operator!=<char>(v6, "V3ryStr0ngp@ssw0rd") )
  {
    v3 = std::operator<<<std::char_traits<char>>(&std::cout, "Wrong password!");
    std::ostream::operator<<(v3, &std::endl<char,std::char_traits<char>>);
  }
  else
  {
    std::operator<<<std::char_traits<char>>(&std::cout, "flag{");
    std::ostream::flush((std::ostream *)&std::cout);
    v11 = 1;
    for ( i = 0; i <= 31; ++i )
    {
      v9 = f(v11);
      std::operator<<<std::char_traits<char>>(&std::cout, (unsigned int)v9);
      std::ostream::flush((std::ostream *)&std::cout);
      if ( i == 7 || i == 12 || i == 17 || i == 22 )
      {
        std::operator<<<std::char_traits<char>>(&std::cout, "-");
        std::ostream::flush((std::ostream *)&std::cout);
      }
      v11 *= 8LL;
      v11 += i + 64;
      v8 = 1;
      std::chrono::duration<long,std::ratio<1l,1l>>::duration<int,void>(v7, &v8);
      std::this_thread::sleep_for<long,std::ratio<1l,1l>>(v7);
    }
    v4 = std::operator<<<std::char_traits<char>>(&std::cout, "}");
    std::ostream::operator<<(v4, &std::endl<char,std::char_traits<char>>);
  }
  std::string::~string(v6);
  return 0;
}
__int64 __fastcall f(unsigned __int64 a1)
{
  __int64 v2; // [rsp+10h] [rbp-20h]
  unsigned __int64 i; // [rsp+18h] [rbp-18h]
  __int64 v4; // [rsp+20h] [rbp-10h]
  __int64 v5; // [rsp+28h] [rbp-8h]

  v5 = 0;
  v4 = 1;
  for ( i = 0; i < a1; ++i )
  {
    v2 = v4;
    v4 = ((_BYTE)v5 + (_BYTE)v4) & 0xF;
    v5 = v2;
  }
  return *(unsigned __int8 *)std::string::operator[](&K, v5);
}

分析

题目给了个64 位 Linux 动态链接可执行文件(PIE),目标机器不是 Windows。
判断为re+密码 题,主要是没接触过逆向,逆出来的东西有点懵逼。

我们可以知道程序的运行逻辑是输入密码,然后运行f函数,逐步输出flag。其中密码在逆出来的伪代码中可以直接得到。

因为f函数的作用是计算斐波那契数列,如果按照程序里面的输入密码运行程序那么在有限时间内无法获取flag,我们真正需要的只是 fib(v) mod 16,不是整个斐波那契数列。而在模 16 下,斐波那契序列是周期的 —— 这个周期叫 Pisano 周期,所以通过数学分析其实一下子就能得到flag。

其中生成flag的字符集为

由这个字符集得到的斐波那契模 16 的周期如下:

​

0 1 1 2 3 5 8 13 5 2 7 9 0 9 9 2 11 13 8 5 13 2 15 1
接下来对比索引即可得到答案,然后可以通过简短得运行程序来验证答案。

​

结果为

flag{10632674-1d219-09f29-14769-f60219a24}



RSA_NestingDoll

task

from Crypto.Util.number import *  
from tqdm import tqdm  
import os  
  
flag=open("./flag.txt","rb").read()  
flag=bytes_to_long(flag+os.urandom(2048//8-len(flag)))  
e=65537  
  
def get_smooth_prime(bits, smoothness, max_prime=None):     #1024 20  
    assert bits - 2 * smoothness > 0  
  
    p = 2  
    if max_prime!=None:  
        assert max_prime>smoothness  
        p*=max_prime  
          
    while p.bit_length() < bits - 2 * smoothness:  
        factor = getPrime(smoothness)  
        p *= factor  
  
    bitcnt = (bits - p.bit_length()) // 2  
    while True:  
        prime1 = getPrime(bitcnt)  
        prime2 = getPrime(bitcnt)  
        tmpp = p * prime1 * prime2  
        if tmpp.bit_length() < bits:  
            bitcnt += 1  
            continue  
  
        if tmpp.bit_length() > bits:  
            bitcnt -= 1  
            continue  
        if isPrime(tmpp + 1):  
            p = tmpp + 1  
            break  
    return p  
  
p1=getPrime(512)  
q1=getPrime(512)  
r1=getPrime(512)  
s1=getPrime(512)  
n1=p1*q1*r1*s1  
assert n1>flag  
  
p=get_smooth_prime(1024,20,p1)  
q=get_smooth_prime(1024,20,q1)  
r=get_smooth_prime(1024,20,r1)  
s=get_smooth_prime(1024,20,s1)  
n=p*q*r*s  
  
print(f"[+] inner RSA modulus = {n1}")  
print(f"[+] outer RSA modulus = {n}")  
print(f"[+] Ciphertext = {pow(flag,e,n1)}")  
  
[+] inner RSA modulus = 16141229822582999941795528434053604024130834376743380417543848154510567941426284503974843508505293632858944676904777719167211264225017879544879766461905421764911145115313698529148118556481569662427943129906246669392285465962009760415398277861235401144473728421924300182818519451863668543279964773812681294700932779276119980976088388578080667457572761731749115242478798767995746571783659904107470270861418250270529189065684265364754871076595202944616294213418165898411332609375456093386942710433731450591144173543437880652898520275020008888364820928962186107055633582315448537508963579549702813766809204496344017389879  
[+] outer RSA modulus = 484831124108275939341366810506193994531550055695853253298115538101629337644848848341479419438032232339003236906071864005366050185096955712484824249228197577223248353640366078747360090084446361275032026781246854700074896711976487694783856878403247312312487197243272330518861346981470353394149785086635163868023866817552387681890963052199983782800993485245670437818180617561464964987316161927118605512017355921555464359512280368738197370963036482455976503266489446554327046948670215814974461717020804892983665655107351050779151227099827044949961517305345415735355361979690945791766389892262659146088374064423340675969505766640604405056526597458482705651442368165084488267428304515239897907407899916127394598273176618290300112450670040922567688605072749116061905175316975711341960774150260004939250949738836358264952590189482518415728072191137713935386026127881564386427069721229262845412925923228235712893710368875996153516581760868562584742909664286792076869106489090142359608727406720798822550560161176676501888507397207863998129261472631954482761264406483807145805232317147769145985955267206369675711834485845321043623959730914679051434102698588945009836642922614296598336035078421463808774940679339890140690147375340294139027290793  
[+] Ciphertext = 657984921229942454933933403447729006306657607710326864301226455143743298424203173231485254106370042482797921667656700155904329772383820736458855765136793243316671212869426397954684784861721375098512569633961083815312918123032774700110069081262242921985864796328969423527821139281310369981972743866271594590344539579191695406770264993187783060116166611986577690957583312376226071223036478908520539670631359415937784254986105845218988574365136837803183282535335170744088822352494742132919629693849729766426397683869482842748401000853783134170305075124230522253670782186531697976487673160305610021244587265868919495629


分析

先来看参数生成逻辑,out 提供了加密的公钥 n1 和 e,以及加密后的密文 c,还有本题的关键信息,由生成 n1 的各因子再派生出的 n。

观察 get_smooth 函数我们可以得知 p1,q1,r1,s1 的生成逻辑如下:

很明显的 p-1 光滑攻击,但是这里直接用板子的话时间会爆炸,因为上界来到了 512 位的大小。

解决方法就是将我们已知的 n1 当作已构造的光滑数,再去爆破其他的小素数,这样速度就能得到很好的改善。因为 n1 中已经包含了素因子 pqrs,而多出来的数其实不会影响结果:


exp

import random  
import sys  
import time  
from gmpy2 import gcd, is_prime, mpz, powmod  
from Crypto.Util.number import *  
  
def split(m):  
    a=1  
    for attempt in range(40):  
        a =a+1  
        t0 = time.time()  
        x = powmod(a, n1, m)               # 第一步:乘 n1,消掉 512 位大因子  
        x = powmod(x, L, m)                 # 第二步:乘光滑部分  
  
        for g in (gcd(x - 1, m), gcd(x + 1, m)):  
            if 1 < g < m:  
                return int(g)  
  
    return None  
  
  
def factor(m, out):  
    """递归分解。指数 L 不变、模数逐层变小,所以耗时递减。"""  
    if is_prime(mpz(m)):  
        out.append(m)  
        return  
    g = split(mpz(m))  
  
    factor(g, out)                   #这里递归调用是为了防止第一次分解出合数,所以重复调用知道分解出素数为止  
    factor(m // g, out)  
  
def primes_below(hi):           #埃氏筛,返回 [2, hi) 内全部素数  
    sieve = bytearray([1]) * hi  
    sieve[0:2] = b"\x00\x00"  
    for i in range(2, int(hi ** 0.5) + 1):  
        if sieve[i]:  
            sieve[i * i::i] = bytearray(len(range(i * i, hi, i)))  
    return [i for i in range(2, hi) if sieve[i]]  
  
  
n1 = mpz(16141229822582999941795528434053604024130834376743380417543848154510567941426284503974843508505293632858944676904777719167211264225017879544879766461905421764911145115313698529148118556481569662427943129906246669392285465962009760415398277861235401144473728421924300182818519451863668543279964773812681294700932779276119980976088388578080667457572761731749115242478798767995746571783659904107470270861418250270529189065684265364754871076595202944616294213418165898411332609375456093386942710433731450591144173543437880652898520275020008888364820928962186107055633582315448537508963579549702813766809204496344017389879)  
n = mpz(484831124108275939341366810506193994531550055695853253298115538101629337644848848341479419438032232339003236906071864005366050185096955712484824249228197577223248353640366078747360090084446361275032026781246854700074896711976487694783856878403247312312487197243272330518861346981470353394149785086635163868023866817552387681890963052199983782800993485245670437818180617561464964987316161927118605512017355921555464359512280368738197370963036482455976503266489446554327046948670215814974461717020804892983665655107351050779151227099827044949961517305345415735355361979690945791766389892262659146088374064423340675969505766640604405056526597458482705651442368165084488267428304515239897907407899916127394598273176618290300112450670040922567688605072749116061905175316975711341960774150260004939250949738836358264952590189482518415728072191137713935386026127881564386427069721229262845412925923228235712893710368875996153516581760868562584742909664286792076869106489090142359608727406720798822550560161176676501888507397207863998129261472631954482761264406483807145805232317147769145985955267206369675711834485845321043623959730914679051434102698588945009836642922614296598336035078421463808774940679339890140690147375340294139027290793)  
c = mpz(657984921229942454933933403447729006306657607710326864301226455143743298424203173231485254106370042482797921667656700155904329772383820736458855765136793243316671212869426397954684784861721375098512569633961083815312918123032774700110069081262242921985864796328969423527821139281310369981972743866271594590344539579191695406770264993187783060116166611986577690957583312376226071223036478908520539670631359415937784254986105845218988574365136837803183282535335170744088822352494742132919629693849729766426397683869482842748401000853783134170305075124230522253670782186531697976487673160305610021244587265868919495629)  
e = mpz(65537)  
  
L=1  
for i in primes_below(1 << 20):  
    if i >= (1 << 9):  
        L=L*i  
  
outer = []  
factor(n, outer)  
  
inner = [int(gcd(mpz(p) - 1, n1)) for p in outer]  
  
phi = 1  
for f in inner:  
    phi *= f - 1  
  
d = powmod(e, -1, phi)  
m = int(powmod(c, d, n1))  
print("FLAG =", long_to_bytes(m))


结果如下:


ECDSA

#!/usr/bin/env python3  
from ecdsa import SigningKey, NIST521p  
from hashlib import sha512  
from Crypto.Util.number import long_to_bytes  
import random  
import binascii  
import sys  
  
digest_int = int.from_bytes(sha512(b"Welcome to this challenge!").digest(), "big")    #导入参数  
curve_order = NIST521p.order  
  
priv_int = digest_int % curve_order  
priv_bytes = long_to_bytes(priv_int, 66)  
  
sk = SigningKey.from_string(priv_bytes, curve=NIST521p)     #用私钥字节创建一个签名对象  
vk = sk.verifying_key       #从私钥对象里取出对应的公钥对象。  
  
f_pub = open("public.pem", "wb")  
f_pub.write(vk.to_pem())  
f_pub.close()  
  
def nonce(i):    #生成seed  
    seed = sha512(b"bias" + bytes([i])).digest()  
    k = int.from_bytes(seed, "big")  
    return k  
  
msgs = [b"message-" + bytes([i]) for i in range(60)]  
sigs = []  
  
for i, msg in enumerate(msgs):  
    k = nonce(i)  
    sig = sk.sign(msg, k=k)  
    sigs.append((binascii.hexlify(msg).decode(), binascii.hexlify(sig).decode()))   #转16进制  
  
f_sig = open("signatures.txt", "w")  
for m, s in sigs:  
    f_sig.write("%s:%s\n" % (m, s))  
f_sig.close()

分析

本题给了很多的混淆信息?又是pem文件又是签名前后的结果,但是题目只要求拿到私钥,并且私钥的生成非常的清晰,都摆在源码里面了,只能说这题是在吓唬不懂ecdsa的人hhh。

运行逻辑为通过指定字符串的sha512和NIST521p曲线的阶来生成私钥,然后用私钥创建信息,提取公钥,依次从不知道拿来的 bytes列表中提取元素加密,最后写入文件,后面其实都没必要看。

因为字符串已经写在源码,且NIST521p曲线的阶也能查到,所以简单计算一下就行了。exp都没必要写

总结

……