[NewStarCTF2023]Week4


NewStarctf2023_Week4_Offical

Reverse

easy_js

js代码有obfuscator混淆
obfuscator解码
解码出来的代码修一修能跑

function _0x4a2285(_0x2b5a00) {

    var _0x289ba9 = -92 * -67 + -6972 + 808;
    while (true) {
        var _0x3e831f = new Array(6);
        _0x3e831f = ['0','2','3','4','5','1'];
        switch (_0x3e831f[_0x289ba9++]) {
        case '0':
            var array = new Array(-1 * 3007 + -9073 + -24 * -514);
            continue;
        case '1':
            return array_1;
        case '2':
            var _0x563e78_2;
            var _0x1be492;
            var _0x563e78_2_1;
            continue;
        case '3':
            var array_1 = new Array(-517 * 11 + 927 + -1 * -5016);
            continue;
        case '4':
            for (_0x563e78_2 = -2 * -2767 + 3191 + -349 * 25; _0x563e78_2 < 697 + -207 + -234; _0x563e78_2++) {
                array_1[_0x563e78_2] = _0x563e78_2;
                array[_0x563e78_2] = _0x2b5a00.charCodeAt(_0x563e78_2 % _0x2b5a00.length);
            }
            continue;
        case '5':
            for (_0x563e78_2 = _0x1be492 = -1 * 7016 + 19 * 508 + -2636; _0x563e78_2 < -8 * 887 + 11 * 741 + -799; _0x563e78_2++) {
                _0x1be492 = (_0x1be492 + array_1[_0x563e78_2] + array[_0x563e78_2]) % (-3514 + -8 * -464 + 58);
                _0x563e78_2_1 = array_1[_0x563e78_2];
                array_1[_0x563e78_2] = array_1[_0x1be492];
                array_1[_0x1be492] = _0x563e78_2_1;
            }
            continue;
        }
        break;
    }
}
function _0x569454(_0x1f637f, _0x46bf6a) {
    return String.fromCharCode(_0x1f637f.charCodeAt(1565 * -6 + -2452 * -4 + -11 * 38) + (-3292 * -1 + -587 * -3 + -5040)) + String.fromCharCode(_0x46bf6a.charCodeAt(-8452 + -2476 * 1 + -2732 * -4) - (-6933 + -3630 + 10564)) + 'wstar' + '_' + String.fromCharCode(_0x46bf6a.charCodeAt(1543 * -3 + -5469 + -10099 * -1) + (-1 * -2873 + 1654 + -73 * 62)) + String.fromCharCode(_0x1f637f.charCodeAt(471 * 3 + -2823 + 3 * 471) + (54 * -109 + -9295 + 15182)) + 's';
}
function _0x221c90(_0x2acf55, _0x549db3) {
    var fromCharCode = '';
    var array = new Array(-8 * 478 + 8657 * 1 + -1 * 4577);
    array = _0x4a2285(_0x2acf55);
    var _0x37dfd2_1;
    var _0x3c262d;
    var _0x37dfd2_1;
    _0x37dfd2_1 = _0x3c262d = -1 * -613 + 8323 + -8936;
    for (var _0x4ff9a6 = -6009 + -1 * 9956 + -3193 * -5; _0x4ff9a6 < _0x549db3.length; _0x4ff9a6++) {
        _0x37dfd2_1 = (_0x37dfd2_1 + (-74 * 52 + 440 + 3409) + (-317 * -15 + 8530 + -13284)) % (-5528 + 5019 + 765);
        _0x3c262d = (_0x3c262d + array[_0x37dfd2_1]) % (-2077 * -3 + -3001 * 1 + 1 * -2974);
        _0x37dfd2_1 = array[_0x37dfd2_1];
        array[_0x37dfd2_1] = array[_0x3c262d];
        array[_0x3c262d] = _0x37dfd2_1;
        fromCharCode += String.fromCharCode(_0x549db3.charCodeAt(_0x4ff9a6) ^ array[(array[_0x37dfd2_1] + array[_0x3c262d]) % (1631 * 5 + -1774 * 2 + -4351)] ^ 6545 + 1177 + -83 * 93);
    }
    return fromCharCode;
}
window._0x54cd23 = _0x54cd23_1;
var _0x24eb58 = window._0x54cd23;
function _0x54cd23_1(_0x3cc7e4) {
    return btoa(_0x3cc7e4) === 'Cn8RHIJEVdvlrRESjETCscwQZdlhRfsRkWoHCTa0HcfLPg==';
}
function _0x499d16() {
    var _0x3b2b6e = -5913 + 5062 + 851;
    while (true) {
        var _0x27b01d = new Array(5);
        _0x27b01d = ['0','1','2','3','4'];
        switch (_0x27b01d[_0x3b2b6e++]) {
        case '0':
            var value = document.getElementById('username').value;
            continue;
        case '1':
            alert('来试试吧');
            continue;
        case '2':
            var value_1 = document.getElementById('password').value;
            continue;
        case '3':
            var value_2 = document.getElementById('flagtext').value;
            continue;
        case '4':
            if (value === 'admin' && value_1 === '123456') {
                var _0x4e60d2 = -23 * 121 + 251 * 37 + 1084 * -6;
                while (true) {
                    var _0x32853b = new Array(5);
                    _0x32853b = ['4','0','2','3','1'];
                    switch (_0x32853b[_0x4e60d2++]) {
                    case '0':
                        alert('账号密码正确\uFF01再接再厉');
                        continue;
                    case '1':
                        if (_0x24eb58(_0x221c90_1)) {
                            alert('flag正确\uFF01');
                        } else {
                            alert('不行不行!');
                        };
                        continue;
                    case '2':
                        alert('正在验证flag------');
                        continue;
                    case '3':
                        var _0x221c90_1 = _0x221c90(_0x569454(value, value_1), value_2);
                        continue;
                    case '4':
                        alert('正在验证账号密码------');
                        continue;
                    }
                    break;
                }
            } else {
                alert('不行不行');
            }
            continue;
        }
        break;
    }
}

通过调试,整理代码,可以阅读出来,整体式一个rc4加密
_0x569454()为S盒生成函数, _0x221c90为rc4加密结果异或3
加密过程就是由_0x569454('admin','123456')生成S盒
然后rc4加密并异或3,最后base64得到'Cn8RHIJEVdvlrRESjETCscwQZdlhRfsRkWoHCTa0HcfLPg=='
由于rc4为对称加密,考虑直接调用_0x569454解密
直接用原来的js代码,在控制台里输入
document.write(_0x221c90(_0x569454('admin', '123456'), atob('Cn8RHIJEVdvlrRESjETCscwQZdlhRfsRkWoHCTa0HcfLPg==')));
img

flag

flag{h0w_you_know_h0w_t0_S1lve_js}

VM题,先把opcode dump出来

addr = 0x61FC5C
arr = []
for i in range(0,0x1AB,4):
    arr.append(Dword(addr + i))
print(arr)

然后模拟一下VM过程

def F1(a1,a2,a3):
    if (a3):
        if (a3 == 1):
            print(f'reg[{a1}] = reg[{a2}]')
        elif (a3 == 2):
            print(f'reg[{a1}] = flag[{a2}]')
    else:
        print(f'reg[{a1}] = {a2}')
def F2(a1,a2,a3):
    if (a3):
        if (a3 == 1):
            print(f'reg[{a1}] += reg[{a2}]')
        elif (a3 == 2):
            print(f'reg[{a1}] += flag[{a2}]')
    else:
        print(f'reg[{a1}] += {a2}')
def F3(a1,a2):
    print(f'reg[{a1}] <<= {a2}')
def F4(a1,a2):
    print(f'reg[{a1}] ^= reg[{a2}]')
