CTFHub-py4h4sher

py4h4sher

打开就一个 OK

用御剑扫了一波,什么都没有

fuzz了一圈,就什么都没有

回过头来看了下题,hitcon,那没事了,就当作学习新 姿势 知识了

看了原题,要访问/cgi-bin/py4h4sher才能到题目入口

点击Get Source获得源码:

#!/usr/bin/python
# coding: utf-8

import os
import re
import sys
import cgi
import hashlib
from urllib import unquote
from passlib.utils.pbkdf2 import pbkdf2


sys.path.append('/home/www-data/secret_file')
from secret_file import SECRET # 160 bytes secret
from secret_file import FLAG


print('Content-Type: text/html

')


def _pbkdf2(text):
    return pbkdf2(text, 'noggnogg', 1337).encode('hex').lower()

def _md5(text):
    return hashlib.md5( text ).hexdigest().lower()

def getenv(name):
    return unquote( os.environ.get(name) ) or ''

def gotoFail():
    print('goto fail')
    print
    exit()

def m_hash(password):
    nr = int( 'P0W5'.encode('hex'), 16 )
    add = 7
    nr2 = 305419889

    for c in (ord(x) for x in password if x not in (' ', '	')):
        nr^= (((nr & 63)+add)*c)+ (nr << 8) & 0xFFFFFFFF
        nr2= (nr2 + ((nr2 << 8) ^ nr)) & 0xFFFFFFFF
        add= (add + c) & 0xFFFFFFFF

    return "%08x%08x" % (nr & 0x7FFFFFFF,nr2 & 0x7FFFFFFF)

request = cgi.FieldStorage() 

checksum  = request.getvalue('checksum') or ''
query_str = getenv('QUERY_STRING')
if _md5( SECRET + query_str ) == checksum:
    # 从下文可以知道query_str == filename=py4h4sher&mode=download
    mode = request.getvalue('mode') or ''

    if mode == 'download':
        filename = request.getvalue('filename') or ''
        filename = os.path.basename( filename )
        # 这里的filename会被覆盖,所以不可控
        try:
            print (open(filename).read())
        except IOError as e:
            print ('No such file or directory')
    elif mode == 'eval':
        bad_string = request.getvalue('filename') or ''
        good_string = bad_string.encode('hex')
        # 传入的filename要被hex编码再eval,也不可行
        eval(good_string)
	
    # 就只剩这一种方法了,要mode!= download or eval
    else:
        stage1 = request.getvalue('stage1') or ''
        if m_hash(stage1) != '4141414141414141':
            # m_hash(stage1)的返回值等于4141414141414141
            gotoFail()
 
        
        plaintext = getenv('HTTP_USER_AGENT')
        stage2 = request.getvalue('stage2') or ''
        if stage2 == plaintext:
            # stage2的值不能等于user-agent
            gotoFail()
        
        if _pbkdf2(plaintext) != _pbkdf2(stage2):
            # plaintext和stage2的pbkf2()返回值要相等
            gotoFail()


        stage3 = request.getvalue('stage3') or ''
        stage3 = stage3[0]+stage3[1]+stage3[3]+stage3[5]
        if _md5( stage3 ) != '90954349a0e42d8e4426a4672bde16b9':
            # stage3的md5等于90954349a0e42d8e4426a4672bde16b9
            gotoFail()


        print ('Congrat! The flag is %s' % FLAG )

else:
    checksum = _md5( SECRET + 'filename=py4h4sher&mode=download' )
    print ("""
<!DOCTYPE html>
<html>
<head>
  <meta http-equiv='Content-Type' content='text/html; charset=utf-8'>
  <meta name="author" content="orange@chroot.org">
  <title> PY4H4SHER </title>
  <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">

  <style>
/* 
Inspired by http://dribbble.com/shots/890759-Ui-Kit-Metro/attachments/97174
*/
.out {
    white-space: -moz-pre-wrap;
    white-space: -pre-wrap;
    white-space: -o-pre-wrap;
    white-space: pre-wrap; 
    word-wrap: break-word; /* Internet Explorer 5.5+ */ 
    background-color: white;
    border: 0px;
}

.nav-row {
  text-align: center;
}
.nav-row p {
  padding: 5px;
}
.nav-row .col-md-2 {
  background-color: #fff;
  border: 1px solid #e0e1db;
  border-right: none;
}
.nav-row .col-md-2:last-child {
  border: 1px solid #e0e1db;
}
.nav-row .col-md-2:first-child {
  border-radius: 5px 0 0 5px;
}
.nav-row .col-md-2:last-child {
  border-radius: 0 5px 5px 0;
}
.nav-row .col-md-2:hover {
  color: #e92d00;
  cursor: pointer;
}
.nav-row .glyphicon {
  padding-top: 15px;
  font-size: 40px;
}

  </style>
   <script src="//ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
</head>
<body>             
<script>
  var script_name = '/cgi-bin/py4h4sher';

  function gohome(){
    window.open( 'http://hitcon.org' );
  }
  function getflag(){
      $.get( script_name , 
              function(data){         
                  $('.out').text('nothing to do :(');
              });
  }
  function getsource(){
      $.post( script_name + '?filename=py4h4sher&mode=download', 
              {'checksum': '%s'},
              function(data){
                  $('.out').text(data);
              });
  }
</script>

<div class="container" style="margin-top:160px;">
    <div class="row nav-row">
      <div class="col-md-3">
      </div>
      <div class="col-md-2" onclick='gohome()'>
        <span class="glyphicon glyphicon-home"></span>
        <p> Go Home </p>
      </div>
      <div class="col-md-2" onclick='getflag()'>
        <span class="glyphicon glyphicon-flag"></span>
        <p> Get Flag </p>
      </div>
      <div class="col-md-2" onclick='getsource()'>
        <span class="glyphicon glyphicon-cloud-download"></span>
        <p> Get Source </p>
      </div>
    </div>

    <div class='row nav-row'>
      <pre class='out' style='padding-top:64px; '>
      </pre>
    </div>
</div>

</body>
</html>
""" % checksum)

看了一下源码,接下来要构造请求,需要满足以下要求:

  • checksum=af247ce6e8c70768eae27ec6feae34f6
  • query_str == filename=py4h4sher&mode=download
  • mode != download or eval
  • m_hash(stage1) == 4141414141414141
  • stage2 != user-agent
  • _pbkf2(user-agent) == _pbkf2(stage2)
  • _md5(stage3[0]+stage3[1]+stage3[3]+stage3[5]) == 90954349a0e42d8e4426a4672bde16b9

= =!前两个好说,第三个用参数污染,分别用GET和POST传输,最后一个暴力跑一下就可,但剩下的有点懵逼

贴个Write up

知识点:

  • HTTP Parameter Pollution
    • 我手动测试了以下,如果get和post传输同名参数,后端会获取POST参数(可能不通用

后边这俩算是密码学的东西,第一个是passlib.mysql323老版本的mysql加密哈希函数,第二个是PBKDF2-HMAC-SHA1哈希函数,利用的是两个哈希函数的漏洞

  • MySQL old_password hash collisions
    • 这篇文章讲的比较清楚如何寻找第一个stage,我对加密算法不怎么了解,表达不是很好,感兴趣的话可以去那篇文章里看
  • PBKDF2+HMAC hash collisions explained(这篇文章也讲的很清楚)
    • 简单点说就是这个式子:PBKDF2_HMAC_SHA1(chosen_password) == PBKDF2_HMAC_SHA1(HEX_TO_STRING(SHA1(chosen_password)))

最终的python代码

import requests

headers = {'User-Agent': 'chosen-prefix_hash_collisions_ftw_aaaaaaaaaaaaaaaaaaaaaaaafikpjor'}
base_url = 'http://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
params = {'checksum': 'af247ce6e8c70768eae27ec6feae34f6',
          'mode': 'r3col',
          'stage1': '*WqX%n~""8DpVv',
          'stage2': "|BHwN@zatb0TT:5I3|7<",
          'stage3': ['e', 'n', 'x', 'i', 'x', 'gma'],
          }
res = requests.post(url=base_url + "/cgi-bin/py4h4sher?filename=py4h4sher&mode=download", data=params,
                    headers=headers)
print(res.text)

理解有限,如果有误,还请各位师傅指正

原文地址:https://www.cnblogs.com/R3col/p/12694129.html