前言:
把攻防世界难度四全部整合了,有点难度,还好,脑洞题,有些挺简单的
作者-叁玖
1.Cat_Jump

010搜索CatCTF{
就行

CatCTF{EFI_1sv3ry_funn9}
2.miao~

图片进行foremost 可以得到音频

Audacity 查看频谱图

可以发现CatCTF 这个应该是一个密钥
用DeepSound隐写


兽音解密

CatCTF{d0_y0u_Hate_c4t_ba3k1ng_?_M1ao~}
3.cantnim



经典的 Nim 游戏:
有多堆石子,玩家轮流从任意一堆中取出任意数量的石子(至少一个)
无法取石子的玩家输
先手玩家有 50 秒时间限制,必须通过多轮游戏获胜才能获得 flag
关键:当且仅当所有堆石子数的 **异或和(Nim-sum)不为 0** 时,先手有必胜策略
必胜策略:每次移动后使所有堆石子数的异或和变为 0
操作方法:计算当前异或和 `nim_sum`,找到一堆石子 `piles[i]` 使得 `piles[i] ^ nim_sum < piles[i]`,从该堆取走 `piles[i] - (piles[i] ^ nim_sum)` 个石子
解题步骤
连接服务器:选择正式游戏(输入 `N`)
数据解析:服务器返回格式为 `Now pile:` 后跟一长串数字,表示各堆石子数
自动交互:编写 Python 脚本实现:
- 解析石子堆数据(注意:服务器使用 **0-based 索引**)
- 计算异或和并执行必胜策略
- 依次发送 `堆索引` 和 `取石数量`
重复游戏:持续进行直到获胜
py3脚本呈现
from pwn import *
import re
def solve():
# 连接服务器
host = '61.147.171.35'
port = 57796
conn = remote(host, port)
# 选择正式游戏(不要试玩)
conn.recvuntil(b'(Y/N)?')
conn.sendline(b'N')
game_round = 1
try:
while True:
# 接收数据直到"where:"
data = conn.recvuntil(b'where:').decode()
# 打印调试信息(前几轮)
if game_round <= 5:
print(f"n=== Round {game_round} ===")
print(f"Received data:n{data[-500:]}")
# 检查是否获胜或失败
if 'You win' in data or 'You lose' in data:
print(f"nGame Over: {'Win' if 'You win' in data else 'Lose'}")
print("Full data:", data)
break
# 查找石子堆信息
# 可能有多行,找到最后出现的"Now pile:"
now_pile_index = data.rfind('Now pile:')
if now_pile_index == -1:
print("Error: 'Now pile:' not found")
print("Data received:", data)
break
# 提取石子堆行
pile_line = data[now_pile_index:]
# 可能包含换行,取第一行
if 'n' in pile_line:
pile_line = pile_line.split('n')[0]
# 提取数字
numbers = re.findall(r'bd+b', pile_line)
# 跳过"Now pile:"这个词本身
if numbers and numbers[0].isdigit():
piles = list(map(int, numbers))
else:
print("Error: No numbers found in pile line")
print("Pile line:", pile_line)
break
# 打印当前石子堆信息
print(f"Round {game_round}: Found {len(piles)} piles")
if len(piles) <= 10:
print(f"Piles: {piles}")
else:
print(f"First 5 piles: {piles[:5]}")
# 计算Nim游戏的异或和
xor_sum = 0
for p in piles:
xor_sum ^= p
print(f"Nim xor sum: {xor_sum}")
# 寻找必胜策略
move_found = False
pile_index = -1
take_amount = 0
if xor_sum != 0:
# 有必胜策略,找到一堆使异或和变为0
for i, pile in enumerate(piles):
if pile ^ xor_sum < pile:
pile_index = i # 0-based索引
take_amount = pile - (pile ^ xor_sum)
move_found = True
break
if not move_found:
# 没有必胜策略(必败局面),从第一个非空堆取1个
for i, pile in enumerate(piles):
if pile > 0:
pile_index = i
take_amount = 1
print(f"Warning: No winning move found, using fallback")
break
if pile_index == -1:
print("Error: No valid move found")
break
print(f"Move: Take {take_amount} from pile {pile_index}")
# 发送堆索引(0-based)
conn.sendline(str(pile_index).encode())
# 接收"count:"提示
count_prompt = conn.recvuntil(b'count:')
# 发送取石数量
conn.sendline(str(take_amount).encode())
game_round += 1
# 检查是否超过一定轮数(防止无限循环)
if game_round > 1000:
print("Warning: Exceeded 1000 rounds, stopping")
break
except EOFError:
print("nConnection closed by server")
# 尝试获取最后的数据
try:
final_data = conn.recvall(timeout=2).decode()
print("Final data received:")
print(final_data)
# 检查是否有flag
if 'flag{' in final_data.lower() or 'ctf{' in final_data.lower():
print("n" + "="*50)
print("FLAG FOUND!")
print("="*50)
# 尝试提取flag
flag_match = re.search(r'(flag{[^}]+}|ctf{[^}]+})', final_data, re.IGNORECASE)
if flag_match:
print(f"Flag: {flag_match.group(1)}")
else:
print("Could not extract flag, here's the full data:")
print(final_data)
except:
pass
except Exception as e:
print(f"nError occurred: {e}")
import traceback
traceback.print_exc()
finally:
conn.close()
if __name__ == '__main__':
solve()

cyberpeace{aca1afe82bab143749aac938be674d59}
4.Time_losing

主要看时间

将所有文件按数字排序(从0到46,因为文件名是0.txt到46.txt)
对于每个文件,计算修改时间与2033-05-18 11:33:20的时间差(秒)
将时间差(整数秒)转换为字符(ASCII)
合并所有字符得到flag,flag格式是XMan{}
- 获取文件夹中所有txt文件,按文件名数字排序
- 将参考时间设为2033-05-18 11:33:20
- 遍历每个文件,获取修改时间,计算与参考时间的时间差(秒)
- 将时间差转换为整数,然后转换为对应的ASCII字符
- 合并这些字符,最后用XMan{}包裹
py3脚本呈现
import os
import datetime
def extract_flag():
txt_files = [f for f in os.listdir('.') if f.endswith('.txt')]
def file_num(f):
return int(f.split('.')[0])
txt_files.sort(key=file_num)
reference = datetime.datetime(2033, 5, 18, 11, 33, 20)
flag_chars = []
for filename in txt_files:
mtime = os.path.getmtime(filename)
file_dt = datetime.datetime.fromtimestamp(mtime)
time_diff = (file_dt - reference).total_seconds()
ascii_val = int(time_diff)
flag_chars.append(chr(ascii_val))
flag = ''.join(flag_chars)
return flag
if __name__ == "__main__":
result = extract_flag()
print(f"XMan{{{result}}}")

XMan{seems_to_be_related_to_the_special_guests}
5.Keyword

需要用这个工具
livz/cloacked-pixel: LSB steganography and detection
图片给了密码

lsb隐写
python lsb.py extract 1.png 1.txt lovekfc


PVSF{vVckHejqBOVX9C1c13GFfkHJrjIQeMwf}

是这个密码
一个变种
py3脚本呈现
import string
def decode_nihilist_cipher():
txt = 'PVSF{vVckHejqBOVX9C1c13GFfkHJrjIQeMwf}'
table = 'LOVEKFCABDGHIJMNPQRSTUWXY'
flag = ''
for i in txt:
if i in string.ascii_lowercase:
# 在table的小写版本中查找索引
index = table.lower().index(i)
# 使用相同索引的ASCII小写字母替换
flag += string.ascii_lowercase[index]
continue
if i in string.ascii_uppercase:
# 在table的大写版本中查找索引
index = table.upper().index(i)
# 使用相同索引的ASCII大写字母替换
flag += string.ascii_uppercase[index]
continue
# 非字母字符直接保留
flag += i
return flag
result = decode_nihilist_cipher()
print(f"解码结果: {result}")

QCTF{cCgeLdnrIBCX9G1g13KFfeLNsnMRdOwf}
6.picture2

010查看

看文件头发现是jpg图片
直接binwalk


发现是base64

kp应该是一个zip

直接解码为hex
全部小写
4b5003041400010000003930974c6ce31f7c5a0000004e00000004000000636f6465e3de81f00fae476784c1b681bf3afc01c7a52d0632a0ab47db36d5b3f8dd2aa67ac2fea101a8e74963cf8cc872dab122c4c7576e7ed664c4eef15cf6097921489fedfd7fac72291ea97b9ea28f6fed8cea744bbb0854f266ffc3504b01023f001400010000003930974c6ce31f7c5a0000004e000000040024000000000000002000000000000000636f64650a0020000000000001001800005bc38385dad3013c8009ad87dad3013c8009ad87dad301504b05060000000001000100560000007c000000dc005b507974686f6e20322e375d0d0a3e3e3e20a87da87da87d0d0a0d0a54726163656261636b20286d6f737420726563656e742063616c6c206c617374293a0d0a202046696c6520223c70797368656c6c23303e222c206c696e6520312c20696e203c6d6f64756c653e0d0a20202020a87da87da87d0d0a5a65726f4469766973696f6e4572726f723a20a87da87da87da87da87da87da87da87da87da87da87da87da87da87da87da87da87da87da87da87da87da87da87da87da87da87da87da87da87da87d203c2d2070617373776f7264203b290d0a3e3e3e2000
010导入十六进制 改一下文件头


需要密码密码是这个报错提示
[Python 2.7]
>>> ▆▆▆
Traceback (most recent call last):
File "<pyshell#0>", line 1, in <module>
▆▆▆
ZeroDivisionError: ▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆▆ <- password ;)
>>>
根据这个 Python 2.7 的交互式会话错误信息,这是一个 ZeroDivisionError(除零错误)。在 Python 中,当试图除以零时会引发此错误。

