week1 刷题
- week1 刷题
- 1. [GXYCTF2019]simple CPP
- 2. [网鼎杯 2020 青龙组]singal
- 3. [buuctf]firmware
- 4. [2019红帽杯]xx
1. [GXYCTF2019]simple CPP
IDA载入直接分析,这个程序的主要加密逻辑有几块:

第一块是一个异或,和key进行异或

第二部分是将8个字符拼成一个QWORD,然后存放到v20当中

最后就是将v20的值拿出来计算然后进行比较判断。在最后这个加密当中,运用了一大堆的运算,那么z3是最好的方式,直接z3求解即可。
这里有一个注意的地方是,我们并不知道输入有多长,只知道最后比较的长度是32字节,所以这里的z3需要从分组之后开始求解。求解出来后再回推input。这里试了一下从开头写z3到结尾会导致解出来的值不太一样。
- exp:
from z3 import*
f1, f2, f3, f4 = BitVecs('f1 f2 f3 f4', 64)
s = Solver()s.add(f3 & (~f1) == 0x11204161012)
s.add((f3 & (~f2)) & f1 | f3 & ((f2 & f1) | f2 & (~f1) | (~(f2 | f1))) == 0x8020717153E3013)
s.add((f3 & ~f1) | (f2 & f1) | (f3 & (~f2)) | (f1 & ~f2) == 0x3E3A4717373E7F1F)
s.add((((f3 & (~f1)) | (f2 & f1) | (f3 & (~f2)) | (f1 & ~f2)) ^ f4) == 0x3E3A4717050F791F)
s.add((((f3 & (~f1)) | (f2 & f1) | f2 & f3)) == (~f1 & f3 | 0xC00020130082C0C))
print(s.check())d2 = []
d = s.model()
flag1 = hex(d[f1].as_long())[2:].rjust(16, "0")
flag2 = hex(d[f2].as_long())[2:].rjust(16, "0")
flag3 = hex(d[f3].as_long())[2:].rjust(16, "0")
flag4 = hex(d[f4].as_long())[2:-2]def decode(flag, d): le = len(flag)for i in range(0, le, 2):if flag[i:i+2] != "": d.append(int(flag[i:i+2], 16)) decode(flag1, d2)
decode(flag2, d2)
decode(flag3, d2)
decode(flag4, d2)
key = 'i_will_check_is_debug_or_not'
fake_flag = ""
for i in range(len(d2)):fake_flag += chr(ord(key[i]) ^ d2[i])
print(fake_flag)
# We1l_D0ndeajoa_Slgebra_am_iright_flag = ''
right_flag += fake_flag[:8]
right_flag += 'e!P0or_a' # 多解,比赛的时候hint需要将这个替换上去
right_flag += fake_flag[16:]
print(right_flag)# flag{We1l_D0ne!P0or_algebra_am_i}
2. [网鼎杯 2020 青龙组]singal
非常基础的一道vm题,直接ida分析:

使用idapython将code字节码抓出来:
start = 0x403040
print("[")
for i in range(455):print(ida_bytes.get_byte(start+i), end=", ")
print("\n]")

