hitcon2023 reverse engineering The Blade WP

hitcon2023 reverse The Blade WP

png

Run the given program socket communication related

Described by the title A Rust tool for executing shellcode in a seccomp environment.

String search and positioning found that the verify method is suspicious

It was found that a command flag was hidden. The flag was entered by adding command line parameters. Debugging revealed that the length of the input flag was 64 bits.

F7 enters verify and finds that the encryption logic is simply a large 256-time while loop in which there are several ways to scramble the input through a fixed table, and then a somewhat complicated encryption logic, including remainder, division and XOR, so there is I have an idea for a cool operation. Since the reverse encryption algorithm is more complicated, just find the mapping of 00-ff. Because it is encrypted bitwise, and the previous several shuffles can be regarded as one hit because they are fixed tables. Just find the order of index exchange in random order.

do
  {<!-- -->
     + + v8;
    memcpy(dest, "/", 0x200uLL);
    v9 = 64LL;
    v10 = & amp;dest[1];
    do
    {<!-- -->
      v11 = *(v10 - 1);
      if ( v11 >= 64
        || (v12 = flagg[v9 - 1], flagg[v9 - 1] = flagg[v11], flagg[v11] = v12, v13 = *v10, (unsigned __int64)*v10 > 0x3F) )
      {<!-- -->
LABEL_53:
        core::panicking::panic_bounds_check::h7d0e683548e4cb10();
      }
      v14 = flagg[v9 - 2];
      flagg[v9 - 2] = flagg[v13];
      flagg[v13] = v14;
      v10 + = 2;
      v9 -= 2LL;
    }
    while ( v9 );
    memcpy(dest, & amp;unk_55C3745E9D20, 0x200uLL);
    v15 = 64LL;
    v16 = & amp;dest[1];
    do
    {<!-- -->
      v17 = *(v16 - 1);
      if(v17>63)
        goto LABEL_53;
      v18 = flagg[v15 - 1];
      flagg[v15 - 1] = flagg[v17];
      flagg[v17] = v18;
      v19 = *v16;
      if ( (unsigned __int64)*v16 > 63 )
        goto LABEL_53;
      v20 = flagg[v15 - 2];
      flagg[v15 - 2] = flagg[v19];
      flagg[v19] = v20;
      v16 + = 2;
      v15 -= 2LL;
    }
    while ( v15 );
    memcpy(dest, & amp;unk_55C3745E9F20, 0x200uLL);
    v21 = 64LL;
    v22 = & amp;dest[1];
    do
    {<!-- -->
      v23 = *(v22 - 1);
      if(v23>0x3F)
        goto LABEL_53;
      v24 = flagg[v21 - 1];
      flagg[v21 - 1] = flagg[v23];
      flagg[v23] = v24;
      v25 = *v22;
      if ( (unsigned __int64)*v22 > 0x3F )
        goto LABEL_53;
      v26 = flagg[v21 - 2];
      flagg[v21 - 2] = flagg[v25];
      flagg[v25] = v26;
      v22 + = 2;
      v21 -= 2LL;
    }
    while ( v21 );
    memcpy(dest, & amp;unk_55C3745EA120, 0x200uLL);
    v27 = 64LL;
    v28 = & amp;dest[1];
    do
    {<!-- -->
      v29 = *(v28 - 1);
      if(v29>0x3F)
        goto LABEL_53;
      v30 = flagg[v27 - 1];
      flagg[v27 - 1] = flagg[v29];
      flagg[v29] = v30;
      v31 = *v28;
      if ( (unsigned __int64)*v28 > 0x3F )
        goto LABEL_53;
      v32 = flagg[v27 - 2];
      flagg[v27 - 2] = flagg[v31];
      flagg[v31] = v32;
      v28 + = 2;
      v27 -= 2LL;
    }
    while ( v27 );
    memcpy(dest, & amp;unk_55C3745EA320, 0x200uLL);
    v33 = 64LL;
    v34 = & amp;dest[1];
    do
    {<!-- -->
      v35 = *(v34 - 1);
      if(v35>0x3F)
        goto LABEL_53;
      v36 = flagg[v33 - 1];
      flagg[v33 - 1] = flagg[v35];
      flagg[v35] = v36;
      v37 = *v34;
      if ( (unsigned __int64)*v34 > 0x3F )
        goto LABEL_53;
      v38 = flagg[v33 - 2];
      flagg[v33 - 2] = flagg[v37];
      flagg[v37] = v38;
      v34 + = 2;
      v33 -= 2LL;
    }
    while ( v33 );
    memcpy(dest, & amp;unk_55C3745EA520, 0x200uLL);
    v39 = 64LL;
    v40 = & amp;dest[1];
    do
    {<!-- -->
      v41 = *(v40 - 1);
      if(v41>0x3F)
        goto LABEL_53;
      v42 = flagg[v39 - 1];
      flagg[v39 - 1] = flagg[v41];
      flagg[v41] = v42;
      v43 = *v40;
      if ( (unsigned __int64)*v40 > 0x3F )
        goto LABEL_53;
      v44 = flagg[v39 - 2];
      flagg[v39 - 2] = flagg[v43];
      flagg[v43] = v44;
      v40 + = 2;
      v39 -= 2LL;
    }
    while ( v39 );
    memcpy(dest, & amp;unk_55C3745EA720, 0x200uLL);
    v45 = 64LL;
    v46 = & amp;dest[1];
    do
    {<!-- -->
      v47 = *(v46 - 1);
      if(v47>0x3F)
        goto LABEL_53;
      v48 = flagg[v45 - 1];
      flagg[v45 - 1] = flagg[v47];
      flagg[v47] = v48;
      v49 = *v46;
      if ( (unsigned __int64)*v46 > 0x3F )
        goto LABEL_53;
      v50 = flagg[v45 - 2];
      flagg[v45 - 2] = flagg[v49];
      flagg[v49] = v50;
      v46 + = 2;
      v45 -= 2LL;
    }
    while ( v45 );
    memcpy(dest, & amp;unk_55C3745EA920, 0x200uLL);
    v52 = 64LL;
    v53 = & amp;dest[1];
    do
    {<!-- -->
      v54 = *(v53 - 1);
      if(v54>0x3F)
        goto LABEL_53;
      v55 = flagg[v52 - 1];
      flagg[v52 - 1] = flagg[v54];
      flagg[v54] = v55;
      v56 = *v53;
      if ( (unsigned __int64)*v53 > 0x3F )
        goto LABEL_53;
      v57 = flagg[v52 - 2];
      flagg[v52 - 2] = flagg[v56];
      flagg[v56] = v57;
      v53 + = 2;
      v52 -= 2LL;
    }
    while ( v52 );
    v58 = 0LL;
    do
    {<!-- -->
      v59 = (unsigned __int8)flagg[v58] + 1;
      LOWORD(v51) = 1;
      LOWORD(v52) = 257;
      v60 = 0;
      do
      {<!-- -->
        v62 = v52;
        LOWORD(v52) = (unsigned __int16)v52 / (unsigned __int16)v59;
        v61 = v62 % (unsigned __int16)v59;
        v63 = v51;
        v51 = v60 - v51 * v52;
        LODWORD(v52) = v59;
        v59 = (unsigned __int16)(v62 % (unsigned __int16)v59);
        v60 = v63;
      }
      while ( v61 );
      v64 = 0;
      if ( (__int16)v63 > 0 )
        v64 = v63;
      flagg[v58] = ((unsigned __int16)(v64 + ((__int16)v63 >> 15) - v63) / 0x101u
                   + v63
                   + ((unsigned __int16)v63 >> 15)
                   + 113) ^ 0x89;
      v52 = v58 + 1;
      v58 = v52;
    }
    while ( v52 != 64 );
  }
  while (v8 != 256);//The total encryption extracted