def F5(a1,a2):
    print(f'reg[{a1}] >>= {a2}')
enc = [
  0x1A, 0x62, 0xB9, 0x76, 0xDE, 0xAD, 0xE4, 0xCC, 0xC8, 0xBF, 0xC8, 0x25, 0x72, 0xD4, 0xC2, 0x16, 0x3A, 0xD5, 0x17, 0xF3, 0xA1, 0x11, 0xA1, 0xF2, 0xE6, 0xF0, 0x89, 0xDF, 0x23, 0xA6, 0xCD, 0xDC, 0x09, 0xF4, 0xC2, 0x21, 0x63, 0x8D, 0xD8, 0xDB]
opcode = [
  240, 241, 0, 0, 2, 241, 1, 1, 2, 241, 3, 0, 0, 241, 2, 0, 0, 242, 3, 2654435769, 0, 241, 4, 1, 1, 245, 4, 5, 242, 4, 1, 2, 241, 5, 1, 1, 243, 5, 5, 242, 5, 0, 2, 244, 4, 5, 241, 5, 1, 1, 242, 5, 3, 1, 244, 4, 5, 242, 0, 4, 1, 241, 4, 0, 1, 245, 4, 5, 242, 4, 3, 2, 241, 5, 0, 1, 243, 5, 5, 242, 5, 2, 2, 244, 4, 5, 241, 5, 0, 1, 242, 5, 3, 1, 244, 4, 5, 242, 1, 4, 1, 242, 2, 1, 0, 255]
k = [2,0,2,3]
reg = [0]*5
flag = ''
i = 0
while(opcode[i] != 255):
  if opcode[i] == 0xF0:
      i += 1
  elif opcode[i] == 0xF1:
      F1(opcode[i+1], opcode[i+2], opcode[i+3])
      i += 4
  elif opcode[i] == 0xF2:
      F2(opcode[i+1], opcode[i+2], opcode[i+3])
      i += 4
  elif opcode[i] == 0xF3:
      F3(opcode[i+1], opcode[i+2])
      i += 3
  elif opcode[i] == 0xF4:
      F4(opcode[i+1], opcode[i+2])
      i += 3
  elif opcode[i] == 0xF5:
      F5(opcode[i+1], opcode[i+2])
      i += 3
"""
reg[0] = flag[0]
reg[1] = flag[1]
reg[3] = 0
reg[2] = 0
reg[3] += 2654435769
reg[4] = reg[1]
reg[4] >>= 5
reg[4] += flag[1]
reg[5] = reg[1]
reg[5] <<= 5
reg[5] += flag[0]
reg[4] ^= reg[5]
reg[5] = reg[1]
reg[5] += reg[3]
reg[4] ^= reg[5]
reg[0] += reg[4]
reg[4] = reg[0]
reg[4] >>= 5
reg[4] += flag[3]
reg[5] = reg[0]
reg[5] <<= 5
reg[5] += flag[2]
reg[4] ^= reg[5]
reg[5] = reg[0]
reg[5] += reg[3]
reg[4] ^= reg[5]
reg[1] += reg[4]
reg[2] += 1
"""

阅读一下可以得到F0-F6含义如下

0xF0 init
0xF1 mov
0xF2 add
0xF3 shl
0xF4 xor
0xF5 shr
0xF6 judge

结合栈空间的操作大致可以猜出来是tea,轮次40轮,两个位移都改成了5
看不出来也没关系 signsrch 也可以识别出来有个tea的常数delta
img

exp

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