密码是:
integer division or modulo by zero

UUencode解码

CISCN{2388AF2893EB85EB1B439ABFF617319F}
7.test.pyc

一个pyc文件使用 uncompyle6进行反编译
# 直接输出反编译后的代码到控制台
uncompyle6 1.pyc
# 将反编译结果保存到 .py 文件
uncompyle6 1.pyc > 2.py

2.py
# uncompyle6 version 3.9.2
# Python bytecode version base 2.7 (62211)
# Decompiled from: Python 3.12.0 (tags/v3.12.0:0fb18b0, Oct 2 2023, 13:03:39) [MSC v.1935 64 bit (AMD64)]
# Embedded file name: test.py
# Compiled at: 2016-10-19 09:08:27
-- Stacks of completed symbols:
START ::= |- stmts .
and ::= expr . JUMP_IF_FALSE_OR_POP expr COME_FROM
and ::= expr . jmp_false expr e_come_from_opt
and ::= expr . jmp_false expr come_from_opt
assert ::= assert_expr . jmp_true LOAD_ASSERT RAISE_VARARGS_1
assert2 ::= assert_expr . jmp_true LOAD_ASSERT expr CALL_FUNCTION_1 RAISE_VARARGS_1
assert_expr ::= expr .
assert_expr_and ::= assert_expr . jmp_false expr
assert_expr_or ::= assert_expr . jmp_true expr
assign ::= expr . DUP_TOP designList
assign ::= expr . store
assign2 ::= expr . expr ROT_TWO store store
assign3 ::= expr . expr expr ROT_THREE ROT_TWO store store store
aug_assign1 ::= expr . expr inplace_op ROT_FOUR STORE_SLICE+3
aug_assign1 ::= expr . expr inplace_op ROT_THREE STORE_SLICE+1
aug_assign1 ::= expr . expr inplace_op ROT_THREE STORE_SLICE+2
aug_assign1 ::= expr . expr inplace_op ROT_THREE STORE_SUBSCR
aug_assign1 ::= expr . expr inplace_op ROT_TWO STORE_SLICE+0
aug_assign1 ::= expr . expr inplace_op store
aug_assign2 ::= expr . DUP_TOP LOAD_ATTR expr inplace_op ROT_TWO STORE_ATTR
bin_op ::= expr . expr binary_operator
buildclass ::= LOAD_CONST . expr mkfunc CALL_FUNCTION_0 BUILD_CLASS
call ::= expr . CALL_FUNCTION_0
call_stmt ::= expr . POP_TOP
classdefdeco1 ::= expr . classdefdeco1 CALL_FUNCTION_1
classdefdeco1 ::= expr . classdefdeco2 CALL_FUNCTION_1
compare_chained ::= expr . compared_chained_middle ROT_TWO POP_TOP e__come_froms
compare_chained ::= expr . compared_chained_middle ROT_TWO POP_TOP _come_froms
compare_single ::= expr . expr COMPARE_OP
delete ::= expr . DELETE_ATTR
delete_subscript ::= expr . expr DELETE_SUBSCR
expr ::= LOAD_CONST .
expr_jitop ::= expr . JUMP_IF_TRUE_OR_POP
expr_jt ::= expr . jmp_true
if_exp ::= expr . jmp_false expr JUMP_ABSOLUTE expr
if_exp ::= expr . jmp_false expr JUMP_FORWARD expr COME_FROM
if_exp_lambda ::= expr . jmp_false expr return_if_lambda return_stmt_lambda LAMBDA_MARKER
if_exp_not ::= expr . jmp_true expr _jump expr COME_FROM
if_exp_not_lambda ::= expr . jmp_true expr return_if_lambda return_stmt_lambda LAMBDA_MARKER
if_exp_true ::= expr . JUMP_FORWARD expr COME_FROM
import ::= LOAD_CONST . LOAD_CONST alias
import_from ::= LOAD_CONST . LOAD_CONST IMPORT_NAME importlist POP_TOP
import_from_star ::= LOAD_CONST . LOAD_CONST IMPORT_NAME IMPORT_STAR
importmultiple ::= LOAD_CONST . LOAD_CONST alias imports_cont
mkfuncdeco ::= expr . mkfuncdeco CALL_FUNCTION_1
mkfuncdeco ::= expr . mkfuncdeco0 CALL_FUNCTION_1
print_items_nl_stmt ::= expr . PRINT_ITEM e_print_items_opt PRINT_NEWLINE_CONT
print_items_nl_stmt ::= expr . PRINT_ITEM print_items_opt PRINT_NEWLINE_CONT
print_items_stmt ::= expr . PRINT_ITEM e_print_items_opt
print_items_stmt ::= expr . PRINT_ITEM print_items_opt
print_nl_to ::= expr . PRINT_NEWLINE_TO
print_to ::= expr . print_to_items POP_TOP
print_to_nl ::= expr . print_to_items PRINT_NEWLINE_TO
raise_stmt1 ::= expr . RAISE_VARARGS_1
raise_stmt2 ::= expr . expr RAISE_VARARGS_2
raise_stmt3 ::= expr . expr expr RAISE_VARARGS_3
ret_and ::= expr . JUMP_IF_FALSE_OR_POP return_expr_or_cond COME_FROM
ret_or ::= expr . JUMP_IF_TRUE_OR_POP return_expr_or_cond COME_FROM
return ::= return_expr . RETURN_VALUE
return_expr ::= expr .
return_expr_lambda ::= return_expr . RETURN_VALUE_LAMBDA
return_expr_lambda ::= return_expr . RETURN_VALUE_LAMBDA LAMBDA_MARKER
slice0 ::= expr . DUP_TOP SLICE+0
slice0 ::= expr . SLICE+0
slice1 ::= expr . expr DUP_TOPX_2 SLICE+1
slice1 ::= expr . expr SLICE+1
slice2 ::= expr . expr DUP_TOPX_2 SLICE+2
slice2 ::= expr . expr SLICE+2
slice3 ::= expr . expr expr DUP_TOPX_3 SLICE+3
slice3 ::= expr . expr expr SLICE+3
subscript ::= expr . expr BINARY_SUBSCR
subscript2 ::= expr . expr DUP_TOPX_2 BINARY_SUBSCR
testfalse ::= expr . jmp_false
testtrue ::= expr . jmp_true
unary_convert ::= expr . UNARY_CONVERT
unary_not ::= expr . UNARY_NOT
unary_op ::= expr . unary_operator
with ::= expr . SETUP_WITH POP_TOP e_suite_stmts_opt POP_BLOCK LOAD_CONST COME_FROM_WITH WITH_CLEANUP END_FINALLY
with ::= expr . SETUP_WITH POP_TOP suite_stmts_opt POP_BLOCK LOAD_CONST COME_FROM_WITH WITH_CLEANUP END_FINALLY
with_as ::= expr . SETUP_WITH store e_suite_stmts_opt POP_BLOCK LOAD_CONST COME_FROM_WITH WITH_CLEANUP END_FINALLY
with_as ::= expr . SETUP_WITH store suite_stmts_opt POP_BLOCK LOAD_CONST COME_FROM_WITH WITH_CLEANUP END_FINALLY
yield ::= expr . YIELD_VALUE
Instruction context:
L. 1 0 LOAD_CONST '=cWbihGfyMzNllzZ'
-> 3 NOP
4 NOP
5 NOP
6 LOAD_CONST '0cjZzMW'
9 LOAD_CONST 'N5cTM4Y'
12 LOAD_CONST 'jYygTOy'
15 LOAD_CONST 'cmNycWNyYmM1Ujf'
18 BINARY_ADD
19 STORE_NAME 0 'str'
从反编译的字节码中提取所有LOAD_CONST加载的字符串常量,这些常量会通过BINARY_ADD进行拼接,最终存储到str变量中。我们只需要提取这些字符串并按顺序拼接
# 提取字节码中的LOAD_CONST字符串常量
const_strs = [
'=cWbihGfyMzNllzZ',
'0cjZzMW',
'N5cTM4Y',
'jYygTOy',
'cmNycWNyYmM1Ujf'
]
# 拼接所有常量(字节码中BINARY_ADD会按栈顺序拼接)
flag_content = ''.join(const_strs)
flag = f'flag{{{flag_content}}}'
print(flag)

