Wednesday, January 7, 2015

IPSec VPN with pfSense using a Pre-Shared Key, Part 2

Let's remember our network:


As you can see, I have two pfSense boxes here:

One that has a WAN IP address of 172.20.0.1 and LAN IP address 10.0.0.1;
Another that that a WAN IP address of 172.30.0.1, LAN IP address 192.168.100.1 and OPT1 IP address 192.168.110.1.

What I want to do is create an IPSec VPN tunnel between the 10.0.0.0/24 and the 192.168.100.0/24 network. Remember, all settings must be the same between our two firewalls.

Time to go to our second node (192.168.0.100.1) and select VPN->IPSec.


Now select the "Tunnels" tab (if not already there) and click on the "+" (add phase1 entry).


Here we'll need to add the public IP address of the other gateway (in our case, the second pfSense box), which in our case is 172.20.0.1 and a description for this.

 
Now, everything below this, we should make sure that it matches our other pfSense's box settings. Here's one catch though.

Remember I had changed the identifiers? In this case, "My identifier" is this pfSense's identifier and the peer identifier is the other ones. Which means that we should reverse the user distinguished names identifiers.

Everything else is made sure to be exactly the same as the other box.



After we've saved, we should now be on our VPN:IPSec page, Tunnels tab. Click on the "+" button below our newly created Phase 1 entry, right next to the "Show 0 Phase-2 entries":


A new entry below our Phase 1 entry should now appear with the fields "Mode/P2 Protocol/P2 Transforms/P2 Auth Methods". Let's click on the "+" sign right next to it.


Now, we need to connect our LAN interface with the LAN interface of the other pfSense box. In other words, I'll need to choose "LAN subnet" as the local network and "10.0.0.0/8" as the remote network.





Remember, everything should be the same on both sides.


Now, let's go to Firewall, Rules and click on the IPSec tab. Click on the "+".


Over here, we should allow any traffic we'd expect to see through the IPSec tunnel. For instance, if only SSH is expected then only TCP port 22 should be allowed. In reality though, most IPSec tunnels are set to allow all.

Now, we'll need to add a rule to our WAN interface so as to actually allow the other box to be able to connect in the first place. In real life situations, we might trust the incoming IPSec traffic, but allowing all from the firewall on the other side is something that is generally frowned upon (unless of course you own both boxes, in which case it's fine). From the pfSense docs:

Rules are automatically added to the WAN to allow the tunnel to connect, but if the option to disable automatic VPN rules is checked, then manual rules may be required. In that case, check the WAN rules to ensure that the traffic from the remote pfSense host is allowed. IPsec uses UDP port 500 and 4500, and protocol ESP (or AH if set that way). If there is trouble establishing a tunnel, check the firewall logs (Status > System Logs, Firewall tab), and if blocked packets are seen, add appropriate rules to allow that traffic. 

Obviously, I own both boxes so I'll just allow all from my other pfSense's IP.


Now, we need to make sure that we have enabled our IPSec service on both boxes:

pfSense 1 (10.0.0.1):



 pfSense 2 (192.168.100.1):


Let's see if it all works:


Brilliant!

IPSec VPN with pfSense using a Pre-Shared Key, Part 1

I will try to make this detailed and also try to create a relatively complicated network; something you'd run into in real life, not in ideal lab situations.

So here's the network:


As you can see, I have two pfSense boxes here:

One that has a WAN IP address of 172.20.0.1 and LAN IP address 10.0.0.1;
Another that that a WAN IP address of 172.30.0.1, LAN IP address 192.168.100.1 and OPT1 IP address 192.168.110.1.

What I want to do is create an IPSec VPN tunnel between the 10.0.0.0/8 and the 192.168.100.0/24 network. There are two ways we can do this: one is through using a Pre-Shared Key, and the other is through an RSA Signature.

We'll use a PSK for this example.

Let's go to our first node (10.0.0.1) and select VPN->IPSec.



Now select the "Tunnels" tab (if not already there) and click on the "+" (add phase1 entry).


Here we'll need to add the public IP address of the other gateway (in our case, the second pfSense box), which in our case is 172.30.0.1 and a description for this.


Next, we should leave the Authentication method to "Mutual PSK", unless we needed to use an RSA signature instead. We'll leave the Negotiation Mode to "aggresive" too. From the pfSense docs:

This is the type of authentication security that will be used. Unless extreme levels of security are called for, it is best to leave this as aggressive. It is indeed far faster and will insure that the VPN tunnel will rebuild itself quickly and probably won't time out an application if the tunnel was down when the resource on the other end was requested. (more about that under Lifetime)

