VNCTF

2025-01-15

Fuko’s starfish

  1. 首先进行猜数游戏,正确后调用dll中的贪吃蛇游戏函数 image
  2. 通关后会调用这个函数并且有花指令nop掉retn即可f5 image image

  3. 可以发现这是一个AES加密,并且有反调试并且将key进行xor 0x17

image-20250209200548533

image-20250209200519832

image-20250209200530303

  1. 生成key的地方还有花指令通过随机数生成key

image-20250209200701299

  1. 编写脚本生成key,拿key进行AES解密即可
#include <stdio.h>
#include <stdlib.h>


int main()
{
 srand(0x1BF52);
 for (int i = 0; i < 16; i++)
 {
  int temp = rand();
  printf("%d,",((temp + temp / 255)&0xff) ^ 0x17);
 }

 return 0;
}
from Crypto.Cipher import AES

key = bytes([9,229,253,235,104,49,117,182,177,59,132,8,145,235,120,210])
enc = bytes([  0x3D, 0x01, 0x1C, 0x19, 0x0B, 0xA0, 0x90, 0x81, 0x5F, 0x67,
  0x27, 0x31, 0xA8, 0x9A, 0xA4, 0x74, 0x97, 0x36, 0x21, 0x67,
  0xAB, 0x2E, 0xB4, 0xA0, 0x94, 0x18, 0xD3, 0x7D, 0x93, 0xE6,
  0x46, 0xE7])

a = AES.new(key=key,mode=AES.MODE_ECB)
print(a.decrypt(enc))

kotlindroid

  1. 通过字符串定位到check处,发现调用了AES加密

image-20250209201437080

image-20250209201446065

  1. 交叉引用check函数发现生成key到地方,并hook出key

image-20250209201745715

function hook(){
    let SearchActivityKt = Java.use("com.atri.ezcompose.SearchActivityKt");
    SearchActivityKt["check"].implementation = function (text, context, key) {
        showStack();
        console.log(`SearchActivityKt.check is called: text=${text}, context=${context}, key=${key}`);
        this["check"](text, context, key);
    };

}

  1. 进行分析发现AES GCM加密,IV为114514,最后调用base64进行编码

image-20250209202808046

image-20250209202851990

  1. 由于base64加密的时候将iv也加密了解密后需要去除iv进行aes解密
from Crypto.Cipher import AES
import base64
key = bytes([97,116,114,105,107,101,121,115,115,121,101,107,105,114,116,97])
enc = base64.b64decode("MTE0NTE0HMuJKLOW1BqCAi2MxpHYjGjpPq82XXQ/jgx5WYrZ2MV53a9xjQVbRaVdRiXFrSn6EcQPzA==").replace(b"114514",b"")

a = AES.new(key=key,mode=AES.MODE_GCM,nonce=b"114514")
print(a.decrypt(enc))

Hook Fish

  1. 监听下载按钮,点击后先对输入调用encrypt函数进行加密,然后调用fish函数下载dex

image-20250209203827091

image-20250209203912958

  1. 在下载完成后调用loadclass函数通过反射调用encode和check函数,再删除下载的dex文件

image-20250209204024541

image-20250209204037644

  1. 在string里面可以找到url下载后进行分析

image-20250209204223570

  1. encode函数就是一个字符替换

image-20250209204523290

  1. 程序提供了解密函数直接复制调用就行得到结果0qksrtuw0x74r2n3s2x3ooi4ps54r173k2os12r32pmqnu73r1h432n301twnq43prruo2h5

image-20250209204610760

  1. EXP
import binascii

enc = "0qksrtuw0x74r2n3s2x3ooi4ps54r173k2os12r32pmqnu73r1h432n301twnq43prruo2h5"
flag = ""
for i in range(len(enc)):
    for j in range(32,127):
        if ord("a") <= j <= ord("f"):
            res = chr(((j - 0x31) + (i % 4)))
        else:
            res = chr(((j + 0x37) + (i % 10)))
        if res == enc[i]:
            flag += chr(j)
            break