=cWbihGfyMzNllzZ0cjZzMWN5cTM4YjYygTOycmNycWNyYmM1Ujf
#反转
fjU1MmYyNWcyNmcyOTgyYjY4MTc5NWMzZjc0ZzllNzMyfGhibWc=
#base64解码
~552f25g26g2982b681795c3f74g9e732|hbmg
#反转
gmbh|237e9g47f3c597186b2892g62g52f255~
我们发现gmbh|237e9g47f3c597186b2892g62g52f255
gmbh就是flag 相差1 直接凯撒

flag{237d9f47e3b597186a2892f62f52e255}
发现不对
发现是ASCII减一 ,如4 变成3 b变成a这个样
py3脚本呈现
s = "gmbh|237e9g47f3c597186b2892g62g52f255"
new_s = ''.join([chr(ord(c) - 1) for c in s])
flag = f"{{{new_s}}}"
print(flag)

flag{126d8f36e2b486075a1781f51f41e144}
8.Avatar

outguess 隐写先下载

outguess -r 035bfaa85410429495786d8ea6ecd296.jpg -t flag.txt

flag
We should blow up the bridge at midnight
9.qr-easy

一个二维码
需要修复

需要单独提取出来ps但是不会ps 所以看wp了
文章地址:【愚公系列】2022年02月 攻防世界-进阶题-MISC-83(qr-easy)_攻防世界qr-easy-CSDN博客

