越权修改
疑问开始
在进行渗透测试的时候,发现了一个越权修改用户密码的例子。
在修改密码页面抓包,替换成其他用户的id且用户密码相同的情况下,便可以成功修改该用户密码。由于服务端只将用户的id和旧密码进行了匹配,所以攻击者可以收集大量用户名后,对使用了某些相同弱密码的用户进行批量修改密码
那个时候不知道修复建议怎么写,便问了ying哥修复建议是什么?
回答是:要从session id 中获取id再进行id与旧密码的匹配。不能直接从用户那获取了id,进行匹配。
当时听了有点云里雾里的,不过还好最近在代码审计的学习过程中碰到了一个类似的例子。
例子
黑盒:
数据库中有两个用户
登录用户test,在修改用户名页面抓包
把id修改为9,name为hello,就可以越权修改另一个用户的名字。
再次查看数据库,发现数据已经更新。
白盒:
看一看updateName.php的代码
<?php
include_once('../sys/config.php');
if (isset($_POST['submit']) && !empty($_POST['username']) ) {
//看用户是否提交了数据,用户名是否为空
if (strlen($_POST['username'])>16) {
$_SESSION['error_info'] = '用户名過長(用戶名長度<=16)';
header('Location: edit.php');
exit;
}
//用户名长度不可以过长
$clean_username = clean_input($_POST['username']);
$clean_user_id = clean_input($_POST['id']);
//洗净从用户那里提交过来的数据:username和id
//判断用户名已是否存在,不可以存在两个同样姓名的用户
$query = "SELECT * FROM users WHERE user_name = '$clean_username'";
$data = mysql_query($query, $conn);
if (mysql_num_rows($data) == 1) {
$_SESSION['error_info'] = '用户名已存在';
header('Location: edit.php');
exit;
}
//开始更新数据,即修改用户名
$query = "UPDATE users SET user_name = '$clean_username' WHERE user_id = '$clean_user_id'";
//由于where后面的数据是由用户控制的,所以用户可以随意修改id
mysql_query($query, $conn) or die("update error!");
mysql_close($conn);
//刷新缓存
$_SESSION['username'] = $clean_username;
header('Location: edit.php');
}
else {
not_find($_SERVER['PHP_SELF']);
}
?>
最后还刷新缓存,越权修改密码后还可以越权登录。
修复后的代码:
$query = "UPDATE users SET user_name = '$clean_username' WHERE user_id = '$_SESSION['user_id']'";
不从用户那里接收id这个参数,而是从session中直接提取。
session
session id 是从哪里开始就有的?
用户将用户名和密码发送到logCheck.php,如果正确就会重定向到user.php页面。
if ( !isset( $SESSION['user_id'] ) ) {
$query = "SELECT * FROM users WHERE user_name = '{$_SESSION['username']}'";
$data = mysql_query( $query, $conn ) or die( 'Error!!' );
mysql_close( $conn );
$result = mysql_fetch_array( $data );
$_SESSION['user_id'] = $result['user_id'];
}
检测用户中有没有session[user_id]这个字段,如果没有的话,从数据库中查询并且赋值。
session 保存在哪?
一般的答案是保存在服务端,那么具体的在哪?
查了一下后,session的文件位置可以在php.ini中找到,搜索session.save_path。
文件名以 sess_ 为前缀,后跟 SESSION ID,如:sess_c72665af28a8b14c0fe11afe3b59b51b。文件中的数据即是序列化之后的 SESSION 数据了。
新的cookie就有一个新的session id 文件。
session_start等函数所做的实际操作是什么?
动态调试发现,session_start()一执行后,服务器端就多了一个新的session文件
session_start,可以看成是创建一个session文件。假如有原来的session文件,或许没有创建。引入一个。
往session文件中写值,那是代码“$_SESSION['']=" ";
赋值所完成的操作。
session_start()生成一个新的session文件名时。会判断是否存在cookie名为PHPSESSID的值。如果存在,那么就会按照它的值,组合成一个文件名”sess_[phpcookie值]”。所以,在目录下,老是能够看到之前删除过的session文件名。如果将浏览器中对应的cookie(PHPSESSID)删掉。那么就不会生成同样的名字了。如果不存在名为PHPSESSID的cookie。php所做的估计为:先发送一个cookie,然后按照cookie的值生成一个(我可以在浏览器中马上看到一个名为PHPSESSID的cookie)
其实,现在也更加深刻地理解了一个知识:在调用session_start()之前不能有任何输出。有输出就会报错。
session_start()已经封装了发送cookie的操作(发送一个名称为PHPSESSID的cookie到浏览器)。涉及到http的一个原理:头部信息必须在内容之前发送才行。
可以这样认为:session_start()内部已经进行了一次发送头部动作。所以之前不能有任何输出内容。
手册中的英文大致是这样说的:创建一个session,或者恢复当前一个session(基于request请求传递的session id,这里应该值的就是http请求时传递的名为PHPSESSID的cookie)
测试会话固定
cookie:
cookie保存在哪里?
在Firefox隐私里,可以看到保存在本地的cookie。
PHPSESSID该cookie的过期时间在浏览器中显示:会话结束后过期
cookie名称在哪定义?
在php.ini 配置里面
什么时候服务器给用户cookie?
删掉旧有的cookie之后,再次访问网站
请求包:
返回包:
在返回包的Header中会有个set—cookie字段,也就是session_start()函数起作用。
session_start()已经封装了发送cookie的操作。
一开始疑惑不知道服务器端什么时候发放cookie,直到我用动态调试去跟踪变量之后,发现在config.php中session_start()这个函数执行后才有了PHPSID这个变量,而基本上没有php脚本的开头都包含了config.php这个文件:require_once('sys/config.php');
也就是说如果你是第一次访问这个网站,无论是哪个页面,服务器都会给你发送cookie。
由于之前不知道什么时候发放cookie,所以之前在写一个爬虫的时候折腾了许久。
认证和授权:
什么是认证和授权?
谈到cookie和session之后,用金字塔原理向上抽象出两个名词:
Authentication(鉴定、认证)和Authorization(授权)
之前一直没有好好看这两个词。
从时间顺序来看:一般是先认证,后授权
举个例子
Authentication就是要证明你是谁。举个例子,你告诉别人你的名字叫Alice,怎么样让别人确信你就是Alice,这就是Authentication。
Authorization则是当别人已经相信是你以后,你是不是被允不允许做做某件事儿。比如,当你已经证明了你就是Alice了,你可以查你自己的信用卡刷卡记录,但不能查Bob的刷卡记录,这就是Authorization(当然,如果Alice是Bob的老婆这种情况除外)
记忆方式:先认证后授权:c在z之前。
基本认证机制
1认证:
认证就是要给出一些身份证明。
例子:
- 驾照
- 身份证
- 密码
1.1 HTTP的质询/响应认证框架
HTTP 提供了一个原生的质询 / 响应(challenge/response)框架,简化了对用户的认证过程。
Web 应用程序收到一条 HTTP 请求报文时,服务器没有按照请求执行动作,而是以一个“认证质询”进行响应,要求用户提供一些保密信息来说明他是谁,从而对其进行质询。
用户再次发起请求时,要附上保密证书(用户名和密码)。如果证书不匹配,服务器可以再次质询客户端,或产生一条错误信息。如果证书匹配,就可以正常完成请求了。
1.2 认证协议与首部
HTTP 通过一组可定制的控制首部,为不同的认证协议提供了一个可扩展框架。表 12-1 列出的首部格式和内容会随认证协议的不同而发生变化。
认证协议也是在 HTTP 认证首部中指定的。
来看看实例。
服务器对用户进行质询时,会返回一条 401 Unauthorized 响应,并在 WWW-Authenticate 首部说明如何以及在哪里进行认证(参见图 12-2b)。
当客户端授权服务器继续处理时,会重新发送请求,但会在 Authorization 首部附上加密的密码和其他一些认证参数(参见图 12-2c)。
授权请求成功完成时,服务器会返回一个正常的状态码(比如,200 OK);对高级认证算法来说,可能还会在 Authentication-Info 首部附加一些额外的信息(参见图 12-2d)。
接触过一些页面,你先去访问(请求)。网站会返回401,拒绝你的请求,要求你输入用户名密码。只有你过了认证这关,此时你会在头部附件一个Authorization 首部,用来说明认证算法、用户名和密码。成功之后,服务器才会让你继续执行下一步。
1.3 安全域
- HTTP 是怎样允许服务器为不同的资源使用不同的访问权限?
Web 服务器会将受保护的文档组织成一个安全域(security realm)。
每个安全域都可以有不同的授权用户集。
- 例子:
比如,假设 Web 服务器建立了两个安全域:一个用于公司的财务信息,另一个用于个人家庭文档(参见图 12-3)。不同的用户对各个安全域的访问权限是不同的。公司的 CEO 应该能够访问销售额预测资料,但不应该允许他访问员工和其家人度假的照片!
在日常测试越权访问的时候,有些页面是需要登陆后才能访问的,这个时候的测试方法是用两个不同的浏览器,A浏览器登录访问后的一些页面,直接复制URL到B浏览器去,看看B浏览器在未登录情况下能不能访问,如果能访问就存在越权访问漏洞。
1.有些页面需要设置在安全域下 2.未登录的浏览器是不带cookie的,或者说是没有一个成功登录后的cookie,这个时候如果服务端有没有对cookie进行验证。
那么cookie是放在请求里面的header,看看一个普通网站的header.php。
<?php if ( isset( $_SESSION['username'] ) ) {?>
<li><a href="/user/user.php"><?php echo $_SESSION['username'];?></a></li>
<li><a href="/user/logout.php">退出</a></li>
<?php } else if ( isset( $_SESSION['admin'] ) ) {?>
<li><a href="/admin/manage.php"><?php echo $_SESSION['admin'];?></a></li>
<li><a href="/user/logout.php">退出</a></li>
<?php } else {?>
<li><a href="/user/login.php">登录</a></li>
<li><a href="/user/reg.php">注册</a></li>
<?php }?>
脚本中,每个cookie制定了服务器中某个session,验证方法就是从服务器的session文件看看有没有$_SESSION['username']
或者$_SESSION['admin']
,如果有的话,就分别重定向到特定的页面。
如果以上两者都没有,说明这个用户是未登录的用户,就会重定向到登录页面。
2.基本认证
基本认证是最流行的 HTTP 认证协议。
几乎每个主要的客户端和服务器都实现了基本认证机制。基本认证最初是在 HTTP/1.0 规范中提出的,但此后被移到了 RFC 2617 中,它详细介绍了 HTTP 的认证机制。
在基本认证中,Web 服务器可以拒绝一个事务,质询客户端,请用户提供有效的用户名和密码。服务器会返回 401 状态码,而不是 200 状态码来初始化认证质询,并用 WWW-Authenticate 响应首部指定要访问的安全域。
浏览器收到质询时,会打开一个对话框,请求用户输入这个域的用户名和密码。然后将用户名和密码稍加扰码,再用 Authorization 请求首部回送给服务器。
之前就碰到这种情况,要进入一个tomcat页面,服务器就弹出一个对话框要求我输入用户名和密码。原来是我的服务器受到了质询
实例
在图 12-2a 中,用户请求了私人家庭相片 /family/jeff.jpg。
在图12-2b 中,服务器回送一条 401 Authorization Required,对私人家庭相片进行密码质询,同时回送的还有 WWW-Authenticate 首部。这个首部请求对 Family 域进行基本认证。
在图 12-2c 中,浏览器收到了 401 质询,弹出对话框,询问 Family 域的用户名和密码。用户输入用户名和密码时,浏览器会用一个冒号将其连接起来,编码成“经过扰码的” Base-64 表示形式(下节介绍), 然后将其放在 Authorization 首部中回送。
在图 12-2d 中,服务器对用户名和密码进行解码,验证它们的正确性,然后用一条 HTTP 200 OK 报文返回所请求的报文。
HTTP 基本认证的 WWW-Authenticate 和 Authorization 首部。
2.2 Base-64 用户名/密码编码
- 什么是Base-64 编码?
简单来说,Base-64 编码会将一个 8 位字节序列划分为一些 6 位的块。用每个 6 位的块在一个特殊的由 64 个字符组成的字母表中选择一个字符,这个字母表中包含了大部分字母和数字。
- 为什么要用Base-64 编码?
Base-64 编码可以接受二进制字符串、文本、国际字符表示的数据(在某些系统中会引发一些问题),将其暂时转换成一个易移植的字母表以便传输。然后,在远端就可以解码出原始字符串,而无需担心传输错误了。
有些用户名和密码中会包含国际字符或其他在 HTTP 首部中非法的字符(比如引号、冒号和回车换行符),对这些用户名和密码来说,Base-64 编码是非常有用的。而且,Base-64 编码扰乱了用户名和密码,这样也可以防止管理员在管理服务器和网络时,不小心看到用户名和密码。
2.3 代理认证
中间的代理服务器也可以实现认证功能。有些组织会在用户访问服务器、LAN 或无线网络之前,用代理服务器对其进行认证。可以在代理服务器上对访问策略进行集中管理,因此,通过代理服务器提供对某组织内部资源的统一访问控制是一种很便捷的方式。这个过程的第一步就是通过代理认证(proxy authentication)来识别身份。
代理认证的步骤与 Web 服务器身份验证的步骤相同。但首部和状态码都有所不同。表 12-3 列出了 Web 服务器和代理在认证中使用的状态码和首部的差异。
3 基本认证的安全缺陷
基本认证存在下列安全缺陷。
- 1.基本认证会通过网络发送用户名和密码,这些用户名和密码都是以一种很容易解码的形式表示的。实际上,密码是以明文形式传输的,任何人都可以读取并将其捕获。
- 虽然 Base-64 编码通过隐藏用户名和密码,致使友好的用户不太可能在进行网络观测时无意中看到密码,但 Base-64 编码的用户名和密码可以很轻易地通过反向编码过程进行解码,甚至可以用纸笔在几秒钟内手工对其进行解码!
所以经过 Base-64 编码的密码实际上就是“明文”传送的。如果有动机的第三方用户有可能会去拦截基本认证发送的用户名和密码,就要通过 SSL 加密信道发送所有的 HTTP 事务,或者使用更安全的认证协议,比如摘要认证。
2.即使密码是以更难解码的方式加密的,第三方用户仍然可以捕获被修改过的用户名和密码,并将修改过的用户名和密码一次一次地重放给原始服务器,以获得对服务器的访问权。没有什么措施可用来防止这些重放攻击。
3.即使将基本认证用于一些不太重要的应用程序,比如公司内部网络的访问控制或个性化内容的访问,一些不良习惯也会让它变得很危险。很多用户由于受不了大量密码保护的服务,会在这些服务间使用相同的用户名和密码。比如说,某个狡猾的恶徒会从免费的因特网邮件网站捕获明文形式的用户名和密码,然后会发现用同样的用户名和密码还可以访问重要的在线银行网站!
4.基本认证没有提供任何针对代理和作为中间人的中间节点的防护措施,它们没有修改认证首部,但却修改了报文的其余部分,这样就严重地改变了事务的本质。
5.假冒服务器很容易骗过基本认证。如果在用户实际连接到一台恶意服务器或网关的时候,能够让用户相信他连接的是一个受基本认证保护的合法主机,攻击者就可以请求用户输入密码,将其存储起来以备未来使用,然后捏造一条错误信息传送给用户。
这一切说明,在友好的环境,或者说是希望有隐私保护但隐私保护并不十分必要的环境中,可以通过基本认证来提供便捷的文档个性化服务或访问控制保护。通过这种方式,可以用基本认证来防止一些好奇的用户无意中或不小心对文档进行访问。1
1 小心,基本认证中使用的用户名和密码要有别于你在更安全的系统中所使用的密码,否则恶意用户就可以用它们来攻破你的安全账户了!
比如,在一个公司内部,产品管理可能要对未来的产品计划进行密码保护,以防止信息的过早发布。对一般用户而言,基本认证就足以让他们感到不便而不会再去访问这些数据了。2 同样,你可能会用密码来保护那些并非高度机密的,或者没什么信息价值的私人照片或私有站点,这些信息确实和其他人也没什么关系。
2 尽管不是非常安全,但公司内部的员工通常也没有太大的动力去恶意捕获这些密码。这也说明,公司确实会有间谍,也确实会有不满,想要报复的员工,所以,明智的做法是对一旦被恶意获取就会造成很大损害的数据应用更安全的策略。
将基本认证与加密数据传输(比如 SSL)配合使用,向恶意用户隐藏用户名和密码,会使基本认证变得更加安全。这是一种常用的技巧。
我们会在第 14 章讨论安全加密技术。下一章将介绍更复杂的 HTTP 认证协议——摘要认证,摘要认证具有比基本认证更强的安全特性。
cookie和session 认证与授权 之间的关系?
cookie和认证授权没有很紧密的关系。
因为就算是一个用户没有进行登录,网站也会给这个用户发放一个cookie
而session跟认证和授权就有点关系了,通过提取服务器端的session文件中的一些字段,服务器可以用来判断要不要授权给用户去访问某些页面。