前言
赛制是 理论 + 工控ctf + 渗透or运维build
理论赛本地大模型魅力时刻
ctf 部分上来先考计算机网络/路由交换实践,而我还在重修计网,令人感叹(
然后用 ctf 的分换取渗透/build启动环境的机会,渗透每次启动环境都只有一小时的时间能打,只能说根本打不完555
设备如下,是西门子的,交换机是tplink的,比赛的时候显示器接的是 SCADA:
任务一:网络拓扑构建
以本机能够ping通192.168.N.86成功为标准构建拓扑(N是自己队伍的C段)
安装所给的console驱动
笔记本 usb 插 console 线,另一头插交换机的 console 口进行连接,xshell用 SERIAL 协议连接 COM7 口(在设备管理器-端口处查看对应的端口)
波特率设置为主办方提供的38400,设备管理器那边也要设置为38400(波特率不对会没有输入输出)
然后就能连上
拓扑图:
console连上交换机2进行配置,键入?
可以查看可用命令和帮助,tab可以补全,删除字符要用del:
TL-SG5210>
broadcast - Write message to all users logged in,at most 256
characters
enable - Enter Privileged EXEC Mode
ping - Ping command
tracert - Tracet route to destination
exit - Exit current mode
TL-SG5210>enable
TL-SG5210#
broadcast - Write message to all users logged in,at most 256
characters
configure - Enter Global Configuration Mode
copy - Config file commands
debug - Debugging commands
enable-admin - Achieve the admin privilege
firmware - Firmware commands
logout - Logout the system
ping - Ping command
reboot - Reboot the system
remove - Config file commands
reset - Reset the system
tracert - Tracet route to destination
clear - Reset functions
exit - Exit current mode
show - Display system information
TL-SG5210#ping 192.168.52.86
Pinging 192.168.52.86 with 64 bytes of data :
Destination Host Unreachable!
Destination Host Unreachable!
Destination Host Unreachable!
Destination Host Unreachable!
Ping statistics for 192.168.52.86:
Packets: Sent = 0 , Received = 0 , Lost = 0 (0% loss)
Approximate round trip times in milli-seconds:
Minimum = 0ms , Maximum = 0ms , Average = 0ms
TL-SG5210#enable-admin
Authentication fail. You should enable AAA first.
Error password!
TL-SG5210#configure
TL-SG5210(config)#
aaa - Authentication Authorization Accounting commands
access-list - Create a temporary Access-List entry
arp - Address Resolution Protocol (ARP)
boot - Boot configuration
cloud-server - Config Cloud Server status
contact-info - Contact information
dns - Configure domain name system
dot1q-tunnel - Dot1q-tunnel configuration
dot1x - Globally Enable 802.1x feature
enable - Configure enable password
gvrp - GVRP global configuration
holiday - Configure the holiday time range
hostname - System name
interface - Select an interface to configure
ip - IP address commands
ipv6 - Internet Protocol version 6 (IPv6)
lacp - Configure LACP global system priority
line - Enter Line Configuration Mode
lldp - Configure LLDP global subcommands
location - System location
logging - System log configuration
loopback-detection - Enable loopback-detection
mac - Global Bridge table configuration commands
mac-vlan - Configure a MAC-based VLAN entry
monitor - Monitoring different system events
port-channel - EtherChannel configuration
protocol-vlan - Protocol VLAN commands
qos - Configure quality of service (QoS) on the device
radius-server - Modify RADIUS query parameters
rmon - SNMP RMON configuration
router - router
service - Change Service state
snmp-server - SNMP server configuration
spanning-tree - Configure Spanning Tree Subsystem
system-time - System-time configuration
tacacs-server - Modify TACACS query parameters
telnet - Telnet configuration
time-range - Configure the time range
ucl - UCL commands
user - Add a new user or modify an exist user
vlan - VLAN commands
voice - Voice VLAN global commands
clear - Reset functions
end - Return to Privileged EXEC Mode
exit - Exit current mode
no - Negate command
show - Display system information
TL-SG5210(config)#interface
fastEthernet - FastEthernet IEEE 802.3
gigabitEthernet - GigabitEthernet IEEE 802.3z
loopback - Loopback interface
port-channel - Ethernet Channel of interfaces
range - Interface Range Mode
ten-gigabitEthernet - Ten-GigabitEthernet IEEE 802.3ae
vlan - Interface VLAN Mode
那么就是要在config模式下对交换机路由进行配置,测试发现interface gigabitEthernet 1/0/2
进入 config-if 模式下配不了 ip 地址
先看一下现有的路由配置
TL-SG5210#show
aaa - Display the AAA configuration
access-list - Display ACL information
arp - Display ARP information
bandwidth - Display bandwidth rate configuration
boot - Display the boot configuration
cable-diagnostics - Display Cable diagnostics results
cloud-server - cloud-server
cluster - Display cluster information
cpu-utilization - Display CPU utilization
debugging - Dispaly modules debug status
dot1q-tunnel - Display VLAN VPN configuration
dot1x - Display 802.1x configuration information
etherchannel - Display EtherChannel information
gvrp - Display GVRP information
history - Display command history
holiday - Display holiday information
image-info - Display the image info
interface - Display interface status and configuration
ip - Display IP information
ipv6 - Internet Protocol version 6 (IPv6)
lacp - Display LACP configuration
lldp - Display LLDP information
logging - Display log information
loopback-detection - Display loopback detection information
mac - Display mac information
mac-vlan - Display MAC Based VLAN information
memory-utilization - Display memory utilization
monitor - Display monitor sessions information
port - Display Ethernet port configuration
protocol-vlan - Display protocol VLAN configuration
qos - Display QoS related information
radius-server - Show radius server information
rmon - Display SNMP RMON information
running-config - Display running config
snmp-server - Display SNMP related information
spanning-tree - Display spanning tree information
startup-config - Display startup config
storm-control - Display interface's storm-control configuration
system-info - Display system information
system-time - Display current system time information
tacacs-server - Show tacacs+ server information
telnet-status - Display telnet status
time-range - Display time segment information
ucl - Display UCL information
user - Display user account information
vlan - Display VLAN information
voice - Display voice VLAN configuration
TL-SG5210#show ip interface
brief - Brief summary of IP status and configuration
fastEthernet - FastEthernet IEEE 802.3
gigabitEthernet - GigabitEthernet IEEE 802.3z
loopback - Loopback interface
port-channel - Ethernet Channel of interfaces
ten-gigabitEthernet - Ten-GigabitEthernet IEEE 802.3ae
vlan - VLAN interface
<cr>
TL-SG5210#show ip interface brief
Interface IP-Address Method Status Protocol Shutdown
--------- ---------- ------ ------ -------- --------
Vlan1 192.168.0.1/24 Static up up no
config 模式下启用AAA
TL-SG5210(config)#aaa
accounting - Configure accounting method list
authentication - Configure authentication method list
enable - Enable AAA
group - Configure AAA group
TL-SG5210(config)#aaa enable
TL-SG5210(config)#exit
TL-SG5210#enable-admin
接下来结合这张表配置 vlan
TL-SG5210(config)#interface
fastEthernet - FastEthernet IEEE 802.3
gigabitEthernet - GigabitEthernet IEEE 802.3z
loopback - Loopback interface
port-channel - Ethernet Channel of interfaces
range - Interface Range Mode
ten-gigabitEthernet - Ten-GigabitEthernet IEEE 802.3ae
vlan - Interface VLAN Mode
TL-SG5210(config)#interface vlan
<1-4094> - VLAN interface number
TL-SG5210(config)#interface vlan 4
TL-SG5210(config-if)#
access-list - Configure the access list control module
arp - Address Resolution Protocol (ARP)
description - Specify vlan interface description
ip - Configure interface internet protocol
ipv6 - IPv6 interface subcommands
shutdown - Shutdown the selected interface
clear - Reset functions
end - Return to Privileged EXEC Mode
exit - Exit current mode
no - Negate command
show - Display system information
TL-SG5210(config-if)#ip
address - Set IP address manually
address-alloc - Set IP address by bootp or dhcp
helper-address - Specify a destination address for DHCP broadcasts
local-proxy-arp - Specify local proxy ARP configuration
proxy-arp - Specify proxy ARP configuration
rip - Specify RIP protocol interface configuration
TL-SG5210(config-if)#ip address
address address-alloc
TL-SG5210(config-if)#ip address 192.168.52.86
A.B.C.D - Specify the IP subnet mask
TL-SG5210(config-if)#ip address 192.168.52.86 255.255.255.0
TL-SG5210(config-if)#no shutdown
shutdown
这样就配置完成了,然后把电脑的网线另一头从hub上拔下来接到交换机的 lan 口上就能ping通了
任务二:交换机配置(Unsolved)
对竞赛平台中的网络设备进行配置,完成该任务应能够实现以下效果:
1. 工业防火墙、工业日志审计的管理口同处于交换机 SW1 的 VLAN 5 下,操作人员可通过桌面 Hub 访问 MES 的 Web 页面,通过 G1/0/3 访问工业日志审计管理页面、工业防火墙管理页面。
2. SCADA、HMI 同处于 VLAN 下,操作人员可通过 SCADA 访问 MES 的 web 页面。
3. 通过工业日志审计能够监控流经交换机 SW1 和交换机 SW2 的所有流量数据。
4. 将交换机 1 的 G1/0/8 的 IP 地址配置为 12.0.N.254/24。
5. 添加静态路由 171.16.0.0/16,网关为 12.0.N.1
直接把console线插到交换机1上,用同样的方式配置 vlan5,使其能够通过网线访问工业防火墙、工业日志审计
但是还差一个 通过桌面 Hub 访问 MES 的 Web 页面
hint:MES的web服务开在不常见端口
于是把65535全扫了一遍。。然后没扫到,不知道怎么搞的
任务十三:代码安全审计
对以下这段代码进行安全审计:
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$url = isset($_POST['url']) ? trim($_POST['url']) : '';
if (filter_var($url, FILTER_VALIDATE_URL) === false) {
$error = "无效的 URL,请检查格式。";
} else {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_MAXREDIRS, 5);
$response = curl_exec($ch);
if ($response === false) {
$error = 'cURL 错误: ' . curl_error($ch);
}
curl_close($ch);
}
}
?>
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>危化品处理系统</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f5f5f5;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
.container {
background-color: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
width: 400px;
}
h1 {
text-align: center;
}
label {
font-size: 14px;
margin-bottom: 8px;
display: block;
}
input[type="text"] {
width: 100%;
padding: 10px;
margin-bottom: 15px;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
}
.btn {
width: 100%;
padding: 10px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
.btn:hover {
background-color: #45a049;
}
.error {
color: red;
font-size: 14px;
}
.result {
margin-top: 20px;
background-color: #f1f1f1;
padding: 10px;
border-radius: 4px;
word-wrap: break-word;
}
</style>
</head>
<body>
<div class="container">
<h1>危化品处理</h1>
<form method="POST" action="">
<label for="url">请输入 URL:</label>
<input type="text" id="url" name="url" placeholder="请输入有效的 URL" value="<?php echo isset($url) ? htmlspecialchars($url) : ''; ?>" required>
<button type="submit" class="btn">提交处理</button>
<!-- 显示错误消息 -->
<?php if (isset($error)): ?>
<p class="error"><?php echo htmlspecialchars($error); ?></p>
<?php endif; ?>
</form>
<?php if (isset($response) && !isset($error)): ?>
<div class="result">
<h3>请求结果:</h3>
<pre><?php echo htmlspecialchars($response); ?></pre>
</div>
<?php endif; ?>
</div>
</body>
</html>
指出代码存在漏洞的行数:一眼在12行
$response = curl_exec($ch);
这里可以随便ssrf如果要对代码进行修复,应该做出怎样修改:简单过滤一手
<?php if ($_SERVER['REQUEST_METHOD'] === 'POST') { $url = isset($_POST['url']) ? trim($_POST['url']) : ''; if (filter_var($url, FILTER_VALIDATE_URL) === false || !preg_match('/^(https?|ftp):\/\/.[^\s]*$/', $url)) { $error = "无效的 URL,请检查格式。"; } else { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($ch, CURLOPT_MAXREDIRS, 5); $response = curl_exec($ch); if ($response === false) { $error = 'cURL 错误: ' . curl_error($ch); } curl_close($ch); } } ?>
渗透-数字化装配场景
日志处存在任意文件读取,甚至可以列出目录
../../../../proc/self/cmdline
读取的日志有最大长度
../data_monitor.py
"""
opc-ua类: 实现工业机器交互
opc-ua安装使用方式:pip install opcua
opencv-python
"""
import os
import tempfile
import threading
import time
import datetime
import json
from functools import wraps
from itertools import islice
from flask import Flask, send_file, request, Response, make_response
from flask_cors import CORS
from opcua import Client, ua, Node
import cv2
from base_info import sub_ids, ip, port, host, user, pw, database
from get_generate_plan import get_random_plan
from update_value import monitor_task
from mysql_db import MySQLDatabase
app = Flask(__name__)
CORS(app, resources={r"/*": {"origins": "*"}})
abs_path = os.path.dirname(os.path.abspath(__file__))
print(abs_path)
@app.route("/dashboard")
def get_monitor():
try:
with MySQLDatabase(host, user, pw, database) as db:
data = db.execute_query("SELECT name_key, content FROM monitor",)
info = dict(zip([x[0] for x in data], [x[1] for x in data]))
return {'code': 200, 'data': info}
except Exception as e:
return {'code': 500, 'data': str(e)}
@app.route("/plan")
def get_plan():
try:
plan_list = get_random_plan()
return {'code': 200, 'data': plan_list}
except Exception as e:
return {'code': 500, 'data': str(e)}
@app.route("/get_log", methods=["POST"])
def get_log():
filename = request.json.get("name")
file_path = f"{abs_path}/log/{filename}"
if not os.path.isdir(file_path):
if os.path.exists(file_path):
with open(file_path, 'r', encoding='utf-8') as f:
lines = list(islice(f, 100))
with tempfile.NamedTemporaryFile(delete=False, mode='w', encoding='utf-8') as temp_file:
temp_file.writelines(lines)
temp_file_path = temp_file.name
response = make_response(send_file(temp_file_path))
response.headers['Content-Type'] = 'application/json'
return response
else:
return "文件不存在", 400
else:
return Response(
json.dumps(os.listdir(f"{abs_path}/{filename}")),
content_type="application/json",
headers=[("Content-Type", "application/json")]
)
class VideoCamera(object):
def __init__(self, host="", account="", passwd=""):
self.url = f"rtsp://{account}:{passwd}@{host}:554/Streaming/Channels/1"
self.video = cv2.VideoCapture(self.url)
@property
def check(self):
if not self.video.isOpened():
return False
return True
def __del__(self):
self.video.release()
def get_frame(self):
success, image = self.video.read()
ret, jpeg = cv2.imencode('.jpg', image, [int(cv2.IMWRITE_JPEG_QUALITY), 30])
return jpeg.tobytes()
../mysql_db.py
import mysql.connector
class MySQLDatabase:
def __init__(self, host, user, password, database):
self.host = host
self.user = user
self.password = password
self.database = database
self.conn = None
self.cursor = None
def __enter__(self):
self.conn = mysql.connector.connect(
host=self.host,
user=self.user,
password=self.password,
database=self.database
)
self.cursor = self.conn.cursor()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if self.cursor:
self.cursor.close()
if self.conn:
self.conn.close()
def execute_query(self, query, params=()):
self.cursor.execute(query, params)
result = self.cursor.fetchall()
return result
def update_record(self, query, params=()):
self.cursor.execute(query, params)
self.conn.commit()
return self.cursor.lastrowid
""" 使用with语句进行数据库操作
with MySQLDatabase(host='localhost', user='yourusername', password='yourpassword', database='mydatabase') as db:
result = db.execute_query("SELECT * FROM customers")
for x in result:
print(x)
updated_rows = db.update_record("UPDATE customers SET address = 'Canyon 123' WHERE address = 'Valley 345'")
print(updated_rows, "记录被修改")
"""
../base_info.py
"""
sis监控文件统一放到 /home/opt/monitor目录下
mis前端文件统一放到 /var/www/html 并指定默认访问页面为index.php
"""
# mysql
host = "127.0.0.1"
user = "root"
pw = "abc123!@#"
database = "mesdb"
# 组态系统信息
ip = '192.168.2.32'
port = 4862
# 监控节点数据
sub_ids = (
'ns=1;s=t|DLSSL', # 打螺丝数量
'ns=1;s=t|FTQS', # 发条圈数
'ns=1;s=t|FZJD', # 翻转角度
'ns=1;s=t|FZZP_Run', # 翻转装配运行
# 'ns=1;s=t|FZZP_Start', # 翻转装配启动
# 'ns=1;s=t|FZZP_Stop', # 翻转装配停止
# 'ns=1;s=t|hand', # 手自动模式
'ns=1;s=t|HJ_SD', # 环境湿度
'ns=1;s=t|HJ_WD', # 环境温度
'ns=1;s=t|JGDB_Run', # 激光打标运行
# 'ns=1;s=t|JGDB_Start', # 激光打标启动
# 'ns=1;s=t|JGDB_Stop', # 激光打标停止
'ns=1;s=t|JGPL', # 激光频率
'ns=1;s=t|JXSL_Run', # 机芯上料运行
# 'ns=1;s=t|JXSL_Start', # 机芯上料启动
# 'ns=1;s=t|JXSL_Stop', # 机芯上料停止
'ns=1;s=t|JZDL_Run', # 基座打料运行
# 'ns=1;s=t|JZDL_Start', # 基座打料启动
# 'ns=1;s=t|JZDL_Stop', # 基座打料停止
'ns=1;s=t|SCSL', # 生产数量
'ns=1;s=t|SGDLS_Run', # 上盖打螺丝运行
# 'ns=1;s=t|SGDLS_Start', # 上盖打螺丝启动
# 'ns=1;s=t|SGDLS_Stop', # 上盖打螺丝停止
'ns=1;s=t|SJSFT_Run', # 视觉上发条运行
# 'ns=1;s=t|SJSFT_Start', # 视觉上发条启动
# 'ns=1;s=t|SJSFT_Stop', # 视觉上发条停止
'ns=1;s=t|SLSD', # 上料速度
'ns=1;s=t|tongxun_state', # PLC通讯状态
'ns=1;s=t|TPZY_Run', # 托盘转运运行
# 'ns=1;s=t|TPZY_Start', # 托盘转运启动
# 'ns=1;s=t|TPZY_Stop', # 托盘转运停止
'ns=1;s=t|YJ_Run', # 一键运行
# 'ns=1;s=t|YJ_Start', # 一键启动
# 'ns=1;s=t|YJ_Stop', # 一键停止
'ns=1;s=t|ZN_M_OVERSPEED', # 电机超速报警
)
../update_value.py
import re
import os
import time
import datetime
from base_info import sub_ids, host, user, pw, database
from mysql_db import MySQLDatabase
node_dict = {x: re.search(r'\|(.*)', x).group(1) for x in sub_ids}
abs_path = os.path.dirname(os.path.abspath(__file__))
def monitor_task(key, *args):
start_time = time.time()
try:
with MySQLDatabase(host, user, pw, database) as db:
result = db.execute_query(
"SELECT * FROM monitor WHERE name= %s", (key,))
if result:
db.update_record(
"UPDATE monitor SET content=%s WHERE name = %s", (str(*args), key))
else:
db.update_record(
"INSERT INTO monitor (name, name_key, content) VALUES (%s, %s, %s)",
(key, node_dict.get(key), str(*args)))
print(f">>>>> {node_dict.get(key)} 记录被修改 >>>>>")
except Exception as e:
print(f"monitor_task_error: {str(e)}")
finally:
print(f"monitor_task_use_time: {time.time() - start_time}\n")
../get_generate_plan.py
import random
from datetime import datetime, timedelta
NUM_PLANS = 20
MIN_PLAN_QUANTITY = 10
MAX_PLAN_QUANTITY = 100
MIN_PLAN_DURATION = 1
MAX_PLAN_DURATION = 14
def generate_plan(start_date, end_date, i):
order_number = generate_plan_number(start_date, i)
plan_quantity = random.randint(MIN_PLAN_QUANTITY, MAX_PLAN_QUANTITY)
plan_start = random_date(start_date, end_date).strftime("%Y-%m-%d")
plan_duration = random.randint(MIN_PLAN_DURATION, MAX_PLAN_DURATION)
plan_end = (start_date + timedelta(days=plan_duration)).strftime("%Y-%m-%d")
order_status = random.choice(["正在进行", "已完成", "取消"])
completion_rate = round(random.uniform(0, 1) * 100, 2)
return {
"order_number": order_number,
"plan_quantity": plan_quantity,
"plan_start": plan_start,
"plan_end": plan_end,
"order_status": order_status,
"completion_rate": completion_rate
}
def generate_plan_number(start_date, i):
return f"ORDER-{start_date.strftime('%Y%m%d')}{str(i+1).zfill(2)}"
def random_date(start_date, end_date):
time_between = end_date - start_date
random_days = random.randint(0, time_between.days)
return start_date + timedelta(days=random_days)
def production_plan(start_date, end_date, num_plans):
return [generate_plan(start_date, end_date, i) for i in range(num_plans)]
def get_random_plan():
start_date = datetime.now().date()
end_date = start_date + timedelta(days=30)
random.seed(42)
return production_plan(start_date, end_date, NUM_PLANS)
../../../../../home/scada/app.py,是5001端口上的服务
from flask import Flask, render_template, request, redirect, url_for, session, render_template_string,Markup,make_response,flash
import random
import hashlib
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash, check_password_hash
import pymysql
from jinja2 import Template
# flag{Yes_You_find_Me!}
app = Flask(__name__)
app.secret_key = 'icssla' # 设置一个密钥用于session加密
# 数据库连接配置
db_config = {
'host': 'localhost',
'user': 'root',
'password': 'abc123!@#',
'db': 'flask_login_db',
'charset': 'utf8mb4',
'cursorclass': pymysql.cursors.DictCursor,
}
# 设置 session cookie 的 HttpOnly 和 Secure 标志
app.config['SESSION_COOKIE_HTTPONLY'] = False # 设置为 False,允许 JavaScript 访问
app.config['SESSION_COOKIE_SECURE'] = False # 设置为 False,允许在 HTTP 上发送 cookie
def contains_sql_keywords(input_string, keywords):
for keyword in keywords:
if keyword in input_string:
return True
return False
def contains_ssti_keywords(input_string, keywords):
for keyword in keywords:
if keyword in input_string:
return True
return False
# 假设这是你的用户数据库
#users = {
# "admin": "admin@123"
#}
md5_hash = hashlib.md5()
# 生成随机风机数据
def generate_random_data(page, num_entries=100):
data = []
for i in range(num_entries):
fan = f"Fan {i+1}"
speed = random.randint(1000, 3000) # 转速范围 1000-3000 RPM
power = random.uniform(100.0, 500.0) # 发电量范围 100-500 kW
data.append({"fan": fan, "speed": speed, "power": power})
page_size = 10 # 每页显示的数据条数
try:
page = int(page)
start_index = (page - 1) * page_size
end_index = start_index + page_size
return data[start_index:end_index], page
except ValueError:
return [], 1 # 如果输入的不是有效的整数,返回空数据和默认页数1
@app.route('/')
def index():
if 'username' in session:
return render_template('dashboard.html')
return redirect(url_for('login'))
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
sql_keywords = ['select', 'from', 'where', 'union', 'join', 'sleep',' ']
input_string = f"{username}:{password}"
md5_hash.update(input_string.encode('utf-8'))
hex_dig = md5_hash.hexdigest()
connection = pymysql.connect(**db_config)
if contains_sql_keywords(username, sql_keywords):
return "Error: There are illegal characters in the character you entered.", 400
try:
with connection.cursor() as cursor:
# 执行查询
sql = "SELECT * FROM users WHERE username ='" + username +"'"
cursor.execute(sql)
#sql = "SELECT * FROM users WHERE username = %s"
#cursor.execute(sql, (username,))
result = cursor.fetchone()
# 验证用户名和密码
if result and result['password'] == password:
flash('Login successful!', 'success')
session['username'] = username
# 假设 session_token 是与 session 相关的一个值
session_token = str(random.randint(100000, 999999)) # 仅为示例
response = make_response(redirect(url_for('index')))
response.set_cookie('username', hex_dig, httponly=False, secure=False)
return response
else:
flash('Invalid username or password', 'danger')
return 'Invalid credentials'
finally:
connection.close()
return render_template('login.html')
@app.route('/logout')
def logout():
最后在root下翻出一个flag,home下好像也有一个flag,交了俩flag
接下来应该是拿shell然后打横向,但是时间太短反应不过来(