If nothing else, the 64-bit comparison data is cmp=[0xA7, 0x51, 0x68, 0x52, 0x85, 0x27, 0xFF, 0x31, 0x88, 0x87, 0xD2, 0xC7, 0xD3, 0x23, 0x3F, 0x52, 0x55, 0x10, 0x1F, 0xAF, 0x27, 0xF0, 0x94, 0x5C, 0xCD, 0x3F, 0x7A, 0x79, 0x9F, 0x2F, 0xF0, 0xE7, 0x45, 0xF0, 0x86, 0x3C, 0xF9, 0xB0, 0xEA, 0x6D , 0x90, 0x42, 0xF7, 0x91, 0xED, 0x3A, 0x9A, 0x7C, 0x01, 0x6B, 0x84, 0xDC, 0x6C, 0xC8, 0x43, 0x07, 0x5C, 0x08, 0xF7, 0xDF, 0xEB, 0xE3, 0xAE, 0xA4] (that is, these 64 bits 4 xmmwords )

Test data 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ{}

Original order0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ{}
The order of breakpoints must be scrambledHfVl{qPcCYNMoRi6D7Jr}espOL3FhwdWAtTGZba4Ugjvnx1QkKE2IS9yuz5BX08m

Character encryption logic:

do
    {
      v59 = (unsigned __int8)flagg[v58] + 1;
      LOWORD(v51) = 1;
      LOWORD(v52) = 257;
      v60 = 0;
      do
      {
        v62 = v52;
        LOWORD(v52) = (unsigned __int16)v52 / (unsigned __int16)v59;
        v61 = v62 % (unsigned __int16)v59;
        v63 = v51;
        v51 = v60 - v51 * v52;
        LODWORD(v52) = v59;
        v59 = (unsigned __int16)(v62 % (unsigned __int16)v59);
        v60 = v63;
      }
      while ( v61 );
      v64 = 0;
      if ( (__int16)v63 > 0 )
        v64 = v63;
      flagg[v58] = ((unsigned __int16)(v64 + ((__int16)v63 >> 15) - v63) / 0x101u
                   + v63
                   + ((unsigned __int16)v63 >> 15)
                   + 113) ^ 0x89;
      v52 = v58 + 1;
      v58 = v52;
    }
    while ( v52 != 64 );

Gotta decrypt script

def decrypt(data: list, table: dict):
    tmp=[]
    for i in data:
        tmp.append(table[i])
    return tmp

def reverse_order(data:list, table: list):
    tmp = []
    for i in range(64):
        tmp.append(data[table[i]])
    return tmp

#get crypto table
s0_255 = [0xFB, 0x7B, 0x4E, 0xBB, 0x51, 0x15, 0x8D, 0xDB, 0xB0, 0xAC, 0xA5, 0x8E, 0xAA, 0xB2, 0x60, 0xEB, 0x63, 0x5C, 0xDE, 0x42, 0x2B, 0xC6, 0xA6, 0x35 , 0x30, 0x43, 0xD6, 0x5F, 0xBD, 0x24, 0xB1, 0xE3, 0x8C, 0xA7, 0xD5, 0x2A, 0x7C, 0x6D, 0x8B, 0x17, 0x9D, 0x83, 0xFE, 0x69, 0 x10, 0x59, 0xA9, 0x9E, 0x0F , 0x1C, 0x66, 0x97, 0x5B, 0x61, 0xED, 0xAD, 0xE0, 0xDA, 0x27, 0x06, 0x25, 0xDC, 0x5E, 0xE7,
        0x41, 0x32, 0xD2, 0xD9, 0x8F, 0xEE, 0xAF, 0x03, 0x93, 0x3A, 0x00, 0xA2, 0xE1, 0xB3, 0xEC, 0x81, 0x9F, 0xCA, 0x58, 0xB7, 0x79, 0xFD, 0x3B, 0xA0, 0x02, 0x0C, 0xCB, 0xA8, 0x80, 0xC0, 0x16, 0x4D, 0x2F, 0x75, 0x71, 0x0A, 0x04, 0x39, 0xFF, 0xC1, 0x9C, 0xAB, 0xEF, 0xA4, 0xD8, 0xE2, 0x14, 0xC2, 0x6C, 0x64, 0x1E, 0x6B, 0x7E, 0x99, 0x2E, 0x09, 0x0B, 0x86, 0x74, 0x6A, 0xC4, 0x2D, 0x4F, 0xF9,
        0xFA, 0x94, 0xB6, 0x1F, 0x89, 0x6F, 0x5D, 0xE8, 0xEA, 0xB5, 0x5A, 0x65, 0x88, 0xC5, 0x7F, 0x77, 0x11, 0xCF, 0xF1, 0x1B, 0x3F , 0xF4, 0x48, 0x47, 0x12, 0xE4, 0xBA, 0xDF, 0xE9, 0x62, 0x6E, 0xB4, 0x96, 0xCD, 0x13, 0x53, 0x4B, 0x28, 0xD7, 0xD1, 0x33, 0xB8, 0xE6, 0x7A, 0x2C, 0x9B , 0x29, 0x44, 0x52, 0xF7, 0x20, 0xF2, 0x31, 0xD3, 0xB9, 0x40, 0xD0, 0x34, 0xF5, 0x54, 0x1A, 0x01, 0xA1, 0x92,
        0xFC, 0x85, 0x07, 0xBE, 0xDD, 0xBC, 0x19, 0xF3, 0x36, 0xF6, 0x72, 0x98, 0x4C, 0x7D, 0xC7, 0xD4, 0x45, 0x4A, 0x9A, 0xC3, 0x8A, 0xE5, 0x50, 0x46, 0xCC, 0x68, 0x76, 0x67, 0xC9, 0x0E, 0x3C, 0x57, 0xF0, 0x22, 0xBF, 0x26, 0x84, 0x0D, 0x90, 0xA3, 0xAE, 0x3D, 0x1D, 0xC8, 0x91, 0x0 5, 0x87, 0x70, 0x08, 0x73, 0x21, 0x49, 0x55, 0x3E, 0x37, 0x23, 0x18, 0x56, 0xCE, 0x82, 0x38, 0x95, 0x78, 0xF8]
load_data = [0xA7, 0x51, 0x68, 0x52, 0x85, 0x27, 0xFF, 0x31, 0x88, 0x87, 0xD2, 0xC7, 0xD3, 0x23, 0x3F, 0x52, 0x55, 0x10, 0x1F, 0xAF, 0x27, 0xF0, 0x94, 0x5C , 0xCD, 0x3F, 0x7A, 0x79, 0x9F, 0x2F, 0xF0, 0xE7, 0x45, 0xF0, 0x86, 0x3C, 0xF9, 0xB0, 0xEA, 0x6D, 0x90, 0x42, 0xF7, 0x91, 0 xED, 0x3A, 0x9A, 0x7C, 0x01 , 0x6B, 0x84, 0xDC, 0x6C, 0xC8, 0x43, 0x07, 0x5C, 0x08, 0xF7, 0xDF, 0xEB, 0xE3, 0xAE, 0xA4]
crypto_table = dict(zip(s0_255,range(0x100)))

#get order table
source = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ{}'
replaced = 'HfVl{qPcCYNMoRi6D7Jr}espOL3FhwdWAtTGZba4Ugjvnx1QkKE2IS9yuz5BX08m'
revsere_table = [] #index is the subscript of source in replace
for i in source:
    revsere_table.append(replaced.find(i))

tmp = [0x52, 0xCB, 0x15, 0x10, 0x7E, 0xD3, 0x78, 0x26, 0xC2, 0x14, 0x09, 0x50, 0x55, 0xFA, 0xEE, 0xC3, 0x0A, 0x97, 0xB9, 0x38, 0x12, 0x3D, 0x0E, 0xE9 , 0xBE, 0xF6, 0x2B, 0x66, 0x67, 0xA8, 0x87, 0xAE, 0x1D, 0x53, 0x62, 0xEC, 0xFC, 0x5C, 0x88, 0x68, 0x23, 0x5B, 0x36, 0x13, 0xFB , 0xD7, 0xCA, 0x7A, 0xBD , 0xD9, 0x69, 0x6A, 0xE4, 0x2A, 0x6C, 0x9D, 0x86, 0xE0, 0xA4, 0x01, 0xBA, 0x3B, 0x20, 0x92]
for i in range(256):
    tmp = decrypt(tmp, crypto_table)
    tmp = reverse_order(tmp, revsere_table)
print(''.join(chr(i) for i in tmp))
#0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ{}
#(The value of tmp is the test data dumped after complete shuffling and encryption, not the final cmp_data)

The above script is used for decryption sorting + encryption

But using this script to decrypt the comparison data is garbled, indicating that it is either comparing data and operations, or input and operations.

