MoeCTF-2024-wp
- IT业界
- 2025-09-17 15:51:01

Crypto 1、Signin
考点:RSA (1)正常的rsa解密
2、Big and small考点:RSA (1)低解密指数攻击
3、ez_hash考点:hash (1)简单的hash爆破
from hashlib import sha256 from tqdm import trange hash_value = '3a5137149f705e4da1bf6742e62c018e3f7a1784ceebcb0030656a2b42f50b6a' for i in trange(1000000): tmp = sha256(("2100" + str(i).zfill(6)).encode()).hexdigest() if tmp == hash_value: print(f"{i = }") 4、More_secure_RSA(1)题目:
from Crypto.Util.number import * flag = b'moectf{xxxxxxxxxxxxxxxxx}' m = bytes_to_long(flag) p = getPrime(1024) q = getPrime(1024) n = p * q e = 0x10001 c = pow(m, e, n) print(f'c = {c}') print(f'n = {n}') r = getPrime(1024) n = n * r=q*p*r c = pow(m, e, n) print(f'C = {c}') print(f'N = {n}')由于 ⟨n,N⟩ 已知,我们可以求出 r=N/n;考虑将 C 模 r,得到 C mod r=m^e modr;可以推导出m=m modr
from Crypto.Util.number import * assert N1 % n1 == 0 r = N1//n1 assert isPrime(r) fake_flag = b'moectf{????????????????????????}' assert bytes_to_long(fake_flag) <= r dr = inverse(e, r-1) m = pow(C1,dr,r) flag = long_to_bytes(m) 5、ezlegendre考点: 经过欧拉判别法验证, aa 是 pp 的二次非剩余, 而 a+1a+1 是 pp 的二次剩余,我们给定的 ee 是一个奇数,根据勒让德符号可知,最后得到的cipertext[i],也会是相对应的二次剩余或者是二次非剩余,遍历一遍通过欧拉判法可以得到最终的结果。
from sage.all import * from Crypto.Util.number import * ''' p = 303597842163255391032954159827039706827 a = 34032839867482535877794289018590990371 ciphertext = [……] ''' msg_bin = '' for i in range(len(ciphertext)): msg_bin += '1' if pow(ciphertext[i], (p - 1) // 2, p) == 1 else '0' flag = long_to_bytes(int(msg_bin,2)) print(flag) # Flag: moectf{minus_one_1s_n0t_qu4dr4tic_r4sidu4_when_p_mod_f0ur_equ41_to_thr33} 6、RSA_revenge考点:rsa (1)题目的核心代码是这个
def emirp(x): y = 0 while x !=0: y = y*2 + x%2 x = x//2 return y通过代码,我们发现用这个函数生成的p和q他们在二进制上相反的,比如p是1010,那么q就是0101。除此之外,我们会发现p和q的低位乘起来就是n的低位。通过这些性质我们可以直接开始进行爆破。
from Crypto.Util.number import* from gmpy2 import * n = 141326884939079067429645084585831428717383389026212274986490638181168709713585245213459139281395768330637635670530286514361666351728405851224861268366256203851725349214834643460959210675733248662738509224865058748116797242931605149244469367508052164539306170883496415576116236739853057847265650027628600443901 c = 47886145637416465474967586561554275347396273686722042112754589742652411190694422563845157055397690806283389102421131949492150512820301748529122456307491407924640312270962219946993529007414812671985960186335307490596107298906467618684990500775058344576523751336171093010950665199612378376864378029545530793597 def blast(a, b, k): if k == 256: if a*b == n: print((a,b)) return for i in range(2): for j in range(2): a1 = a + i*(2**k) + j*(2**(511-k)) b1 = b + j*(2**k) + i*(2**(511-k)) if a1*b1 > n: continue if (a1+(2**(511-k)))*(b1+(2**(511-k))) < n: continue if ((a1*b1)%(2**(k+1))) != (n%(2**(k+1))): continue blast(a1, b1, k+1) for i in range(2): blast(i*(2**256), i*(2**256), 0) p,q = (12119998731259483292178496920109290754181396164390285597126378297678818779092115139911720576157973310671490865211601201831597946479039132512609504866583931, 11660635291534613230423193509391946961264539191735481147071890944740311229658362673314192872117237108949853531941630122241060679012089130178372253390640871) assert p*q == n phi = (p-1)*(q-1) e = 65537 d = pow(e,-1,phi) m = pow(c,d,n) print(long_to_bytes(m)) Reverse 1、dynamic考点:XXTEA (1)这其实是一个标准的XXTEA,当n>1的时候(也就是+12)的时候,是加密,-12就是解密。 所以这个程序其实在做一个事情:把内置的flag进行解密后又马上加密回去,所以我们只需要动态调试,找到解密后的flag即可
2、upx考点:UPX (1)正常的UPX脱壳;再拖入IDA,即可查看正确的flag
3、xor考点:XOR (1)简单的XOR操作
enc = [ 73, 75, 65, 71, 80, 66, 95, 65, 28, 22, 70, 16, 19, 28, 64, 9, 66, 22, 70, 28, 9, 16, 16, 66, 29, 9, 70, 21, 20, 20, 9, 23, 22, 20, 65, 64, 64, 22, 20, 71, 18, 64, 20, 89 ] result = ''.join([chr(x ^ 0x24) for x in enc]) print(result) 4、逆向工程入门指北考点:签到 (1)给了一个程序;运行就是flag
5、d0tN3t考点:XOR (1)给出的是dll文件;需要用dnSpy逆向分析:加密逻辑
(byte)((int)((byte)text[i] + 0x72 ^ 0x72) ^ i * i)写出解密脚本即可
enc = [173, 146, 161, 174, 132, 179, 187, 234, 231, 244, 177, 161, 65, 13, 18, 12, 166, 247, 229, 207, 125, 109, 67, 180, 230, 156, 125, 127, 182, 236, 105, 21, 215, 148, 92, 18, 199, 137, 124, 38, 228, 55, 62, 164] for i in range(len(enc)): print(chr(((i*i ^ 114 ^ enc[i]) - 114) & 0xff), end='') # moectf{7ce581d2-b2ab-4ceb-9bbe-435873083db6} 6、rc4考点:rc4 (1)显然题目说了是Rc4,并且RC4_1s_4w3s0m3很可能是密钥,我们直接拿上面的HEX进行解密就出了
def rc4_init(key): S = list(range(256)) j = 0 for i in range(256): j = (j + S[i] + key[i % len(key)]) % 256 S[i], S[j] = S[j], S[i] return S def rc4_crypt(S, data): i = j = 0 result = bytearray() for byte in data: i = (i + 1) % 256 j = (j + S[i]) % 256 S[i], S[j] = S[j], S[i] result.append(byte ^ S[(S[i] + S[j]) % 256]) return bytes(result) encrypted_data = bytes.fromhex('A71A68ECD82711CC8C9B16155CD2673E82ADCE75D4BC5756C28A52B86BD6CCF8A4BA722FE05715B92411') key = b"RC4_1s_4w3s0m3" S = rc4_init(key) decrypted_data = rc4_crypt(S, encrypted_data) print(decrypted_data.decode('utf-8', errors='replace')) 7、upx-revengekao考点:魔改UPX (1)魔改UPX;脱壳机无法正常脱壳;猜测可能修改了头部或者特征码 (2)这个程序直接upx -d是显示无法进行解压的,原因是UPX解压的时候会检测程序段名称是否为UPX0\UPX1这类的,但是我改成了vmp0,导致无法直接解压,我们使用010Editor打开后更改这个字符串也就可以解密;或者动态调试就能看到flag
8、xtea考点:tea加密 (1)xtea加密,这里key是12个字节,先对前八个字节加密后,将加密后的后四个字节与后四个字节再次加密 (2)很明显了;需要将enc内容还原得到Key就是正确的flag 加密函数:非常明显的tea加密 (3)解密
from typing import List, Tuple from struct import unpack from ctypes import c_uint32 # 将字节数组转换为 32 位整数列表 def byte2dword(x: List[int]) -> List[int]: if len(x) % 4 != 0: if type(x) == bytes: x += b'\x00' * (4 - (len(x) % 4)) else: x += [0] * (4 - (len(x) % 4)) return [v[0] for v in (unpack('<I', bytes(x[i:i+4])) for i in range(0, len(x), 4))] # XTEA 解密函数 def xtea_decrypt( src: Tuple[int, int], key: List[int], delta: int = 0x9E3779B9, rounds: int = 32 ) -> Tuple[int, int]: l, r = c_uint32(src[0]), c_uint32(src[1]) sum = c_uint32(delta * rounds) k = [c_uint32(key[0]), c_uint32(key[1]), c_uint32(key[2]), c_uint32(key[3])] for _ in range(rounds): r.value -= (((l.value << 4) ^ (l.value >> 5)) + l.value) ^ ( sum.value + k[(sum.value >> 11) & 3].value ) sum.value -= delta l.value -= (((r.value << 4) ^ (r.value >> 5)) + r.value) ^ ( sum.value + k[sum.value & 3].value ) return (l.value, r.value) # 将 32 位整数列表转换为字节数组 def dword2byte(x: List[int]) -> bytes: result = [] if type(x) == int: for j in range(4): result.append((x >> j*8) & 0xff) return bytes(result) for i in range(len(x)): for j in range(4): result.append((x[i] >> j*8) & 0xff) return bytes(result) # 加密后的数据 enc = [0xA3, 0x69, 0x96, 0x26, 0xBD, 0x78, 0x0B, 0x3D, 0x9D, 0xA5, 0x28, 0x62] # 密钥 key = [2, 0, 2, 4] # 将字节数组转换为 32 位整数 dw1, dw2, dw3 = byte2dword(enc) # 解密 (dw2, dw3) dw2, dw3 = xtea_decrypt((dw2, dw3), key=key, delta=-0x33004445, rounds=32) # 解密 (dw1, dw2) dw1, dw2 = xtea_decrypt((dw1, dw2), key=key, delta=-0x33004445, rounds=32) # 将 32 位整数转换回字节数组 original_data = dword2byte([dw1, dw2, dw3]) # 输出结果 print("解密后的原始输入:", original_data.decode('utf-8', errors='replace')) 9、xxtea考点:xxtea
#include <stdio.h> #include <string.h> #include <stdint.h> #define DELTA 0x9e3779b9 #define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[((p&3)^e)&0xff] ^ z))) void btea(uint32_t* v, int n, const uint32_t key[4]) { uint32_t y, z, sum; unsigned p, rounds, e; if (n > 1) /* Coding Part */ { rounds = 6 + 52 / n; sum = 0; z = v[n - 1]; do { sum += DELTA; e = (sum >> 2) & 3; for (p = 0; p < n - 1; p++) { y = v[p + 1]; z = v[p] += MX; } y = v[0]; z = v[n - 1] += MX; } while (--rounds); } else if (n < -1) /* Decoding Part */ { n = -n; rounds = 6 + 52 / n; sum = rounds * DELTA; y = v[0]; do { e = (sum >> 2) & 3; for (p = n - 1; p > 0; p--) { z = v[p - 1]; y = v[p] -= MX; } z = v[n - 1]; y = v[0] -= MX; sum -= DELTA; } while (--rounds); } } int main() { unsigned char enc[] = { 0x64,0xf5,0xe1,0x78,0xe1,0xf0, 0x35,0xa8,0x34,0xff,0x12,0x05, 0xfb,0x13,0xe9,0xb0,0x50,0xa3, 0xb9,0x89,0xb1,0xda,0x43,0xc9, 0x4f,0xc8,0xdb,0x01,0x20,0xdb, 0x16,0xaf,0xed,0x67,0x17,0x96 }; int r = 9; unsigned char input_key[13] = "moectf2024!!"; // Adjusted size to fit 12 characters + null terminator const uint32_t k[4] = { *((uint32_t*)input_key),*((uint32_t*)(input_key + 4)), *((uint32_t*)(input_key + 8)),0xccffbbbb }; btea(((uint32_t*)enc),-r,k); //moectf{j9h8hg75nky6vhkslh5v5awibr4i} //btea(((uint32_t*)input), -r, ((uint32_t*)input_key)); for (int i = 0; i < 36; i++) { printf("%c",enc[i]); } return 0; } 10、Just-Run-It考点:四个可执行文件 (1)0x0.exe:先脱壳;得到第一部分:6257396c5933526d657a55355a6d45 (2)0x1.elf 运行得到第二部分
moectf2024@xdsec ~> cat /flag.1 324d444a6a4c5459794e4745744e44(3)0x2.apk;安装并运行后
moectf2024@xdsec ~> cat /flag.2 42694e7930345954566a4c57557a4e(4)0x3 riscv ELF64程序 尝试运行发现错误;linux下运行64elf文件需要安装qemu-user
qemu-riscv64 ./0x3.riscv64.elf最后
6257396c5933526d657a55355a6d45324d444a6a4c5459794e4745744e4442694e7930345954566a4c57557a4e bW9lY3RmezU5ZmE2MDJjLTYyNGEtNDBiNy04YTVjLWUzN 再加上最后0x3的WU1NzRjZjliOX0= bW9lY3RmezU5ZmE2MDJjLTYyNGEtNDBiNy04YTVjLWUzNWU1NzRjZjliOX0= base64 decode # moectf{59fa602c-624a-40b7-8a5c-e35e574cf9b9} 11、TEA考点:TEA (1)最简单的TEA加密 解密
def decrypt(v, k): v0, v1 = v delta = 0x9e3779b9 sum_ = delta * 32 k0, k1, k2, k3 = k for _ in range(32): v1 -= ((v0 << 4) + k2) ^ (v0 + sum_) ^ ((v0 >> 5) + k3) v1 &= 0xFFFFFFFF # Ensure 32-bit overflow v0 -= ((v1 << 4) + k0) ^ (v1 + sum_) ^ ((v1 >> 5) + k1) v0 &= 0xFFFFFFFF # Ensure 32-bit overflow sum_ -= delta return v0, v1 def main(): # Encrypted data v = [0x284c2234, 0x3910c558] # Key (converted from "base64xorteaxtea" to 4 uint32 values) key = [ 0x62617365, # "base" 0x78363478, # "x64x" 0x6f727465, # "orte" 0x61787465 # "axte" ] # Decrypt the data v0, v1 = decrypt(v, key) # Format the flag flag = f"moectf{{{v0:08x}-{v1 >> 16:04x}-{v1 & 0xFFFF:04x}-9c42-caf30620caaf}}" print(flag) if __name__ == "__main__": main() 12、ezMAZE考点:迷宫 (1)先脱壳;输入w、a、s、d来移动;最终到达目标位置 (75, 55) 以赢得游戏并获取 flag;现在就需要找到地图
__int64 __fastcall sub_140001190(int a1, int a2) { unsigned __int8 v3; // [rsp+0h] [rbp-28h] if ( a1 > 80 || a2 > 56 || a1 < 1 || a2 < 1 ) return 1i64; v3 = word_140005000[10 * a2 - 10 + (a1 - 1) / 8]; word_140005000[10 * a2 - 10 + (a1 - 1) / 8] = (1 << (7 - (a1 - 1) % 8)) | v3; return ((int)v3 >> (7 - (a1 - 1) % 8)) & 1; }(2)找到地图之后按照main函数编写脚本拿到路径然后就可以得到flag moectf{the_18446744024826406994_amazing_maze!!}
MoeCTF-2024-wp由讯客互联IT业界栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“MoeCTF-2024-wp”