分类 XCTF 下的文章

ez_cython

from regadgets import *
ans = [4108944556, 3404732701, 1466956825, 788072761, 1482427973, 782926647, 
       1635740553, 4115935911, 2820454423, 3206473923, 1700989382, 2460803532,
       2399057278, 968884411, 1298467094, 1786305447, 3953508515, 2466099443,
       4105559714, 779131097, 288224004, 3322844775, 4122289132, 2089726849, 
       656452727, 3096682206, 2217255962, 680183044, 3394288893, 697481839, 
       1109578150, 2272036063]

def xxtea_sctf2024_shift(z, y, sum, k, p, debug = False):
    e = (sum.value >> 3) & 3
    PE = (p & 2) ^ e
    Ly = y.value << 3
    Ry = y.value >> 4
    Lz = z.value << 2
    Rz = z.value >> 3 

    LzRy = Rz ^ Ly
    LyRz = Ry ^ Lz
    SY = sum.value ^ y.value
    K = k[PE].value
    KZ = K ^ z.value
    result = (LzRy + LyRz) ^ (KZ + SY)
    return result
        
key = [0x53, 0x79, 0x43, 0x31] # Syc1

dec = xxtea_decrypt(ans, key, delta=0x9e3779b9, round_base=4, round_addi=60, shift_func=xxtea_sctf2024_shift)
print(bytes(dec))
# b'SCTF{w0w_y0U_wE1_kNOw_of_cYtH0N}'

BBox

Android
超绝一键apk混淆捏

const char *__fastcall Java_com_example_bbandroid_MainActivity_checkFlag(_JNIEnv *a1, __int64 a2, __int64 a3)
{
  time_t v5; // w22
  const char *result; // x0
  const char *v7; // x21
  unsigned int v8; // w23
  signed int v9; // w22
  __int64 v10; // x19
  __int64 v11; // x20
  char *v12; // x22
  char v13; // w0
  int v14; // w9
  signed int v15; // w8
  unsigned __int64 v16; // x10
  unsigned __int64 v17; // x11
  int v18; // w12
  int v19; // w13
  char flag[256]; // [xsp+8h] [xbp-108h] BYREF
  __int64 v21; // [xsp+108h] [xbp-8h]

  v21 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
  v5 = time(0LL);
  result = (const char *)((__int64 (__fastcall *)(_JNIEnv *, __int64, _QWORD))a1->functions->GetStringUTFChars)(
                           a1,
                           a3,
                           0LL);
  if ( result )
  {
    v7 = result;
    v8 = v5 / 1000000 / 100;
    strncpy(flag, result, 0xFFu);
    flag[255] = 0;
    v9 = __strlen_chk(flag, 0x100u);
    ((void (__fastcall *)(_JNIEnv *, __int64, const char *))a1->functions->ReleaseStringUTFChars)(a1, a3, v7);
    srand(v8);
    if ( v9 >= 4 )
    {
      v10 = 0LL;
      v11 = (unsigned int)v9 >> 2;
      do
      {
        v12 = &flag[4 * v10];
        *v12 ^= rand();
        v12[1] ^= rand();
        v12[2] ^= rand();
        v13 = rand();
        v14 = 32;
        v12[3] ^= v13;
        v15 = *(_DWORD *)v12;
        do
        {
          if ( v15 >= 0 )
            v15 *= 2;
          else
            v15 = (2 * v15) ^ 0x85B6874F;
          --v14;
        }
        while ( v14 );
        *(_DWORD *)&flag[4 * v10++] = v15;
      }
      while ( v10 != v11 );
    }
    if ( flag[0] == '3' )
    {
      v16 = 0LL;
      do
      {
        v17 = v16;
        if ( v16 == 0x27 )
          break;
        v18 = (unsigned __int8)flag[v16 + 1];
        v19 = dword_B14[++v16];
      }
      while ( v18 == v19 );
      return (const char *)(v17 > 0x26);
    }
    else
    {
      return 0LL;
    }
  }
  return result;
}

Exp

from regadgets import *
from z3 import *
from copy import deepcopy
enc =  [0x33, 0xC0, 0xC8, 0xA3, 0xF3, 0xBF, 0x1D, 0x1A, 0x3B, 0x41, 
  0xB7, 0xC6, 0xF1, 0x5E, 0x86, 0x52, 0x52, 0xCF, 0x6B, 0x1E, 
  0xC5, 0xF9, 0xCB, 0xBF, 0xED, 0x7B, 0x62, 0xF1, 0xF7, 0x43, 
  0x48, 0x54, 0xFB, 0x85, 0x4C, 0xD9, 0x35, 0x30, 0xF2, 0x6E]
print(bytes(enc))
dw = byte2dword(enc)

print(dw)
s = Solver()
x = [BitVec(f"x{i}", 32) for i in range(len(dw))]
y = deepcopy(x)
for i in range(len(dw)):
    for j in range(32):
        x[i] = If(LShR(x[i], 31) == 1, 2*x[i] ^ 0x85b6874f, 2*x[i])
    s.add(x[i] == dw[i])
print(s.check())
m = s.model()
r = []
for i in y:
    r.append(m[i].as_long())
print(r)
randv = [0x49308bb9,0x3cb3ad,0xfb4e87f,0x75655103,0x6d505b9f,0x1d20580f,
         0xdcf4af1,0x3e381967,0x54bcf579,0x73c09db7,0x501b2039,0x1b8950dd,
         0x23e73393,0x2b480a88,0x6818cdae,0x61d009ea,0x44c0c5b0,0x385aff3d,
         0x5cfb2a7a,0x587f9c07,0x158172f2,0x4d334c89,0x302b76e5,0x5e17f434,
         0x692de923,0x806d155,0x3d2c61d8,0x1d09ef4e,0x7c3d83b7,0x1d7621da,
         0x2dc0a3ec,0x456e0f71,0x1db2d588,0x3d758c6c,0x3ad36074,0xb033127,0x5a95e47b,0x48a2ab65,0x493b4a8e,0x2f52d9f5 ]
randv = [i & 0xff for i in randv]

v1 = bxor(dword2byte(r)[:len(enc)], bytes(randv)[:len(enc)])
v2 = bxor_cycle(v1, b'\x1e').decode()
print(decode_b64(v2, "nopqrstDEFGHIJKLhijklUVQRST/WXYZabABCcdefgmuv6789+wxyz012345MNOP"))
# b'Y0u_@re_r1ght_r3ver53_is_easy!'

其中 randv 是通过hook得到的rand()的结果。中间是一个不安全的CRC。

easyMCU

我自认为这是一个逆向题目,所以把它放到了Reverse下。
首先,使用010Editor,打开mcu.s19,发现它直接自动给我转hex了,我们Ctrl+S保存到文件即可。
隔壁队伍使用的是bincopy convert mcu.s19 -o binary out.bin进行的转换,也是一种方法。

通过图片中的TriCore,我们可以知道固件是Tri
经过翻找,我们找到关键代码


/* WARNING: Globals starting with '_' overlap smaller symbols at the same address */

undefined4 FUN_80000690(void)

{
  byte bVar1;
  bool bVar2;
  uint uVar3;
  undefined4 uVar4;
  int i;
  
  bVar2 = FUN_8000125a(0x6000009c,(undefined *)0x60000004,(short *)0x60000000,_DAT_80003990,
                       iRam80003994);
  if (bVar2) {
    AES_ENCRYPT(0x60000004,-0x7fffc65d,0x6000007c,0x20);
    for (i = 0; i < 0x20; i += 1) {
      uVar3 = rol((uint)*(byte *)(i + 0x6000007c),3);
      *(char *)(i + 0x6000007c) = (char)uVar3;
      bVar1 = bRam6000007c;
      if (i < 0x1f) {
        bVar1 = *(byte *)(i + 0x6000007d);
      }
      *(byte *)(i + 0x6000007c) = bVar1 ^ *(byte *)(i + 0x6000007c);
      *(byte *)(i + 0x6000007c) = *(byte *)(i + 0x6000007c) ^ 0xff;
    }
    FUN_80001278((int *)0x6000009c,(undefined *)0x6000007c,(short *)0x60000000,_DAT_80003990,
                 iRam80003994);
    uVar4 = 0;
  }
  else {
    uVar4 = 0xffffffff;
  }
  return uVar4;
}


