[SeKai CTF 2022] rev的3个Matrix_lib

由浅入深

1,Matrix_lib_1

给了一个class文件,用jadx打开,看源码

package defpackage;

import java.util.Scanner;

/* renamed from: Sekai  reason: default package */
/* loaded from: Matrix_Lab_1.class */
public class Sekai {
    private static int length;
    static final /* synthetic */ boolean $assertionsDisabled;

    static {
        $assertionsDisabled = !Sekai.class.desiredAssertionStatus();
        length = ((int) Math.pow(2.0d, 3.0d)) - 2;
    }

    public static void main(String[] strArr) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("Enter the flag: ");
        String next = scanner.next();
        if (next.length() != 43) {
            System.out.println("Oops, wrong flag!");
            return;
        }
        String substring = next.substring(0, length);
        String substring2 = next.substring(length, next.length() - 1);
        String substring3 = next.substring(next.length() - 1);
        if (substring.equals("SEKAI{") && substring3.equals("}")) {
            if (!$assertionsDisabled && substring2.length() != length * length) {
                throw new AssertionError();
            }
            if (solve(substring2)) {
                System.out.println("Congratulations, you got the flag!");
                return;
            } else {
                System.out.println("Oops, wrong flag!");
                return;
            }
        }
        System.out.println("Oops, wrong flag!");
    }

    public static String encrypt(char[] cArr, int i) {
        char[] cArr2 = new char[length * 2];
        int i2 = length - 1;
        int i3 = length;
        for (int i4 = 0; i4 < length * 2; i4 = i4 + 1 + 1) {
            int i5 = i2;
            i2--;
            cArr2[i4] = cArr[i5];
            int i6 = i3;
            i3++;
            cArr2[i4 + 1] = cArr[i6];
        }
        for (int i7 = 0; i7 < length * 2; i7++) {
            int i8 = i7;
            cArr2[i8] = (char) (cArr2[i8] ^ ((char) i));
        }
        return String.valueOf(cArr2);
    }

    public static char[] getArray(char[][] cArr, int i, int i2) {
        char[] cArr2 = new char[length * 2];
        int i3 = 0;
        for (int i4 = 0; i4 < length; i4++) {
            cArr2[i3] = cArr[i][i4];
            i3++;
        }
        for (int i5 = 0; i5 < length; i5++) {
            cArr2[i3] = cArr[i2][(length - 1) - i5];
            i3++;
        }
        return cArr2;
    }

    public static char[][] transform(char[] cArr, int i) {
        char[][] cArr2 = new char[i][i];
        for (int i2 = 0; i2 < i * i; i2++) {
            cArr2[i2 / i][i2 % i] = cArr[i2];
        }
        return cArr2;
    }

    public static boolean solve(String str) {
        char[][] transform = transform(str.toCharArray(), length);
        for (int i = 0; i <= length / 2; i++) {
            for (int i2 = 0; i2 < (length - (2 * i)) - 1; i2++) {
                char c = transform[i][i + i2];
                transform[i][i + i2] = transform[((length - 1) - i) - i2][i];
                transform[((length - 1) - i) - i2][i] = transform[(length - 1) - i][((length - 1) - i) - i2];
                transform[(length - 1) - i][((length - 1) - i) - i2] = transform[i + i2][(length - 1) - i];
                transform[i + i2][(length - 1) - i] = c;
            }
        }
        return "oz]{R]3l]]B#50es6O4tL23Etr3c10_F4TD2".equals(encrypt(getArray(transform, 0, 5), 2) + encrypt(getArray(transform, 1, 4), 1) + encrypt(getArray(transform, 2, 3), 0));
    }
}

检查流程

  1. 先看长度是43,再查头尾
  2. 到solve
    1. 转成矩阵,说是矩阵但没用到行列式的任何功能,只能算是数组
    2. 作一个复杂的交换
    3. 分别取出05,14,23加密作异或
    4. 加密先将两行一正一逆交互连一起
    5. 再重排一下