Continue to look at the back and compare the logic.

 like_i = 4LL;
  do
  {<!-- -->
    v77 = v84; // 0xff
    if (v84 < 205)
      goto LABEL_74;
    *(_DWORD *)v78 = *(_DWORD *)((char *)dest + like_i);
    v79 = v82;
    v82[204] = input[like_i];
    if (v77 < 224)
      goto LABEL_74;
    v79[223] = v78[0];
    v79[205] = input[like_i + 1];
    if ( v77 == 224 )
      goto LABEL_74;
    v79[224] = v78[1];
    v79[206] = input[like_i + 2];
    if (v77 < 226)
      goto LABEL_74;
    v79[225] = v78[2];
    v79[207] = input[like_i + 3];
    if ( v77 == 226 )
      goto LABEL_74;
    v79[226] = v78[3];
    _$LT$$RF$std..net..tcp..TcpStream$u20$as$u20$std..io..Write$GT$::write::h0bbfc2d1fa700c7a();
    if ( v81[0] )
      goto LABEL_50;
    v81[0] = 0LL;
    v75 = std::io::default_read_exact::h61fb53e2a02eb302( & amp;v85, v81, 8LL);
    if(v75)
      goto LABEL_55;
    if ( !v81[0] )
      goto LABEL_70;
    v80 = (unsigned __int64)(like_i + 1) <= 60;
    like_i + = 4LL;
  }
  while ( v80 );

Each time, 4 bytes of input and 4 bytes of comparison data are assigned, but I don’t know where they went.

And v79[204] v79[205] v79[206] v79[207] is a fixed position in the stack. Why is the subscript strange?

This can be found by looking for cross-references

v79 is the initial 255 bytes long shellcode

Some IDA recognitions are not accurate. View in assembly