此二维码的大小为 29×29,版本V的大小为N × N,N = 17 + 4V,所以这是版本 3。
二维码格式信息
该区域表示二维码的格式信息。实际上,格式信息是 15 位长,区域有最后 8 位。

搜索所有格式信息字符串的列表,我们可以发现类型信息位是001001110111110.所以这个二维码有ECC级别H和掩码模式1。
所有格式信息字符串的列表:

1 号掩码有公式(row) mod 2 == 0。注意行号是从0开始的,所以我们要切换坐标为0,2,4,…,28的行的位。

所以4行2列一组,原始的 D1-D26 是:
D1 = 0b11101100
D14 = 0b10000010
D2 = 0b11111000
D15 = 0b10010101
D3 = 0b00110110
D16 = 0b00111101
D4 = 0b01110110
D17 = 0b01100010
D5 = 0b00100010
D18 = 0b11101001
D6 = 0b11110001
D19 = 0b10100001
D7 = 0b00110111
D20 = 0b11100101
D8 = 0b01010010
D21 = 0b11010101
D9 = 0b00010111
D22 = 0b00101101
D10 = 0b11011110
D23 = 0b10010111
D11 = 0b01000100
D24 = 0b10001011
D12 = 0b01010100
D25 = 0b01111000
D13 = 0b11001101
D26 = 0b11000110
(行) mod 2 == 0 掩码之后
D1 = 0b00100000
D14 = 0b01001110
D2 = 0b00110100
D15 = 0b01011001
D3 = 0b11111010
D16 = 0b00001110
D4 = 0b01000101
D17 = 0b01010001
D5 = 0b00010001
D18 = 0b11011010
D6 = 0b00111101
D19 = 0b10010010
D7 = 0b00000100
D20 = 0b11010101
D8 = 0b10011110
D21 = 0b00011001
D9 = 0b11010100
D22 = 0b00010001
D10 = 0b00010100
D23 = 0b00001110
D11 = 0b11011101
D24 = 0b00010010
D12 = 0b11010010
D25 = 0b00011111
D13 = 0b01010100
D26 = 0b01000000
数据解码
解码有模式指示符:
0001:数字模式(每 3 位 10 位)
0010:字母数字模式(每 2 个字符 11 位)
0100:字节模式(每个字符 8 位)
1000:汉字模式(每个字符 13 位)
0111: ECI 模式
字符计数指示符跟在模式指示符之后。
版本 1-9
数字模式:10 位
字母数字模式:9位
字节模式:8位
汉字模式:8位
版本 10–26
数字模式:12位
字母数字模式:11 位
字节模式:16位
汉字模式:10位
版本 27–40
数字模式:14位
字母数字模式:13 位
字节模式:16位
汉字模式:12位
Ruby脚本
data = '00100000'
'00110100'
'11111010'
'01000101'
'00010001'
'00111101'
'00000100'
'10011110'
'11010100'
'00010100'
'11011101'
'11010010'
'01010100'
'01001110'
'01011001'
'00001110'
'01010001'
'11011010'
'10010010'
'11010101'
'00011001'
'00010001'
'00001110'
'00010010'
'00011111'
'01000000'
alphanumeric = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:'.chars
def read(str, size)
str.slice!(0, size)
end
def kanji(num)
if num >= 0x1740
(0xC140 + num / 0xC0 * 0x100 + num % 0xC0)
.chr(Encoding::Shift_JIS).encode(Encoding::UTF_8)
else
(0x8140 + num / 0xC0 * 0x100 + num % 0xC0)
.chr(Encoding::Shift_JIS).encode(Encoding::UTF_8)
end
end
loop do
case mode = read(data, 4)
when '0010' # Alphanumeric
count = read(data, 9).to_i(2)
(count / 2).times do
chunk = read(data, 11).to_i(2)
print alphanumeric[chunk / 45] + alphanumeric[chunk % 45]
end
print alphanumeric[read(data, 11).to_i(2)] if count.odd?
when '0100' # Byte
count = read(data, 8).to_i(2)
count.times do
print read(data, 8).to_i(2).chr
end
when '1000' # Kanji
count = read(data, 8).to_i(2)
count.times do
print kanji(read(data, 13).to_i(2))
end
when '0000' # Terminate
break
else
fail "Unhandled mode #{mode}"
end
end