其实就是排来排去,没多大难度,只是比较麻烦

o1 = [5,6,4,7,3,8,2,9,1,10,0,11]
cipher = b"oz]{R]3l]]B#50es6O4tL23Etr3c10_F4TD2"

arr = [0]*36

c1 = [v^2 for v in cipher[:12]]
d1 = [0]*12
for i in range(12):
    d1[o1[i]] = c1[i]
for i in range(6):
    arr[i] = d1[i]
    arr[30+i] = d1[11-i]
    
c1 = [v^1 for v in cipher[12:24]]
d1 = [0]*12
for i in range(12):
    d1[o1[i]] = c1[i]
for i in range(6):
    arr[6+i] = d1[i]
    arr[24+i] = d1[11-i]

c1 = [v for v in cipher[24:]]
d1 = [0]*12
for i in range(12):
    d1[o1[i]] = c1[i]
for i in range(6):
    arr[12+i] = d1[i]
    arr[18+i] = d1[11-i]

o2 = [i for i in range(36)]
for i in range(6):
    for j in range(6-i*2-1):
        c = o2[i*6 + i + j]
        o2[i*6 + i + j] = o2[(5-i -j)*6 + i]
        o2[(5-i -j)*6 + i] = o2[ (5-i)*6 + 5-i -j ]
        o2[ (5-i)*6 + 5-i -j ] = o2[(i+j)*6 + 5-i]
        o2[(i+j)*6 + 5-i] = c 

flag = [0]*36
for i in range(36):
    flag[o2[i]] = arr[i]

print(bytes(flag))
 
#m4tr1x_d3cryP710N_15_Fun_M4T3_@2D2D!
#SEKAI{m4tr1x_d3cryP710N_15_Fun_M4T3_@2D2D!}

2,Matrix_Lib_2

看图标这是个python编译的exe文件,先用python_exe_unpack.py解包,再打开Matrix_Lab文件和struct文件,将struct前16字节加到Matrix_Lab文件的开头,保存扩展名为pyc,然后拿到网上反编译http://tool.lu/pyc/

#!/usr/bin/env python
# visit https://tool.lu/pyc/ for more information
# Version: Python 3.7

print('Welcome to Matrix Lab 2! Hope you enjoy the journey.')
print('Lab initializing...')

try:
    import matlab.engine as matlab
    engine = matlab.engine.start_matlab()
    flag = input('Enter the lab passcode: ').strip()
    outcome = False
    if len(flag) == 23 and flag[:6] == 'SEKAI{' and flag[-1:] == '}':
        A = (lambda .0: [ ord(i) ^ 42 for i in .0 ])(flag[6:-1])   #^42
        B = matlab.double((lambda .0: [ A[i:i + 4] for i in .0 ])(range(0, len(A), 4))) #每4个一组转double
        X = (lambda .0: [ list(map(int, i)) for i in .0 ])(engine.magic(4))
        Y = (lambda .0: [ list(map(int, i)) for i in .0 ])(engine.pascal(4))
        C = (lambda .0: [ (lambda .0: [ None for _ in .0 ])(range(len(X))) for _ in .0 ]
)(range(len(X)))
        for i in range(len(X)):
            for j in range(len(X[i])):
                C[i][j] = X[i][j] + Y[i][j]
            
        
        C = matlab.double(C)
        if engine.mtimes(C, engine.rot90(engine.transpose(B), 1337)) == matlab.double([
            [
                2094,
                2962,
                1014,
                2102],
            [
                2172,
                3955,
                1174,
                3266],
            [
                3186,
                4188,
                1462,
                3936],
            [
                3583,
                5995,
                1859,
                5150]]):
            outcome = True
    if outcome:
        print('Access Granted! Your input is the flag.')
    else:
        print('Access Denied! Your flag: SADGE{aHR0cHM6Ly95b3V0dS5iZS9kUXc0dzlXZ1hjUQ==}')
