周末刚好没有太多的事情,参加了 Google CTF 来玩玩,印象中是第一次参加这个比赛。总的来说 google 题目还是属于质量不错的一类,但是 web 题目难度梯度太大了,前面二道还可以接受,但是最后二道好像到比赛结束都是零解,佛了 ~ 佛了 ~

通过阅读本文章,你将学会:

  • blind XXE by local dtd
  • SQL injection by order with side channels
  • 组合数学

bnv

题目链接是:https://bnv.web.ctfcompetition.com/

访问主页会看到:

查看源代码看到有一个 post.js 文件,访问可以得到重要的 javascript 代码:

function AjaxFormPost() {
  var datasend;
  var message = document.getElementById('message').value;
  message = message.toLowerCase();

  var blindvalues = [
    '10',    '120',   '140',    '1450',   '150',   '1240',  '12450',
    '1250',  '240',   '2450',   '130',    '1230',  '1340',  '13450',
    '1350',  '12340', '123450', '12350',  '2340',  '23450', '1360',
    '12360', '24560', '13460',  '134560', '13560',
  ];

  var blindmap = new Map();
  var i;
  var message_new = '';

  for (i = 0; i < blindvalues.length; i++) {
    blindmap[i + 97] = blindvalues[i];
  }

  for (i = 0; i < message.length; i++) {
    message_new += blindmap[(message[i].charCodeAt(0))];
  }

  datasend = JSON.stringify({
    'message': message_new,
  });
  var url = '/api/search';
  xhr = new XMLHttpRequest();
  xhr.open('POST', url, true);
  xhr.setRequestHeader('Content-type', 'application/json');

  xhr.onreadystatechange =
      function() {
    if (xhr.readyState == 4 && xhr.status == 200) {
        console.log(xhr.getResponseHeader('Content-Type'));
        if (xhr.getResponseHeader('Content-Type') == "application/json; charset=utf-8") {
            try {
                var json = JSON.parse(xhr.responseText);
                document.getElementById('database-data').value = json['ValueSearch'];
            }
            catch(e) {;
                document.getElementById('database-data').value = e.message;
            }
        }
        else {
            document.getElementById('database-data').value = xhr.responseText;
        }
    }
}
      xhr.send(datasend);
}

这段代码就是把 City 输入框的值每个字母转换为小写再映射到 长度为 26 的 blindvalue 数组对应位置的数字,然后将这串数字发送到 api/search 后端,Content-type 类型是 application/json,如果使用 Burp 截取数据包如下:

首先,思考这个命名为 blindvalue 的数组是什么含义,为什么需要这么映射?联想主页的标题有许多点,我很快就意识到这可能是个盲文,果然通过搜索就找到这就是 布莱叶盲文

然后对此我编写一个 py 脚本用于将小写字母映射到 blindvalue 数字:

# encoding:utf-8
blindvalues = [
    '10',    '120',   '140',    '1450',   '150',   '1240',  '12450',
    '1250',  '240',   '2450',   '130',    '1230',  '1340',  '13450',
    '1350',  '12340', '123450', '12350',  '2340',  '23450', '1360',
    '12360', '24560', '13460',  '134560', '13560',
  ]

msg = "Paris"
msg = msg.lower()
blindmap = {}
new = ""
for i in range(len(blindvalues)):
    blindmap[i + 97] = blindvalues[i]
for j in range(len(msg)):
    new += blindmap[ord(msg[j])]
print(new)

到这一步基本把题目的信息理解清楚了,接下来我考虑到几种思路:

  • 盲文是否支持其他字符的映射?

  • 不提交数字会引发服务器什么问题,是否存在注入等问题?

  • 观察主页的 city 选项框的选项,全是 google 公司所在的城市(Zurish、Paris、Bangalore),题目的描述是:

    Please use the search engine below to find the closest association near you.

    是否尝试提交其他城市的 message 可以获得 flag?

  • 是否有其他可以提交的 json 健值对?

接下里的几个小时就是对这些思路的验证,非常遗憾没有一个能让我利用,也不存在其他的 json 键值对,并且服务器只允许提交这 Zurish、Paris、Bangalore 三个城市的盲文数字。在这过程中也有一些收获,比如:

  • 知道目标服务器是 wsgi + flask 模式
  • 后端使用 json.loads 解码 JSON 字符串
  • 知道了 google 在全球的办公地点。。

后来突然想到既然是 application/json 那么是否支持 XML 呢?于是尝试了修改 Content-type 类型为 application/xml,竟然成功!下图返回结果说明服务端试图解析 XML 数据!

尝试网上公开的各种 XML 相关的利用:

点击收藏 | 2 关注 | 2
  • 动动手指,沙发就是你的了!
登录 后跟帖