.rodata:000055E808DA1B2B sub_55E808DA1B2B proc near ; DATA XREF: seccomp_shell::shell::verify::h898bf5fa26dafbab + 44D↑o
.rodata:000055E808DA1B2B push rsp
.rodata:000055E808DA1B2C pop rbp
.rodata:000055E808DA1B2D xor esi, esi ; flags
.rodata:000055E808DA1B2F mov rcx, 379F3A62B80657A1h
.rodata:000055E808DA1B39 mov rdx, 37F7494DD66F358Eh
.rodata:000055E808DA1B43 xor rcx, rdx
.rodata:000055E808DA1B46 push rcx
.rodata:000055E808DA1B47 push rsp
.rodata:000055E808DA1B48 pop rdi; filename
.rodata:000055E808DA1B49 push 2
.rodata:000055E808DA1B4B pop rax
.rodata:000055E808DA1B4C cdq; mode
.rodata:000055E808DA1B4D syscall; LINUX - sys_open
.rodata:000055E808DA1B4F xchg rax, rdi; fd
.rodata:000055E808DA1B51 xor eax, eax
.rodata:000055E808DA1B53 push rax
.rodata:000055E808DA1B54 push rsp
.rodata:000055E808DA1B55 pop rsi ; buf
.rodata:000055E808DA1B56 push 4
.rodata:000055E808DA1B58 pop rdx; count
.rodata:000055E808DA1B59 syscall; LINUX - sys_read
.rodata:000055E808DA1B5B pop r12
.rodata:000055E808DA1B5D push 3
.rodata:000055E808DA1B5F pop rax
.rodata:000055E808DA1B60 syscall; LINUX - sys_close
.rodata:000055E808DA1B62 xor esi, esi ; flags
.rodata:000055E808DA1B64 mov rcx, 0AAC06463C36F3B3Bh
.rodata:000055E808DA1B6E mov rdx, 0AAC06463C30B4C48h
.rodata:000055E808DA1B78 xor rcx, rdx
.rodata:000055E808DA1B7B push rcx
.rodata:000055E808DA1B7C mov rcx, 7DA9F8D67582578Ch
.rodata:000055E808DA1B86 mov rdx, 0EC888F916F632A3h
.rodata:000055E808DA1B90 xor rcx, rdx
.rodata:000055E808DA1B93 push rcx
.rodata:000055E808DA1B94 push rsp
.rodata:000055E808DA1B95 pop rdi; filename
.rodata:000055E808DA1B96 push 2
.rodata:000055E808DA1B98 pop rax
.rodata:000055E808DA1B99 cdq; mode
.rodata:000055E808DA1B9A syscall; LINUX - sys_open
.rodata:000055E808DA1B9C xchg rax, rdi ; fd
.rodata:000055E808DA1B9E xor eax, eax
.rodata:000055E808DA1BA0 push rax
.rodata:000055E808DA1BA1 push rsp
.rodata:000055E808DA1BA2 pop rsi ; buf
.rodata:000055E808DA1BA3 push 4
.rodata:000055E808DA1BA5 pop rdx; count
.rodata:000055E808DA1BA6 syscall; LINUX - sys_read
.rodata:000055E808DA1BA8 pop r13
.rodata:000055E808DA1BAA push 3
.rodata:000055E808DA1BAC pop rax
.rodata:000055E808DA1BAD syscall; LINUX - sys_close
.rodata:000055E808DA1BAF xor esi, esi ; flags
.rodata:000055E808DA1BB1 push 6Fh ; 'o'
.rodata:000055E808DA1BB3 mov rcx, 77D9F62D0C06E559h
.rodata:000055E808DA1BBD mov rdx, 5BC8C027A638176h
.rodata:000055E808DA1BC7 xor rcx, rdx
.rodata:000055E808DA1BCA push rcx
.rodata:000055E808DA1BCB push rsp
.rodata:000055E808DA1BCC pop rdi; filename
.rodata:000055E808DA1BCD push 2
.rodata:000055E808DA1BCF pop rax
.rodata:000055E808DA1BD0 cdq; mode
.rodata:000055E808DA1BD1 syscall; LINUX - sys_open
.rodata:000055E808DA1BD3 xchg rax, rdi; fd
.rodata:000055E808DA1BD5 xor eax, eax
.rodata:000055E808DA1BD7 push rax
.rodata:000055E808DA1BD8 push rsp
.rodata:000055E808DA1BD9 pop rsi ; buf
.rodata:000055E808DA1BDA push 4
.rodata:000055E808DA1BDC pop rdx; count
.rodata:000055E808DA1BDD syscall; LINUX - sys_read
.rodata:000055E808DA1BDF pop rax
.rodata:000055E808DA1BE0 not rax
.rodata:000055E808DA1BE3 shr rax, 1Dh
.rodata:000055E808DA1BE7 cqo
.rodata:000055E808DA1BE9 push 29h ; ')'
.rodata:000055E808DA1BEB pop rcx
.rodata:000055E808DA1BEC div rcx
.rodata:000055E808DA1BEF xchg rax, r14
.rodata:000055E808DA1BF1 push 3
.rodata:000055E808DA1BF3 pop rax
.rodata:000055E808DA1BF4 syscall; LINUX - sys_close
.rodata:000055E808DA1BF6 mov eax, 267814C2h
.rodata:000055E808DA1BFB add eax, r12d
.rodata:000055E808DA1BFE xor eax, r13d
.rodata:000055E808DA1C01 ror eax, 0Bh
.rodata:000055E808DA1C04 not eax
.rodata:000055E808DA1C06 xor eax, r14d
.rodata:000055E808DA1C09 cmp eax, 31FF2788h
.rodata:000055E808DA1C0E jnz short loc_55E808DA1C15
.rodata:000055E808DA1C0E
.rodata:000055E808DA1C10 push 1
.rodata:000055E808DA1C12 pop rax
.rodata:000055E808DA1C13 jmp short loc_55E808DA1C18
.rodata:000055E808DA1C13
.rodata:000055E808DA1C15; ------------------------------------------------ ----------------------------------
.rodata:000055E808DA1C15
.rodata:000055E808DA1C15 loc_55E808DA1C15: ; CODE XREF: sub_55E808DA1B2B + E3↑j
.rodata:000055E808DA1C15 xor rax, rax
.rodata:000055E808DA1C15
.rodata:000055E808DA1C18
.rodata:000055E808DA1C18 loc_55E808DA1C18: ; CODE XREF: sub_55E808DA1B2B + E8↑j
.rodata:000055E808DA1C18 push rax
.rodata:000055E808DA1C19 push rbx
.rodata:000055E808DA1C1A pop rdi; fd
.rodata:000055E808DA1C1B push rsp
.rodata:000055E808DA1C1C pop rsi ; buf
.rodata:000055E808DA1C1D push 8
.rodata:000055E808DA1C1F pop rdx; count
.rodata:000055E808DA1C20 push 1
.rodata:000055E808DA1C22 pop rax
.rodata:000055E808DA1C23 syscall; LINUX - sys_write
.rodata:000055E808DA1C25 push rbp
.rodata:000055E808DA1C26 pop rsp
.rodata:000055E808DA1C27 jmp r15
.rodata:000055E808DA1C27
.rodata:000055E808DA1C27 sub_55E808DA1B2B endp
.rodata:000055E808DA1C27