void encrypt (uint32_t* v, uint32_t* k) {
    uint32_t sum = 0; 
    uint32_t v0 = v[0], v1 = v[1];
    uint32_t delta = 0x9e3779b9;
    uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];

    for (int i=0; i<32; i++) {
        sum += delta;
        v0 += ((v1<<5) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
        v1 += ((v0<<5) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
    }

    v[0]=v0; 
    v[1]=v1;
}

void decrypt (uint32_t* v, uint32_t* k) {
    uint32_t v0 = v[0], v1 = v[1];
    uint32_t delta = 0x9e3779b9;
    uint32_t sum = delta * 40;
    uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];

    for (int i=0; i<40; i++) {
        v1 -= ((v0<<5) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
        v0 -= ((v1<<5) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
        sum -= delta;
    }

    v[0]=v0; 
    v[1]=v1;
}

int main(){
    uint32_t v[] = {0x76B9621A, 0xCCE4ADDE, 0x25C8BFC8, 0x16C2D472, 0xF317D53A, 0xF2A111A1, 0xDF89F0E6, 0xDCCDA623, 
    0x21C2F409, 0xDBD88D63};
    uint32_t k[4]= {2,0,2,3};
    for(int i=0; i<5; i++) decrypt(v + i*2, k);
    printf("%s", (char *)v);
    return 0;
}
/*
flag{WOWOW!Y0uG0tTh3F0urthPZGALAXY1eve1}
*/

flag

flag{WOWOW!Y0uG0tTh3F0urthPZGALAXY1eve1}

iwannarest

先用jadx 看看MainActivity
img
encode 函数在native层
进入\iwannarest\lib\x86_64 看看so文件
so文件里面没有encode但是函数较少,可以猜到zzz应该就是encode(后面看了官方wp才知道是采用了动态注册的方式去注册native层函数)
zzz应该是个xxtea,并且findcrypt插件也识别出来了
img
直接解应该解不出来,再观察发现init函数内修改了key,值是[1,1,4,5]
img
写脚本梭

exp

#include <stdint.h>
#include <stdio.h>
#include <string.h>
#define DELTA 0x9E3779B9
#define MX (z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (k[(p & 3) ^ e] ^ z)

void xxtea_encrypt(uint32_t *v, uint32_t len, uint32_t *k) {
    uint32_t n = len - 1;
    uint32_t y, z, sum = 0, e, p, q;
    q = 6 + 52 / len;
    while (q-- > 0) {
        sum += DELTA;
        e = sum >> 2 & 3;
        for (p = 0; p < n; p++) {
            y = v[p + 1];
            z = v[p] += MX;
        }
        y = v[0];
        z = v[n] += MX;
    }
}

void xxtea_decrypt(uint32_t *v, uint32_t len, uint32_t *k) {
    uint32_t n = len - 1;
    uint32_t y, z, sum, e, p, q;
    q = 6 + 52 / len;
    sum = q * DELTA;
    y = v[0];
    while (sum != 0) {
        e = sum >> 2 & 3;
        for (p = n; p > 0; p--) {
            z = v[p - 1];
            y = v[p] -= MX;
        }
        z = v[n];
        y = v[0] -= MX;
        sum -= DELTA;
    }
}

int main() {
    uint32_t v[8] = { 
      3962803346,3145558623,1541010058,1093420299,
      3315082129,861271549,352576336,60051825, };
      uint32_t key[4] = { 1,1,4,5 };
      uint32_t i;
      uint32_t temp[2];
      for (i = 0; i < 8; i += 2){
        temp[0] = v[i]; 
        temp[1] = v[i + 1]; 
        xxtea_decrypt(temp, 2, key);
        v[i] = temp[0];
        v[i + 1] = temp[1];
      }
    printf("%s", (char *)v);
    return 0;
}
//flag{GDu7Il0v3uP1zl3tm3Gr@duate}?

flag

flag{GDu7Il0v3uP1zl3tm3Gr@duate}

简单的跨栏

先看MainActivity
这部分应该是个反调试
img
这部分生成S盒,交换,异或,一眼RC4
img
这部分先用key="runrunrun"RC4加密输入然后调用了native层的encode
img
去so中找找逻辑
在xxx中发现了密文
img
然后b64中明显的变表base64
img
写脚本梭,对密文先解base64,然后用key='runrunrun'解rc4但是结果一直是错的
因为在java字符串传递到jni接口时,采用了GetStringUtfChars,这个函数会自动采用utf-8的解码格式。
我们需要将base64解密后的utf-8编码的数据转化为对应的Unicode编码

UTF-8编码,是Unicode的一种可变长度字符编码,是Unicode的一种实现方式,又称万国码;UTF8使用1~4字节为每个字符编码,相对于Unicode 固定的四字节长度,更节省存储空间。UTF-8字节长度与Unicode 码点对应关系如下:
一字节(0x00-0x7F)-> U+00~U+7F
二字节(0xC280-0xDFBF)-> U+80~U+7FF
三字节(0xE0A080-0xEFBFBF)-> U+800~U+FFFF
四字节(0xF0908080-0xF48FBFBF)-> U+10000~U+10FFFF
字符U+0000到U+007F(ASCII)被编码为字节0×00到0x7F(ASCIⅡ兼容)。这意味着只包含7位ASCIl字符的文件在ASCIⅡ和UTF-8两种编码方式下是一样的。
所有大于0x007F的字符被编码为一个有多个字节的串,每个字节都有标记位集,常用汉字基本上都被编码成三字节。

exp

from Crypto.Cipher import ARC4
import base64
def custom_base64_decode(data):
    custom_chars = '0123456789ABCMtuvwxNOPQRabcdefghijklSTUVWXDEFGHIJKLYZmnopqrsyz+/'
    # 创建标准Base64字符集和自定义字符集的映射表
    base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
    char_map = str.maketrans( custom_chars, base64_chars)
    # 替换为自定义字符
    data = data.translate(char_map)
    # 使用标准Base64解码
    decoded_data = list(base64.b64decode(data.encode()))
    return decoded_data
enc = 'Jr0VJ81KJD1PJpY2WytxgeAnJWu2H1qutyA0xFtyJrZHJrZeJrM8ATCWJWft9FtgJsV2SCtR4yASJrE2Uv=='
data = custom_base64_decode(enc)
result = []

i = 0
while i < len(data):
    byte = data[i]
    if byte == 0xc0 and i+1 < len(data) and data[i+1] == 0x80:
        result.append(0x00)
        i += 2
    elif 0x00 <= byte <= 0x7F:
        result.append(byte)
        i += 1
    elif 0xC0 <= byte <= 0xDF and i+1 < len(data):
        char = bytes([data[i], data[i+1]])
        decoded = char.decode('utf-8', errors='ignore')
        if decoded:
            result.append(ord(decoded))
        else:
            result.append(byte)
        i += 2
    elif 0xE0 <= byte <= 0xEF and i+2 < len(data):
        char = bytes([data[i], data[i+1], data[i+2]])
        decoded = char.decode('utf-8', errors='ignore')
        if decoded:
            result.append(ord(decoded))
        else:
            result.append(byte)
        i += 3
    else:
        result.append(byte)
        i += 1
# print(result)
a = [
    0xe0,0x27,0x00,0x71,0xa0,0x55,0xcc,0xa3,0xd2,0x79,
    0xb6,0x83,0xb8,0x1e,0x4f,0x3b,0x80,0x4a,0xfc,0xed,
    0x2e,0xed,0x1c,0xe3,0x48,0x2a,0x53,0x28,0x87,0x4e,
    0x26,0xde,0xf9,0x90,0xd7,0x13,0xa4,0xea,0x99]
key = b'runrunrun'

enc = b''.join([bytes([i]) for i in result])
rc4 = ARC4.new(key)
decrypted_data = rc4.decrypt(enc)
print(decrypted_data.decode('utf-8'))
# flag{DED3D3DED3DE03DEBUG1sfunnyr1gh7??}

flag

flag{DED3D3DED3DE03DEBUG1sfunnyr1gh7??}

posted @ 2023-10-28 00:33  Tree_24  阅读(177)  评论(0编辑  收藏  举报