flag_list = [ord(i) for i in flag]
for i in range(0,len(flag),2):
    flag_list[i] ^= flag_list[i + 1]
    flag_list[i + 1] ^= flag_list[i]
    flag_list[i] ^= flag_list[i + 1]

res = binascii.a2b_hex(bytes(flag_list).decode())

for i in res:
    print(chr(i - 68),end="")

抽奖转盘

  1. 一开始看java层没看见啥东西,先看so提供字符串定位到了关键代码

image-20250209210345078

  1. 首先读取到输入后转成C语言字符串后每位➕3

image-20250209210548965

  1. 调用RC4函数和BASE64加密,RC4多xor了一个40

image-20250209210647062

image-20250209210705149

  1. so层没有看见check函数再来java层看看发现一个数组,转为字符串后发现不是base64加密后的正常字符集,并且base64没有魔改好明显有算法对base64对结果加密了翻看后可以定位到

image-20250209210756149

image-20250209210931525

  1. EXP
import base64
from Crypto.Cipher import ARC4
enc = [101, 74, 76, 49, 101, 76, 117, 87, 55, 69, 118, 68, 118, 69, 55, 67, 61, 83, 62, 111, 81, 77, 115, 101, 53, 73, 83, 66, 68, 114, 109, 108, 75, 66, 97, 117, 93, 127, 115, 124, 109, 82, 93, 115]
for i in range(len(enc)):
    enc[i] = (enc[i] ^ 7) - 1
print(bytes(enc))
res = base64.b64decode(bytes(enc).decode())

key = b"Take_it_easy"
a = ARC4.new(key=key)
res = a.decrypt(res)

for i in res:
    print(chr((i ^ 40) - 3),end="")

AndroidLux

  1. 调用函数创建本地socket连接并且将输入的字符串发生到服务端,服务端返回数据后调用check函数判断返回到字符是不是ok,现在需要寻找到服务端

image-20250209211318944

image-20250209211532371

  1. 在翻看代码的时候发现执行了apk缓存目录下的一个env文件

image-20250209213707917

  1. 使用MT管理器将files文件设为adb权限即可将缓存区文件pull到本地发现这是一个linux的环境,后面发现这个Linux环境是assets目录下的env压缩包在init函数中解压到缓存区的

image-20250209214059655

  1. 有花指令nop发现这个env就是socket的服务端进行了一个魔改base64加密

image-20250209214149780

image-20250209214202341

  1. 解密发现解密不出猜测还有东西改变了结果翻看文件夹的时候发现了libexec.so的文件发现他hook了read和strncmp函数

image-20250209215019838

image-20250209215117363

  1. EXP
enc = "RPVIRN40R9PU67ue6RUH88Rgs65Bp8td8VQm4SPAT8Kj97QgVG"

for i in range(len(enc)):
    if ord('M') < ord(enc[i]) <= ord("Z"):
        print(chr(ord(enc[i]) - 13),end="")
    elif ord('m') < ord(enc[i]) <= ord("z"):
        print(chr(ord(enc[i]) - 13),end="")
    elif ord('A') <= ord(enc[i]) <= ord("M"):
        print(chr(ord(enc[i]) + 13), end="")
    elif ord('a') <= ord(enc[i]) <= ord("m"):
        print(chr(ord(enc[i]) + 13), end="")
    else:
        print(enc[i],end="")
print()
table = "TUVWXYZabcdefghijABCDEF456789GHIJKLMNOPQRSklmnopqrstuvwxyz0123+/"
enc = "ECIVEA40E9CH67hr6EHU88Etf65Oc8gq8IDz4FCNG8Xw97DtIT"
index = [table.index(i) for i in enc]
index.append(0)
index.append(0)
flag = []

for i in range(0,len(index),4):
    a = index[i]
    b = index[i + 1]
    c = index[i + 2]
    d = index[i + 3]
    flag.append(((a & 0b111111) << 2) | (b & 0b11))
    flag.append((((b >> 2) & 0xf) << 4) | (c & 0xf))
    flag.append ((((c >> 4) & 0b11) << 6) | d)
for i in flag:
    print(chr(i ^ 1),end="")