呜呜呜,自闭了,SM4的侧信道攻击肝了2天,啃了几篇论文,敲了几百行代码,算出来还是错的,没打过OI,优化算法太痛苦了
ps:经过HWS群里的@JZAX大佬指点,终于复现了SM4的侧信道攻击

趁大佬们都去打强网杯了,偷偷拿个第一

硬件(Crypto侧信道)

Read&Solve

RSA侧信道,根据CTFWiki的图,直接对着波形提取二进制转hex提交

足够安全

AES侧信道,github上可以搜到一个项目(https://github.com/wlmnzf/CPA)
自己改改脚本把json的数据扔进去即可

MISC

最伟大的作品

听一下可以听出来是由不同音调组成,找个在线网站翻译下(https://bideyuanli.com/pp)
把数字前对应的字母连起来提交即可

random

random的size固定,然后按照顺序生成加密时的伪随机数,flag所在位置替换为从\x00到\x27,就可以获得打乱后flag每个字符的顺序,恢复即可

# from flag import FLAG
import random
from PIL import Image
from hashlib import md5
from Crypto.Util.number import long_to_bytes as n2b

random.seed(793211)

def pbl(bits):
    num = random.getrandbits(bits)
    bins = []
    while num:
        bins.append(num & 1)
        num >>= 1
    while len(bins) != bits:
        bins.append(0)
    return bins

cat = Image.open('1.png')
catx = Image.open('xx.png')

x, y = cat.size
bits = x * y
r1, r2 = pbl(bits), pbl(bits)
fake_flag=b''
for i in range(28):
    fake_flag+=n2b(i)
r3 = fake_flag+n2b(random.getrandbits((bits - 28) * 8))
r3 = list(r3)
random.shuffle(r3)

flag=[0 for _ in range(28)]

i=0
j=0

for i in range(x):
    for j in range(y):
        pix = cat.getpixel((i, j))
        pixx = catx.getpixel((i, j))
        if r3[i * y + j]!=(pix[2] ^ pixx[2]):
            flag[r3[i * y + j]]=pix[2] ^ pixx[2]

for i in flag:
    print(chr(i),end='')

What's this

先用uncompyle6反编译出py文件,查看py文件可知,其每次会在程序最后执行一个新的程序。把源代码改一下,把每次要执行的代码给输出到另一个文件里,套了几层娃,最后输出的文件里面有flag明文

CRYPTO

HWS-easyRSA

LCG,找个脚本解一下就行

from gmpy2 import gcd
from Crypto.Util.number import *

n = 31893593182018727625473530765941216190921866039118147474754069955393226712079257707838327486268599271803
output =  [25820280412859586557218124484272275594433027771091486422152141535682739897353623931875432576083022273940,24295465524789348024814588142969609603624462580932512051939198335014954252359986260009296537423802567677,14963686422550871447791815183480974143372785034397446416396172429864269108509521776424254168481536292904]

MMI = lambda A, n,s=1,t=0,N=0: (n < 2 and t%N or MMI(n, A%n, t, s-A//n*t, N or n),-1)[n<1] #逆元计算
a=(output[2]-output[1])*MMI((output[1]-output[0]),n)%n
ani=MMI(a,n)
b=(output[1]-a*output[0])%n
seed = (ani*(output[0]-b))%n
plaintext=seed
print(long_to_bytes(plaintext))

RE

re1

很明显的XXTEA加密,把密文密钥提出来,解密一下

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

#define DELTA 0x9e3779b9
#define MX                                     \
    (((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4)) ^ \
     ((sum ^ y) + (key[(p & 3) ^ e] ^ z)))

uint32_t cipher[] = {
    0x10BD3B47, 0x6155E0F9, 0x6AF7EBC5, 0x8D23435F, 0x1A091605, 0x0D43D40EF, 0x0B4B16A67, 0x6B3578A9 ,0x0
};

uint32_t *xxtea_uint_decrypt(uint32_t *data, size_t len, uint32_t *key)
{
    uint32_t n =
        (uint32_t)len - 1;
    uint32_t z, y = data[0], p, q = 6 + 52 / (n + 1), sum = q * DELTA, e;
    if (n < 1)
        return data;

    while (sum != 0)
    {
        e = sum >> 2 & 3;
        for (p = n; p > 0; p--)
        {
            z = data[p - 1];
            y = data[p] -= MX;
        }
        z = data[n];
        y = data[0] -= MX;
        sum -= DELTA;
    }
    return data;
}
int main()
{
    uint32_t key[] = {0x1234, 0x2345, 0x4567, 0x6789};
    xxtea_uint_decrypt(cipher, 8, key);
    printf("%s", &cipher);
}

re2

可以看到有个TLSCallback函数,异或了一块数据块后将其作为代码执行,把debug_break给patch掉,然后动调可以发现是RC4加密,加密前异或了0x48
找个在线网站解密一下

re3

apk拖入GDA逆向,可以在入口处找到以下检查函数,tran函数用于将输入的10进制数三位一组,转换为2字节36进制存储,因为check在libc里,接下来去逆向libc


libc里的Java_com_example_ctf_MainActivity_check函数没什么用处,对sure_flag交叉引用找到检查函数sub_D060

红框处代码因为wsl环境不方便装模拟器调试,所以拖出来用c调,可以发现功能是将36进制数转换为16进制数
下面是rsa加密函数,m是我们的输入,n和e分别是73AA和73A3,c是sure_flag,加密后进行比较。识别rsa加密函数可以根据rsa的特征,常见代码如下:

static int public_block_operation(uint8_t *out, uint32_t *out_len, uint8_t *in, uint32_t in_len, rsa_pk_t *pk)
{
    uint32_t edigits, ndigits;
    bn_t c[BN_MAX_DIGITS], e[BN_MAX_DIGITS], m[BN_MAX_DIGITS], n[BN_MAX_DIGITS];

    bn_decode(m, BN_MAX_DIGITS, in, in_len);
    bn_decode(n, BN_MAX_DIGITS, pk->modulus, RSA_MAX_MODULUS_LEN);
    bn_decode(e, BN_MAX_DIGITS, pk->exponent, RSA_MAX_MODULUS_LEN);

    ndigits = bn_digits(n, BN_MAX_DIGITS);
    edigits = bn_digits(e, BN_MAX_DIGITS);

    if(bn_cmp(m, n, ndigits) >= 0) {
        return ERR_WRONG_DATA;
    }

    bn_mod_exp(c, m, e, edigits, n, ndigits);

    *out_len = (pk->bits + 7) / 8;
    bn_encode(out, *out_len, c, ndigits);

    // Clear potentially sensitive information
    memset((uint8_t *)c, 0, sizeof(c));
    memset((uint8_t *)m, 0, sizeof(m));

    return 0;
}

把c,n,e提取出来解密出m,然后逆tran转为我们的输入,脚本如下:

from Crypto.Util.number import *
import gmpy2

n=0x7019325B70F4A2F26E921102A0206DE415CAEB535CD4EC9D23D6608630DD00A9DB5DB8FAEF4621CCB2E775844C7447A1A843EBAC03ECA6F329FEABCD6560B80AACF7A54A298548827C9D75E1450FCF7E53DAC37C0F7FD25D509C342C23BDA0619504B28EC903C56C87
p=1475203612633975218848450285487339190962027688336790188873776418606441616307026173067
q=1475203612633975218848450285487339190962027688336790188873776418606441616307046219549
r=1475203612633975218848450285487339190962027688336790188873776418606441616307129708089
e=0x10001
c=bytes_to_long(b"I am sure it is fl4g")

phi=(p-1)*(q-1)*(r-1)
d=gmpy2.invert(e,phi)
m=gmpy2.powmod(c,d,n)

s=hex(m)[2:]

for i in range(len(s)//2):
    print('%03d'%(int(s[i*2:i*2+2],16)),end="")

输出直接md5加密即为flag


现在、你眼中看到了什么?