I found that the previously assigned input and comparison data came here.

add eax, r12d #First add eax and r12d
xor eax, r13d #Then perform XOR operation on eax and r13d
ror eax, 0Bh #Then rotate eax 11 bits to the right
not eax#Then negate eax
xor eax, r14d# Then perform XOR operation on eax and r14d

The key is the above assembly instructions, which also operate on the input.

The key is to find the values of r12 r13 r14

But this shellcode seems not to be executed in the program (maybe because I don’t know how to adjust it)

Moreover, this program seems to simply verify whether the input on the server side and the client side are the same, which is a bit confusing.

(There is a std::io::default_read_exact that is accepted from the client. This can also be found when the IDA dynamic debugging gets stuck)

Analysis of the shellcode shows that r12 r13 r14 is a fixed value.

Analysis method: Just find a simple program and use IDApatch to turn it into shellcode and execute it.

Directly use lazyIDA paste data

You can find out what the fixed values of r12 r13 r14 are. Then you can directly write the exp script to solve the flag and it will be OK.

import numpy as np
def decrypt(data: list, table: dict):
    tmp=[]
    for i in data:
        tmp.append(table[i])
    return tmp

def reverse_order(data:list, table: list):
    tmp = []
    for i in range(64):
        tmp.append(data[table[i]])
    return tmp



#Define a function that accepts one parameter and returns the decrypted value
def de_cmp(x):
    # Define the values of r12, r13, r14
    r12 = 0x0000000464C457F
    r13 = 0x0000000746F6F72
    r14 = 0x000000031F3831F
    # Convert x to an unsigned 32-bit integer
    x = np.uint32(x)
    # XOR x with r14
    x = np.uint32(x ^ r14)
    # Negate x
    x = np.uint32(~x)
    # Rotate x 11 places to the left
    x = np.uint32((x << 11) | (x >> (32 - 11)))
    # XOR x with r13
    x = np.uint32(x ^ r13)
    # Subtract r12 from x
    x = np.uint32(x - r12)
    # Print the hexadecimal representation of x
    print(hex(x))
    # Convert x to a signed 32-bit integer
    x = np.int32(x)
    # Return four bytes of x
    return [x & 0xff, (x & 0xff00) >> 8, (x & 0xff0000) >> 16, (x & 0xff000000) >> 24]
