LOADING

加载过慢请开启缓存 浏览器默认开启

CtfNewStar Week3 Writeup

Reverse

(・∀・)つ原题

simpleAndroid

打开后发现了一个checkActivity的文件里发现我们输入的字符串被传到了so文件里加密,于是解压出so文件丢入ida分析,找到了一个datacheck函数,一进去就发现了一个base64编码和编码密钥。
加密代码看似很多其实总共就两次,一次是反转,还有一次是一个”|”和”&”运算,把反转后的字符串逐个进行运算,最后和密文进行比较。
直接抄下来就行,解密脚本反一下就行,先运算后反转(脚本如下)
最后用密钥在赛博厨师解base64编码就行。

#include <stdio.h>
#include <string.h>

unsigned char data_1[32] = {
    0xB2, 0x74, 0x45, 0x16, 0x47, 0x34, 0x95, 0x36, 0x17, 0xF4, 0x43, 0x95, 0x03, 0xD6, 0x33, 0x95,
    0xC6, 0xD6, 0x33, 0x36, 0xA7, 0x35, 0xE6, 0x36, 0x96, 0x57, 0x43, 0x16, 0x96, 0x97, 0xE6, 0x16
};

void res1(unsigned char* m, int len) {
    for (int i = 0; i < len; ++i) {
        m[i] = (m[i] >> 4) & 0x0F | (16 * m[i]) & 0xF0;
    }
}

void res2(unsigned char* m, int len) {
    for (int i = 0; i < len / 2; ++i) {
        unsigned char temp = m[i];
        m[i] = m[len + ~i];
        m[len + ~i] = temp;
    }
}

int main() {
    int len = sizeof(data_1) / sizeof(data_1[0]);
    res1(data_1, len);
    res2(data_1, len);

    for (int i = 0; i < len; ++i) {
        printf("%c", data_1[i]);
    }

    return 0;
}

取啥名字好呢?

Ida分析发现main函数里套了许多的signal函数,查询了一下发现是收集异常的函数,然后在第一个signal(注意到报错是8,也就是SIGFPE)函数里找到了一段加密代码,大概就是异或下标吧,之后会把密文比较判断。
打算先猜一手,先提取出密文,写出异或解密脚本后发现不正确,于是用动调看input的值的变化,发现input的所有值在第一次报错8之前被统一更改了,后面就是正常地报错8,进行异或下标加密。
输入不同的字符发现改变前后的差是统一的,当我输入0x31时发现数据被转成0xa1,那么就是我输入的字符被减去了0x17,所以写出脚本,输出的就是flag.

#include <stdio.h>

unsigned char m[23] = {
    0x4F, 0x54, 0x48, 0x53, 0x60, 0x45, 0x37, 0x1A, 0x28, 0x41, 0x26, 0x16, 0x3B, 0x45, 0x14, 0x47,
    0x0E, 0x0C, 0x70, 0x3B, 0x3C, 0x3D, 0x70
};

int main() {
    for (int i = 0; i < 23; i++) {
        m[i] ^= i;
        printf("%c", m[i] + (0x31 - 0x1a));
    }
}

flowering_shrubs

die发现是fle64文件,ida分析天崩开局全是花,不会用pythonida脚本去画捏,手动去花,nop掉push和ret之前的内容(包含有call $+5),识别成函数后就是正常内容。分析函数,加密过程大概就是把明文字符四个一组然后在组内分别与key中的一个字符进行异或和运算加密,而加密的顺序据取决于一个随机数生成的下标,这决定了一个组的密钥。 分析随机数的生成,种子是0,但是是在Linux的文件,因为wind和linux的随机数生成不同,我们要写一份生成随机数的代码到Linux进行生成(脚本1)。
在生成随机数时会舍弃掉一次生成的第一个数,之后若生成与之前相同的数就会重复生成直到不重复(在这个过程中不会跳过第一个生成的数),不知道为什么在winds写的代码在Linux里面运行有问题,无奈把随机数全部生成(200个)然后用上面的逻辑,自己手动整理,最后得到的就是加密的下标顺序(index)。
有了每个的加密顺序就直接反向解密就行。(脚本2)

//脚本1
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv[]) {
    int rands;
    srand(0);
    for (int i = 0; i < 10; i++) {
        
        rands = (rand() % 40) & 0xFC;
        printf("%d,", rands);
    }
    return 0;
}
//脚本2
#include <stdio.h>

unsigned char m[40] = {
    0x54, 0xF4, 0x20, 0x47, 0xFC, 0xC4, 0x93, 0xE6, 0x39, 0xE0, 0x6E, 0x00, 0xA5, 0x6E, 0xAA, 0x9F,
    0x7A, 0xA1, 0x66, 0x39, 0x76, 0xB7, 0x67, 0x57, 0x3D, 0x95, 0x61, 0x22, 0x55, 0xC9, 0x3B, 0x4E,
    0x4F, 0xE8, 0x66, 0x08, 0x3D, 0x50, 0x43, 0x3E
};

int index[] = {0,4,32,12,8,24,16,20,28,36};

int dec(unsigned char* input, unsigned char *key) {

    *input ^= input[3];
    input[3] ^= input[2];
    input[2] += *key;
    input[2] ^= input[1];
    input[1] -= *key;
    input[1] ^= *input;
    *input ^= *key;
    return 0;
}

int main() {
    unsigned char key[] = "uarefirst.";

    for (int i = 0; i < 10; i++) {
        dec(&m[index[i]], &key[i]);
    }
    printf("%s", m);
    
    return 0;
}

SMc_math

Ida分析发现里面代码就是一坨,看题目是smc,于是先寻找解密代码,发现在调用加密函数前,有一个for循环对加密函数进行了异或运算,这估计就是解密加密函数的代码,于是在调用处断点。
进行动调,到断点处用快捷键恢复函数,这样就可以看到加密函数的内容了。
分析函数,就是一个七元一次方程,查找资料想到用z3库来解密,脚本如下。
因为加密时以int转入,解方程后的结果应该是4字节数据16进制转化成了数字表示,因为数据是4字节小端序存储,所以我们要用小端序读取并转成字符就可以了(脚本如下)。

from z3 import *

v2, v3, v4, v5, v6, v7, v8 = Ints('v2 v3 v4 v5 v6 v7 v8')

a = Solver()
a.add(5 * (v3 + v2) + 4 * v4 + 6 * v5 + v6 + 9 * v8 + 2 * v7 == 0xD5CC7D4FF)
a.add(4 * v8 + 3 * v5 + 6 * v4 + 10 * v3 + 9 * v2 + 9 * v7 + 3 * v6 == 0x102335844B)
a.add(9 * v6 + 4 * (v5 + v4) + 5 * v3 + 4 * v2 + 3 * v8 + 10 * v7 == 0xD55AEABB9)
a.add(9 * v3 + 5 * v2 + 9 * v8 + 2 * (v4 + 2 * v5 + 5 * v6 + v7) == 0xF89F6B7FA)
a.add(5 * v6 + 9 * v5 + 7 * v2 + 2 * v3 + v4 + 3 * v8 + 9 * v7 == 0xD5230B80B)
a.add(8 * v8 + 6 * v5 + 10 * v4 + 5 * v3 + 6 * v2 + 3 * v7 + 9 * v6 == 0x11E28ED873)
a.add(v2 + 4 * (v4 + v3 + 2 * v5) + 9 * v6 + v7 + 3 * v8 == 0xB353C03E1)

che = a.check()
res = a.model()
print(res)

m = [1734437990, 1596998779, 1601515609, 1999662667, 1129149279, 1148073055, 2100517471]  # 手动收集的数据,因为不会把res中的计算结果转化为列表

flag = ""

for i in m:
    result = i.to_bytes(4, 'little')  # 
    flag += result.decode('utf-8')

print(flag)

SecretsOfKawaii

Jeb解混淆,在mainacticity里面发现了加密代码,在这里主要逻辑就是把明文rc4加密(密钥就在旁边),然后base64编码后交到so层check方法处理。
提取出so文件,查壳发现带壳,后用upx脱壳,然后丢进ida分析。找到一个带check的函数,里面有一个btea函数和密钥,分析btea函数,是一个xxtea加密和密文,把密文用ida提取。
和网上的xxtea加密对比发现delta被修改了,把在网上找到的解密代码的delta进行修改,就可以写出解密脚本(脚本如下)。
把解密后的字符串进行base64编码再rc4解密,密钥是rc4k4y,(http://tool.chacuo.net/cryptrc4),直接输入base64编码就可以解密出结果,结果就是flag

#include <stdio.h>

unsigned char secrets[48] = {
    0xDF, 0xD3, 0x12, 0x8C, 0x37, 0x41, 0x4C, 0x5F,  
    0x02, 0x3D, 0x9D, 0x1A, 0xB7, 0x94, 0x12, 0x2D, 
    0x37, 0x2B, 0x62, 0xFB, 0xE3, 0x84, 0x8D, 0xD1, 
    0x92, 0x45, 0x4C, 0x06, 0xAB, 0x5C, 0x98, 0x16, 
    0x69, 0x6D, 0xB0, 0xFD, 0xE3, 0xB1, 0x30, 0xFB, 
    0xD3, 0x2F, 0x5C, 0x92, 0x0C, 0xB4, 0x1B, 0x2E, 
};

__int64 __fastcall dec2(unsigned int* input, int n, const unsigned int* key)  
{
    int e; 
    int rounds; 
    unsigned int p; 
    unsigned int delta;
    unsigned int z; 
    unsigned int y;

    rounds = 52 / n + 6;
    unsigned int sum;
    delta = 559038737; 
    sum = 0 - delta * rounds; 
    y = input[0];
    do
    {
        e = (sum >> 2) & 3;
        for (p = n - 1; p > 0; p--)
        {
            z = input[p - 1];
            y = input[p] -= (((z ^ key[e ^ p & 3]) + (y ^ sum)) ^ (((4 * z) ^ (y >> 3)) + ((8 * y) ^ (z >> 5))));
        }
        z = input[n - 1];
        y = input[0] -= (((z ^ key[e ^ p & 3]) + (y ^ sum)) ^ (((4 * z) ^ (y >> 3)) + ((8 * y) ^ (z >> 5))));
        sum += delta;

    } while (--rounds);
    return rounds;
}

int main() {

    unsigned char key[] = "meow~meow~tea~~~";
    dec2((unsigned int*)secrets, 12, (const unsigned int*)key); 
    for (int x = 0; x < 48; x++) {
        printf("%c", secrets[x]); 
    }
    return 0;
}

011vm

ollvm平坦化,可以用d810,去一下混淆,效果也不是很好,主要就是猜。
在main函数中可以跟踪一下输入的变量,发现进入了一个函数,查看这个函数,里面有三个128位个字符,里面又发现一个函数,发现这个函数的内容有点像tea加密,对比一下原tea加密代码,好像没有魔改,于是我们把数据提取出来,因为没有发现其他的字符为密钥,推测密钥就在那三个长字符中,刚好满足tea的加密数据格式(4个32位的密钥,和偶数个32位的加密数据),直接进行解密就可以得到结果。

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

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

    for (i = 0; i < 32; i++) {
        sum += delta;
        v0 += ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
        v1 += ((v0 << 4) + 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], sum = 0xC6EF3720, i;	
    uint32_t delta = 0x9e3779b9;
    uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];

    for (i = 0; i < 32; i++) {
        v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
        v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
        sum -= delta;
    }
    v[0] = v0; v[1] = v1;
}

int main()
{
    unsigned char k[] = {
    0x14, 0x13, 0x12, 0x11, 0x25, 0x24, 0x23, 0x22, 0x36, 0x35,
    0x34, 0x33, 0x44, 0x43, 0x42, 0x41
    };
    unsigned char v[] =
    {
      0x28, 0x7E, 0xB9, 0x38, 0xC1, 0x10, 0xE5, 0xB7, 0xAE, 0x9F,
      0xB2, 0xB4, 0xD7, 0xBB, 0x93, 0x55, 0x9E, 0x9B, 0x2E, 0x3C,
      0x37, 0xC6, 0x71, 0x16, 0xB5, 0x8C, 0x3A, 0x8F, 0x15, 0xE5,
      0x16, 0x51
    };
    for (int i = 0; i < 8; i += 2) {
        decrypt(((uint32_t*)v + i), (uint32_t*)k);
    }
    printf("%s", v);

    return 0;
}

PangBai 过家家(3)


用pyinstxtractor进行解包exe文件,再用pycdc提取NotNormalExe.pyc文件,发现代码逻辑不完整并有提示WARNING: Decompyle incomplete,可能是编译不了,于是想到去查看字节码,找ai帮我写了一个用pythondis库查看字节码的脚本(脚本1),看不懂py的字节码,于是丢给ai让它帮我反汇编成代码,这下看到了源码就是完整的了,加密逻辑比较简单就是普通的异或,直接写出脚本就可以解密成功了(脚本2)。

# 脚本1
import marshal
import dis
import os


def disassemble_pyc(file_path):
    with open(file_path, 'rb') as f:
        f.read(16)
        code_obj = marshal.load(f)
        dis.dis(code_obj)

pyc_file = r'D:\Userdata\download\pycdc\NotNormalExe.pyc'
disassemble_pyc(pyc_file)
脚本2
from pwn import xor

enc = [40, 9, 22, 52, 15, 56, 66, 71, 111, 121, 90, 33, 18, 40, 3, 13, 80, 28, 65, 68, 83, 88, 34, 86, 5, 12, 35, 82, 67, 3, 17, 79]
key = "NewStar2024"
for x in range(len(enc)):
    enc[x] ^= ord(key[x % len(key)])

flag = ""

for x in enc:
    re = x.to_bytes(1, "big")
    flag += re.decode('utf-8')
print(flag)

Web

Include Me

Php伪协议,发现很多东西都被过滤了,查了资料看到data协议没有被过滤,至于执行命令可以用base64编码,想用<?php system(“ls /“);>查看目录但发现=被过滤了,所以尝试在base64编码下不含等号的命令。
注意到flag被过滤了,推测flag的文件名就是flag,就尝试直接cat /flag,构造<?php system(“cat /flag”);>,刚好编码后又没有含有=,直接执行,http://eci-2ze4q7yfeafl0jdgf4b6.cloudeci1.ichunqiu.com/?me=data://text/plain;base64,PD9waHAgc3lzdGVtKCJjYXQgL2ZsYWciKTs+

臭皮的计算机

在源码中看到了python源码,发现了eval()函数,想到传入命令给eval执行,但是字母被过滤了,寻找只有数字的表示方法。
查询资料了解到八进制的转译符是\不包含字母,于是用八进制传入命令,用os.system()函数来执行Linux系统命令.
先用ls /看看目录,输入

\157\163\56\163\171\163\164\145\155\50\47\154\163 /\47\51

发现flag文件,直接cat /flag,传入

\157\163\56\163\171\163\164\145\155\50\47\143\141\164\40\57\146\154\141\147\47\51