except:
    print('Unknown error. Maybe you are running the lab in an unsupported environment...')
    print('Your flag: SADGE{ovg.yl/2M6pWQB}')

先是^42,然后每4字节一组转double(这个double可以不管它,最后比较的时候也按整形比较),然后调用matlab的两个函数,magic生成幻方,由于4阶幻方有多个,这里找matlab生成的那一个,后边是pascal在我国叫杨辉三解,这是两个固定的矩阵,最后输入的内容再转置,然后逆转90度(1337%4==1)与C相乘(不是直接乘,是矩阵乘法,这个拿到sage运算)

A = matrix([[2094,2962,1014,2102],
            [2172,3955,1174,3266],
            [3186,4188,1462,3936],
            [3583,5995,1859,5150]])

#magic(4) 4阶幻方
#pascal(4) 帕斯卡矩阵,杨辉三角
X = matrix([[16,2,3,13],
            [5,11,10,8],
     [9,7,6,12],
     [4,14,15,1]])

Y = matrix([[1,1,1,1],
     [1,2,3,4],
     [1,3,6,10],
     [1,4,10,20]])

C = X+Y
B = C.solve_right(A)

#求出B,手工旋转和转置(顺转90+转置=行取反B[::-1]),
B = [[ 26, 103,  25,  11],
     [ 30, 125,  25, 121],
     [ 30, 104,  27,  31],
     [103,  30,  29, 102]]
#转置
print(bytes([v^42 for v in [103,30,29,102,30,104,27,31,30,125,25,121,26,103,25,11]]))
#M47L4B154W3S0M3!
#SEKAI{M47L4B154W3S0M3!}

3,Matrix_Lib_3

这个比较麻烦,WP也没找到,一点点啃

先是读入和判断头尾

  __isoc99_scanf("%64s", command);
  if ( strlen(command) != 64 || strncmp(command, "SEKAI{", 6uLL) || command[63] != 125 )
  {
    puts("Incorrect command format. You cannot unlock the Matrix. :(");
    exit(1);
  }

然后生成一堆矩阵,输入放入8*8矩阵

  A = init(8, command);
  v_A = (vbx_ubyte_t *)vbx_sp_malloc_nodebug(num_elements_80484);
  v_B = (vbx_ubyte_t *)vbx_sp_malloc_nodebug(num_elements_80484);
  v_C = (vbx_ubyte_t *)vbx_sp_malloc_nodebug(num_elements_80484);
  v_D = (vbx_ubyte_t *)vbx_sp_malloc_nodebug(num_elements_80484);
  v_O = (vbx_ubyte_t *)vbx_sp_malloc_nodebug(num_elements_80484);

然后作了一些运算,看着挺难,其实跟进去看很简单就是个按字节的加减和赋值

  vbx_dma_to_vector(v_A, A, num_elements_80484);
  vbx_sync();
  vbx_set_vl(num_elements_80484, 1, 1);
  v15 = 1;
  v16 = 3;
  dest = v_A;
  srcB = v_A;
  vbxsim_SVBBBUUU(VXOR, v_A, 0x13u, v_A);       // v_A^0x13
  v13 = 1;
  v14 = 0;
  v32 = v_B;
  v33 = 0LL;
  vbxsim_SVBBBUUU(VMOV, v_B, 2u, 0LL);          // v_B 填充2
  v11 = 1;
  v12 = 14;
  v30 = v_D;
  v31 = v_A;
  vbxsim_SVBBBUUU(VSGT, v_D, 0x61u, v_A);       // 小于0x61置1
  v9 = 1;
  v10 = 5;
  v27 = v_B;
  srcA = v_B;
  v29 = v_D;
  vbxsim_VVBBBUUU(VSUB, v_B, v_B, v_D);        // B里本来是2现在减1,
  v7 = 1;
  v8 = 8;
  v24 = v_C;
  v25 = v_A;
  v26 = v_B;
  vbxsim_VVBBBUUU(VMUL, v_C, v_A, v_B);         //按字节乘不是矩阵乘 小于0x61的不变,大的*2
  manipulate(v_O, v_C, 8);

