感谢您的支持,我们会继续努力的!

潍坊谷尼软件技术服务有限公司 潍坊谷尼软件技术服务有限公司

2021-02-02
【安全编码】PYTHON安全编码之预防LDAP注入

当前位置:首页 > 资讯 > 网络安全 > 【安全编码】PYTHON安全编码之预防LDAP注入

【安全编码】PYTHON安全编码之预防LDAP注入

发布日期:2021-02-02 15:47:42

游览量:37

作者:谷尼软件



详细解说LDAP注入原理、注入过程以及如何利用python安全编码来防止LDAP注入。


LDAP(Lightweight Directory Access Protocol)轻量级目录协议,是一种在线目录访问协议,主要用于资源查询,是X.500的一种简便的实现。它是一种树结构,查询效率很高,插入效率稍低。目录和数据库有很多相似之处,都用于存放数据,可以查询插入,目录可以存放各种数据,而数据库的数据则有比较严格的约束条件。

LDAP目录以入口(entry,目录中存储的基本信息单元)的形式存储和组织数据结构,每个入口有一个唯一标示自己的专属名称(distnguished name),DN由系列RDNs(ralative distinguished names)组成。另外还有两个常见的结构,对象类和属性。对象类(object class)会定义独一的OID,每个属性(attribute)也会分配唯一的OID号码。

2 LDAP注入原理

谈起LDAP注入首先得从LDAP的查询语法开始,基本的查询语法如下:
search语法:attribute operator value

`search filter options:( "&" or "|" (filter1) (filter2) (filter3) ...) ("!" (filter))`

主要根据属性和值进行搜索,就如浏览网页时我们通常并不会浏览某个目录,而是其下存在的某个文件。
LDAP的URL形式为:

ldap://:/,:[?[??]]`

例如:

`ldap://austin.ibm.com/ou=Austin,o=IBM`

2.1 注入过程

从注入原理来看,ldap注入分为and注入和or注入,先看and注入情形,假设查询结构如下,

`(&(user=username)(passwd=password))`,

这可能是采用采用LDAP进行登录验证的查询语句,其中username和password都是用户可控制的参数,那么可以在user处注入

`admin*)(objectClass=*)`

形成如下

`(&(user=admin*)(objectClass=*))(passwd=password))` 有点小语法错误,如果在user处注入`admin*)(objectClass=*))(&(objectClass=*`
`(&(user=admin*)(objectClass=*))(|(objectClass=void)(passwd=passwd))`

无语法错误,对于openldap来说,只会执行第一个&括号内的内容,由于objectClass=*恒为真,我们就能无需密码以admin用户的身份登录系统

如果不允许两个过滤服务器的执行,则是

`(&(user=username)(injected_filter)(passwd=password))`

对于or注入,查询表达式如下:

`(|(user=username)(email=email_addr))`

假如username可控,即可注入

`username*)(objectClass=void))(|objectClass=void`

形成

`(|(user=username)(objectClass=void))(|objectClass=void)(email=email_addr))`,

由于objectClass=void恒为假,所以只有user=username的时候整个值为真。

 2.2 渗透技巧

对于渗透测试来说,还是要看报错,比如输入,如果关闭了报错接口,可以通过正确参数后加”“字符,如果返回一致,必有蹊跷,很有可能就是一个注入点,接着就可以尝试盲注的方式,简单的盲注就是”a*”这种方式。

2.3 调试与验证

在代码审计过程中,有些时候代码结构庞大,为了验证是否存在注入点,不好直接改写在python命令行中执行,那么就可以尝试打开ldap的日志,这样直接从url参数中加入注入元素,就能很好的观察注入的效果。下面是打开ldap日志的方法。一般的文章中都是打开syslogd,如果已经替换成了rsyslogd,也不要惊慌。rsyslogd是syslogd的升级包,原来的配置文件都还可用,增加了很多新功能,如能监听端口或者IP。下面就是打开LDAP日志的步骤:

1.在slapd.conf中加一行:

```loglevel 4095 ```

2.在/etc/rsyslog.conf 中加入ldap日志文档:

```local4.* /var/log/ldap.log```

3.在终端用命令重启syslog服务

```# service rsyslog restart```

4.在/var/log/下可以找到一个ldap.log文件

随着时间增长,这个日志会增长较快,注意删除。

有时候为了查看实际的搜索结果,可以下载ldap的相关工具,在windows下推荐使用LDAP Administrator,linux下可以使用ldapsearch工具,ldapsearch具体用法如下:

```ldapsearch -h 10.5.5.5 -p 389 -D 'o=customer' -W -x -b "o=customer" "cn=645*"``` ```-D binddn bind DN -W         prompt for bind password -x         Simple authentication -b basedn base dn for search```

3 python 安全编码

如何在python中防止LDAP注入呢?首先我们来看下简单的ldap连接,绑定再到查询的示例,这个查询是存在注入风险的,请不要模仿,请不要模仿,请不要模仿。

``` In [70]: import ldap In [71]: l = ldap.initialize('ldap://10.5.0.220:389') In [76]: l.bind('LDAP_ROOTDN','LDAP_ROOTPW') Out[76]: 4 In [77]: l.search_s('LDAP_ROOTDN',ldap.SCOPE_SUBTREE,'(cn=645*)') Out[77]: [('cn=64502d93-a8ab-3ba1-991a-74cfde8cb333,cn=admin,o=3333049f-92d2-3c3a-91c2-6e1ef4c6a6bf,o=customer',....... ``` </pre> 而且ldap的查询接口不像sql结果,有参数化查询,ldap的接口只能从参数过滤上做功夫来防止注入,但是好歹ldap提供了一个安全过滤接口ldap.filter.在这个接口中有escape_filter_chars函数,源码如下: <pre class="lang:default decode:true ">```python def escape_filter_chars(assertion_value,escape_mode=0): """ Replace all special characters found in assertion_value by quoted notation. escape_mode      If 0 only special chars mentioned in RFC 4515 are escaped.      If 1 all NON-ASCII chars are escaped.      If 2 all chars are escaped. """ if escape_mode:    r = []    if escape_mode==1:      for c in assertion_value:        if c < '0' or c > 'z' or c in "*()":          c = "%02x" % ord(c)        r.append(c)    elif escape_mode==2:      for c in assertion_value:        r.append("%02x" % ord(c))    else:      raise ValueError('escape_mode must be 0, 1 or 2.')    s = ''.join(r) else:    s = assertion_value.replace('', r'c')    s = s.replace(r'*', r'a')    s = s.replace(r'(', r'8')    s = s.replace(r')', r'9')    s = s.replace('�', r'�') return s ``` </pre> 源码解读如下:如果未设置转义模式,就将,*,(,),�这5个字符转成其ascii码值。那么如何过滤呢?代码如下: <pre class="lang:default decode:true ">```python name=ldap.filter.escape_filter_chars(name) ```

经过过滤之后再丢到查询参数中。

或者使用filter_format,注意占位符%s和参数的对应关系。

<pre class=”lang:default decode:true “>“`python

current_app.setdefault(‘LDAP_GROUP_OBJECT_FILTER’, ‘(&(objectclass=Group)(userPrincipalName=%s))’)

query = ldap.filter.filter_format(

current_app[‘LDAP_USER_OBJECT_FILTER’], (user,))

“`

总之记住一条,ldap的搜索参数是需要手工过滤的。


潍坊谷尼软件技术服务有限公司17年行业经验、服务企业5000家,知名企业近160家。谷尼信天翁是山东网站制作机构,是山东企业"互联网+";"移动营销"品牌。执行供给侧改革的网络企业。咨询热线:13188831521
TAG标签:LDAP注入 , python安全编码 , 渗透技巧 ,
未注明作者或来源均属原创文章,转载请注明:转自 谷尼软件

推荐