SECCON{PSwIQ9d9GjKTdD8H}
10.message

考点基于文本隐写
7fffffffffffffffffffffffffffffffbffff0e10419dff07fdc3ffdaeab6deffdbfff6ffed7f7aef3febfffb7ff1bfbc675931e33c79fadfdebbae7aeddedb7dafef7dc37df7ef6dbed777beedbedb77b6de24718f260e0e71879fffffffffffffffffffffffffffffffffffffffffff07f87fc7f9ffffffffdbfbbfdbfeffffffffebfdffdfff7ffffff871c33e6fe7bffffffd5aefeed62dcffffffeadf9fb8bb0efffffff56df5db6dbf7ffffffaa0c21e19e3bffffe07ffffffffff9fffffffffffffffffffffffffffffffffffffff
将十六进制转换成二进制 长度为1679 二进制按照73*23排列
py3
with open('msg.txt', 'r') as f:
hex_data = f.read().strip()
binary_str = bin(int(hex_data, 16))[2:].zfill(1679)
result_lines = []
for i in range(23):
line = binary_str[i*73:(i+1)*73]
result_lines.append(line)
with open('1.txt', 'w') as f:
for line in result_lines:
f.write(line + 'n')

缩小可以看到flag
RCTF{ArEciBo_mEsSaGe}
11.mysql


直接010看ibdata1
搜索flag

71e55075163d5c6410c0d9eae499c977
12.give_you_flag

看图片最后有一个二维码 用stegsolve工具进行动态图片分离
或者gifsplitter2.0分离

发现不完整
少了三个定位符加上就行


flag{e7d478cf6b915f50ab1277f78502a2c5}
13.stegano

提取 PDF 元数据,查看所有字段:

Tm9wZSAsIG5vdCBoZXJlIDspCg==
解码:Nope , not here ;)
直接提取pdf所有内容
py3脚本呈现
import pdfplumber
# -------------------------- 配置项 --------------------------
PDF_PATH = "d802bcf9530b45e0b37170c67b8efcea.pdf" # PDF文件路径(与脚本同目录)
OUTPUT_TXT = "1.txt" # 输出文本文件名
# -------------------------------------------------------------
def pdf_extract_text(pdf_path, output_txt):
# 初始化文本内容
all_text = ""
try:
# 打开PDF文件
with pdfplumber.open(pdf_path) as pdf:
# 遍历所有页面(pdf.pages是所有页面的列表)
for page_num, page in enumerate(pdf.pages, start=1):
print(f"正在提取第 {page_num} 页文字...")
# 提取当前页文本(extract_text()返回字符串,自动处理换行)
page_text = page.extract_text()
# 若当前页有文本,添加到总文本中(加页分隔符便于阅读)
if page_text:
all_text += f"===== 第 {page_num} 页 =====n"
all_text += page_text + "nn"
# 将所有文本写入1.txt(UTF-8编码,避免中文乱码)
with open(output_txt, "w", encoding="utf-8") as f:
f.write(all_text)
print(f"✅ 提取成功!所有文字已写入 {output_txt}")
print(f"📄 共提取 {len(pdf.pages)} 页内容")
except FileNotFoundError:
print(f"❌ 错误:未找到PDF文件!请检查路径是否正确:{pdf_path}")
except Exception as e:
print(f"❌ 提取失败:{str(e)}")
# 执行提取
if __name__ == "__main__":
pdf_extract_text(PDF_PATH, OUTPUT_TXT)