然后是另一条线,生成加密用的keys

先生成前16字节

  rng.seed = 0xDEADBEEF;
  for ( i = 0; i <= 15; ++i )
  {
    do
    {
      do
      {
        v3 = gen(&rng);
        key[i] = v3;
      }
      while ( key[i] <= 0x20u );
    }
    while ( key[i] > 0x7Eu );
  }

gen方法是LCG

unsigned int __cdecl gen(RNG *rng)
{
  rng->seed = 110515245 * rng->seed + 114514;
  return HIWORD(rng->seed) & 0x7FFF;
}

然后调用ks将key补充到176字节

__int64 __fastcall ks2(__int64 in, __int64 out)
{
  __int64 i; // rax
  __int64 result; // rax
  int v4; // edx
  int v5; // edx
  bool v6; // zf

  for ( i = 0LL; i != 16; i += 4LL )
    *(_DWORD *)(out + i) = *(_DWORD *)(in + i);
  for ( result = 4LL; result != 44; ++result )
  {
    v4 = *(_DWORD *)(out + 4 * result - 12) ^ __ROR4__(*(_DWORD *)(out + 4 * result - 4), 3);
    v5 = v4 ^ *(_DWORD *)(out + 4 * result - 16) ^ __ROR4__(v4, 1);
    v6 = byte_6573C[result] == 1;
    *(_DWORD *)(out + 4 * result) = v5 ^ 0xFFFFFFFC;
    if ( v6 )
      *(_DWORD *)(out + 4 * result) = v5 ^ 0xFFFFFFFD;
  }
  return result;
}

这块可以跟进去(需要先把反调nop)dump

最后用enc调用encrypt每次加密8字节再比较

encrypt作22轮异或,像XXX一样

__int64 __fastcall encrypt(int *a1, __int64 a2, int a3)
{
  __int64 v3; // r8
  int v4; // ecx
  int v5; // eax
  int v6; // ecx
  __int64 result; // rax

  v3 = 0LL;
  do
  {
    v4 = a1[1];
    v5 = *(_DWORD *)(a2 + 4 * v3) ^ *a1 ^ __ROL4__(v4, 2) ^ __ROL4__(v4, 8) & __ROL4__(v4, 1);
    *a1 = v5;
    v6 = *(_DWORD *)(a2 + 4 * v3 + 4) ^ v4;
    v3 += 2LL;
    result = __ROL4__(v5, 2) ^ v6 ^ (unsigned int)(__ROL4__(v5, 1) & __ROL4__(v5, 8));
    a1[1] = result;
  }
  while ( a3 > (int)v3 );
  return result;
}

然后一点点逆,还好可以直接跟进去每一步都可以验证(主要是matlib那块是猜的得验证对不对)

先生成key

from pwn import u32,p32

#keys
seed = 0xdeadbeef
def gen():
    global seed 
    seed = ((seed * 110515245 + 114514))&0xffffffff
    return (seed>>16) % 32768

key = [0]*16
for i in range(16):
    while True:
        key[i] = gen()%256
        if key[i] >= 33 and key[i] <=126:
            break 

def ror(v,n):
    return ((v>>n)|(v<<(32-n)))& 0xffffffff

def rol(v,n):
    return ((v<<n)|(v>>(32-n)))& 0xffffffff


#key->keys
byte_6573C = '00001101101110101100011001011110000001001000101001110011010000111'
keys = [0]*44
for i in range(4):
    keys[i] = u32(bytes(key[i*4: i*4+4]))