然后直接将vm逻辑用python抄出来,z3配合直接求解出来:
from z3 import *s = Solver()
x = [BitVec(f"x{i}",8) for i in range(15)]opcode = [
10, 0, 0, 0, 4, 0, 0, 0, 16, 0, 0, 0, 8, 0, 0, 0, 3, 0, 0, 0, 5, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 32, 0, 0, 0, 8, 0, 0, 0, 5, 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 2, 0, 0, 0, 8, 0, 0, 0, 11, 0, 0, 0, 1, 0, 0, 0, 12, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 3, 0, 0, 0, 8, 0, 0, 0, 3, 0, 0, 0, 33, 0, 0, 0, 1, 0, 0, 0, 11, 0, 0, 0, 8, 0, 0, 0, 11, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 9, 0, 0, 0, 8, 0, 0, 0, 3, 0, 0, 0, 32, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 81, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 36, 0, 0, 0, 1, 0, 0, 0, 12, 0, 0, 0, 8, 0, 0, 0, 11, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 2, 0, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, 37, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 54, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 65, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 32, 0, 0, 0, 8, 0, 0, 0, 5, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 3, 0, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, 37, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 9, 0, 0, 0, 8, 0, 0, 0, 3, 0, 0, 0, 32, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 8, 0, 0, 0, 12, 0, 0, 0, 1, 0, 0, 0, 7, 0, 0, 0, 34, 0, 0, 0, 7, 0, 0, 0, 63, 0, 0, 0, 7, 0, 0, 0, 52, 0, 0, 0, 7, 0, 0, 0, 50, 0, 0, 0, 7, 0, 0, 0, 114, 0, 0, 0, 7, 0, 0, 0, 51, 0, 0, 0, 7, 0, 0, 0, 24, 0, 0, 0, 7, 0, 0, 0, 167, 255, 255, 255, 7, 0, 0, 0, 49, 0, 0, 0, 7, 0, 0, 0, 241, 255, 255, 255, 7, 0, 0, 0, 40, 0, 0, 0, 7, 0, 0, 0, 132, 255, 255, 255, 7, 0, 0, 0, 193, 255, 255, 255, 7, 0, 0, 0, 30, 0, 0, 0, 7, 0, 0, 0, 122, 0, 0, 0, 0
]v9 = 0
Str = [0] * 1000
end = 114
v8 = 0
v7 = 0
v6 = 0
v5 = 0
v4 = 0
while v9 < end:op = opcode[v9 * 4]if op == 1:Str[v6+100]=v4v9 += 1v6 += 1v8 += 1elif op == 2:v4 = Str[v8] + opcode[(v9 + 1)*4]v9 += 2elif op == 3:v4 = Str[v8] - opcode[(v9 + 1)*4]v9 += 2elif op == 4:v4 = opcode[(v9 + 1)*4] ^ Str[v8]v9 += 2elif op == 5:v4 = opcode[(v9 + 1)*4] * Str[v8]v9 += 2elif op == 6:v9 += 1elif op == 7:print(Str[v7 + 100])s.add(Str[v7 + 100] == opcode[(v9 + 1)*4])v9 += 2v7 += 1elif op == 8:Str[v5] = v4v9 += 1v5 += 1elif op == 10:print("Read input")for i, v in enumerate(x):Str[i] = vv9 += 1elif op == 11:v4 = Str[v8] - 1v9 += 1elif op == 12:v4 = Str[v8] + 1v9 += 1else:print(f"unknown insn {op}")print(s.check())
if s.check():m = s.model()for i in x:val = m.eval(i, model_completion=True)print(chr(val.as_long()), end="")from z3 import *s = Solver()
x = [BitVec(f"x{i}",8) for i in range(15)]opcode = [
10, 0, 0, 0, 4, 0, 0, 0, 16, 0, 0, 0, 8, 0, 0, 0, 3, 0, 0, 0, 5, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 32, 0, 0, 0, 8, 0, 0, 0, 5, 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 2, 0, 0, 0, 8, 0, 0, 0, 11, 0, 0, 0, 1, 0, 0, 0, 12, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 3, 0, 0, 0, 8, 0, 0, 0, 3, 0, 0, 0, 33, 0, 0, 0, 1, 0, 0, 0, 11, 0, 0, 0, 8, 0, 0, 0, 11, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 9, 0, 0, 0, 8, 0, 0, 0, 3, 0, 0, 0, 32, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 81, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 36, 0, 0, 0, 1, 0, 0, 0, 12, 0, 0, 0, 8, 0, 0, 0, 11, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 2, 0, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, 37, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 54, 0, 0, 0, 8, 0, 0, 0, 4, 0, 0, 0, 65, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 32, 0, 0, 0, 8, 0, 0, 0, 5, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 5, 0, 0, 0, 3, 0, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, 37, 0, 0, 0, 1, 0, 0, 0, 4, 0, 0, 0, 9, 0, 0, 0, 8, 0, 0, 0, 3, 0, 0, 0, 32, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 8, 0, 0, 0, 12, 0, 0, 0, 1, 0, 0, 0, 7, 0, 0, 0, 34, 0, 0, 0, 7, 0, 0, 0, 63, 0, 0, 0, 7, 0, 0, 0, 52, 0, 0, 0, 7, 0, 0, 0, 50, 0, 0, 0, 7, 0, 0, 0, 114, 0, 0, 0, 7, 0, 0, 0, 51, 0, 0, 0, 7, 0, 0, 0, 24, 0, 0, 0, 7, 0, 0, 0, 167, 255, 255, 255, 7, 0, 0, 0, 49, 0, 0, 0, 7, 0, 0, 0, 241, 255, 255, 255, 7, 0, 0, 0, 40, 0, 0, 0, 7, 0, 0, 0, 132, 255, 255, 255, 7, 0, 0, 0, 193, 255, 255, 255, 7, 0, 0, 0, 30, 0, 0, 0, 7, 0, 0, 0, 122, 0, 0, 0, 0
]v9 = 0
Str = [0] * 1000
end = 114
v8 = 0
v7 = 0
v6 = 0
v5 = 0
v4 = 0
while v9 < end:op = opcode[v9 * 4]if op == 1:Str[v6+100]=v4v9 += 1v6 += 1v8 += 1elif op == 2:v4 = Str[v8] + opcode[(v9 + 1)*4]v9 += 2elif op == 3:v4 = Str[v8] - opcode[(v9 + 1)*4]v9 += 2elif op == 4:v4 = opcode[(v9 + 1)*4] ^ Str[v8]v9 += 2elif op == 5:v4 = opcode[(v9 + 1)*4] * Str[v8]v9 += 2elif op == 6:v9 += 1elif op == 7:print(Str[v7 + 100])s.add(Str[v7 + 100] == opcode[(v9 + 1)*4])v9 += 2v7 += 1elif op == 8:Str[v5] = v4v9 += 1v5 += 1elif op == 10:print("Read input")for i, v in enumerate(x):Str[i] = vv9 += 1elif op == 11:v4 = Str[v8] - 1v9 += 1elif op == 12:v4 = Str[v8] + 1v9 += 1else:print(f"unknown insn {op}")print(s.check())
if s.check():m = s.model()for i in x:val = m.eval(i, model_completion=True)print(chr(val.as_long()), end="")# Read input
# (16 ^ x0) - 5
# 3*(32 ^ x1)
# x2 - 2 - 1
# 4 ^ x3 + 1
# 3*x4 - 33
# x5 - 1 - 1
# (9 ^ x6) - 32
# 36 ^ x7 + 81
# x8 + 1 - 1
# 2*x9 + 37
# 65 ^ x10 + 54
# 1*(x11 + 32)
# 3*x12 + 37
# (9 ^ x13) - 32
# x14 + 65 + 1
# sat
# 757515121f3d478 <-- flag
3. [buuctf]firmware
这个是个路由器的固件题,有点misc的味道。下载题目之后是一个.bin文件,丢虚拟机用binwalk看看是个什么玩意。看起来有不少东西