void AES_ENCRYPT(int param_1,int param_2,int param_3,uint param_4)

{
  uint uVar1;
  undefined8 auStack_c0 [2];
  undefined key [176];
  
  FUN_800002b0(param_2,(int)key);
  for (uVar1 = 0; uVar1 < param_4; uVar1 += 0x10) {
    FUN_80003782(auStack_c0,(undefined8 *)(param_1 + uVar1),0x10);
    aes((int)auStack_c0,(int)key);
    FUN_80003782((undefined8 *)(param_3 + uVar1),auStack_c0,0x10);
  }
  return;
}


void AES_ENCRYPT(int param_1,int param_2,int param_3,uint param_4)

{
  uint uVar1;
  undefined8 auStack_c0 [2];
  undefined key [176];
  
  FUN_800002b0(param_2,(int)key);
  for (uVar1 = 0; uVar1 < param_4; uVar1 += 0x10) {
    FUN_80003782(auStack_c0,(undefined8 *)(param_1 + uVar1),0x10);
    aes((int)auStack_c0,(int)key);
    FUN_80003782((undefined8 *)(param_3 + uVar1),auStack_c0,0x10);
  }
  return;
}

写出EXP。

from regadgets import *
enc = bytes.fromhex('63 D4 DD 72 B0 8C AE 31 8C 33 03 22 03 1C E4 D3 C3 E3 54 B2 1D EB EB 9D 45 B1 BE 86 CD E9 93 D8')
print(len(enc))
key = [ 0x2e, 0x35, 0x7d, 0x6a, 0xed, 0x44, 0xf3, 0x4d, 0xad, 0xb9, 0x11, 0x34, 0x13, 0xea, 0x32, 0x4e ]
enc = list(enc)
for i in range(len(enc)):
    enc[31-i] ^= 0xff
    enc[31-i] ^= enc[(32-i) % 32]
    enc[31-i] = ror8(enc[31-i], 3)

aes = AES(key)
dec = aes.decrypt_ecb_block(enc[:16]) + aes.decrypt_ecb_block(enc[16:])
print(dec) # b'SCTF{Wlc_t0_the_wd_oF_IOT_s3cur}'

ez_rust