for i in range(4,44):
    v1 = keys[i-3]
    v2 = keys[i-1]
    v3 = keys[i-4]
    
    v4 = v1 ^ ror(v2, 3)
    v5 = v4 ^ v3 ^ ror(v4,1)
    if byte_6573C[i] == '1':
        keys[i] = v5 ^ 0xFFFFFFFD
    else:
        keys[i] = v5 ^ 0xFFFFFFFC

#okeys = b''.join([p32(i) for i in keys])
#print(okeys)

再用keys和secret得到encrypt前的output

secret = [0x1E,0xCB,0x87,0xC1,0xB4,0x76,0x70,0xB9,
          0x99,0xAD,0xDF,0x84,0x1E,0x62,0x25,0x66,
		  0x38,0x50,0x72,0xE3,0xF1,0x5F,0x6C,0x00, 
		  0x0C,0xEF,0xAF,0x94,0xC6,0x03,0xC4,0xB1,
		  0x7F,0x96,0x18,0xB3,0x7F,0x94,0x54,0x0A,
		  0xC7,0xF8,0xC2,0xF1,0x19,0xE5,0xDA,0xBF,
		  0xD7,0x8F,0xCE,0xBB,0x0E,0x7D,0xE8,0xDD,
		  0xC2,0xCA,0x29,0xCB,0xC1,0x23,0x03,0x66]
secret = bytes(secret)

#key 与secret 得到output
s2 = b''
for i in range(0,64,8):
    p1 = u32(secret[i:i+4])
    p2 = u32(secret[i+4:i+8])
    
    for v3 in range(42,-2,-2):
        p2 = p2 ^ keys[v3+1] ^ rol(p1,2) ^ rol(p1,8) & rol(p1,1)
        p1 = p1 ^ keys[v3] ^ rol(p2,2) ^ rol(p2,8) & rol(p2,1)
    
    s2 += p32(p1)+p32(p2)

print(s2.hex())

再转置除2和异或

#3, 转置
s3 = b''
for i in range(8):
    for j in range(8):
        s3 += s2[j*8+i: j*8+i+1]

print(s3.hex())

#2, >0x61 *2
#1, ^0x13
flag = ''
for v in s3:
    if v>= 0x61*2 :
        flag += chr(v//2 ^0x13)
    else:
        flag += chr(v ^0x13)
 
print(flag) 
#SEKAI{y4y_u_p4ss3d_ScR4TcHp4D_t35t_w1th_V3ct0rB10x_4nd_51M0N_xD}

回答: \[1\]根据引用\[1\]中的描述,当将Smarty嵌入到CI框架中时,可能使用的是一个兼容低版本Smarty的引擎,而不是最新的Smarty引擎。这可能是因为参考的文章比较旧,导致整合时选择了兼容低版本的引擎。 \[2\]引用\[2\]中提到,CI框架的类是按需加载的,而不是自动加载的。这导致在全局搜索__destruct方法时可能会出现很多结果,但实际上无法使用。这可能是导致pop链不好找的一个重要原因。 \[3\]引用\[3\]中提到,在phpinfo中发现了一个flag,这表明还有另一种方法可以获取flag,即通过get_the_flag方法。然而,get_the_flag方法有两个过滤条件,一个是不允许出现"<?",另一个是对文件头字节进行过滤。作者提到可以传递一个文本文件,内容进行base64编码,并加上图片头字节,然后再传递一个.htaccess文件进行解码。 综上所述,\[D3CTF 2019\]EasyWeb可能涉及到使用兼容低版本的Smarty引擎、按需加载的类导致pop链难以找到,以及通过绕过过滤条件来获取flag的方法。 #### 引用[.reference_title] - *1* *2* [d3ctf easyweb题解](https://blog.csdn.net/weixin_42474164/article/details/116281650)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [[SUCTF 2019]EasyWeb](https://blog.csdn.net/shinygod/article/details/124045024)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值