使用binwalk直接将文件拆开:
binwalk -e 51475f91-7b90-41dd-81a3-8b82df4f29d0.bin

整出来之后发现文件夹当中有一个.squashfs的文件,这个文件是一种只读的压缩文件系统。可以使用firmware-mod-kit工具来获取解压缩,这里可以直接去github中将源码下载下来,然后丢到虚拟机,在src文件夹make一下,就可以使用了:
unsquashfs_all.sh 120200.squashfs
然后就能够发现多了一个root-1的文件夹,最后在这个文件夹当中/tmp找到了一个backdoor的文件,复制出来丢die看看有没有壳,发现有一个upx,直接工具脱一下壳,然后丢ida里面,字符串查找就能够发现有一个邮箱的字符串了:

接着直接交叉引用到调用的位置,可以发现这个v3其实就是端口号,如果传入的字串有自己的端口就使用自己的端口,没有的话就使用36667端口

最后直接将echo.bytehost51.com:36667进行md5提交即可。
4. [2019红帽杯]xx
直接丢IDA看看反汇编,main函数看起来非常的乱。开头就是一个输入长度的判断,输入如果不等于19直接退出:

然后直接动调看看关键位置:

第一个关键位置就是这里,这里的v30其实是输入的前4字节,然后传入了这个sub_140001AB0函数当中,进去看了一下发现是一个xxtea加密,然后key就是输入的前四个字节了:

继续往下分析:

这里很明显就是一个乱序了,接着继续往下:

最后这里很明显能看出来是一个异或的加密,而且加密过程是这样的,如果索引值 / 3大于0的话就和从0开始的字符进行逐个异或加密,这里解密的时候需要倒过来。
最后这一块就是比较了:

这里的几块变量的内存都是连续的所以这个就是密文。那么大致的逻辑就是:输入 --> xxtea --> 乱序 ---> 异或 --> 比较可以直接写exp了:
#pragma once
#include <iostream>
#include <stdio.h>
#include <vector>void decrypt_xor(unsigned char* enc)
{for (int i = 23; i > 0; i--){int j = 0;if (i / 3 > 0){do {enc[i] ^= enc[j++];} while (j < i / 3);}}
}void sort(unsigned char* enc)
{std::vector<unsigned char> v;for (int i = 0; i < 24; i++){v.push_back(enc[i]);}char index[] = { 2,0,3,1,6,4,7,5,10,8,11,9,14,12,15,13,18,16,19,17,22,20,23,21 };for (int i = 0; i < 24; i++){enc[index[i]] = v[i];}}uint8_t* tea_decrypt(const uint8_t* cipher, uint64_t cipherlen, const uint8_t* key) {if (!cipherlen || cipherlen < 4) return NULL;// 去掉可能的结尾 0uint64_t usable = cipherlen;if (cipher[cipherlen - 1] == 0 && (cipherlen % 4) == 1) {usable = cipherlen - 1;}if (usable % 4 != 0) return NULL;size_t n = (size_t)(usable / 4);// 组装 vuint32_t* v = (uint32_t*)calloc(n, sizeof(uint32_t));if (!v) return NULL;for (uint64_t i = 0; i < usable; i++) {size_t idx = (size_t)(i >> 2);size_t shift = (size_t)((i & 3) * 8);v[idx] |= ((uint32_t)cipher[i]) << shift;}// 组装 keyuint32_t k[4];for (int j = 0; j < 4; j++) {int base = j * 4;k[j] = (uint32_t)key[base]| ((uint32_t)key[base + 1] << 8)| ((uint32_t)key[base + 2] << 16)| ((uint32_t)key[base + 3] << 24);}// XXTEA 解密核心if (n > 1) {uint32_t delta = 0x9E3779B9;size_t rounds = 6 + 52 / n;uint32_t sum = (uint32_t)(rounds * delta);uint32_t z, y;while (sum) {uint32_t e = (sum >> 2) & 3;y = v[0];for (size_t p = n - 1; p > 0; p--) {z = v[p - 1];v[p] -= (((z >> 5) ^ (y << 2)) + ((y >> 3) ^ (z << 4)))^ ((sum ^ y) + (k[(p & 3) ^ e] ^ z));y = v[p];}z = v[n - 1];v[0] -= (((z >> 5) ^ (y << 2)) + ((y >> 3) ^ (z << 4)))^ ((sum ^ y) + (k[(0 & 3) ^ e] ^ z));sum -= delta;}}// 恢复原始长度uint32_t orig_len = v[n - 1];uint64_t max_plain = (uint64_t)((n - 1) * 4);if ((uint64_t)orig_len > max_plain) {free(v);return NULL;}// 导出明文uint8_t* out = (uint8_t*)malloc((uint64_t)orig_len);if (!out) {free(v);return NULL;}for (uint64_t i = 0; i < (uint64_t)orig_len; i++) {out[i] = (uint8_t)(v[i >> 2] >> (8 * (i & 3)));}free(v);return out;
}void decrypt()
{unsigned char enc[] ={0xCE, 0xBC, 0x40, 0x6B, 0x7C, 0x3A, 0x95, 0xC0, 0xEF, 0x9B,0x20, 0x20, 0x91, 0xF7, 0x02, 0x35, 0x23, 0x18, 0x02, 0xC8,0xE7, 0x56, 0x56, 0xFA};// 解密xordecrypt_xor(enc);printf("[*] : xor finish \r\n");// 乱序sort(enc);printf("[*] : sort success\r\n");// xxteachar key[16] = { 'f','l','a','g'};size_t size = 0;uint8_t* decrpyt = tea_decrypt((const uint8_t*)enc,24,(uint8_t*)key);printf("[*] : xxtea success\r\n");for (int i = 0; i < 19; i++)printf("%c", decrpyt[i]);
}// flag{CXX_and_++tea}