BBAAAABBBAAAAAAAAAAAAAABBAAABBBBAAAAABAAAABBBBBAAAAAABBBBABAAAABBBBBAAAABBBABBBAAABAABAABBAABBAAAABBBBAABBAAABABAABBAABAABBABBBBABAB
摩斯密码
把A换成 . ,B换成-
--....---..............--...----.....-....-----......----.-....-----....---.---...-..-..--..--....----..--...-.-..--..-..--.----.-.-

有问题这个题目
提取应该是

XXXX这个但是下面的BBA是不对的
查看wp

发现确实不一样所以这个题有问题的
-.-. — -. --. .-. .- - …- .-… .- - … — -. … --…-- …-. .-… .- --. —… .---- -. …- .---- … .---- -… .-… …-- – …-- … … …- --. …–

flag{1nv151bl3m3554g3}
14.坚持60s

jar进行解压发现没有什么东西

jd-gui进行java反编译
发现flag

flag{RGFqaURhbGlfSmlud2FuQ2hpamk=}
里面进行base64解码

flag{DajiDali_JinwanChiji}
15.gif

打开发现有很多图片白的黑的可以联想到0和1二进制
方法一:手动收入:01100110011011000110000101100111011110110100011001110101010011100101111101100111011010010100011001111101

flag{FuN_giF}
方法二:
脚本编写:
代码实现:
from PIL import Image
result = ""
# 遍历104张图片
for i in range(104):
try:
# 使用原始字符串处理路径,避免转义问题
img = Image.open(r"C:Users14163Desktop张文攻防世界Misc难度四gifgif/{}.jpg".format(i))
im = img.convert("RGB")
# 获取(1,1)位置的RGB值
r, g, b = im.getpixel((1, 1))
# 根据红色通道值判断二进制位
result += "1" if r != 255 else "0"
except Exception as e:
print(f"处理图片{i}.jpg时出错: {e}")
continue
# 每8位一组转换为ASCII字符
for i in range(0, len(result), 8):
byte = result[i:i+8]
if len(byte) == 8: # 确保有8位
print(chr(int(byte, 2)), end="")

flag{FuN_giF}
16.掀桌子

c8e9aca0c6f2e5f3e8c4efe7a1a0d4e8e5a0e6ece1e7a0e9f3baa0e8eafae3f9e4eafae2eae4e3eaebfa
ebe3f5e7e9f3e4e3e8eaf9eaf3e2e4e6f2
看起来像十六进制
俩个一组,转化为十进制,减去128,再转字符串得到flag,有一点投机取巧。
代码:
str="c8e9aca0c6f2e5f3e8c4efe7a1a0d4e8e5a0e6ece1e7a0e9f3baa0e8eafae3f9e4eafae2eae4e3eaebfaebe3f5e7e9f3e4e3e8eaf9eaf3e2e4e6f2"
strlen=len(str)
print(strlen)
for i in range(0,118,2):
byte=str[i:i+2]
s=int(byte,16)-128
print(chr(s),end="")

flag{hjzcydjzbjdcjkzkcugisdchjyjsbdfr}
17.双色块

可以看到
绿色和粉色的格子
绿色是0, 粉色是1
先将多个图像叠加在一起看看
from PIL import Image
import numpy as np
import os
from natsort import natsorted
dir_path = "gifframe"
file_names = natsorted(os.listdir(dir_path))
new_img_array = np.zeros((240, 240, 4), np.uint8)
for file_name in file_names:
image = Image.open(os.path.join(dir_path, file_name))
image_array = np.array(image)
new_img_array += image_array
new_img = Image.fromarray(new_img_array)
new_img.show()
new_img.save("combined_image.png")

不像二维码,给他绿色0, 粉色是1 转成二进制
py3脚本呈现
from PIL import Image
import numpy as np
import os
from natsort import natsorted
dir_path = r"F:笔记练习靶场笔记攻防世界Misc难度四双色块gifframe"
file_names = natsorted(os.listdir(dir_path))[:576]
binary_576 = ""
row = 0
column = 0
for file_name in file_names:
img = Image.open(os.path.join(dir_path, file_name))
img_arr = np.array(img)[..., 0:3]
binary_576 += "0" if img_arr[row*10, column*10][1] == 255 else "1"
column += 1
if column % 24 == 0:
row += 1
column = 0
print("576位二进制(24列×24行):")
for i in range(0, 576, 24):
print(binary_576[i:i+24])
with open("576位二进制.txt", "w", encoding="utf-8") as f:
for i in range(0, 576, 24):
f.write(binary_576[i:i+24] + "n")

二进制转ASCII

o8DlxK+H8wsiXe/ERFpAMaBPiIcj1sHyGOMmQDkK+uXsVZgre5DSXw==hhhhhhhhhhhhhhhh
原GIF可以binwalk 如何foremost提取


发现密钥
ctfer2333
DES解密就行

