zoukankan      html  css  js  c++  java
  • 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)
    

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

  • 相关阅读:
    “双一流”建设学科名单
    “双一流”建设高校名单
    vue项目上传Github预览
    vue数据绑定源码
    构建工具是如何用 node 操作 html/js/css/md 文件的
    prop 和 attr 中一些羞羞的事情
    用element-ui的走马灯carousel轻松实现自适应全屏banner图
    Vue实例方法之事件的实现
    初学微信小程序 TodoList
    一个页面从输入URL到加载显示完成,发生了什么?
  • 原文地址:https://www.cnblogs.com/R3col/p/12694129.html
Copyright © 2011-2022 走看看