x64dbg开调,搜that's,找到一块
spatchEvent(new Event("input")))}const ln=Symbol("_assign"),zi={created(e,{modifiers:{lazy:t,trim:n,number:s}},r){e[ln]=ws(r);const o=s||r.props&&r.props.type==="number";We(e,t?"change":"input",i=>{if(i.target.composing)return;let c=e.value;n&&(c=c.trim()),o&&(c=cn(c)),e[ln](c)}),n&&We(e,"change",()=>{e.value=e.value.trim()}),t||(We(e,"compositionstart",qi),We(e,"compositionend",Cs),We(e,"change",Cs))},mounted(e,{value:t}){e.value=t??""},beforeUpdate(e,{value:t,modifiers:{lazy:n,trim:s,number:r}},o){if(e[ln]=ws(o),e.composing)return;const i=r||e.type==="number"?cn(e.value):e.value,c=t??"";i!==c&&(document.activeElement===e&&e.type!=="range"&&(n||s&&e.value.trim()===c)||(e.value=c))}},Ji=["ctrl","shift","alt","meta"],Yi={stop:e=>e.stopPropagation(),prevent:e=>e.preventDefault(),self:e=>e.target!==e.currentTarget,ctrl:e=>!e.ctrlKey,shift:e=>!e.shiftKey,alt:e=>!e.altKey,meta:e=>!e.metaKey,left:e=>"button"in e&&e.button!==0,middle:e=>"button"in e&&e.button!==1,right:e=>"button"in e&&e.button!==2,exact:(e,t)=>Ji.some(n=>e[`${n}Key`]&&!t.includes(n))},Qi=(e,t)=>{const n=e._withMods||(e._withMods={}),s=t.join(".");return n[s]||(n[s]=(r,...o)=>{for(let i=0;i<t.length;i++){const c=Yi[t[i]];if(c&&c(r,t))return}return e(r,...o)})},Xi=Y({patchProp:Wi},Ii);let Os;function Zi(){return Os||(Os=ri(Xi))}const ki=(...e)=>{const t=Zi().createApp(...e),{mount:n}=t;return t.mount=s=>{const r=tl(s);if(!r)return;const o=t._component;!A(o)&&!o.render&&!o.template&&(o.template=r.innerHTML),r.innerHTML="";const i=n(r,!1,el(r));return r instanceof Element&&(r.removeAttribute("v-cloak"),r.setAttribute("data-v-app","")),i},t};function el(e){if(e instanceof SVGElement)return"svg";if(typeof MathMLElement=="function"&&e instanceof MathMLElement)return"mathml"}function tl(e){return z(e)?document.querySelector(e):e}const nl=ve("button",{type:"submit"},"go!",-1),sl={__name:"Greet",setup(e){const t=ts(""),n=ts("");function s(o,i="secret"){for(var c="",u=i.length,d=0;d<o.length;d++){var h=o.charCodeAt(d),x=i.charCodeAt(d%u),w=h^x;c+=String.fromCharCode(w)}return c}async function r(){if(n.value===""){t.value="Please enter a name.";return}btoa(s(n.value))==="JFYvMVU5QDoNQjomJlBULSQaCihTAFY="?t.value="Great, you got the flag!":t.value="No, that's not my name."}return(o,i)=>(mr(),br(ge,null,[ve("form",{class:"row",onSubmit:Qi(r,["prevent"])},[Mo(ve("input",{id:"greet-input","onUpdate:modelValue":i[0]||(i[0]=c=>n.value=c),placeholder:"Enter a name..."},null,512),[[zi,n.value]]),nl],32),ve("p",null,Nr(t.value),1)],64))}},rl=(e,t)=>{const n=e.__vccOpts||e;for(const[s,r]of t)n[s]=r;return n},Kn=e=>(bo("data-v-bacdabd6"),e=e(),yo(),e),ol={class:"container"},il=Kn(()=>ve("h1",null,"Welcome to L3HCTF!",-1)),ll=Kn(()=>ve("p",null,"Hope you have a good time playing.",-1)),cl=Kn(()=>ve("p",null,"Now please tell me a name.",-1)),fl={__name:"App",setup(e){return(t,n)=>(mr(),br("div",ol,[il,ll,cl,Ae(sl)]))}},ul=rl(fl,[["__scopeId","data-v-bacdabd6"]]);ki(ul).mount("#app");
分析可知,"JFYvMVU5QDoNQjomJlBULSQaCihTAFY=" == base64_encode(bxor_cycle(input,b'secret'))
L3HCTF{W3LC0M3_n0_RU57_AnyM0r3}

babycom

x64dbg附加,注意到中途主程序会往外写一个com(从资源里面读取)。
注意到是babycom.dll,我们直接断输入的字符串,发现进去后是一个xtea
xtea参数:
key=EA 3E D4 1C 70 CB D7 47 98 5E CA DB 53 0C 39 2B
delta=0x114514
round=32
然后经过对比发现是一个标准的。
2024-09-25T09:52:54.png
继续往下分析,xtea之后还有一个加密。
用了一个Bcrypt的加密,调API就可以解密。

#include <iostream>
#include <Windows.h>
int main()
{
    HCRYPTPROV phProv;
    HCRYPTHASH phHash;
    HCRYPTKEY phKey;
    const BYTE key[] = {
        0xEA, 0x3E, 0xD4, 0x1C, 0x70, 0xCB, 0xD7, 0x47, 0x98, 0x5E, 0xCA, 0xDB, 0x53, 0x0C, 0x39, 0x2B
    };

    const BYTE MultiByteStr[] = {0xb, 0xaf, 0x51, 0x21, 0x9c, 0x52, 0x10, 0x89,
                    0x3f, 0x2c, 0x34, 0x30, 0x87, 0x13, 0xc1, 0x4c,
                    0xc1, 0x7f, 0x81, 0x6e, 0xba, 0xbd, 0xdf, 0x43,
                    0x1a, 0xf0, 0xd7, 0xde, 0x8e, 0x66, 0xb9, 0x7c };
    DWORD pdwDataLen = 32;
    if (CryptAcquireContextA(&phProv, 0LL, 0LL, 0x18u, 0xF0000000)
        && CryptCreateHash(phProv, 32771u, 0LL, 0, &phHash)
        && CryptHashData(phHash, key, 0x10u, 0)
        && CryptDeriveKey(phProv, 0x660Eu, phHash, 1u, &phKey))
    {
        CryptDecrypt(phKey, 0LL, 0, 0, (BYTE*)MultiByteStr, &pdwDataLen);
    }
    return 0;
/* Decrypted
2a b4 c1 74 d6 59 aa 05 73 10 7f 9c 40 49 99 62
3c 84 51 8f 3f 37 ab f1 0e fe 61 96 45 ad 41 6a
*/
}

