10

【Root-Me】 LDAP injection - authentication

 2 years ago
source link: https://exp-blog.com/safe/ctf/rootme/web-server/ldap-injection-authentication/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

【Root-Me】 LDAP injection



知道原理其实就是很水的一道题。

关于 LDAP 的前置知识可以参考这几篇文章:

[CTF 解题报告] SQLi - LDAP.pptx

这里简单解释一下, LDAP(Lightweight Directory Access Protocol,轻量级目录访问协议)其实就是数据库的一种替代形式,其原理是以目录树结构存储数据,不过它的语法比传统数据库要稍微简单一点。

与本题相关的一些 LDAP 语法:

  • WHERE A=xxx AND B=yyy ,转变成 LDAP 语法就是 (&(A=xxx)(B=yyy))
  • WHERE A=xxx OR B=yyy ,转变成 LDAP 语法就是 (|(A=xxx)(B=yyy))
  • WHERE A LIKE 'x%' ,转变成 LDAP 语法就是 (&(A=x*)(B=*))(&(A=x*)(&))(注:(&)是永真式)
  • 支持嵌套,如: (&(A=xxx)(|(A=xxx)(C=zzz)))

LDAP 把括号称之为【过滤器】,所有元素/操作都必须在括号内

找到注入点

知道这些就可以解题了。

注意题目要求是【绕过登陆校验】。

开启挑战后要求输入账号密码,因为知道是 LDAP ,那么就尝试闭合括号令其语法报错,看看会不会返回一些有用的异常信息。

输入探针 Username = admin) , Password = 123456 ,页面回显异常:

ERROR : Invalid LDAP syntax : (&(uid=admin))(userPassword=123456))

01.png

很明显,用于登陆验证的 LDAP 代码逻辑为:

(&(uid=[username])(userPassword=[password]))

其中 [username][password] 就是我们可控的注入点。

构造 payload

其实知道这两个注入点,绕过密码的注入方式就可以很灵活了(我至少想到了 5 种注入方法),关键是题目对 LDAP 的语法校验有多严格,这直接决定有效的 payload 数量。


方法一:正确账号+嵌套式

构造 payload :

  • username = 账号)(|(uid=账号
  • password = exp)

实际拼接成的 payload 为:(&(uid=账号)(|(uid=账号)(userPassword=exp)))

这个 payload 先计算后面的 或运算,当 账号 正确的时候,由于 (uid=账号) 为真,所以无论 userPassword 输入任何值,整个条件式都是真,从而实现密码绕过。所以这个 payload 的关键是找到正确的账号 (相比于猜密码,账号会更好猜)。

可以猜到的 账号 有 4 个:

  • admin

  • administrator

  • ch25 (这是从 URL 取到的题号,从 rootme 的经验上看,题号经常就是账号)

    逐个账号试,发现 ch25 果然就是账号,得到密码(回显被加密,需要看源码),完成挑战:

02.png

方法二:模糊匹配+嵌套式

相较于方法一,其实有另一个更巧妙的方法,可以构造这样的 payload :

  • username = *)(|(uid=*
  • password = exp)

实际拼接成的 payload 为:(&(uid=*)(|(uid=*)(userPassword=exp)))

这个 payload 先计算后面的 或运算,而且由于 (uid=*) 是模糊匹配,必定永真(连账号也不用猜),所以无论 userPassword 输入任何值,整个条件式都是永真。

此方法在本题可以成功绕过验证,得到密码。

使用模糊匹配的前提是 * 没有被过滤,否则也是只能猜账号,但一般来说,账号比密码更好猜。


方法三:永真式+解析器截断

构造 payload (此方式同样要求猜测正确的账号以绕过,由于已经知道是 ch25 就不猜了):

  • username = ch25)(&)
  • password = exp

实际拼接成的 payload 为:(&(uid=ch25)(&))(userPassword=exp))

注意这个 payload 的前半部分 (&(uid=ch25)(&)) 语法是正确的,但后半部分 (userPassword=exp)) 因为括号没有配对,是语法错误的。

在某些对 LDAP 语法校验不严谨的题型,依然会从左到右执行,直到报错为止,因此这个 payload 在某些环境是可以实现绕过的。但是很明显这题不行:

03.png

方法四:永真式+%00截断

构造 payload (此方式同样要求猜测正确的账号以绕过,由于已经知道是 ch25 就不猜了):

  • username = ch25)(&))%00
  • password = exp

实际拼接成的 payload 为:(&(uid=ch25)(&))%00)(userPassword=exp))

这与方法三很类似,只是截断依赖于 %00 而非解析器。

%00 是 C 或 PHP 的字符串终止符 \0 的 URL 编码,只要这个字符没有被过滤,在 LDAP 校验严谨的环境下也能对语法错误的 LDAP 进行截断。但是很明显这题不行:

04.png

方法五:串行过滤器

构造 payload (此方式同样要求猜测正确的账号以绕过,由于已经知道是 ch25 就不猜了):

  • username = ch25)(uid=ch25)) (&(1=0
  • password = exp

实际拼接成的 payload 为:(&(uid=ch25)(uid=ch25)) (&(1=0)(userPassword=exp))

此方法构造了两个过滤器,在语法校验不严谨的 LDAP 中,只会执行第一个过滤器 (&(uid=ch25)(uid=ch25)) ,而第二个过滤器 (&(1=0)(userPassword=exp)) 则不会被执行,从而可以实现密码绕过。

而对于第一个过滤器,只要账号是正确的,就必定为真。不过此方式在本题也行不通:

05.png

flag 下载后的 flagzip 的文件需要手动更改后缀为 *.zip,然后解压即可(为了避免直接刷答案)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK