CISCN2024部分复现

First Post:

Last Update:

CISCN2024部分Re题复现

非常好国赛,使我的逆向旋转,frida,go,rust和cython什么玩意儿都来了,头都要大了

asm_re

上来一个汇编语言,看着就像是从IDA里面直接拖过来的东西

image-20240716145546384

首先是看着像初始化的东西,可以不用管

image-20240716145646570

这里可以看出是加密方法,学点汇编就大概知道是个什么意思了

image-20240716145740884

这边是密文,简单写个脚本解密即可(虽然比赛过程中处处犯傻

1
2
3
4
5
6
7
8
enc = [0x1fd7, 0x21b7, 0x1e47, 0x2027, 0x26e7, 0x10d7, 0x1127, 0x2007, 0x11c7, 0x1e47, 0x1017, 0x1017, 0x11f7, 0x2007,
0x1037, 0x1107, 0x1f17, 0x10d7, 0x1017, 0x1017, 0x1f67, 0x1017, 0x11c7, 0x11c7, 0x1017, 0x1fd7, 0x1f17, 0x1107,
0xf47, 0x1127, 0x1037, 0x1e47, 0x1037, 0x1fd7, 0x1107, 0x1fd7, 0x1107, 0x2787] //密文注意小端序问题
flag = ''
for i in enc:
i = (((i -0x1e) ^ 0x4d) - 0x14) // 0x50
flag += chr(i)
print(flag)

android_re

我勒个frida啊,配环境是一辈子的,frida是不会用的,现在只能开始恶补了(

image-20240716151421506

这个是主函数,可以看到引用了inspect

image-20240716151552227

来到inspect,可以看到是DES加密,需要Key和iv,

image-20240716151859985

可以看到是有个lib被调用了,去找找看

image-20240716152249801

image-20240716152306570

上面是getiv,下面是getkey,可以看得出来这个静态分析的难度那是相当滴大(是这样的,我看了一小时看不出来半点,所以应该采用动调的方式,因为这个是so文件,只是个库,无法主动执行,所以需要用到动调(噩梦来了)

我这边利用的是unidbg,这边可供选择也挺多的,还有可爱(sb)的frida以及objection

他们都有一个共同的特点,环境难配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
package com.ciscn;

import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
// 导入通用且标准的类库
import com.github.unidbg.linux.android.dvm.AbstractJni;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.linux.android.dvm.DalvikModule;
import com.github.unidbg.linux.android.dvm.DvmClass;
import com.github.unidbg.linux.android.dvm.VM;


import java.io.*;

public class ciscna extends AbstractJni {
private final AndroidEmulator emulator; //android 模拟器 a
private final VM vm;//vm 虚拟机
private final Module module;
private final Memory memory;
private final DalvikModule dm;

//将该类封装起来,以后直接套用模板
public ciscna(String apkFilePath, String soFilePath, String apkProcessname) throws IOException {
// 创建模拟器实例,进程名建议依照实际进程名填写,可以规避针对进程名的校验
emulator = AndroidEmulatorBuilder.for64Bit().setProcessName(apkProcessname).build();
memory = emulator.getMemory();
memory.setLibraryResolver(new AndroidResolver(23));
vm = emulator.createDalvikVM(new File(apkFilePath));
vm.setVerbose(false); // 打印日志,会在调用初始化 JNI_unload 打印一些信息,默认:false
// 加载目标 SO
dm = vm.loadLibrary(new File(soFilePath), true); // 加载 so 到虚拟内存,第二个参数:是否需要初始化
//获取本 SO 模块的句柄
module = dm.getModule();
vm.setJni(this); //设置 Jni,防止报错
//创建完后,需要调用 JNI_onload 函数
dm.callJNI_OnLoad(emulator); // 调用 JNI OnLoad,进行动态注册某些函数。如果都是静态注册,那就不用调用这个函数
vm.setVerbose(true);
// Debugger debugger = emulator.attach();
// debugger.addBreakPoint(module.base + 0x1924C);
// debugger.addBreakPoint(module.base + 0x19240);
}

public ciscna(AndroidEmulator emulator, VM vm, Module module, Memory memory, DalvikModule dm) {
this.emulator = emulator;
this.vm = vm;
this.module = module;
this.memory = memory;
this.dm = dm;
}

public String func_getKey() {
DvmClass dvmClass = vm.resolveClass("com.example.re11113.jni");
DvmObject<?> object = dvmClass.newObject(null);
DvmObject<?> object1 = object.callJniMethodObject(emulator, "getkey()Ljava/lang/String;");
return object1.getValue().toString();
}

public String func_getiv() {
DvmClass dvmClass = vm.resolveClass("com.example.re11113.jni");
DvmObject<?> object = dvmClass.newObject(null);
DvmObject<?> object1 = object.callJniMethodObject(emulator, "getiv()Ljava/lang/String;");
return object1.getValue().toString();
}

//创建一个 main 函数
public static void main(String[] args) throws IOException {
// 1、需要调用的 so 文件所在路径
String soFilePath = "/root/Documents/unidbg/unidbg-android/src/test/java/com/ciscn/libSecret_entrance.so";

// 2、APK 的路径
String apkFilePath = "/root/Documents/unidbg/unidbg-android/src/test/java/com/ciscn/app-debug.apk";
// 3、apk 进程名
String apkProcessname = "com.tencent.testvuln";
ciscna myapp = new ciscna(apkFilePath, soFilePath, apkProcessname);
System.out.println("getKey result:" + myapp.func_getKey());
System.out.println("getiv result:" + myapp.func_getiv());
}
}

看得很复杂对吧,实际上就一个模板,改改参数就行了(对我这个安卓动调苦手太好了

image-20240716153508243

跑出来结果

可以看到key和iv的值都被我们成功的hook出来了

image-20240716154149730

用flag{}包起来即可

当时表示用frida狠狠的报错(心态是逐渐崩溃

GoReverse

嘻嘻,加密东西写满了我整个小白板了,你个b东西

首先打开main_main函数

image-20240716161911982

上面部分经过分析是反调试的东西,我谢谢你

接下来来到main__ptr_co6Pxq_Execute函数

image-20240716162109047

查看汇编发现就是一臭打招呼的,可以继续往下走了

然后来到重量级的main__ptr_B2bUPq_Execute

image-20240716162620880

首先这部分代码是判断是否读取到了flag文件

image-20240716162723576

中间这部分是加密(suprise

下面就是判断了

可以看得出来重要的都是一个个main为前缀的函数,一个个来吧

main_ylFyZv

image-20240716163143411

首先是一大堆的代码,不过前面几行不要管,重要的是下面的for循环,可以看到是最友好的异或

image-20240716163333574

这个就是我们拿来异或的密钥了

main_bytesToUint32s

这个是修改类型的,可以不用管

main_zQyveE

image-20240716163749340

可以看出很明显的xxtea算法,delta值是改过的,main_KjmS3y函数就是MX,但是顺序变了

main_Q05qm6

image-20240716164045018

看到那个littleEndian了吗,这个就是这个函数的作用了

main_AkuFrt

image-20240716164601775

最逆天的来了

可以看到这里用了随机数和sm4

看看汇编

image-20240716170242942

可以看到了用了CTR的方法,key值我们是有了,但是iv我们却没有

那么我们的iv哪来呢,用了非常好的随机数,所以说我们需要动调(乐

首先这个程序有两处反调试,第一个我们在main_main函数的时候已经说过了

另一段在0x0438E28,也就是在调用main_main函数的runtime_main函数中,动调的时候发现执行这个call之后程序就退出了,nop掉之后程序正常运行。

image-20240523162357931

随后就可以进行动调了

image-20240523163618505

main_JrkmHd

image-20240716174817735

这个函数则是AES加密,然后经典key值一查就有,iv一查一个不知道

要注意的是有个makeslice函数,这个是go语言里面的切片函数,实际作用就是把你key里面的前16位拿过来然后变成了iv(乐

main_NJVCTq

image-20240716175933383

最后一个base32就好了。

总体来说就是非常的套娃,头大,那么问题来了,密文呢

看看我们的最初的函数,可以看到我们需要有个flag文件,这里是复现,所以我们自己弄一个,然后远程连接一下

image-20240716184526875

这不就来了吗,解密过程就不给了,也是个麻烦仙人(

whereThel1b

人生第二次碰到的cython,给了个py文件和so库,看看py

1
2
3
4
5
6
7
8
9
10
11
12
13
import whereThel1b


flag = input("where is my flag:")
flag = flag.encode()
encry = [108, 117, 72, 80, 64, 49, 99, 19, 69, 115, 94, 93, 94, 115, 71, 95, 84, 89, 56, 101, 70, 2, 84, 75, 127, 68, 103, 85, 105, 113, 80, 103, 95, 67, 81, 7, 113, 70, 47, 73, 92, 124, 93, 120, 104, 108, 106, 17, 80, 102, 101, 75, 93, 68, 121, 26]
whereThel1b.whereistheflag(flag)
ret = whereThel1b.trytry(flag)

if ret == encry:
print("rrrrrrrrrrrright")
else:
print("wwwwwwwwwwwwwwwrong")

可以发现是调用了这个库,于是得对这个文件进行一个快乐的审计

image-20240716192642357

首先是whereistheflag,可以发现有base64加密,然后再往下看

image-20240716193050224

发现有个random库,还有randint方法,按理说应该都会有个seed的,往下翻就没有什么其他有用的东西了,去trytry函数

image-20240716193320519

可以看到有random.seed这个方法,那么问题来了,中间的值呢,比赛期间找到似都找不到

其实挺简单的,首先可以确定的是cython中的常量(即数字)会是__pyx_int_x,其中x是数字,所以seed是

image-20240716194930746

另外的话如果是利用变量这种东西,会以__pyx_n_s_a这种形式出现,其中a就是我们的变量,字符串这种则是__pyx_kp_s_Hello_cython这种形式,Hello_cython就是我们的字符串,具体的东西等我cython调试的博客有生之年出来再说(

exp:

1
2
3
4
5
6
7
8
9
10
11
12
import random
import base64

encry = [108, 117, 72, 80, 64, 49, 99, 19, 69, 115, 94, 93, 94, 115, 71, 95, 84, 89, 56, 101, 70, 2, 84, 75, 127, 68, 103, 85, 105, 113, 80, 103, 95, 67, 81, 7, 113, 70, 47, 73, 92, 124, 93, 120, 104, 108, 106, 17, 80, 102, 101, 75, 93, 68, 121, 26]

random.seed(0)
for i in range(len(encry)):
encry[i] = encry[i] ^ random.randint(0, len(encry))

flag = base64.b64decode(bytes(encry))
print(flag)