Lab: Partial construction race conditions
某些框架尝试通过使用某种形式的请求锁定来防止意外的数据损坏。例如,PHP 的本机会话处理程序模块一次只处理每个会话的一个请求。
许多应用程序通过多个步骤创建对象,这可能会引入可利用对象的临时中间状态。
例如,注册新用户时,应用程序可能会在数据库中创建用户,并使用两个单独的 SQL
语句设置其 API
密钥。这留下了一个用户存在的door
,但其 API
密钥未初始化。
框架通常允许您使用非标准语法传入数组和其他非字符串数据结构。例如,在 PHP 中:
param[]=foo
相当于param = ['foo']
param[]=foo¶m[]=bar
相当于param = ['foo', 'bar']
param[]
相当于param = []
如果注册了账户之后,抢先在SQL
语句赋值之前发出身份验证
本实验包含用户注册机制。争用条件使你能够绕过电子邮件验证,并使用不属于你的任意电子邮件地址进行注册。
要解决实验室问题,请利用此争用条件创建一个帐户,然后登录并删除用户carlos。
https://portswigger.net/web-security/race-conditions/lab-race-conditions-partial-construction
这是SHOP
类型的网站
可以查看文章信息、购买、登录、注册等功能点,根据提示我们来到注册功能点处
需要用户名、邮箱以及密码才可以注册,发现只能用推荐的邮箱注册
尝试注册账户为1,发现注册成功
再次注册1的账户发现无法注册
尝试注册账户2,用同样的邮箱发现注册成功,说明同样的邮箱可以注册复用
但是无法直接用账号密码进行登录,需要邮箱激活
在不清楚邮箱的情况下陷入了困局
正常的流程为
前端注册 → 后端发送邮件并在数据库中预注册用户的账户/密码信息 → 用户激活邮箱 → 后端分配权限给用户 → 用户可以正常访问
注册发送数据包
在此处条件竞争获取的账号也是没有权限的,毫无用处
在翻阅的过程中发现了一处可疑的js
const createRegistrationForm = () => {
const form = document.getElementById('user-registration');
const usernameLabel = document.createElement('label');
usernameLabel.textContent = 'Username';
const usernameInput = document.createElement('input');
usernameInput.required = true;
usernameInput.type = 'text';
usernameInput.name = 'username';
const emailLabel = document.createElement('label');
emailLabel.textContent = 'Email';
const emailInput = document.createElement('input');
emailInput.required = true;
emailInput.type = 'email';
emailInput.name = 'email';
const passwordLabel = document.createElement('label');
passwordLabel.textContent = 'Password';
const passwordInput = document.createElement('input');
passwordInput.required = true;
passwordInput.type = 'password';
passwordInput.name = 'password';
const button = document.createElement('button');
button.className = 'button';
button.type = 'submit';
button.textContent = 'Register';
form.appendChild(usernameLabel);
form.appendChild(usernameInput);
form.appendChild(emailLabel);
form.appendChild(emailInput);
form.appendChild(passwordLabel);
form.appendChild(passwordInput);
form.appendChild(button);
}
const confirmEmail = () => {
const container = document.getElementsByClassName('confirmation')[0];
const parts = window.location.href.split("?");
const query = parts.length == 2 ? parts[1] : "";
const action = query.includes('token') ? query : "";
const form = document.createElement('form');
form.method = 'POST';
form.action = '/confirm?' + action;
const button = document.createElement('button');
button.className = 'button';
button.type = 'submit';
button.textContent = 'Confirm';
form.appendChild(button);
container.appendChild(form);
}
从json
中命名大致了解
createRegistrationForm
通过操作DOM创建注册表单confirmEmail
用于创建确认电子邮件功能。在确认电子邮件功能中 访问的是POST
请求的/confirm
端点,拼接的参数是token
该创建的表单会获取token
并确认提交
token
为空被禁止
token
为1,证明验证了令牌为0不正确
如果是刚注册的用户通常为空,可以发现被禁止了,尝试数组没被禁止
当我注册账号之后,如果存在两句SQL
语句,我可不可以抢先在应用程序赋值token
之前给予账号空的token
,这样就可以绕过用户注册的邮箱验证机制
构造创建用户的枚举,同时并发token
为空验证的条件竞争机制成功获取了用户
用户信息a4016:111111
登录账号
打开管理面板删除用户后完成实验