In most cases, leaving the "My Identifier" and the "Peer Identifier" fields alone works, and there's no real need to change them either. From the pfSense docs:

My Identifier: Identifies this router to the far side. It is best left at My IP Address and the firewall will fill it in as needed. In some cases an FQDN or similar may be entered so that the value is constant. So long as both sides agree on the Identifier it will work.

Peer Identifier: Identifies the router on the far side. It is best left at Peer IP Address and the firewall will fill it in as needed. In some cases an FQDN or similar may be entered so that the value is constant. So long as both sides agree on the Identifier it will work.

Just for the sake of argument, I will change the identifiers. Remember, they both have to agree.

And next, we should type in our PSK.

I will also leave the "Policy Generation" and the "Proposal Checking" Fields alone. From the pfSense docs:

Policy Generation: Controls how the keying daemon creates SPD entries. Leave this at the default setting unless instructed otherwise.

Proposal Checking: Controls how strict (or loose) the Phase 1 parameters are verified during the negotiation. The default value is fine, but it is common to set Proposal Checking to Obey to avoid some issues with building a tunnel when the other side initiates. This can allow for lowered security than what has been chosen. 



Next, I will change the Encryption and Hash algorithms to more secure ones. I will leave everything else alone. From the pfSense docs:

NAT Traversal: Should nearly always be set to Disable unless it is certain that one firewall or the other has a WAN behind another NAT device.

DPD (Dead Peer Detection): This will help fully reestablish tunnel when the other side has a problem. The default values of 10 seconds and 5 retries are good.




And of course, save. Bear in mind that the settings on your pfSense boxes should be exactly the same, so anything you change here must be documented so that the same can be set on your other pfSense box.

After we've saved, we should now be on our VPN:IPSec page, Tunnels tab. Click on the "+" button below our newly created Phase 1 entry, right next to the "Show 0 Phase-2 entries":


A new entry below our Phase 1 entry should now appear with the fields "Mode/P2 Protocol/P2 Transforms/P2 Auth Methods". Let's click on the "+" sign right next to it.


Now, we need to connect our LAN interface with the LAN interface of the other pfSense box. In other words, I'll need to choose "LAN subnet" as the local network and "192.168.100.0/24" as the remote network.


I'm going to leave the "Protocol" option as is. From pfSense docs:

ESP is the de facto standard for what most VPN systems use as a transport protocol. This is the recommended setting. Note: The system should auto generate a firewall rule to allow ESP or AH to the endpoint of the VPN. If it does not, a firewall rule allowing ESP (or AH) traffic to the endpoint interface will need to be created. 

And I'll choose some stronger encryption and hash methods than the defaults. Remember, the other pfSense box should have the same ones.


Everything else I leave untouched. From the pfSense docs:

PFS Key Group: This works similarly to the DH group in phase 1. 1024 bit is a good setting, the default is off.

Lifetime: This is the lifetime the negotiated keys will be valid for. Do not set this to too high of a number (e.g. more than about a day: 86400) as doing so will give people more time to crack the key. Don't be over paranoid either; there is no need to set this to 20 minutes either. One hour (3600) is a good setting.
 

Automatically ping host: An address in the remote Phase 2 network to ping to keep the tunnel alive. See What should I ping for IPsec Keep Alive for details. 

And Save.


Now, let's go to Firewall, Rules and click on the IPSec tab. Click on the "+".



Over here, we should allow any traffic we'd expect to see through the IPSec tunnel. For instance, if only SSH is expected then only TCP port 22 should be allowed. In reality though, most IPSec tunnels are set to allow all.




Now, we'll need to add a rule to our WAN interface so as to actually allow the other box to be able to connect in the first place. In real life situations, we might trust the incoming IPSec traffic, but allowing all from the firewall on the other side is something that is generally frowned upon (unless of course you own both boxes, in which case it's fine). From the pfSense docs:

Rules are automatically added to the WAN to allow the tunnel to connect, but if the option to disable automatic VPN rules is checked, then manual rules may be required. In that case, check the WAN rules to ensure that the traffic from the remote pfSense host is allowed. IPsec uses UDP port 500 and 4500, and protocol ESP (or AH if set that way). If there is trouble establishing a tunnel, check the firewall logs (Status > System Logs, Firewall tab), and if blocked packets are seen, add appropriate rules to allow that traffic. 

Obviously, I own both boxes so I'll just allow all from my other pfSense's IP.



This post is getting rather long and graphics-heavy, so I'll continue with the pfSense 2 settings in my next post.


Monday, December 15, 2014

Windows System error 1219, multiple connections to a server or shared resource by the same user

Here's a funny one I ran into earlier.

So I try to mount a SAMBA share on a Windows system. I get this error:



Wait, are you telling me that I am restricted to having only one share subdirectory connected per PC? What happens if I need two? Now that's some crappy engineering right there Batman!

Oh, I know what this is. It's probably one of those Windows "features" and if I do the same thing from the command line, it'll just work, right? Right? Wrong.

F:\Documents and Settings\user>net use Q: \\192.168.0.254\DIR2 /USER:shareuser /PERSISTENT:NO
The password is invalid for \\192.168.0.254\DIR2.

Enter the password for 'shareuser' to connect to '192.168.0.254':
Multiple connections to a server or shared resource by the same user, using more than one user name, are not allowed. Disconnect all previous connections to the server or shared resource and try again..

Through some digging I managed to find out that this is by design. Windows will connect to a share once; if you try to connect again, it will block you. If you want to connect to a different subdirectory of your SAMBA server, you will need to disconnect from the one you are currently connected to.

Fret not though, for this is Windows; home of the crappiest, nastiest pieces of engineering ever came to existence.

Workaround? Just edit your %WINDIR%/system32/drivers/etc/hosts file and add some more entries that correspond to your share's IP address. For instance:

192.168.0.254 foo
192.168.0.254 bar
192.168.0.254 foobar
192.168.0.254 fubar

And now I can connect to my share using my new aliases:

F:\Documents and Settings\user>net use Q: \\fubar\DIR2 /USER:shareuser /PERSISTENT:NO
The password is invalid for \\fubar\DIR2.

Enter the password for 'shareuser' to connect to 'fubar':
The command completed successfully.

Oh Windows how I hate thee with all my passion.

Friday, December 5, 2014

Recover BMC administrator password on HP Proliant DL180 G6

This is from my notes. I don't have an HP Proliant DL180 G6 server in front of me, but I trust my notes and I think this description should be as accurate as it can. You'll need a PC with an internet connection, a USB drive and a jumper connector. You might also need a Linux Live CD (the instructions here assume that you have a RHEL-based Live CD).

a) Disconnect AC power from the system
b) Insert jumper on connector J27, D group pins 
c) Update BMC firmware using HP ROMPaq Firmware Upgrade for HP ProLiant G6 Lights-Out 100 Remote Management (For USB Key-Media).
    Detailed information is available at the following Web page: http://h20000.www2.hp.com/bizsupport/TechSupport/SoftwareDescription.jsp?lang=en&cc=us&prodTypeId=15351&prodSeriesId=3884343&swItem=MTX-bd27c5aa4f134285aa4825e143&prodNameId=3884344&swEnvOID=1005&swLang=13&taskId=135&mode=4&idx=1
d) Copy the impitool.exe binary from http://www.intel.com/design/servers/ipmi/ipmi_tool.htm onto the USB drive
e) Remove AC power and remove the jumper from J27, D group pins.
f) After BMC recovery is complete, boot from the USB key again and do:
ipmitool.exe 20 18 47 03 02 61 64 6d 69 6e 00 00 00 00 00 00 00 00 00 00 00
g) Remove USB drive and shutdown.
h) If the admininstrator user was "admin" you can now log in as admin/admin. If you still can't log in, proceed as follows (the following instructions are for a RHEL Live CD):
  1. Get a Linux Live CD
  2. yum update
  3. modprobe ipmi_msghandler
  4. modprobe ipmi_devintf
  5. modprobe ipmi_si
  6. yum search ipmi
  7. yum -y install ipmitool
  8. ipmitool user list
  9. note down the name of user id #3 and log in with that as a username and password admin.

Wednesday, December 3, 2014

PCI-DSS, nginx access logs, and credit card masking

PCI-DSS states that the card number must not be displayed in full—no more than the first six and the last four digits may be visible on a screen, a receipt, or on any other media used by the organization.

Sometimes your web server might leave access logs that contain this sort of information though. Here's an nginx access log snippet:

1.2.3.4 - - [02/Dec/2014:14:58:54 +0200] "GET /ccsite/?card_number=5100+1500+0000+0001&amount=10000&cvv=123&holder_name=N.Ame&expiration_month=1&expiration_year=2015&description=Something&_method=POST HTTP/1.1" 200 395 "https://partnersite.com/apps/ccapp?dothis.js" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:33.0) Gecko/20100101 Firefox/33.0"
1.2.3.4 - - [02/Dec/2014:16:07:46 +0200] "password=12345678&username=my_username" 200 416 "-" "Dalvik/1.6.0 (Linux; U; Android 4.3; C1905 Build/15.4.A.1.9)" 

Ouch, we shouldn't be writing stuff like this to our logs!

So what so we do?

