# HSCCTF-2024 Writeup

# 前言

别的方向没怎么看,单看 Re 感觉质量有待提高吧

# Misc[1/8]

# SIGN_IN

先从 jpg 里提取一张图片,得到 HAPPY_NEW_YEAR ,然后用 outguess 得到 flag

# Crypto[1/8]

# FUNNY

题目下下来跑一下就出 flag 了...

# CheckIn[1/1]

# CHECKIN

关注两个公众号

# Re[6/6]

# TEA

朴实无华的 tea

#include <stdio.h>
#include <stdint.h>
void decrypt(uint32_t *v, uint32_t *k)
{
    uint32_t v0 = v[0], v1 = v[1], i;
    uint32_t delta = 0x9e3779b9;
    uint32_t sum = delta * 32;
    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()
{
    uint32_t key[] = {2, 2, 3 ,4};
    uint32_t enc[] = {0xb805d767, 0x63c174c3};
    decrypt(enc, key);
    for (size_t i = 0; i < 2; i++)
        printf("%x ", enc[i]);
}

# ROULETTE

简单扫一眼逻辑,发现在 sub_A5870 下的 return sub_9D0F3(); 就是调用解密函数的地方
img.png

那么就直接调试,改 eip 直接飞过去就好啦
img.png
HSCCTF{H31L0_My_FR13ND!}

# THE_WOLF_SONG

没啥难度,就一个 rc4 然后异或来异或去

#include <stdio.h>
#include <string.h>
void rc4_init(unsigned char *s, unsigned char *key, unsigned long Len)
{
    int i = 0, j = 0;
    char k[256] = {0};
    unsigned char tmp = 0;
    for (i = 0; i < 256; i++)
    {
        s[i] = i;
        k[i] = key[i % Len];
    }
    for (i = 0; i < 256; i++)
    {
        j = (j + s[i] + k[i]) % 256;
        tmp = s[i];
        s[i] = s[j];
        s[j] = tmp;
    }
}
void rc4_crypt(unsigned char *s, unsigned char *data, unsigned long Len)
{
    int i = 0, j = 0, t = 0;
    unsigned long k = 0;
    unsigned char tmp;
    for (k = 0; k < Len; k++)
    {
        i = (i + 1) % 256;
        j = (j + s[i]) % 256;
        tmp = s[i];
        s[i] = s[j];
        s[j] = tmp;
        t = (s[i] + s[j]) % 256;
        data[k] ^= s[t];
    }
}
unsigned char enc[] = {
    0xce, 0x26, 0x9c, 0x7, 0x48, 0xd9, 0xfd, 0x23, 0xba, 0x9a,
    0x40, 0xa8, 0x2e, 0xbd, 0xfc, 0x77, 0xb7, 0x5d, 0x7e, 0x67,
    0x99, 0xfd, 0xcd, 0x63, 0x13, 0xa, 0x94, 0x5b, 0x95, 0x2c,
    0x26, 0x60, 0x1e, 0x1e, 0xb4, 0x30, 0x89, 0xcf, 0xef, 0x68
};
unsigned char *key = "HSCCTF{FAKE_FLAG!}";
int main()
{
    for (size_t i = 0; i < 40; i++)
        enc[i] ^= i;
    for (size_t i = 0; i < strlen(key) - 1; i++)
        enc[i] ^= key[i + 1];
    for (size_t i = 0; i < strlen(key); i++)
        enc[i] ^= key[i];
    for (size_t i = 0; i < 40; i++)
        enc[i] ^= i;
    
    unsigned char s[256] = {0};
    rc4_init(s, key, strlen(key));
    rc4_crypt(s, enc, 40);
    for (size_t i = 0; i < 40; i++)
        enc[i] ^= 3;
    
    for (size_t i = 0; i < 40; i++)
        enc[i] ^= 6;
    for (size_t i = 0; i < 40; i++)
        printf("%c", enc[i]);   
}

HSCCTF{Welcome_To_Participate_In_HSCCTF}

# NO_PY

BeginCTF2024 的原题,直接贴 exp 了

from gmssl import sm4
import base64
key = list('Please_Do_not_py')
key = "".join([chr(ord(i) ^ 102) for i in key])
enc = 'YJ+70VWYioYm3EhF6qdScVXBpCdPm+hCS/HP+Gj421RyxkZbwzni7o2zGG/Mis4Wr6sbXYr4ufnKwQk7vrG8yA=='
def pad_pkcs7(data):
    '''PKCS#7填充'''
    padding_len = 16 - len(data) % 16
    padding = bytes([
        padding_len] * padding_len)
    return data + padding
def unpad_pkcs7(padded_data):
    '''PKCS#7去填充'''
    padding_len = padded_data[-1]
    return padded_data[:-padding_len]
class SM4:
    
    def __init__(self):
        self.gmsm4 = sm4.CryptSM4()
    
    def encryptSM4(self, encrypt_key, value):
        gmsm4 = self.gmsm4
        gmsm4.set_key(encrypt_key.encode(), sm4.SM4_ENCRYPT)
        padded_value = pad_pkcs7(value.encode())
        encrypt_value = gmsm4.crypt_ecb(padded_value)
        return base64.b64encode(encrypt_value)
    
    def decryptSM4(self, key, value):
        data = base64.b64decode(value)
        gmsm4 = self.gmsm4
        gmsm4.set_key(key.encode(), sm4.SM4_DECRYPT)
        decrypt_value = gmsm4.crypt_ecb(data)
        return decrypt_value
s = SM4()
flag = s.decryptSM4(key, enc)
print(unpad_pkcs7(flag).decode())

flag{Pay_M0re_@ttention_to_th3_key!!}

# VM_RE

简单的 vm,动调分析一下看看,实际上就用了仨指令
相当于 (input[i] + 0xA) ^ 0x33

0x39 mov 
0x4D add
0x26 xor
; mov
opcode = a1;
reg_4 = a1[e_ip + 1];
e_ip += 2;
chr = reg_4;
; add
opcode = a1;
reg_4 += a1[e_ip + 1];
e_ip += 2;
chr = reg_4;
; xor
opcode = a1;
reg_4 ^= a1[e_ip + 1];
e_ip += 2;
chr = reg_4;

直接解就好了

#include <stdio.h>
#include <stdint.h>
int main()
{
    uint32_t enc[9];
    enc[0] = 0x42584543;
    enc[1] = 0xF4E58B6;
    enc[2] = 0x70B37143;
    enc[3] = 0x72735D4E;
    enc[4] = 0x5E700E0F;
    enc[5] = 0x5D0C4E72;
    enc[6] = 0x8097071;
    enc[7] = 0x5D73725D;
    enc[8] = 0x39B4;
    for (size_t i = 0; i < 34; i++)
    {
        printf("%c", ((*((char*)enc + i)) & 0xff ^ 0x33) - 10);
    }
    
}

# ANDROID

分析一下 java 层,读取一个图片的数据,然后调用 native 函数,传入输入的内容和图片数据
img.png

分析一下 so 层,图片数据使用 murmur3_32 哈希算法,得到一个 32 字节的哈希,作为 key

encode_fun 是 SM4,key 的前 16 字节加密前 16 个字节的数据,后面 16 个字节加密后面 16 字节的数据
img.png

先 frida hook 一下 murmur3_32 的返回值拿个 key

var func_addr = Module.findExportByName("libmidand.so" , "_Z10murmur3_32PKcjj");
console.log("func addr is ---" + func_addr);
Interceptor.attach(func_addr, {
    onLeave: function(args){
        console.log("enter murmur3_32 retvalue->\n" + hexdump(args));
    }
});
/*
func addr is ---0x7f134457ae40
[PGJM10::midand ]-> enter murmur3_32 retvalue->
               0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
7f13c97b0d00  c4 83 84 72 b8 e1 60 ba 5d 99 5a 6b e3 67 40 17  ...r..`.].Zk.g@.
7f13c97b0d10  7c 3f 33 21 91 1c fa 54 8f 35 30 73 dd 2b 80 a7  |?3!...T.50s.+..
*/

直接解密就好啦

from gmssl import sm4
enc = bytearray.fromhex("731E133EF76A5CD1EF9626A9947CF4A46CE237B70D4905E921E35E2E7D7A1A74")
"""
               0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
7f13c97b0d00  c4 83 84 72 b8 e1 60 ba 5d 99 5a 6b e3 67 40 17  ...r..`.].Zk.g@.
7f13c97b0d10  7c 3f 33 21 91 1c fa 54 8f 35 30 73 dd 2b 80 a7  |?3!...T.50s.+..
"""
key1 = bytearray.fromhex("c4838472b8e160ba5d995a6be3674017")
key2 = bytearray.fromhex("7c3f3321911cfa548f353073dd2b80a7")
s = sm4.CryptSM4(padding_mode=sm4.zero_padding)
s.set_key(key1, sm4.SM4_DECRYPT) 
dec = s.crypt_ecb(enc[:16]) 
print(dec.decode(), end="")
s.set_key(key2, sm4.SM4_DECRYPT) 
dec = s.crypt_ecb(enc[16:]) 
print(dec.decode(), end="")

flag{fad1c7e27ec411eebe3a3e4419a1b3cc}

# BlockChain[1/1]

# BONUS

题目要求让余额为 1,看一眼合约,整型溢出漏洞,余额类型为 uint256, 2^256+1 就可以溢出到 1 了

// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
contract VipCard {
    mapping(address => uint) public balances;
    function applyCard() public {
        balances[msg.sender] = 114514;
    }
    function topUp(uint _amount) public {
        balances[msg.sender] += _amount;
    }
    function buyCandy() public {
        require(balances[msg.sender] > 0, "not sufficient funds");
        balances[msg.sender] -= 1;
    }
}