flag{2ce3b416457d4380dc9a6149858f71db}
18.ewm


85张需要拼接二维码 纯难为人
按修改时间排序:这是关键,因为原脚本就是按修改时间排序的
14行6列网格:这是最重要的线索,原脚本使用14×6=84格,85张图片可能多一张
多种尝试方法:
- 方法1:严格按14×6网格,按修改时间排序(最可能正确)
- 方法2:按相同尺寸分组排列
- 方法3:暴力尝试不同的网格组合
py3脚本呈现
import os
import cv2
import numpy as np
from PIL import Image
import hashlib
import shutil
from datetime import datetime
def create_output_dir():
output_dir = "output"
if os.path.exists(output_dir):
for filename in os.listdir(output_dir):
file_path = os.path.join(output_dir, filename)
try:
if os.path.isfile(file_path) or os.path.islink(file_path):
os.unlink(file_path)
elif os.path.isdir(file_path):
shutil.rmtree(file_path)
except Exception:
pass
else:
os.makedirs(output_dir)
return output_dir
def sort_by_modification_time(img_dir):
files = []
for filename in os.listdir(img_dir):
if filename.lower().endswith(('.jpg', '.jpeg', '.png')):
filepath = os.path.join(img_dir, filename)
mtime = os.path.getmtime(filepath)
files.append((mtime, filename, filepath))
files.sort(key=lambda x: x[0])
return [filename for _, filename, _ in files]
def analyze_image_sizes(img_dir, image_names):
sizes = {}
size_groups = {}
for filename in image_names[:20]:
filepath = os.path.join(img_dir, filename)
img = cv2.imread(filepath)
if img is not None:
h, w = img.shape[:2]
size_key = f"{w}x{h}"
sizes[filename] = (w, h)
if size_key not in size_groups:
size_groups[size_key] = []
size_groups[size_key].append(filename)
print("尺寸分组统计:")
for size, files in size_groups.items():
print(f" 尺寸 {size}: {len(files)} 张")
return sizes, size_groups
def compose_by_time_sorted(img_dir, output_dir, rows=14, cols=6, cell_size=256):
image_names = sort_by_modification_time(img_dir)
print(f"按修改时间排序,共 {len(image_names)} 张图片")
print(f"前10张图片: {image_names[:10]}")
if len(image_names) != rows * cols:
print(f"警告: 图片数量({len(image_names)})不等于网格数量({rows*cols})")
total_width = cols * cell_size
total_height = rows * cell_size
result = Image.new('RGB', (total_width, total_height))
idx = 0
for y in range(rows):
for x in range(cols):
if idx >= len(image_names):
break
img_path = os.path.join(img_dir, image_names[idx])
try:
img = Image.open(img_path)
img = img.resize((cell_size, cell_size), Image.Resampling.LANCZOS)
result.paste(img, (x * cell_size, y * cell_size))
idx += 1
except Exception as e:
print(f"处理图片 {image_names[idx]} 失败: {e}")
idx += 1
continue
output_path = os.path.join(output_dir, f"time_sorted_{rows}x{cols}.png")
result.save(output_path)
print(f"保存到: {output_path}")
return output_path
def compose_by_size_groups(img_dir, output_dir, size_groups):
most_common_size = max(size_groups.items(), key=lambda x: len(x[1]))[0]
print(f"n使用最常见尺寸: {most_common_size}")
files_in_size = size_groups[most_common_size]
cell_w, cell_h = map(int, most_common_size.split('x'))
rows = 10
cols = 9
if len(files_in_size) < rows * cols:
rows = int(np.sqrt(len(files_in_size)))
cols = int(len(files_in_size) / rows)
total_width = cols * cell_w
total_height = rows * cell_h
result = Image.new('RGB', (total_width, total_height))
idx = 0
for y in range(rows):
for x in range(cols):
if idx >= len(files_in_size):
break
img_path = os.path.join(img_dir, files_in_size[idx])
try:
img = Image.open(img_path)
result.paste(img, (x * cell_w, y * cell_h))
idx += 1
except Exception as e:
print(f"处理图片 {files_in_size[idx]} 失败: {e}")
idx += 1
continue
output_path = os.path.join(output_dir, f"size_group_{most_common_size}.png")
result.save(output_path)
print(f"保存到: {output_path}")
return output_path
def compose_qr_pattern(img_dir, output_dir, image_names):
qr_version = 7
qr_size = (qr_version * 4) + 17
cell_size = 30
total_size = qr_size * cell_size
result = Image.new('RGB', (total_size, total_size))
idx = 0
for y in range(qr_size):
for x in range(qr_size):
if idx >= len(image_names):
break
img_path = os.path.join(img_dir, image_names[idx])
try:
img = Image.open(img_path)
img = img.resize((cell_size, cell_size), Image.Resampling.LANCZOS)
result.paste(img, (x * cell_size, y * cell_size))
idx += 1
except Exception:
idx += 1
continue
output_path = os.path.join(output_dir, "qr_pattern_attempt.png")
result.save(output_path)
print(f"保存到: {output_path}")
return output_path
def try_decode_qr(image_path):
try:
import pyzbar.pyzbar as pyzbar
from PIL import Image
img = Image.open(image_path)
results = []
methods = [
("原始", img.convert('L')),
("二值化", img.convert('L').point(lambda x: 0 if x < 128 else 255, '1')),
("高对比度", img.convert('L').point(lambda x: 0 if x < 100 else 255, '1')),
("低对比度", img.convert('L').point(lambda x: 0 if x < 150 else 255, '1')),
]
for method_name, processed_img in methods:
try:
decoded = pyzbar.decode(processed_img)
for d in decoded:
try:
data = d.data.decode('utf-8')
results.append((method_name, data))
print(f" {method_name}: 解码成功 -> {data[:50]}...")
if 'flag' in data.lower() or '{' in data:
return data
except:
pass
except:
pass
return None
except ImportError:
print("提示: 安装pyzbar库: pip install pyzbar")
return None
def brute_force_grids(img_dir, output_dir, image_names):
total_images = len(image_names)
print(f"n尝试不同的网格组合 (共 {total_images} 张图片)")
possible_grids = []
for rows in range(5, 20):
for cols in range(5, 20):
if rows * cols >= total_images - 5 and rows * cols <= total_images + 5:
possible_grids.append((rows, cols))
possible_grids.sort(key=lambda x: abs(x[0]*x[1] - total_images))
results = []
for i, (rows, cols) in enumerate(possible_grids[:10]):
print(f"n尝试 {i+1}: {rows}行 x {cols}列 = {rows*cols}格")
cell_size = 256
total_width = cols * cell_size
total_height = rows * cell_size
result = Image.new('RGB', (total_width, total_height))
idx = 0
for y in range(rows):
for x in range(cols):
if idx >= len(image_names):
break
img_path = os.path.join(img_dir, image_names[idx])
try:
img = Image.open(img_path)
img = img.resize((cell_size, cell_size), Image.Resampling.LANCZOS)
result.paste(img, (x * cell_size, y * cell_size))
idx += 1
except Exception:
idx += 1
continue
output_path = os.path.join(output_dir, f"grid_{rows}x{cols}.png")
result.save(output_path)
results.append(output_path)
return results
def main():
print("二维码拼图解题脚本")
print("=" * 60)
output_dir = create_output_dir()
print(f"输出目录: {output_dir}")
img_dir = "all"
if not os.path.exists(img_dir):
print(f"错误: 目录 '{img_dir}' 不存在")
print("请将图片放在 'all' 目录下")
return
image_names = sort_by_modification_time(img_dir)
print(f"找到 {len(image_names)} 张图片")
if len(image_names) != 85:
print(f"警告: 图片数量 {len(image_names)} 不等于预期的 85")
sizes, size_groups = analyze_image_sizes(img_dir, image_names)
print("n" + "=" * 60)
print("方法1: 按修改时间排序 (14x6网格)")
result1 = compose_by_time_sorted(img_dir, output_dir, rows=14, cols=6)
print("n" + "=" * 60)
print("方法2: 按相同尺寸分组")
result2 = compose_by_size_groups(img_dir, output_dir, size_groups)
print("n" + "=" * 60)
print("方法3: 暴力尝试不同网格")
results = brute_force_grids(img_dir, output_dir, image_names)
print("n" + "=" * 60)
print("尝试解码所有生成的图片...")
all_results = [result1, result2] + results
found_flag = False
for result_file in all_results:
print(f"n解码: {os.path.basename(result_file)}")
flag = try_decode_qr(result_file)
if flag:
print(f"n🎉🎉🎉 找到flag: {flag} 🎉🎉🎉")
print(f"图片文件: {result_file}")
found_flag = True
break
if not found_flag:
print("n自动解码未成功,请尝试:")
print("1. 手动扫描 output 目录中的图片")
print("2. 调整图片对比度后再扫描")
print("3. 尝试不同的二维码扫描APP")
print("n推荐尝试的图片:")
print(f" - {result1} (最有可能,14x6网格)")
print(f" - {result2} (相同尺寸分组)")
for result_file in results[:3]:
print(f" - {result_file}")
if __name__ == "__main__":
try:
from PIL import Image
except ImportError:
print("请安装PIL库: pip install Pillow")
exit(1)
try:
import numpy as np
except ImportError:
print("请安装numpy库: pip install numpy")
exit(1)
main()