Well, in this example I'm going to install the ngx_http_perl_module and use regular expressions to mask out the credit card and CVV.

First of all, let's see if we have Perl installed. Also, in order for Perl to recompile the modified modules during reconfiguration, it should be built with the -Dusemultiplicity=yes or -Dusethreads=yes parameters. Also, to make Perl leak less memory at run time, it should be built with the -Dusemymalloc=no parameter.

Let's check if we're good to go:

[root@webserver ~]# perl -V:usemultiplicity -V:usemymalloc
usemultiplicity='define';
usemymalloc='n';

Let's install some prerequisites to embed our Perl:

[root@webserver ~]# yum install perl-CPAN perl-ExtUtils-CBuilder perl-ExtUtils-MakeMaker perl-ExtUtils-Embed perl-devel 

Great. Now, let's back up our nginx configuration files and recompile it:

[root@webserver ~]# cp -rf /opt/nginx/conf/ /root/nginx_conf_bak

OK, now we should configure nginx as always, but with the addition of --with-http_perl_module. So in my case it is:

[root@webserver nginx-1.6.2]# ./configure --add-module=../naxsi/naxsi-master/naxsi_src/ --prefix=/opt/nginx --error-log-path=/var/log/nginx/nginx_error.log --http-log-path=/var/log/nginx/nginx_access.log --user=www-data --group=www-data --with-http_addition_module --with-http_geoip_module --with-http_gzip_static_module --with-http_stub_status_module --with-http_realip_module --without-mail_pop3_module --without-mail_smtp_module --without-mail_imap_module --without-http_memcached_module --without-http_ssi_module --without-http_uwsgi_module --without-http_scgi_module --with-http_perl_module

If you, like me, like to change the nginx headers and version to spoof their make to something else, say, IIS, then you should also make sure that the #define NGINX_VERSION in /src/core/nginx.h variable consists of numbers and dots only, for example not 3.4.1 (Unix), but 3.4.1, otherwise you'll encounter compilation problems.

Let's proceed to the installation of our new nginx binary that will now support the nginx perl module:

[root@webserver ~]# make
[root@webserver ~]# make install

Let's restore our old config:
[root@webserver ~]# cd /opt/nginx/conf/
[root@webserver ~]# rm -rf *
[root@webserver conf]# cp -rf /root/nginx_conf_bak/* .
[root@webserver conf]# chown -R www-data:www-data *

Time to do some substitutions using regular expressions on our logs. This needs to be in our http block:

[root@webserver ~]# vi nginx.conf
....
http {
perl_set $anonymize_data '
                sub {
                        my $r = shift;
                        my $req =  $r->request_body;
                        $req =~ s/\?card_number\=(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|6(?:011|5[0-9]{2})[0-9]{12}|(?:2131|1800|35\d{3})\d{11})/\?card_number\=XXXX-XXXX-XXXX-XXXX/g;
                        $req =~ s/\&card_number\=(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13}|3(?:0[0-5]|[68][0-9])[0-9]{11}|6(?:011|5[0-9]{2})[0-9]{12}|(?:2131|1800|35\d{3})\d{11})/\&card_number\=XXXX-XXXX-XXXX-XXXX/g;
                        $req =~ s/\&cvv\=\d\d\d/\?cvv\=XXX/g;
                        $req =~ s/\&cvv\=\d\d\d/\&cvv\=XXX/g;
                        $req =~ s/password\=\w+/password\=XXXXXXXX/g;
                        return $req;
                } ';

Notice that I cover both scenarios; if the card_number arrives first (?card_number); or as a secondary/tertiary variable (&card_number). I do the same for the CVV. Replace "card_number" and "cvv" with whatever variable names your requests come as.

Finally, you will need to replace your "$request" with "$anonymize_data" in your log_format, like so:

log_format main $remote_addr - $remote_user [$time_local] "$anonymize_data" $status $body_bytes_sent "$http_referer" "$http_user_agent";

And now the card number, the CVV and the user's password get masked.

1.2.3.4 - - [02/Dec/2014:14:58:54 +0200] "GET /ccsite/?card_number=XXXX-XXXX-XXXX-XXXX&amount=10000&cvv=XXX&holder_name=N.Ame&expiration_month=1&expiration_year=2015&description=Something&_method=POST HTTP/1.1" 200 395 "https://partnersite.com/apps/ccapp?dothis.js" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:33.0) Gecko/20100101 Firefox/33.0"
1.2.3.4 - - [02/Dec/2014:16:07:46 +0200] "password=XXXXXXXX&username=my_username" 200 416 "-" "Dalvik/1.6.0 (Linux; U; Android 4.3; C1905 Build/15.4.A.1.9)"