综上所述

from regadgets import *
encc = pack_dword(byte2dword(bytes.fromhex("""
2a b4 c1 74 d6 59 aa 05 73 10 7f 9c 40 49 99 62
3c 84 51 8f 3f 37 ab f1 0e fe 61 96 45 ad 41 6a
""")))
key = byte2dword(b"\xEA\x3E\xD4\x1C\x70\xCB\xD7\x47\x98\x5E\xCA\xDB\x53\x0C\x39\x2B")
r = b''
for i in encc:
    result = xtea_decrypt(i, key, delta=0x114514, rounds=32)
    r += dword2byte(list(result))
print(r)
# L3HCTF{C0M_Th3C0d3_1s_FuN!!!!!!}

丢一个Ctypes版本的(纯python解决)

from regadgets import *
from ctypes import *
lib_advapi32 = windll.LoadLibrary('advapi32.dll')
pfunc_CryptAcquireContextA = lib_advapi32.CryptAcquireContextA
pfunc_CryptCreateHash = lib_advapi32.CryptCreateHash
pfunc_CryptHashData = lib_advapi32.CryptHashData
pfunc_CryptDeriveKey = lib_advapi32.CryptDeriveKey
pfunc_CryptDecrypt = lib_advapi32.CryptDecrypt
phProv = c_uint64(0)
phHash = c_uint64(0)
phKey = c_uint64(0)
pdwDataLen = c_uint32(32)
if pfunc_CryptAcquireContextA(pointer(phProv), 0, 0, 0x18, 0xF0000000) == 0:
    print("failed 0")
    exit(0)
if pfunc_CryptCreateHash(phProv, 32771, 0, 0, pointer(phHash)) == 0:
    print("failed 1")
    exit(0)
key = b"\xEA\x3E\xD4\x1C\x70\xCB\xD7\x47\x98\x5E\xCA\xDB\x53\x0C\x39\x2B"
if pfunc_CryptHashData(phHash, create_string_buffer(key), 0x10, 0) == 0:
    print("failed 2")
    exit(0)
if pfunc_CryptDeriveKey(phProv, 0x660E, phHash, 1, pointer(phKey)) == 0:
    print("failed 3")
    exit(0)

correct = b"\x0B\xAF\x51\x21\x9C\x52\x10\x89\x3F\x2C\x34\x30\x87\x13\xC1\x4C\xC1\x7F\x81\x6E\xBA\xBD\xDF\x43\x1A\xF0\xD7\xDE\x8E\x66\xB9\x7C"
buf = create_string_buffer(correct)
pfunc_CryptDecrypt(phKey, 0, 0, 0, buf, pointer(pdwDataLen))

encc = pack_dword(byte2dword(buf.raw), padding=True)
r = b''
for i in encc:
    result = xtea_decrypt(i, byte2dword(key), delta=0x114514, rounds=32)
    r += dword2byte(list(result))
print(r)

# b'L3HCTF{C0M_Th3C0d3_1s_FuN!!!!!!}\xa0\xbf\xbc\xcf>\xb1\x194'

x64_extension

AES-256 密钥扩增魔改
2024-09-20T16:26:55.png
2024-09-20T16:26:31.png

from regadgets import *
k = bytes([0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f])
iv = bytes([0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0])
a = AES(k)
data = open('flag.txt.enc', 'rb').read()
print(a.decrypt_cbc(data, iv))
# b"Hey Sekai CTF Player, I hope you are fine and are enjoying the CTF. Keep going, here is your reward! The flag is SEKAI{Pl34Se_It'5_jUs7_@_wAaaarmUp}\n"

SEKAI{Pl34Se_It'5_jUs7_@_wAaaarmUp}