flag{g00d_g00d_study_1jf8988}
19.Disk

使用AccessData FTK Imager 挂载镜像

看flag0.txt的文件列表有二进制直接flag0.txt-flag3.txt全部提取出来
0110011001101100011000010110011101
1110110011010001000100010100110101
1111001100010110111001011111010001
0000110001011100110110101101111101
二进制转ASCII

flag{4DS_1n_D1sk}
20.crc
检测一下是否是伪加密

发现是真加密
爆破也不行
题目名字叫CRC 所以是CRC爆破
工具下载:theonlypwner/crc32: CRC32 tools: reverse, undo/rewind, and calculate hashes

缩包里有 1.txt、2.txt、3.txt 这些小文件(大小都是 6 字节),适合 CRC 爆破,因为文件小,爆破速度快
2.txt BCEE7ED5
1.txt CC86365B
3.txt CCCA7E74
D:ToolMisc压缩包隐写crc32-0.1>python crc32.py reverse 0xBCEE7ED5

2.txt 91ctf 这个密码符合CTF命名规则 这个所以这个就是第二个密码
91ctf_
1.txt

forum_
3.txt

com_66
按照顺序拼接密码
forum_91ctf_com_66

二进制转ASCII

base64转图片


flag{owid0-o91hf-9iahg}
总结:
制作不易,累了