#get crypto table
s0_255 = [0xFB, 0x7B, 0x4E, 0xBB, 0x51, 0x15, 0x8D, 0xDB, 0xB0, 0xAC, 0xA5, 0x8E, 0xAA, 0xB2, 0x60, 0xEB, 0x63, 0x5C, 0xDE, 0x42, 0x2B, 0xC6, 0xA6, 0x35 , 0x30, 0x43, 0xD6, 0x5F, 0xBD, 0x24, 0xB1, 0xE3, 0x8C, 0xA7, 0xD5, 0x2A, 0x7C, 0x6D, 0x8B, 0x17, 0x9D, 0x83, 0xFE, 0x69, 0 x10, 0x59, 0xA9, 0x9E, 0x0F , 0x1C, 0x66, 0x97, 0x5B, 0x61, 0xED, 0xAD, 0xE0, 0xDA, 0x27, 0x06, 0x25, 0xDC, 0x5E, 0xE7,
        0x41, 0x32, 0xD2, 0xD9, 0x8F, 0xEE, 0xAF, 0x03, 0x93, 0x3A, 0x00, 0xA2, 0xE1, 0xB3, 0xEC, 0x81, 0x9F, 0xCA, 0x58, 0xB7, 0x79, 0xFD, 0x3B, 0xA0, 0x02, 0x0C, 0xCB, 0xA8, 0x80, 0xC0, 0x16, 0x4D, 0x2F, 0x75, 0x71, 0x0A, 0x04, 0x39, 0xFF, 0xC1, 0x9C, 0xAB, 0xEF, 0xA4, 0xD8, 0xE2, 0x14, 0xC2, 0x6C, 0x64, 0x1E, 0x6B, 0x7E, 0x99, 0x2E, 0x09, 0x0B, 0x86, 0x74, 0x6A, 0xC4, 0x2D, 0x4F, 0xF9,
        0xFA, 0x94, 0xB6, 0x1F, 0x89, 0x6F, 0x5D, 0xE8, 0xEA, 0xB5, 0x5A, 0x65, 0x88, 0xC5, 0x7F, 0x77, 0x11, 0xCF, 0xF1, 0x1B, 0x3F , 0xF4, 0x48, 0x47, 0x12, 0xE4, 0xBA, 0xDF, 0xE9, 0x62, 0x6E, 0xB4, 0x96, 0xCD, 0x13, 0x53, 0x4B, 0x28, 0xD7, 0xD1, 0x33, 0xB8, 0xE6, 0x7A, 0x2C, 0x9B , 0x29, 0x44, 0x52, 0xF7, 0x20, 0xF2, 0x31, 0xD3, 0xB9, 0x40, 0xD0, 0x34, 0xF5, 0x54, 0x1A, 0x01, 0xA1, 0x92,
        0xFC, 0x85, 0x07, 0xBE, 0xDD, 0xBC, 0x19, 0xF3, 0x36, 0xF6, 0x72, 0x98, 0x4C, 0x7D, 0xC7, 0xD4, 0x45, 0x4A, 0x9A, 0xC3, 0x8A, 0xE5, 0x50, 0x46, 0xCC, 0x68, 0x76, 0x67, 0xC9, 0x0E, 0x3C, 0x57, 0xF0, 0x22, 0xBF, 0x26, 0x84, 0x0D, 0x90, 0xA3, 0xAE, 0x3D, 0x1D, 0xC8, 0x91, 0x0 5, 0x87, 0x70, 0x08, 0x73, 0x21, 0x49, 0x55, 0x3E, 0x37, 0x23, 0x18, 0x56, 0xCE, 0x82, 0x38, 0x95, 0x78, 0xF8]
#load_data = [0xA7, 0x51, 0x68, 0x52, 0x85, 0x27, 0xFF, 0x31, 0x88, 0x87, 0xD2, 0xC7, 0xD3, 0x23, 0x3F, 0x52, 0x55, 0x10, 0x1F, 0xAF , 0x27, 0xF0, 0x94, 0x5C, 0xCD, 0x3F, 0x7A, 0x79, 0x9F, 0x2F, 0xF0, 0xE7, 0x45, 0xF0, 0x86, 0x3C, 0xF9, 0xB0, 0xEA, 0x6D, 0x90, 0x42, 0xF7, 0x9 1, 0xED, 0x3A, 0x9A, 0x7C, 0x01, 0x6B, 0x84, 0xDC, 0x6C, 0xC8, 0x43, 0x07, 0x5C, 0x08, 0xF7, 0xDF, 0xEB, 0xE3, 0xAE, 0xA4]
crypto_table = dict(zip(s0_255,range(0x100)))

#get order table
source = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ{}'
replaced = 'HfVl{qPcCYNMoRi6D7Jr}espOL3FhwdWAtTGZba4Ugjvnx1QkKE2IS9yuz5BX08m'
revsere_table = [] #index is the subscript of source in replace
for i in source:
    revsere_table.append(replaced.find(i))



cmp_data = [0x526851A7, 0x31FF2785, 0xC7D28788, 0x523F23D3, 0xAF1F1055, 0x5C94F027, 0x797A3FCD, 0xE7F02F9F, 0x3C86F045, 0x6DEAB0F9, 0 x91F74290, 0x7C9A3AED, 0xDC846B01, 0x0743C86C, 0xDFF7085C, 0xA4AEE3EB]
tmp = []
for i in cmp_data:
    tmp + =de_cmp(i)
print(tmp)

for i in range(256):
    tmp = decrypt(tmp, crypto_table)
    tmp = reverse_order(tmp, revsere_table)
print(''.join(chr(i) for i in tmp))

The bit operations of python and C language are different (for the processing of sign bits), so you need to use the np library

Run to get the flag

flag is a music website, it can also be regarded as a little easter egg.

Summary

The encryption algorithm is too bad and cannot be reversed. It uses a violent method, which is equivalent to a table of 00-ff. I feel that this can be done if we encounter complex operations in the future but they are bit-wise encrypted?